|
1473
|
32
|
42
|
2026-04-11T17:31:23.291659+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928683291_m1.jpg...
|
Code
|
report.py — screenpipe [SSH: 100.73.206.126]
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
"""Structured work report — no LLM needed."""
from datetime import date, datetime, timezone, timedelta
from typing import Any
from urllib.parse import urlparse
from db import get_conn, date_range, today as _today
BREAK_THRESHOLD_MIN = 10 # gap > 10 min = break
WORK_APPS = {
"code": {"iTerm2", "Terminal", "Code", "Visual Studio Code", "PyCharm", "Xcode", "Cursor"},
"browser": {"Firefox", "Safari", "Chrome", "Arc", "Dia"},
"comms": {"Slack", "Teams", "Discord", "Zoom", "Telegram", "WhatsApp"},
"docs": {"Word", "Pages", "Notion", "Obsidian", "Bear", "Typora"},
"ai": {"Claude", "ChatGPT"},
"design": {"Figma", "Sketch", "Photoshop", "Illustrator"},
"media": {"QuickTime Player", "VLC", "Spotify"},
"system": {"Finder", "Activity Monitor", "System Preferences", "System Settings",
"Raycast", "Control Centre", "UserNotificationCenter", "NetAuthAgent"},
}
def _categorize(app_name: str) -> str:
for cat, apps in WORK_APPS.items():
if app_name in apps:
return cat
return "other"
def _extract_domain(url: str) -> str:
try:
h = urlparse(url).netloc
return h.removeprefix("www.") if h else url[:40]
except Exception:
return url[:40]
def _parse_ts(ts: str) -> datetime:
ts = ts.replace("+00:00", "+00:00") # already UTC
try:
return datetime.fromisoformat(ts)
except ValueError:
return datetime.fromisoformat(ts[:26] + "+00:00")
def session_breakdown(for_date: date | None = None, break_min: int = BREAK_THRESHOLD_MIN) -> dict[str, Any]:
"""
Detect work sessions and breaks from frame timestamps.
Returns sessions, breaks, and totals.
"""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"SELECT timestamp FROM frames WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp",
(start, end),
).fetchall()
if not rows:
return {"sessions": [], "breaks": [], "total_active_min": 0, "total_break_min": 0, "date": str(d)}
timestamps = [_parse_ts(r["timestamp"]) for r in rows]
threshold = timedelta(minutes=break_min)
sessions: list[dict] = []
breaks: list[dict] = []
sess_start = timestamps[0]
prev = timestamps[0]
for ts in timestamps[1:]:
gap = ts - prev
if gap > threshold:
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
breaks.append({
"start": prev.isoformat(),
"end": ts.isoformat(),
"duration_min": round(gap.total_seconds() / 60, 1),
})
sess_start = ts
prev = ts
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
total_active = sum(s["duration_min"] for s in sessions)
total_break = sum(b["duration_min"] for b in breaks)
total_span = (timestamps[-1] - timestamps[0]).total_seconds() / 60
return {
"date": str(d),
"day_start": timestamps[0].isoformat(),
"day_end": timestamps[-1].isoformat(),
"total_span_min": round(total_span, 1),
"total_active_min": round(total_active, 1),
"total_break_min": round(total_break, 1),
"sessions": sessions,
"breaks": breaks,
}
def domain_breakdown(for_date: date | None = None) -> list[dict[str, Any]]:
"""Browser time grouped by domain."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT browser_url, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ?
AND browser_url IS NOT NULL AND browser_url != ''
GROUP BY browser_url
ORDER BY frames DESC
""",
(start, end),
).fetchall()
# Group by domain
domains: dict[str, dict] = {}
for r in rows:
domain = _extract_domain(r["browser_url"])
if domain not in domains:
domains[domain] = {"domain": domain, "minutes": 0, "urls": []}
domains[domain]["minutes"] = round(domains[domain]["minutes"] + r["minutes"], 1)
domains[domain]["urls"].append({"url": r["browser_url"], "minutes": r["minutes"]})
return sorted(domains.values(), key=lambda x: x["minutes"], reverse=True)
def slack_activity(for_date: date | None = None) -> dict[str, Any]:
"""Slack-specific: time, channels, message activity."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
# Time in Slack
time_row = conn.execute(
"""
SELECT COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
# Channels from window names
channels = conn.execute(
"""
SELECT window_name, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
AND window_name IS NOT NULL AND window_name != ''
GROUP BY window_name ORDER BY frames DESC LIMIT 20
""",
(start, end),
).fetchall()
# Keystrokes in Slack (proxy for messages sent)
events = conn.execute(
"""
SELECT
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END) as keystrokes,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END) as clicks
FROM ui_events
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
return {
"minutes": time_row["minutes"] if time_row else 0,
"frames": time_row["frames"] if time_row else 0,
"keystrokes": events["keystrokes"] if events else 0,
"clicks": events["clicks"] if events else 0,
"channels": [dict(r) for r in channels],
}
def work_report(for_date: date | None = None) -> dict[str, Any]:
"""Full structured work report — no AI needed."""
from consumers.activity import app_time, ui_event_summary
d = for_date or _today()
apps = app_time(d)
# Annotate with category
categorized: dict[str, list] = {}
for a in apps:
cat = _categorize(a["app_name"])
categorized.setdefault(cat, []).append(a)
return {
"date": str(d),
"sessions": session_breakdown(d),
"apps_by_category": categorized,
"domains": domain_breakdown(d),
"slack": slack_activity(d),
"ui_events": ui_event_summary(d),
}
"""Structured work report — no LLM needed."""
from datetime import date, datetime, timezone, timedelta
from typing import Any
from urllib.parse import urlparse
from db import get_conn, date_range, today as _today
BREAK_THRESHOLD_MIN = 10 # gap > 10 min = break
WORK_APPS = {
"code": {"iTerm2", "Terminal", "Code", "Visual Studio Code", "PyCharm", "Xcode", "Cursor"},
"browser": {"Firefox", "Safari", "Chrome", "Arc", "Dia"},
"comms": {"Slack", "Teams", "Discord", "Zoom", "Telegram", "WhatsApp"},
"docs": {"Word", "Pages", "Notion", "Obsidian", "Bear", "Typora"},
"ai": {"Claude", "ChatGPT"},
"design": {"Figma", "Sketch", "Photoshop", "Illustrator"},
"media": {"QuickTime Player", "VLC", "Spotify"},
"system": {"Finder", "Activity Monitor", "System Preferences", "System Settings",
"Raycast", "Control Centre", "UserNotificationCenter", "NetAuthAgent"},
}
def _categorize(app_name: str) -> str:
for cat, apps in WORK_APPS.items():
if app_name in apps:
return cat
return "other"
def _extract_domain(url: str) -> str:
try:
h = urlparse(url).netloc
return h.removeprefix("www.") if h else url[:40]
except Exception:
return url[:40]
def _parse_ts(ts: str) -> datetime:
ts = ts.replace("+00:00", "+00:00") # already UTC
try:
return datetime.fromisoformat(ts)
except ValueError:
return datetime.fromisoformat(ts[:26] + "+00:00")
def session_breakdown(for_date: date | None = None, break_min: int = BREAK_THRESHOLD_MIN) -> dict[str, Any]:
"""
Detect work sessions and breaks from frame timestamps.
Returns sessions, breaks, and totals.
"""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"SELECT timestamp FROM frames WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp",
(start, end),
).fetchall()
if not rows:
return {"sessions": [], "breaks": [], "total_active_min": 0, "total_break_min": 0, "date": str(d)}
timestamps = [_parse_ts(r["timestamp"]) for r in rows]
threshold = timedelta(minutes=break_min)
sessions: list[dict] = []
breaks: list[dict] = []
sess_start = timestamps[0]
prev = timestamps[0]
for ts in timestamps[1:]:
gap = ts - prev
if gap > threshold:
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
breaks.append({
"start": prev.isoformat(),
"end": ts.isoformat(),
"duration_min": round(gap.total_seconds() / 60, 1),
})
sess_start = ts
prev = ts
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
total_active = sum(s["duration_min"] for s in sessions)
total_break = sum(b["duration_min"] for b in breaks)
total_span = (timestamps[-1] - timestamps[0]).total_seconds() / 60
return {
"date": str(d),
"day_start": timestamps[0].isoformat(),
"day_end": timestamps[-1].isoformat(),
"total_span_min": round(total_span, 1),
"total_active_min": round(total_active, 1),
"total_break_min": round(total_break, 1),
"sessions": sessions,
"breaks": breaks,
}
def domain_breakdown(for_date: date | None = None) -> list[dict[str, Any]]:
"""Browser time grouped by domain."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT browser_url, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ?
AND browser_url IS NOT NULL AND browser_url != ''
GROUP BY browser_url
ORDER BY frames DESC
""",
(start, end),
).fetchall()
# Group by domain
domains: dict[str, dict] = {}
for r in rows:
domain = _extract_domain(r["browser_url"])
if domain not in domains:
domains[domain] = {"domain": domain, "minutes": 0, "urls": []}
domains[domain]["minutes"] = round(domains[domain]["minutes"] + r["minutes"], 1)
domains[domain]["urls"].append({"url": r["browser_url"], "minutes": r["minutes"]})
return sorted(domains.values(), key=lambda x: x["minutes"], reverse=True)
def slack_activity(for_date: date | None = None) -> dict[str, Any]:
"""Slack-specific: time, channels, message activity."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
# Time in Slack
time_row = conn.execute(
"""
SELECT COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
# Channels from window names
channels = conn.execute(
"""
SELECT window_name, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
AND window_name IS NOT NULL AND window_name != ''
GROUP BY window_name ORDER BY frames DESC LIMIT 20
""",
(start, end),
).fetchall()
# Keystrokes in Slack (proxy for messages sent)
events = conn.execute(
"""
SELECT
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END) as keystrokes,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END) as clicks
FROM ui_events
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
return {
"minutes": time_row["minutes"] if time_row else 0,
"frames": time_row["frames"] if time_row else 0,
"keystrokes": events["keystrokes"] if events else 0,
"clicks": events["clicks"] if events else 0,
"channels": [dict(r) for r in channels],
}
def work_report(for_date: date | None = None) -> dict[str, Any]:
"""Full structured work report — no AI needed."""
from consumers.activity import app_time, ui_event_summary
d = for_date or _today()
apps = app_time(d)
# Annotate with category
categorized: dict[str, list] = {}
for a in apps:
cat = _categorize(a["app_name"])
categorized.setdefault(cat, []).append(a)
return {
"date": str(d),
"sessions": session_breakdown(d),
"apps_by_category": categorized,
"domains": domain_breakdown(d),
"slack": slack_activity(d),
"ui_events": ui_event_summary(d),
}
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Python
Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions
LF
UTF-8
Spaces: 4
Ln 17, Col 87
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"__init__.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"activity.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"audio.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"ocr.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"report.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"summary.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXTextArea","text":"\"\"\"Structured work report — no LLM needed.\"\"\"\nfrom datetime import date, datetime, timezone, timedelta\nfrom typing import Any\nfrom urllib.parse import urlparse\n\nfrom db import get_conn, date_range, today as _today\n\nBREAK_THRESHOLD_MIN = 10 # gap > 10 min = break\nWORK_APPS = {\n \"code\": {\"iTerm2\", \"Terminal\", \"Code\", \"Visual Studio Code\", \"PyCharm\", \"Xcode\", \"Cursor\"},\n \"browser\": {\"Firefox\", \"Safari\", \"Chrome\", \"Arc\", \"Dia\"},\n \"comms\": {\"Slack\", \"Teams\", \"Discord\", \"Zoom\", \"Telegram\", \"WhatsApp\"},\n \"docs\": {\"Word\", \"Pages\", \"Notion\", \"Obsidian\", \"Bear\", \"Typora\"},\n \"ai\": {\"Claude\", \"ChatGPT\"},\n \"design\": {\"Figma\", \"Sketch\", \"Photoshop\", \"Illustrator\"},\n \"media\": {\"QuickTime Player\", \"VLC\", \"Spotify\"},\n \"system\": {\"Finder\", \"Activity Monitor\", \"System Preferences\", \"System Settings\",\n \"Raycast\", \"Control Centre\", \"UserNotificationCenter\", \"NetAuthAgent\"},\n}\n\ndef _categorize(app_name: str) -> str:\n for cat, apps in WORK_APPS.items():\n if app_name in apps:\n return cat\n return \"other\"\n\ndef _extract_domain(url: str) -> str:\n try:\n h = urlparse(url).netloc\n return h.removeprefix(\"www.\") if h else url[:40]\n except Exception:\n return url[:40]\n\ndef _parse_ts(ts: str) -> datetime:\n ts = ts.replace(\"+00:00\", \"+00:00\") # already UTC\n try:\n return datetime.fromisoformat(ts)\n except ValueError:\n return datetime.fromisoformat(ts[:26] + \"+00:00\")\n\n\ndef session_breakdown(for_date: date | None = None, break_min: int = BREAK_THRESHOLD_MIN) -> dict[str, Any]:\n \"\"\"\n Detect work sessions and breaks from frame timestamps.\n Returns sessions, breaks, and totals.\n \"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"SELECT timestamp FROM frames WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp\",\n (start, end),\n ).fetchall()\n\n if not rows:\n return {\"sessions\": [], \"breaks\": [], \"total_active_min\": 0, \"total_break_min\": 0, \"date\": str(d)}\n\n timestamps = [_parse_ts(r[\"timestamp\"]) for r in rows]\n threshold = timedelta(minutes=break_min)\n\n sessions: list[dict] = []\n breaks: list[dict] = []\n sess_start = timestamps[0]\n prev = timestamps[0]\n\n for ts in timestamps[1:]:\n gap = ts - prev\n if gap > threshold:\n dur = (prev - sess_start).total_seconds() / 60\n sessions.append({\n \"start\": sess_start.isoformat(),\n \"end\": prev.isoformat(),\n \"duration_min\": round(dur, 1),\n })\n breaks.append({\n \"start\": prev.isoformat(),\n \"end\": ts.isoformat(),\n \"duration_min\": round(gap.total_seconds() / 60, 1),\n })\n sess_start = ts\n prev = ts\n\n dur = (prev - sess_start).total_seconds() / 60\n sessions.append({\n \"start\": sess_start.isoformat(),\n \"end\": prev.isoformat(),\n \"duration_min\": round(dur, 1),\n })\n\n total_active = sum(s[\"duration_min\"] for s in sessions)\n total_break = sum(b[\"duration_min\"] for b in breaks)\n total_span = (timestamps[-1] - timestamps[0]).total_seconds() / 60\n\n return {\n \"date\": str(d),\n \"day_start\": timestamps[0].isoformat(),\n \"day_end\": timestamps[-1].isoformat(),\n \"total_span_min\": round(total_span, 1),\n \"total_active_min\": round(total_active, 1),\n \"total_break_min\": round(total_break, 1),\n \"sessions\": sessions,\n \"breaks\": breaks,\n }\n\n\ndef domain_breakdown(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Browser time grouped by domain.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT browser_url, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ?\n AND browser_url IS NOT NULL AND browser_url != ''\n GROUP BY browser_url\n ORDER BY frames DESC\n \"\"\",\n (start, end),\n ).fetchall()\n\n # Group by domain\n domains: dict[str, dict] = {}\n for r in rows:\n domain = _extract_domain(r[\"browser_url\"])\n if domain not in domains:\n domains[domain] = {\"domain\": domain, \"minutes\": 0, \"urls\": []}\n domains[domain][\"minutes\"] = round(domains[domain][\"minutes\"] + r[\"minutes\"], 1)\n domains[domain][\"urls\"].append({\"url\": r[\"browser_url\"], \"minutes\": r[\"minutes\"]})\n\n return sorted(domains.values(), key=lambda x: x[\"minutes\"], reverse=True)\n\n\ndef slack_activity(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Slack-specific: time, channels, message activity.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n # Time in Slack\n time_row = conn.execute(\n \"\"\"\n SELECT COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n \"\"\",\n (start, end),\n ).fetchone()\n\n # Channels from window names\n channels = conn.execute(\n \"\"\"\n SELECT window_name, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n AND window_name IS NOT NULL AND window_name != ''\n GROUP BY window_name ORDER BY frames DESC LIMIT 20\n \"\"\",\n (start, end),\n ).fetchall()\n\n # Keystrokes in Slack (proxy for messages sent)\n events = conn.execute(\n \"\"\"\n SELECT\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END) as keystrokes,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END) as clicks\n FROM ui_events\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n \"\"\",\n (start, end),\n ).fetchone()\n\n return {\n \"minutes\": time_row[\"minutes\"] if time_row else 0,\n \"frames\": time_row[\"frames\"] if time_row else 0,\n \"keystrokes\": events[\"keystrokes\"] if events else 0,\n \"clicks\": events[\"clicks\"] if events else 0,\n \"channels\": [dict(r) for r in channels],\n }\n\n\ndef work_report(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Full structured work report — no AI needed.\"\"\"\n from consumers.activity import app_time, ui_event_summary\n\n d = for_date or _today()\n\n apps = app_time(d)\n # Annotate with category\n categorized: dict[str, list] = {}\n for a in apps:\n cat = _categorize(a[\"app_name\"])\n categorized.setdefault(cat, []).append(a)\n\n return {\n \"date\": str(d),\n \"sessions\": session_breakdown(d),\n \"apps_by_category\": categorized,\n \"domains\": domain_breakdown(d),\n \"slack\": slack_activity(d),\n \"ui_events\": ui_event_summary(d),\n }","depth":28,"value":"\"\"\"Structured work report — no LLM needed.\"\"\"\nfrom datetime import date, datetime, timezone, timedelta\nfrom typing import Any\nfrom urllib.parse import urlparse\n\nfrom db import get_conn, date_range, today as _today\n\nBREAK_THRESHOLD_MIN = 10 # gap > 10 min = break\nWORK_APPS = {\n \"code\": {\"iTerm2\", \"Terminal\", \"Code\", \"Visual Studio Code\", \"PyCharm\", \"Xcode\", \"Cursor\"},\n \"browser\": {\"Firefox\", \"Safari\", \"Chrome\", \"Arc\", \"Dia\"},\n \"comms\": {\"Slack\", \"Teams\", \"Discord\", \"Zoom\", \"Telegram\", \"WhatsApp\"},\n \"docs\": {\"Word\", \"Pages\", \"Notion\", \"Obsidian\", \"Bear\", \"Typora\"},\n \"ai\": {\"Claude\", \"ChatGPT\"},\n \"design\": {\"Figma\", \"Sketch\", \"Photoshop\", \"Illustrator\"},\n \"media\": {\"QuickTime Player\", \"VLC\", \"Spotify\"},\n \"system\": {\"Finder\", \"Activity Monitor\", \"System Preferences\", \"System Settings\",\n \"Raycast\", \"Control Centre\", \"UserNotificationCenter\", \"NetAuthAgent\"},\n}\n\ndef _categorize(app_name: str) -> str:\n for cat, apps in WORK_APPS.items():\n if app_name in apps:\n return cat\n return \"other\"\n\ndef _extract_domain(url: str) -> str:\n try:\n h = urlparse(url).netloc\n return h.removeprefix(\"www.\") if h else url[:40]\n except Exception:\n return url[:40]\n\ndef _parse_ts(ts: str) -> datetime:\n ts = ts.replace(\"+00:00\", \"+00:00\") # already UTC\n try:\n return datetime.fromisoformat(ts)\n except ValueError:\n return datetime.fromisoformat(ts[:26] + \"+00:00\")\n\n\ndef session_breakdown(for_date: date | None = None, break_min: int = BREAK_THRESHOLD_MIN) -> dict[str, Any]:\n \"\"\"\n Detect work sessions and breaks from frame timestamps.\n Returns sessions, breaks, and totals.\n \"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"SELECT timestamp FROM frames WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp\",\n (start, end),\n ).fetchall()\n\n if not rows:\n return {\"sessions\": [], \"breaks\": [], \"total_active_min\": 0, \"total_break_min\": 0, \"date\": str(d)}\n\n timestamps = [_parse_ts(r[\"timestamp\"]) for r in rows]\n threshold = timedelta(minutes=break_min)\n\n sessions: list[dict] = []\n breaks: list[dict] = []\n sess_start = timestamps[0]\n prev = timestamps[0]\n\n for ts in timestamps[1:]:\n gap = ts - prev\n if gap > threshold:\n dur = (prev - sess_start).total_seconds() / 60\n sessions.append({\n \"start\": sess_start.isoformat(),\n \"end\": prev.isoformat(),\n \"duration_min\": round(dur, 1),\n })\n breaks.append({\n \"start\": prev.isoformat(),\n \"end\": ts.isoformat(),\n \"duration_min\": round(gap.total_seconds() / 60, 1),\n })\n sess_start = ts\n prev = ts\n\n dur = (prev - sess_start).total_seconds() / 60\n sessions.append({\n \"start\": sess_start.isoformat(),\n \"end\": prev.isoformat(),\n \"duration_min\": round(dur, 1),\n })\n\n total_active = sum(s[\"duration_min\"] for s in sessions)\n total_break = sum(b[\"duration_min\"] for b in breaks)\n total_span = (timestamps[-1] - timestamps[0]).total_seconds() / 60\n\n return {\n \"date\": str(d),\n \"day_start\": timestamps[0].isoformat(),\n \"day_end\": timestamps[-1].isoformat(),\n \"total_span_min\": round(total_span, 1),\n \"total_active_min\": round(total_active, 1),\n \"total_break_min\": round(total_break, 1),\n \"sessions\": sessions,\n \"breaks\": breaks,\n }\n\n\ndef domain_breakdown(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Browser time grouped by domain.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT browser_url, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ?\n AND browser_url IS NOT NULL AND browser_url != ''\n GROUP BY browser_url\n ORDER BY frames DESC\n \"\"\",\n (start, end),\n ).fetchall()\n\n # Group by domain\n domains: dict[str, dict] = {}\n for r in rows:\n domain = _extract_domain(r[\"browser_url\"])\n if domain not in domains:\n domains[domain] = {\"domain\": domain, \"minutes\": 0, \"urls\": []}\n domains[domain][\"minutes\"] = round(domains[domain][\"minutes\"] + r[\"minutes\"], 1)\n domains[domain][\"urls\"].append({\"url\": r[\"browser_url\"], \"minutes\": r[\"minutes\"]})\n\n return sorted(domains.values(), key=lambda x: x[\"minutes\"], reverse=True)\n\n\ndef slack_activity(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Slack-specific: time, channels, message activity.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n # Time in Slack\n time_row = conn.execute(\n \"\"\"\n SELECT COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n \"\"\",\n (start, end),\n ).fetchone()\n\n # Channels from window names\n channels = conn.execute(\n \"\"\"\n SELECT window_name, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n AND window_name IS NOT NULL AND window_name != ''\n GROUP BY window_name ORDER BY frames DESC LIMIT 20\n \"\"\",\n (start, end),\n ).fetchall()\n\n # Keystrokes in Slack (proxy for messages sent)\n events = conn.execute(\n \"\"\"\n SELECT\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END) as keystrokes,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END) as clicks\n FROM ui_events\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n \"\"\",\n (start, end),\n ).fetchone()\n\n return {\n \"minutes\": time_row[\"minutes\"] if time_row else 0,\n \"frames\": time_row[\"frames\"] if time_row else 0,\n \"keystrokes\": events[\"keystrokes\"] if events else 0,\n \"clicks\": events[\"clicks\"] if events else 0,\n \"channels\": [dict(r) for r in channels],\n }\n\n\ndef work_report(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Full structured work report — no AI needed.\"\"\"\n from consumers.activity import app_time, ui_event_summary\n\n d = for_date or _today()\n\n apps = app_time(d)\n # Annotate with category\n categorized: dict[str, list] = {}\n for a in apps:\n cat = _categorize(a[\"app_name\"])\n categorized.setdefault(cat, []).append(a)\n\n return {\n \"date\": str(d),\n \"sessions\": session_breakdown(d),\n \"apps_by_category\": categorized,\n \"domains\": domain_breakdown(d),\n \"slack\": slack_activity(d),\n \"ui_events\": ui_event_summary(d),\n }","role_description":"editor","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"\"\"\"Structured work report — no LLM needed.\"\"\"\nfrom datetime import date, datetime, timezone, timedelta\nfrom typing import Any\nfrom urllib.parse import urlparse\n\nfrom db import get_conn, date_range, today as _today\n\nBREAK_THRESHOLD_MIN = 10 # gap > 10 min = break\nWORK_APPS = {\n \"code\": {\"iTerm2\", \"Terminal\", \"Code\", \"Visual Studio Code\", \"PyCharm\", \"Xcode\", \"Cursor\"},\n \"browser\": {\"Firefox\", \"Safari\", \"Chrome\", \"Arc\", \"Dia\"},\n \"comms\": {\"Slack\", \"Teams\", \"Discord\", \"Zoom\", \"Telegram\", \"WhatsApp\"},\n \"docs\": {\"Word\", \"Pages\", \"Notion\", \"Obsidian\", \"Bear\", \"Typora\"},\n \"ai\": {\"Claude\", \"ChatGPT\"},\n \"design\": {\"Figma\", \"Sketch\", \"Photoshop\", \"Illustrator\"},\n \"media\": {\"QuickTime Player\", \"VLC\", \"Spotify\"},\n \"system\": {\"Finder\", \"Activity Monitor\", \"System Preferences\", \"System Settings\",\n \"Raycast\", \"Control Centre\", \"UserNotificationCenter\", \"NetAuthAgent\"},\n}\n\ndef _categorize(app_name: str) -> str:\n for cat, apps in WORK_APPS.items():\n if app_name in apps:\n return cat\n return \"other\"\n\ndef _extract_domain(url: str) -> str:\n try:\n h = urlparse(url).netloc\n return h.removeprefix(\"www.\") if h else url[:40]\n except Exception:\n return url[:40]\n\ndef _parse_ts(ts: str) -> datetime:\n ts = ts.replace(\"+00:00\", \"+00:00\") # already UTC\n try:\n return datetime.fromisoformat(ts)\n except ValueError:\n return datetime.fromisoformat(ts[:26] + \"+00:00\")\n\n\ndef session_breakdown(for_date: date | None = None, break_min: int = BREAK_THRESHOLD_MIN) -> dict[str, Any]:\n \"\"\"\n Detect work sessions and breaks from frame timestamps.\n Returns sessions, breaks, and totals.\n \"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"SELECT timestamp FROM frames WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp\",\n (start, end),\n ).fetchall()\n\n if not rows:\n return {\"sessions\": [], \"breaks\": [], \"total_active_min\": 0, \"total_break_min\": 0, \"date\": str(d)}\n\n timestamps = [_parse_ts(r[\"timestamp\"]) for r in rows]\n threshold = timedelta(minutes=break_min)\n\n sessions: list[dict] = []\n breaks: list[dict] = []\n sess_start = timestamps[0]\n prev = timestamps[0]\n\n for ts in timestamps[1:]:\n gap = ts - prev\n if gap > threshold:\n dur = (prev - sess_start).total_seconds() / 60\n sessions.append({\n \"start\": sess_start.isoformat(),\n \"end\": prev.isoformat(),\n \"duration_min\": round(dur, 1),\n })\n breaks.append({\n \"start\": prev.isoformat(),\n \"end\": ts.isoformat(),\n \"duration_min\": round(gap.total_seconds() / 60, 1),\n })\n sess_start = ts\n prev = ts\n\n dur = (prev - sess_start).total_seconds() / 60\n sessions.append({\n \"start\": sess_start.isoformat(),\n \"end\": prev.isoformat(),\n \"duration_min\": round(dur, 1),\n })\n\n total_active = sum(s[\"duration_min\"] for s in sessions)\n total_break = sum(b[\"duration_min\"] for b in breaks)\n total_span = (timestamps[-1] - timestamps[0]).total_seconds() / 60\n\n return {\n \"date\": str(d),\n \"day_start\": timestamps[0].isoformat(),\n \"day_end\": timestamps[-1].isoformat(),\n \"total_span_min\": round(total_span, 1),\n \"total_active_min\": round(total_active, 1),\n \"total_break_min\": round(total_break, 1),\n \"sessions\": sessions,\n \"breaks\": breaks,\n }\n\n\ndef domain_breakdown(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Browser time grouped by domain.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT browser_url, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ?\n AND browser_url IS NOT NULL AND browser_url != ''\n GROUP BY browser_url\n ORDER BY frames DESC\n \"\"\",\n (start, end),\n ).fetchall()\n\n # Group by domain\n domains: dict[str, dict] = {}\n for r in rows:\n domain = _extract_domain(r[\"browser_url\"])\n if domain not in domains:\n domains[domain] = {\"domain\": domain, \"minutes\": 0, \"urls\": []}\n domains[domain][\"minutes\"] = round(domains[domain][\"minutes\"] + r[\"minutes\"], 1)\n domains[domain][\"urls\"].append({\"url\": r[\"browser_url\"], \"minutes\": r[\"minutes\"]})\n\n return sorted(domains.values(), key=lambda x: x[\"minutes\"], reverse=True)\n\n\ndef slack_activity(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Slack-specific: time, channels, message activity.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n # Time in Slack\n time_row = conn.execute(\n \"\"\"\n SELECT COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n \"\"\",\n (start, end),\n ).fetchone()\n\n # Channels from window names\n channels = conn.execute(\n \"\"\"\n SELECT window_name, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes\n FROM frames\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n AND window_name IS NOT NULL AND window_name != ''\n GROUP BY window_name ORDER BY frames DESC LIMIT 20\n \"\"\",\n (start, end),\n ).fetchall()\n\n # Keystrokes in Slack (proxy for messages sent)\n events = conn.execute(\n \"\"\"\n SELECT\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END) as keystrokes,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END) as clicks\n FROM ui_events\n WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'\n \"\"\",\n (start, end),\n ).fetchone()\n\n return {\n \"minutes\": time_row[\"minutes\"] if time_row else 0,\n \"frames\": time_row[\"frames\"] if time_row else 0,\n \"keystrokes\": events[\"keystrokes\"] if events else 0,\n \"clicks\": events[\"clicks\"] if events else 0,\n \"channels\": [dict(r) for r in channels],\n }\n\n\ndef work_report(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Full structured work report — no AI needed.\"\"\"\n from consumers.activity import app_time, ui_event_summary\n\n d = for_date or _today()\n\n apps = app_time(d)\n # Annotate with category\n categorized: dict[str, list] = {}\n for a in apps:\n cat = _categorize(a[\"app_name\"])\n categorized.setdefault(cat, []).append(a)\n\n return {\n \"date\": str(d),\n \"sessions\": session_breakdown(d),\n \"apps_by_category\": categorized,\n \"domains\": domain_breakdown(d),\n \"slack\": slack_activity(d),\n \"ui_events\": ui_event_summary(d),\n }","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Python","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"LF","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"UTF-8","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Spaces: 4","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Ln 17, Col 87","depth":16,"bounds":{"left":1.0,"top":0.0,"width":-0.0645833,"height":0.024444444},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"bounds":{"left":0.9513889,"top":0.0,"width":0.048611104,"height":0.024444444},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"expanded","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n[rerun: b4]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bootstrap pip then install packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10","depth":24,"role_description":"text"}]...
|
-6374743697256088562
|
-8717864774537180650
|
app_switch
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
"""Structured work report — no LLM needed."""
from datetime import date, datetime, timezone, timedelta
from typing import Any
from urllib.parse import urlparse
from db import get_conn, date_range, today as _today
BREAK_THRESHOLD_MIN = 10 # gap > 10 min = break
WORK_APPS = {
"code": {"iTerm2", "Terminal", "Code", "Visual Studio Code", "PyCharm", "Xcode", "Cursor"},
"browser": {"Firefox", "Safari", "Chrome", "Arc", "Dia"},
"comms": {"Slack", "Teams", "Discord", "Zoom", "Telegram", "WhatsApp"},
"docs": {"Word", "Pages", "Notion", "Obsidian", "Bear", "Typora"},
"ai": {"Claude", "ChatGPT"},
"design": {"Figma", "Sketch", "Photoshop", "Illustrator"},
"media": {"QuickTime Player", "VLC", "Spotify"},
"system": {"Finder", "Activity Monitor", "System Preferences", "System Settings",
"Raycast", "Control Centre", "UserNotificationCenter", "NetAuthAgent"},
}
def _categorize(app_name: str) -> str:
for cat, apps in WORK_APPS.items():
if app_name in apps:
return cat
return "other"
def _extract_domain(url: str) -> str:
try:
h = urlparse(url).netloc
return h.removeprefix("www.") if h else url[:40]
except Exception:
return url[:40]
def _parse_ts(ts: str) -> datetime:
ts = ts.replace("+00:00", "+00:00") # already UTC
try:
return datetime.fromisoformat(ts)
except ValueError:
return datetime.fromisoformat(ts[:26] + "+00:00")
def session_breakdown(for_date: date | None = None, break_min: int = BREAK_THRESHOLD_MIN) -> dict[str, Any]:
"""
Detect work sessions and breaks from frame timestamps.
Returns sessions, breaks, and totals.
"""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"SELECT timestamp FROM frames WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp",
(start, end),
).fetchall()
if not rows:
return {"sessions": [], "breaks": [], "total_active_min": 0, "total_break_min": 0, "date": str(d)}
timestamps = [_parse_ts(r["timestamp"]) for r in rows]
threshold = timedelta(minutes=break_min)
sessions: list[dict] = []
breaks: list[dict] = []
sess_start = timestamps[0]
prev = timestamps[0]
for ts in timestamps[1:]:
gap = ts - prev
if gap > threshold:
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
breaks.append({
"start": prev.isoformat(),
"end": ts.isoformat(),
"duration_min": round(gap.total_seconds() / 60, 1),
})
sess_start = ts
prev = ts
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
total_active = sum(s["duration_min"] for s in sessions)
total_break = sum(b["duration_min"] for b in breaks)
total_span = (timestamps[-1] - timestamps[0]).total_seconds() / 60
return {
"date": str(d),
"day_start": timestamps[0].isoformat(),
"day_end": timestamps[-1].isoformat(),
"total_span_min": round(total_span, 1),
"total_active_min": round(total_active, 1),
"total_break_min": round(total_break, 1),
"sessions": sessions,
"breaks": breaks,
}
def domain_breakdown(for_date: date | None = None) -> list[dict[str, Any]]:
"""Browser time grouped by domain."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT browser_url, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ?
AND browser_url IS NOT NULL AND browser_url != ''
GROUP BY browser_url
ORDER BY frames DESC
""",
(start, end),
).fetchall()
# Group by domain
domains: dict[str, dict] = {}
for r in rows:
domain = _extract_domain(r["browser_url"])
if domain not in domains:
domains[domain] = {"domain": domain, "minutes": 0, "urls": []}
domains[domain]["minutes"] = round(domains[domain]["minutes"] + r["minutes"], 1)
domains[domain]["urls"].append({"url": r["browser_url"], "minutes": r["minutes"]})
return sorted(domains.values(), key=lambda x: x["minutes"], reverse=True)
def slack_activity(for_date: date | None = None) -> dict[str, Any]:
"""Slack-specific: time, channels, message activity."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
# Time in Slack
time_row = conn.execute(
"""
SELECT COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
# Channels from window names
channels = conn.execute(
"""
SELECT window_name, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
AND window_name IS NOT NULL AND window_name != ''
GROUP BY window_name ORDER BY frames DESC LIMIT 20
""",
(start, end),
).fetchall()
# Keystrokes in Slack (proxy for messages sent)
events = conn.execute(
"""
SELECT
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END) as keystrokes,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END) as clicks
FROM ui_events
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
return {
"minutes": time_row["minutes"] if time_row else 0,
"frames": time_row["frames"] if time_row else 0,
"keystrokes": events["keystrokes"] if events else 0,
"clicks": events["clicks"] if events else 0,
"channels": [dict(r) for r in channels],
}
def work_report(for_date: date | None = None) -> dict[str, Any]:
"""Full structured work report — no AI needed."""
from consumers.activity import app_time, ui_event_summary
d = for_date or _today()
apps = app_time(d)
# Annotate with category
categorized: dict[str, list] = {}
for a in apps:
cat = _categorize(a["app_name"])
categorized.setdefault(cat, []).append(a)
return {
"date": str(d),
"sessions": session_breakdown(d),
"apps_by_category": categorized,
"domains": domain_breakdown(d),
"slack": slack_activity(d),
"ui_events": ui_event_summary(d),
}
"""Structured work report — no LLM needed."""
from datetime import date, datetime, timezone, timedelta
from typing import Any
from urllib.parse import urlparse
from db import get_conn, date_range, today as _today
BREAK_THRESHOLD_MIN = 10 # gap > 10 min = break
WORK_APPS = {
"code": {"iTerm2", "Terminal", "Code", "Visual Studio Code", "PyCharm", "Xcode", "Cursor"},
"browser": {"Firefox", "Safari", "Chrome", "Arc", "Dia"},
"comms": {"Slack", "Teams", "Discord", "Zoom", "Telegram", "WhatsApp"},
"docs": {"Word", "Pages", "Notion", "Obsidian", "Bear", "Typora"},
"ai": {"Claude", "ChatGPT"},
"design": {"Figma", "Sketch", "Photoshop", "Illustrator"},
"media": {"QuickTime Player", "VLC", "Spotify"},
"system": {"Finder", "Activity Monitor", "System Preferences", "System Settings",
"Raycast", "Control Centre", "UserNotificationCenter", "NetAuthAgent"},
}
def _categorize(app_name: str) -> str:
for cat, apps in WORK_APPS.items():
if app_name in apps:
return cat
return "other"
def _extract_domain(url: str) -> str:
try:
h = urlparse(url).netloc
return h.removeprefix("www.") if h else url[:40]
except Exception:
return url[:40]
def _parse_ts(ts: str) -> datetime:
ts = ts.replace("+00:00", "+00:00") # already UTC
try:
return datetime.fromisoformat(ts)
except ValueError:
return datetime.fromisoformat(ts[:26] + "+00:00")
def session_breakdown(for_date: date | None = None, break_min: int = BREAK_THRESHOLD_MIN) -> dict[str, Any]:
"""
Detect work sessions and breaks from frame timestamps.
Returns sessions, breaks, and totals.
"""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"SELECT timestamp FROM frames WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp",
(start, end),
).fetchall()
if not rows:
return {"sessions": [], "breaks": [], "total_active_min": 0, "total_break_min": 0, "date": str(d)}
timestamps = [_parse_ts(r["timestamp"]) for r in rows]
threshold = timedelta(minutes=break_min)
sessions: list[dict] = []
breaks: list[dict] = []
sess_start = timestamps[0]
prev = timestamps[0]
for ts in timestamps[1:]:
gap = ts - prev
if gap > threshold:
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
breaks.append({
"start": prev.isoformat(),
"end": ts.isoformat(),
"duration_min": round(gap.total_seconds() / 60, 1),
})
sess_start = ts
prev = ts
dur = (prev - sess_start).total_seconds() / 60
sessions.append({
"start": sess_start.isoformat(),
"end": prev.isoformat(),
"duration_min": round(dur, 1),
})
total_active = sum(s["duration_min"] for s in sessions)
total_break = sum(b["duration_min"] for b in breaks)
total_span = (timestamps[-1] - timestamps[0]).total_seconds() / 60
return {
"date": str(d),
"day_start": timestamps[0].isoformat(),
"day_end": timestamps[-1].isoformat(),
"total_span_min": round(total_span, 1),
"total_active_min": round(total_active, 1),
"total_break_min": round(total_break, 1),
"sessions": sessions,
"breaks": breaks,
}
def domain_breakdown(for_date: date | None = None) -> list[dict[str, Any]]:
"""Browser time grouped by domain."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT browser_url, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ?
AND browser_url IS NOT NULL AND browser_url != ''
GROUP BY browser_url
ORDER BY frames DESC
""",
(start, end),
).fetchall()
# Group by domain
domains: dict[str, dict] = {}
for r in rows:
domain = _extract_domain(r["browser_url"])
if domain not in domains:
domains[domain] = {"domain": domain, "minutes": 0, "urls": []}
domains[domain]["minutes"] = round(domains[domain]["minutes"] + r["minutes"], 1)
domains[domain]["urls"].append({"url": r["browser_url"], "minutes": r["minutes"]})
return sorted(domains.values(), key=lambda x: x["minutes"], reverse=True)
def slack_activity(for_date: date | None = None) -> dict[str, Any]:
"""Slack-specific: time, channels, message activity."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
# Time in Slack
time_row = conn.execute(
"""
SELECT COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
# Channels from window names
channels = conn.execute(
"""
SELECT window_name, COUNT(*) as frames, ROUND(COUNT(*)*2.0/60,1) as minutes
FROM frames
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
AND window_name IS NOT NULL AND window_name != ''
GROUP BY window_name ORDER BY frames DESC LIMIT 20
""",
(start, end),
).fetchall()
# Keystrokes in Slack (proxy for messages sent)
events = conn.execute(
"""
SELECT
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END) as keystrokes,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END) as clicks
FROM ui_events
WHERE timestamp >= ? AND timestamp < ? AND app_name = 'Slack'
""",
(start, end),
).fetchone()
return {
"minutes": time_row["minutes"] if time_row else 0,
"frames": time_row["frames"] if time_row else 0,
"keystrokes": events["keystrokes"] if events else 0,
"clicks": events["clicks"] if events else 0,
"channels": [dict(r) for r in channels],
}
def work_report(for_date: date | None = None) -> dict[str, Any]:
"""Full structured work report — no AI needed."""
from consumers.activity import app_time, ui_event_summary
d = for_date or _today()
apps = app_time(d)
# Annotate with category
categorized: dict[str, list] = {}
for a in apps:
cat = _categorize(a["app_name"])
categorized.setdefault(cat, []).append(a)
return {
"date": str(d),
"sessions": session_breakdown(d),
"apps_by_category": categorized,
"domains": domain_breakdown(d),
"slack": slack_activity(d),
"ui_events": ui_event_summary(d),
}
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Python
Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions
LF
UTF-8
Spaces: 4
Ln 17, Col 87
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10...
|
NULL
|
|
1472
|
32
|
41
|
2026-04-11T17:31:22.340077+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928682340_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKERlanguages• 881DEV (-zsh)O 82APP (-zsh)• хз-zsh• ×4-zsh• 85-zsh886-zshO x7Sat 11 Apr 20:31:21-zsh181• *8all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled.onlyerror-level data willbe sent.to disable, use the --disable-telemetry flag.checklatestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFOscreenpipe:starting UI2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.shartine inseven copture2026-04-11T20:26:05.358540ZINFOscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05.378325ZINFO2026-04-11T20:26:05.378392Zscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)INFO screenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server:Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listпгагоа2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine:: eventdriven.capture: sontent dequp: Skipping copture for monitor 1 (ha5 -92544353114798391, trigger-visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55..9926882INFOscreenpipe_engine::event_driven_capture: content dedup: skippingcapture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11720:26:57.331771ZINFO2026-04-11T20:27:05.408691ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible framesscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB → 3.3MB (2.8x), 61 JPEGs deleted2026-04-11T20:31:05.374969ZINFOscreenpipe_engine::server:api_usage_5min: 1 requests...
|
NULL
|
-7690677685252217676
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKERlanguages• 881DEV (-zsh)O 82APP (-zsh)• хз-zsh• ×4-zsh• 85-zsh886-zshO x7Sat 11 Apr 20:31:21-zsh181• *8all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled.onlyerror-level data willbe sent.to disable, use the --disable-telemetry flag.checklatestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFOscreenpipe:starting UI2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.shartine inseven copture2026-04-11T20:26:05.358540ZINFOscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05.378325ZINFO2026-04-11T20:26:05.378392Zscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)INFO screenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server:Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listпгагоа2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine:: eventdriven.capture: sontent dequp: Skipping copture for monitor 1 (ha5 -92544353114798391, trigger-visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55..9926882INFOscreenpipe_engine::event_driven_capture: content dedup: skippingcapture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11720:26:57.331771ZINFO2026-04-11T20:27:05.408691ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible framesscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB → 3.3MB (2.8x), 61 JPEGs deleted2026-04-11T20:31:05.374969ZINFOscreenpipe_engine::server:api_usage_5min: 1 requests...
|
NULL
|
|
1471
|
32
|
40
|
2026-04-11T17:31:13.554224+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928673554_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKERlanguages• 881DEV (-zsh)O 82APP (-zsh)• хз-zsh• ×4-zsh• 85-zsh886-zshO 87Sat 11 Apr 20:31:13-zsh181• *8|all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled.onlyerror-level data willbe sent.to disable, use the --disable-telemetry flag.checklatestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFOscreenpipe:starting UI2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.shartine inseven copture2026-04-11T20:26:05.358540ZINFOscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting Ul event capture2026-04-11T20:26:05.378325ZINFO2026-04-11T20:26:05.378392Zscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)INFO screenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server:Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listпгагоа2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine:: eventdrivencapture: sontent dedup: skipping copture for monitor 1 (ha5 -921544353114798391, trigger-visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55..9926882INFOscreenpipe_engine::event_driven_capture: content dedup: skippingcapture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11720:26:57.331771ZINFO2026-04-11T20:27:05.408691ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible framesscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB → 3.3MB (2.8x), 61 JPEGs deleted2026-04-11T20:31:05.374969ZINFOscreenpipe_engine::server:api_usage_5min: 1 requests...
|
NULL
|
1352809889339809861
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKERlanguages• 881DEV (-zsh)O 82APP (-zsh)• хз-zsh• ×4-zsh• 85-zsh886-zshO 87Sat 11 Apr 20:31:13-zsh181• *8|all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled.onlyerror-level data willbe sent.to disable, use the --disable-telemetry flag.checklatestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFOscreenpipe:starting UI2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.shartine inseven copture2026-04-11T20:26:05.358540ZINFOscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting Ul event capture2026-04-11T20:26:05.378325ZINFO2026-04-11T20:26:05.378392Zscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)INFO screenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server:Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listпгагоа2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine:: eventdrivencapture: sontent dedup: skipping copture for monitor 1 (ha5 -921544353114798391, trigger-visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55..9926882INFOscreenpipe_engine::event_driven_capture: content dedup: skippingcapture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11720:26:57.331771ZINFO2026-04-11T20:27:05.408691ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible framesscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB → 3.3MB (2.8x), 61 JPEGs deleted2026-04-11T20:31:05.374969ZINFOscreenpipe_engine::server:api_usage_5min: 1 requests...
|
1470
|
|
1470
|
32
|
39
|
2026-04-11T17:31:07.659092+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928667659_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKERlanguages• 881DEV (-zsh)O 82APP (-zsh)• хз-zsh• ×4-zsh• 85-zsh886-zshO x7Sat 11 Apr 20:31:07-zsh181• *8|all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled.onlyerror-level data willbe sent.to disable, use the --disable-telemetry flag.checklatestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFOscreenpipe:starting UI2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.shartine inseven copture2026-04-11T20:26:05.358540ZINFOscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting Ul event capture2026-04-11T20:26:05.378325ZINFO2026-04-11T20:26:05.378392Zscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)INFO screenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server:Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFO2026-04-11T20:26:05.528107Zscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 3030INFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listпгагоа2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine:: eventdrivencapture: sontent dedup: skipping copture for monitor 1 (ha5 -921544353114798391, trigger-visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55..9926882INFOscreenpipe_engine::event_driven_capture: content dedup: skippingcapture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11720:26:57.331771ZINFO2026-04-11T20:27:05.408691ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible framesscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB → 3.3MB (2.8x), 61 JPEGs deleted2026-04-11T20:31:05.374969ZINFOscreenpipe_engine::server:api_usage_5min: 1 requests...
|
NULL
|
8428550508947476827
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKERlanguages• 881DEV (-zsh)O 82APP (-zsh)• хз-zsh• ×4-zsh• 85-zsh886-zshO x7Sat 11 Apr 20:31:07-zsh181• *8|all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled.onlyerror-level data willbe sent.to disable, use the --disable-telemetry flag.checklatestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFOscreenpipe:starting UI2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.shartine inseven copture2026-04-11T20:26:05.358540ZINFOscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting Ul event capture2026-04-11T20:26:05.378325ZINFO2026-04-11T20:26:05.378392Zscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)INFO screenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server:Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFO2026-04-11T20:26:05.528107Zscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 3030INFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listпгагоа2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine:: eventdrivencapture: sontent dedup: skipping copture for monitor 1 (ha5 -921544353114798391, trigger-visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55..9926882INFOscreenpipe_engine::event_driven_capture: content dedup: skippingcapture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11720:26:57.331771ZINFO2026-04-11T20:27:05.408691ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible framesscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB → 3.3MB (2.8x), 61 JPEGs deleted2026-04-11T20:31:05.374969ZINFOscreenpipe_engine::server:api_usage_5min: 1 requests...
|
NULL
|
|
1469
|
32
|
38
|
2026-04-11T17:31:05.672507+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928665672_m1.jpg...
|
System Settings
|
Appearance
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Search
Lukas Kovalik, Apple ID
1
1
Wi‑Fi
Bluetooth Search
Lukas Kovalik, Apple ID
1
1
Wi‑Fi
Bluetooth
Network
VPN
Notifications
Sound
Focus
Screen Time
General
Appearance
Accessibility
Control Centre
Siri & Spotlight
Privacy & Security
Desktop & Dock
Displays
Wallpaper
Screen Saver
Battery
Lock Screen
Touch ID & Password
Users & Groups
Passwords
Internet Accounts
Game Center
Wallet & Apple Pay
Keyboard
Mouse
Trackpad
Printers & Scanners
Appearance
Light
Dark
Auto
Accent colour
Multicolour
Blue
Purple
Pink
Red
Orange...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Search","depth":5,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Lukas Kovalik, Apple ID","depth":8,"automation_id":"com.apple.systempreferences.AppleIDSettings*AppleIDSettings","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"1","depth":8,"automation_id":"com.apple.FollowUpSettings.FollowUpSettingsExtension*FOLLOWUP_SINGLE_ACTION_changePhoneNumber","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"1","depth":8,"automation_id":"com.apple.FollowUpSettings.FollowUpSettingsExtension*FOLLOWUP_SINGLE_ACTION_com.apple.SoftwareUpdateServices.followup:com.apple.Software-Update-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Wi‑Fi","depth":8,"automation_id":"com.apple.wifi-settings-extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Bluetooth","depth":8,"automation_id":"com.apple.BluetoothSettings","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Network","depth":8,"automation_id":"com.apple.Network-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"VPN","depth":8,"automation_id":"com.apple.NetworkExtensionSettingsUI.NESettingsUIExtension*vpn","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Notifications","depth":8,"automation_id":"com.apple.Notifications-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Sound","depth":8,"automation_id":"com.apple.Sound-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Focus","depth":8,"automation_id":"com.apple.Focus-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Screen Time","depth":8,"automation_id":"com.apple.Screen-Time-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"General","depth":8,"automation_id":"com.apple.systempreferences.GeneralSettings","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Appearance","depth":8,"automation_id":"com.apple.Appearance-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Accessibility","depth":8,"automation_id":"com.apple.Accessibility-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Control Centre","depth":8,"automation_id":"com.apple.ControlCenter-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Siri & Spotlight","depth":8,"automation_id":"com.apple.Siri-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Privacy & Security","depth":8,"automation_id":"com.apple.settings.PrivacySecurity.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Desktop & Dock","depth":8,"automation_id":"com.apple.Desktop-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Displays","depth":8,"automation_id":"com.apple.Displays-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Wallpaper","depth":8,"automation_id":"com.apple.Wallpaper-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Screen Saver","depth":8,"automation_id":"com.apple.ScreenSaver-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Battery","depth":8,"automation_id":"com.apple.Battery-Settings.extension*BatteryPreferences","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Lock Screen","depth":8,"automation_id":"com.apple.Lock-Screen-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Touch ID & Password","depth":8,"automation_id":"com.apple.Touch-ID-Settings.extension*TouchIDPasswordPrefs","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Users & Groups","depth":8,"automation_id":"com.apple.Users-Groups-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Passwords","depth":8,"automation_id":"com.apple.Passwords-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Internet Accounts","depth":8,"automation_id":"com.apple.Internet-Accounts-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Game Center","depth":8,"automation_id":"com.apple.Game-Center-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Wallet & Apple Pay","depth":8,"automation_id":"com.apple.WalletSettingsExtension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Keyboard","depth":8,"automation_id":"com.apple.Keyboard-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Mouse","depth":8,"automation_id":"com.apple.Mouse-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Trackpad","depth":8,"automation_id":"com.apple.Trackpad-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Printers & Scanners","depth":8,"automation_id":"com.apple.Print-Scan-Settings.extension","help_text":"","role_description":"text"},{"role":"AXStaticText","text":"Appearance","depth":7,"role_description":"text"},{"role":"AXButton","text":"Light","depth":7,"help_text":"Use a light appearance for buttons, menus and windows.","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Dark","depth":7,"help_text":"Use a dark appearance for buttons, menus and windows.","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Auto","depth":7,"help_text":"Automatically adjusts the appearance of buttons, menus and windows throughout the day.","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Accent colour","depth":7,"role_description":"text"},{"role":"AXButton","text":"Multicolour","depth":7,"help_text":"Multicolour","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Blue","depth":7,"help_text":"Blue","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Purple","depth":7,"help_text":"Purple","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Pink","depth":7,"help_text":"Pink","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Red","depth":7,"help_text":"Red","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Orange","depth":7,"help_text":"Orange","role_description":"button","is_enabled":true,"is_focused":false}]...
|
-768773058401852050
|
8555651664244700337
|
visual_change
|
accessibility
|
NULL
|
Search
Lukas Kovalik, Apple ID
1
1
Wi‑Fi
Bluetooth Search
Lukas Kovalik, Apple ID
1
1
Wi‑Fi
Bluetooth
Network
VPN
Notifications
Sound
Focus
Screen Time
General
Appearance
Accessibility
Control Centre
Siri & Spotlight
Privacy & Security
Desktop & Dock
Displays
Wallpaper
Screen Saver
Battery
Lock Screen
Touch ID & Password
Users & Groups
Passwords
Internet Accounts
Game Center
Wallet & Apple Pay
Keyboard
Mouse
Trackpad
Printers & Scanners
Appearance
Light
Dark
Auto
Accent colour
Multicolour
Blue
Purple
Pink
Red
Orange...
|
1468
|
|
1468
|
32
|
37
|
2026-04-11T17:31:03.082589+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928663082_m1.jpg...
|
Activity Monitor
|
Activity Monitor – All Processes
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Physical Memory:
16,00 GB
Memory Used:
13,32 GB
Ca Physical Memory:
16,00 GB
Memory Used:
13,32 GB
Cached Files:
2,66 GB
Swap Used:
0 bytes
6,19 GB
Wired Memory:
Compressed:
1,96 GB
4,59 GB
App Memory:
MEMORY PRESSURE
Claude Helper (Renderer)
683,7 MB
15
218
2517
lukas
Firefox
642,7 MB
89
797
16623
lukas
WindowServer
627,2 MB
22
6 113
407
_windowserver
FirefoxCP Isolated Web Content
586,8 MB
24
120
18343
lukas
FirefoxCP Isolated Web Content
586,3 MB
27
125
18287
lukas
FirefoxCP Isolated Web Content
576,5 MB
24
121
16656
lukas
Firefox GPU Helper
538,0 MB
30
243
16628
lukas
Notion Helper (Renderer)
364,5 MB
20
315
1148
lukas
FirefoxCP Isolated Web Content
352,3 MB
30
137
16799
lukas
FirefoxCP Isolated Web Content
352,0 MB
24
116
17335
lukas
FirefoxCP Isolated Web Content
322,3 MB
27
127
16750
lukas
Postman Helper (Renderer)
292,7 MB
24
253
42547
lukas
Code Helper (Renderer)
271,9 MB
20
308
32708
lukas
FirefoxCP Isolated Web Content
232,9 MB
24
121
17319
lukas
iTerm2
216,2 MB
7
919
4878
lukas
FirefoxCP Privileged Mozilla
195,4 MB
23
125
18134
lukas
Code Helper (Renderer)
181,0 MB
16
167
42991
lukas
Claude
180,0 MB
55
713
2501
lukas
FirefoxCP Isolated Web Content
177,2 MB
23
118
18359
lukas
FirefoxCP Isolated Web Content
176,6 MB
23
121
42273
lukas
Code Helper (GPU)
161,6 MB
19
200
32705
lukas
FirefoxCP WebExtensions
158,8 MB
24
116
16633
lukas
Postman
148,0 MB
46
430
42531
lukas
Code
145,8 MB
42
598
32700
lukas
Wispr Flow Helper (GPU)
138,4 MB
17
224
3813
lukas
Bitwarden Helper (Renderer)
138,3 MB
17
208
16862
lukas
CursorUIViewService (Not Responding)
134,1 MB
3
4 550
842
lukas
FirefoxCP Isolated Web Content
128,0 MB
22
113
18346
lukas
FirefoxCP Isolated Web Content
127,7 MB
23
118
26456
lukas
Wispr Flow
126,9 MB
50
535
3800
lukas
Wispr Flow Helper (Renderer)
117,1 MB
23
486
3837
lukas
Raycast
110,1 MB
10
407
38842
lukas
Finder
102,4 MB
6
517
743
lukas
FirefoxCP Isolated Web Content
99,5 MB
23
114
18293
lukas
Claude Helper
95,8 MB
18
207
2511
lukas
FirefoxCP Isolated Web Content
88,2 MB
23
116
27445
lukas
FirefoxCP Privileged Content
88,1 MB
22
116
16644
lukas
Activity Monitor
86,7 MB
5
385
17101
lukas
FirefoxCP Isolated Web Content
85,8 MB
23
114
18329
lukas
FirefoxCP Isolated Web Content
84,6 MB
24
121
17432
lukas
Code Helper
83,5 MB
23
112
32929
lukas
Notion
82,0 MB
45
527
843
lukas
FirefoxCP Isolated Web Content
79,2 MB
22
112
18290
lukas
Bitwarden
78,2 MB
44
482
16856
lukas
FirefoxCP Isolated Web Content
77,1 MB
23
114
18320
lukas
Firefox Media Plugin Helper
76,8 MB
6
72
26451
lukas
loginwindow
76,4 MB
3
606
410
lukas
VTDecoderXPCService
76,3 MB
2
72
995
lukas
FirefoxCP Isolated Web Content
74,6 MB
22
113
18330
lukas
Notion Helper (Renderer)
74,3 MB
18
171
1822
lukas
Code Helper (Plugin)
59,6 MB
22
120
42911
lukas
Wispr Flow Helper (Renderer)
59,3 MB
17
362
3834
lukas
1Password
58,8 MB...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Physical Memory:","depth":3,"automation_id":"_NS:456","help_text":"Total amount of memory installed in this computer.","role_description":"text"},{"role":"AXStaticText","text":"16,00 GB","depth":3,"automation_id":"_NS:448","help_text":"Total amount of memory installed in this computer.","role_description":"text"},{"role":"AXStaticText","text":"Memory Used:","depth":3,"bounds":{"left":0.85833335,"top":0.0,"width":0.05625,"height":0.015555556},"automation_id":"_NS:441","help_text":"Amount of physical memory that is in use by apps or the system.","role_description":"text"},{"role":"AXStaticText","text":"13,32 GB","depth":3,"bounds":{"left":0.9409722,"top":0.0,"width":0.038194444,"height":0.015555556},"automation_id":"_NS:435","help_text":"Amount of physical memory that is in use by apps or the system.","role_description":"text"},{"role":"AXStaticText","text":"Cached Files:","depth":3,"bounds":{"left":0.85833335,"top":0.0,"width":0.052083332,"height":0.015555556},"automation_id":"_NS:428","help_text":"Size of files cached by the system into unused memory to improve performance.","role_description":"text"},{"role":"AXStaticText","text":"2,66 GB","depth":3,"bounds":{"left":0.9458333,"top":0.0,"width":0.033333335,"height":0.015555556},"automation_id":"_NS:422","help_text":"Size of files cached by the system into unused memory to improve performance.","role_description":"text"},{"role":"AXStaticText","text":"Swap Used:","depth":3,"bounds":{"left":0.85833335,"top":0.0,"width":0.046527777,"height":0.015555556},"automation_id":"_NS:415","help_text":"Amount of compressed data temporarily moved to disk to make room in memory for more recently used data.","role_description":"text"},{"role":"AXStaticText","text":"0 bytes","depth":3,"bounds":{"left":0.94930553,"top":0.0,"width":0.029861111,"height":0.015555556},"automation_id":"_NS:409","help_text":"Amount of compressed data temporarily moved to disk to make room in memory for more recently used data.","role_description":"text"},{"role":"AXStaticText","text":"6,19 GB","depth":3,"bounds":{"left":1.0,"top":0.0,"width":-0.079861164,"height":0.015555556},"automation_id":"_NS:400","help_text":"Physical memory allocated by apps and system processes.","role_description":"text"},{"role":"AXStaticText","text":"Wired Memory:","depth":3,"bounds":{"left":0.9930556,"top":0.0,"width":0.006944418,"height":0.015555556},"automation_id":"_NS:393","help_text":"Physical memory containing data that cannot be compressed or swapped to disk.","role_description":"text"},{"role":"AXStaticText","text":"Compressed:","depth":3,"bounds":{"left":0.9930556,"top":0.0,"width":0.006944418,"height":0.015555556},"automation_id":"_NS:386","help_text":"Physical memory used to store a compressed version of data that has not been used recently.","role_description":"text"},{"role":"AXStaticText","text":"1,96 GB","depth":3,"bounds":{"left":1.0,"top":0.0,"width":-0.079861164,"height":0.015555556},"automation_id":"_NS:380","help_text":"Physical memory containing data that cannot be compressed or swapped to disk.","role_description":"text"},{"role":"AXStaticText","text":"4,59 GB","depth":3,"bounds":{"left":1.0,"top":0.0,"width":-0.079861164,"height":0.015555556},"automation_id":"_NS:374","help_text":"Physical memory used to store a compressed version of data that has not been used recently.","role_description":"text"},{"role":"AXStaticText","text":"App Memory:","depth":3,"bounds":{"left":0.9930556,"top":0.0,"width":0.006944418,"height":0.015555556},"automation_id":"_NS:367","help_text":"Physical memory allocated by apps and system processes.","role_description":"text"},{"role":"AXStaticText","text":"MEMORY PRESSURE","depth":3,"automation_id":"_NS:359","role_description":"text"},{"role":"AXStaticText","text":"Claude Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"683,7 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"15","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"218","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"2517","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Firefox","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"642,7 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"89","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"797","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16623","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"WindowServer","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"627,2 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"22","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"6 113","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"407","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"_windowserver","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"586,8 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"24","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"120","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18343","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"586,3 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"27","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"125","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18287","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"576,5 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"24","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"121","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16656","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Firefox GPU Helper","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"538,0 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"30","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"243","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16628","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Notion Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"364,5 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"20","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"315","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"1148","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"352,3 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"30","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"137","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16799","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"352,0 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"24","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"116","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"17335","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"322,3 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"27","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"127","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16750","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Postman Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"292,7 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"24","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"253","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"42547","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Code Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"271,9 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"20","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"308","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"32708","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"232,9 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"24","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"121","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"17319","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"iTerm2","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"216,2 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"7","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"919","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"4878","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Privileged Mozilla","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"195,4 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"125","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18134","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Code Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"181,0 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"167","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"42991","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Claude","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"180,0 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"55","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"713","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"2501","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"177,2 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"118","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18359","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"176,6 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"121","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"42273","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Code Helper (GPU)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"161,6 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"19","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"200","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"32705","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP WebExtensions","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"158,8 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"24","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"116","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16633","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Postman","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"148,0 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"46","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"430","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"42531","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Code","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"145,8 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"42","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"598","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"32700","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Wispr Flow Helper (GPU)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"138,4 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"17","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"224","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"3813","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Bitwarden Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"138,3 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"17","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"208","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16862","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"CursorUIViewService (Not Responding)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"134,1 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"3","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"4 550","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"842","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"128,0 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"22","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"113","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18346","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"127,7 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"118","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"26456","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Wispr Flow","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"126,9 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"50","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"535","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"3800","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Wispr Flow Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"117,1 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"486","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"3837","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Raycast","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"110,1 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"10","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"407","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"38842","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Finder","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"102,4 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"6","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"517","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"743","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"99,5 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"114","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18293","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Claude Helper","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"95,8 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"207","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"2511","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"88,2 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"116","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"27445","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Privileged Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"88,1 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"22","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"116","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16644","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Activity Monitor","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"86,7 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"5","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"385","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"17101","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"85,8 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"114","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18329","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"84,6 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"24","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"121","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"17432","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Code Helper","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"83,5 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"112","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"32929","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Notion","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"82,0 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"45","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"527","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"843","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"79,2 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"22","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"112","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18290","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Bitwarden","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"78,2 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"44","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"482","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"16856","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"77,1 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"23","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"114","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18320","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Firefox Media Plugin Helper","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"76,8 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"6","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"72","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"26451","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"loginwindow","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"76,4 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"3","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"606","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"410","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"VTDecoderXPCService","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"76,3 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"2","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"72","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"995","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"FirefoxCP Isolated Web Content","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"74,6 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"22","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"113","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18330","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Notion Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"74,3 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"18","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"171","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"1822","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Code Helper (Plugin)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"59,6 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"22","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"120","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"42911","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"Wispr Flow Helper (Renderer)","depth":5,"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"59,3 MB","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"17","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"362","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"3834","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":5,"automation_id":"_NS:9","role_description":"text"},{"role":"AXStaticText","text":"1Password","depth":5,"bounds":{"left":0.5083333,"top":0.0,"width":0.175,"height":0.015555556},"automation_id":"_NS:31","role_description":"text"},{"role":"AXStaticText","text":"58,8 MB","depth":5,"bounds":{"left":0.6951389,"top":0.0,"width":0.044444446,"height":0.015555556},"automation_id":"_NS:9","role_description":"text"}]...
|
5393706182861645365
|
538643591658919879
|
app_switch
|
accessibility
|
NULL
|
Physical Memory:
16,00 GB
Memory Used:
13,32 GB
Ca Physical Memory:
16,00 GB
Memory Used:
13,32 GB
Cached Files:
2,66 GB
Swap Used:
0 bytes
6,19 GB
Wired Memory:
Compressed:
1,96 GB
4,59 GB
App Memory:
MEMORY PRESSURE
Claude Helper (Renderer)
683,7 MB
15
218
2517
lukas
Firefox
642,7 MB
89
797
16623
lukas
WindowServer
627,2 MB
22
6 113
407
_windowserver
FirefoxCP Isolated Web Content
586,8 MB
24
120
18343
lukas
FirefoxCP Isolated Web Content
586,3 MB
27
125
18287
lukas
FirefoxCP Isolated Web Content
576,5 MB
24
121
16656
lukas
Firefox GPU Helper
538,0 MB
30
243
16628
lukas
Notion Helper (Renderer)
364,5 MB
20
315
1148
lukas
FirefoxCP Isolated Web Content
352,3 MB
30
137
16799
lukas
FirefoxCP Isolated Web Content
352,0 MB
24
116
17335
lukas
FirefoxCP Isolated Web Content
322,3 MB
27
127
16750
lukas
Postman Helper (Renderer)
292,7 MB
24
253
42547
lukas
Code Helper (Renderer)
271,9 MB
20
308
32708
lukas
FirefoxCP Isolated Web Content
232,9 MB
24
121
17319
lukas
iTerm2
216,2 MB
7
919
4878
lukas
FirefoxCP Privileged Mozilla
195,4 MB
23
125
18134
lukas
Code Helper (Renderer)
181,0 MB
16
167
42991
lukas
Claude
180,0 MB
55
713
2501
lukas
FirefoxCP Isolated Web Content
177,2 MB
23
118
18359
lukas
FirefoxCP Isolated Web Content
176,6 MB
23
121
42273
lukas
Code Helper (GPU)
161,6 MB
19
200
32705
lukas
FirefoxCP WebExtensions
158,8 MB
24
116
16633
lukas
Postman
148,0 MB
46
430
42531
lukas
Code
145,8 MB
42
598
32700
lukas
Wispr Flow Helper (GPU)
138,4 MB
17
224
3813
lukas
Bitwarden Helper (Renderer)
138,3 MB
17
208
16862
lukas
CursorUIViewService (Not Responding)
134,1 MB
3
4 550
842
lukas
FirefoxCP Isolated Web Content
128,0 MB
22
113
18346
lukas
FirefoxCP Isolated Web Content
127,7 MB
23
118
26456
lukas
Wispr Flow
126,9 MB
50
535
3800
lukas
Wispr Flow Helper (Renderer)
117,1 MB
23
486
3837
lukas
Raycast
110,1 MB
10
407
38842
lukas
Finder
102,4 MB
6
517
743
lukas
FirefoxCP Isolated Web Content
99,5 MB
23
114
18293
lukas
Claude Helper
95,8 MB
18
207
2511
lukas
FirefoxCP Isolated Web Content
88,2 MB
23
116
27445
lukas
FirefoxCP Privileged Content
88,1 MB
22
116
16644
lukas
Activity Monitor
86,7 MB
5
385
17101
lukas
FirefoxCP Isolated Web Content
85,8 MB
23
114
18329
lukas
FirefoxCP Isolated Web Content
84,6 MB
24
121
17432
lukas
Code Helper
83,5 MB
23
112
32929
lukas
Notion
82,0 MB
45
527
843
lukas
FirefoxCP Isolated Web Content
79,2 MB
22
112
18290
lukas
Bitwarden
78,2 MB
44
482
16856
lukas
FirefoxCP Isolated Web Content
77,1 MB
23
114
18320
lukas
Firefox Media Plugin Helper
76,8 MB
6
72
26451
lukas
loginwindow
76,4 MB
3
606
410
lukas
VTDecoderXPCService
76,3 MB
2
72
995
lukas
FirefoxCP Isolated Web Content
74,6 MB
22
113
18330
lukas
Notion Helper (Renderer)
74,3 MB
18
171
1822
lukas
Code Helper (Plugin)
59,6 MB
22
120
42911
lukas
Wispr Flow Helper (Renderer)
59,3 MB
17
362
3834
lukas
1Password
58,8 MB...
|
NULL
|
|
1467
|
32
|
36
|
2026-04-11T17:31:01.468696+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928661468_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"__init__.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"activity.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"audio.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"ocr.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"report.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"summary.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXTextArea","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"expanded","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n[rerun: b4]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bootstrap pip then install packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n/usr/bin/python3: No module named ensurepip\n[rerun: b5]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip via apt","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n[rerun: b6]","depth":23,"role_description":"text"}]...
|
-4563309486379008757
|
388312387041331809
|
idle
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]...
|
NULL
|
|
1466
|
32
|
35
|
2026-04-11T17:30:31.281825+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928631281_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"__init__.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"activity.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"audio.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"ocr.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"report.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"summary.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXTextArea","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"expanded","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"}]...
|
-2883720108714655905
|
523426972932229729
|
click
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?...
|
1465
|
|
1465
|
32
|
34
|
2026-04-11T17:30:25.536346+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928625536_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:30:25-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-5640713178418392866
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:30:25-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
|
1464
|
32
|
33
|
2026-04-11T17:30:22.710998+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928622710_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:30:22-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
8560970640497364601
|
NULL
|
visual_change
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:30:22-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1463
|
|
1463
|
32
|
32
|
2026-04-11T17:30:20.682025+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928620682_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"__init__.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"activity.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"audio.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"ocr.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"report.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"summary.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXTextArea","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"expanded","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n[rerun: b4]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bootstrap pip then install packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n/usr/bin/python3: No module named ensurepip\n[rerun: b5]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip via apt","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n[rerun: b6]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip with sudo","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Reason:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"lets make it in docker, not to install it in nas directly","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Good call. Let me check what Docker setup you already have and build a proper containerized app.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check existing docker-compose for reference","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"consume\ndata\ndb\ndocker-compose.yml\nexport\nmcp-server\nmedia\nredis\ntrash\nservices:\n redis:\n image: redis:8\n command:\n - /bin/sh\n - -c\n - redis-server --requirepass redispass\n container_name: PaperlessNGX-REDIS\n hostname: paper-redis\n security_opt:\n - no-new-privileges:true\n read_only: true\n user: 1000:10\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli ping || exit 1\"]\n volumes:\n - /volume2/docker/paperlessngx/redis:/data:rw\n environment:\n TZ: Europe/Sofia\n restart: always\n\n db:\n image: postgres:17\n container_name: PaperlessNGX-DB\n hostname: paper-db\n mem_limit: 1g\n security_opt:\n - no-new-privileges:true\n healthcheck:\n test: [\"CMD\", \"pg_isready\", \"-q\", \"-d\", \"paperless\", \"-U\", \"paperlessuser\"]\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Docker availability","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"}]...
|
3683259882905867712
|
388033112159521377
|
app_switch
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN...
|
NULL
|
|
1462
|
32
|
31
|
2026-04-11T17:30:15.436991+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928615436_m1.jpg...
|
Firefox
|
Irán – Denník N — Personal
|
True
|
dennikn.sk/tema/iran/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Close tab
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Preskočiť hlavnú navigáciu
Preskočiť hlavnú navigáciu
Denník N
Denník N
Vyhľadávaný výraz
Hľadaj
Hľadaj
Vypnúť reklamu
Vypnúť reklamu
SK
SK
HU
HU
CZ
CZ
EU
EU
Otvoriť hlavné menu
MENU
SVET
SVET
DONALD TRUMP
DONALD TRUMP
IRÁN
IRÁN
VIKTOR ORBÁN
VIKTOR ORBÁN
VOJNA NA UKRAJINE
VOJNA NA UKRAJINE
KONFLIKT NA BLÍZKOM VÝCHODE
KONFLIKT NA BLÍZKOM VÝCHODE
MAĎARSKÉ VOĽBY
MAĎARSKÉ VOĽBY
USA
USA
RUSKO
RUSKO
VLADIMIR PUTIN
VLADIMIR PUTIN
VÝVOJ BOJOV NA UKRAJINE
VÝVOJ BOJOV NA UKRAJINE
Posun na nasledujúce
Irán
Irán
ODOBERAŤ
ODOBERAŤ
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
The New York Times
The New York Times
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 4)
4
KOMENTÁRE
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Soňa Weissová
Soňa Weissová
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 24)
24
KOMENTÁRE
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Denisa Ballová
Denisa Ballová
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 4)
4
KOMENTÁRE
Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Trumpov svet (vývoj v Bielom dome) Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Trumpov svet (vývoj v Bielom dome) Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Jiří Sobota
Jiří Sobota
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 29)
29
KOMENTÁRE
Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Svetový newsfilter Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Svetový newsfilter Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Denník N
Denník N
a ďalší
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Tomáš Zemko
Tomáš Zemko
9. apríla 2026
Zobraziť diskusiu (počet príspevkov: 4)
4
KOMENTÁRE
Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Graf dňa Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Graf dňa Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Tomáš Zemko
Tomáš Zemko
9. apríla 2026
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Jan Wirnitzer
Jan Wirnitzer
9. apríla 2026
Zobraziť diskusiu (počet príspevkov: 3)
3
KOMENTÁRE
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Tomáš Vasilko
Tomáš Vasilko
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Denisa Ballová
Denisa Ballová
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 9)
9
KOMENTÁRE
Petr Mlejnek
Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu
Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu
Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu
Petr Mlejnek
Petr Mlejnek
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Ekonomický newsfilter Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Ekonomický newsfilter Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Tomáš Zemko
Tomáš Zemko
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 3)
3
KOMENTÁRE
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Denisa Ballová
Denisa Ballová
7. apríla 2026
Zobraziť diskusiu (počet príspevkov: 3)
3
KOMENTÁRE
Dalibor Roháč
O kolektívnej nepripravenosti na asymetrickú dronovú vojnu
O kolektívnej nepripravenosti na asymetrickú dronovú vojnu
O kolektívnej nepripravenosti na asymetrickú dronovú vojnu
Dalibor Roháč
Dalibor Roháč
7. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Trumpov svet (vývoj v Bielom dome) Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Trumpov svet (vývoj v Bielom dome) Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Anastasiia Furman
Anastasiia Furman
7. apríla 2026
Zobraziť diskusiu (počet príspevkov: 12)
12
KOMENTÁRE
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Tomáš Čorej
Tomáš Čorej
5. apríla 2026
Zobraziť diskusiu (počet príspevkov: 60)
60
KOMENTÁRE
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Tomáš Čorej
Tomáš Čorej
3. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Preskočiť hlavnú navigáciu","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Preskočiť hlavnú navigáciu","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Denník N","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Denník N","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Vyhľadávaný výraz","depth":9,"help_text":"","placeholder":"Zadajte vyhľadávaný výraz …","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Hľadaj","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Hľadaj","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Vypnúť reklamu","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vypnúť reklamu","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SK","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SK","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"HU","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"HU","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"CZ","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CZ","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"EU","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"EU","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Otvoriť hlavné menu","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MENU","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SVET","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SVET","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"DONALD TRUMP","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"DONALD TRUMP","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"IRÁN","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"IRÁN","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VIKTOR ORBÁN","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VIKTOR ORBÁN","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VOJNA NA UKRAJINE","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VOJNA NA UKRAJINE","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"KONFLIKT NA BLÍZKOM VÝCHODE","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"KONFLIKT NA BLÍZKOM VÝCHODE","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"MAĎARSKÉ VOĽBY","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"MAĎARSKÉ VOĽBY","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"USA","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"USA","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"RUSKO","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"RUSKO","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VLADIMIR PUTIN","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VLADIMIR PUTIN","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VÝVOJ BOJOV NA UKRAJINE","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VÝVOJ BOJOV NA UKRAJINE","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Posun na nasledujúce","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Irán","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Irán","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"ODOBERAŤ","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ODOBERAŤ","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"The New York Times","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"The New York Times","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 4)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Soňa Weissová","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Soňa Weissová","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 24)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"24","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Denisa Ballová","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Denisa Ballová","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 4)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Trumpov svet (vývoj v Bielom dome) Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Trumpov svet (vývoj v Bielom dome) Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jiří Sobota","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiří Sobota","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 29)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"29","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Svetový newsfilter Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Svetový newsfilter Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Denník N","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Denník N","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"a ďalší","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 1)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Tomáš Zemko","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tomáš Zemko","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"9. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 4)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Graf dňa Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Graf dňa Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Tomáš Zemko","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tomáš Zemko","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"9. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jan Wirnitzer","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jan Wirnitzer","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"9. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 3)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Tomáš Vasilko","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tomáš Vasilko","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 1)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Denisa Ballová","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Denisa Ballová","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 9)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"9","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Petr Mlejnek","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Petr Mlejnek","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Petr Mlejnek","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8. apríla 2026","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 1)","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán","depth":12,"bounds":{"left":0.61527777,"top":0.0,"width":0.125,"height":0.13333334},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Ekonomický newsfilter Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán","depth":12,"bounds":{"left":0.32152778,"top":0.0,"width":0.28541666,"height":0.05666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Ekonomický newsfilter Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán","depth":13,"bounds":{"left":0.32152778,"top":0.0,"width":0.27569443,"height":0.06333333},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán","depth":14,"bounds":{"left":0.32152778,"top":0.0,"width":0.27569443,"height":0.06333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Tomáš Zemko","depth":13,"bounds":{"left":0.32152778,"top":0.057777777,"width":0.06388889,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tomáš Zemko","depth":14,"bounds":{"left":0.32152778,"top":0.057777777,"width":0.06388889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8. apríla 2026","depth":13,"bounds":{"left":0.39375,"top":0.057777777,"width":0.063194446,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 3)","depth":13,"bounds":{"left":0.57430553,"top":0.05,"width":0.03263889,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.58055556,"top":0.06111111,"width":0.009027778,"height":0.013333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3","depth":14,"bounds":{"left":0.59375,"top":0.06,"width":0.0055555557,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty","depth":12,"bounds":{"left":0.61527777,"top":0.14,"width":0.125,"height":0.13333334},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty","depth":12,"bounds":{"left":0.32152778,"top":0.14,"width":0.28541666,"height":0.08444444},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty","depth":13,"bounds":{"left":0.32152778,"top":0.13666667,"width":0.26597223,"height":0.09111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty","depth":14,"bounds":{"left":0.32152778,"top":0.13666667,"width":0.26597223,"height":0.09111111},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Denisa Ballová","depth":13,"bounds":{"left":0.32152778,"top":0.2588889,"width":0.06666667,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Denisa Ballová","depth":14,"bounds":{"left":0.32152778,"top":0.2588889,"width":0.06666667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7. apríla 2026","depth":13,"bounds":{"left":0.39652777,"top":0.2588889,"width":0.061805554,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 3)","depth":13,"bounds":{"left":0.57430553,"top":0.25111112,"width":0.03263889,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.58055556,"top":0.26222223,"width":0.009027778,"height":0.013333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3","depth":14,"bounds":{"left":0.59375,"top":0.26,"width":0.0055555557,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Dalibor Roháč","depth":12,"bounds":{"left":0.65694445,"top":0.34111112,"width":0.083333336,"height":0.13333334},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"O kolektívnej nepripravenosti na asymetrickú dronovú vojnu","depth":12,"bounds":{"left":0.32152778,"top":0.34111112,"width":0.28541666,"height":0.055555556},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"O kolektívnej nepripravenosti na asymetrickú dronovú vojnu","depth":13,"bounds":{"left":0.32152778,"top":0.33777776,"width":0.23402777,"height":0.062222224},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"O kolektívnej nepripravenosti na asymetrickú dronovú vojnu","depth":14,"bounds":{"left":0.32152778,"top":0.33777776,"width":0.23402777,"height":0.062222224},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Dalibor Roháč","depth":13,"bounds":{"left":0.32152778,"top":0.44666666,"width":0.06388889,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dalibor Roháč","depth":14,"bounds":{"left":0.32152778,"top":0.44666666,"width":0.06388889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7. apríla 2026","depth":13,"bounds":{"left":0.39375,"top":0.44666666,"width":0.061805554,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 1)","depth":13,"bounds":{"left":0.57569444,"top":0.43888888,"width":0.03125,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.58194447,"top":0.45,"width":0.009027778,"height":0.013333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1","depth":14,"bounds":{"left":0.5951389,"top":0.44777778,"width":0.004166667,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom","depth":12,"bounds":{"left":0.61527777,"top":0.5288889,"width":0.125,"height":0.13333334},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Trumpov svet (vývoj v Bielom dome) Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom","depth":12,"bounds":{"left":0.32152778,"top":0.5288889,"width":0.28541666,"height":0.083333336},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Trumpov svet (vývoj v Bielom dome) Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom","depth":13,"bounds":{"left":0.32152778,"top":0.52555555,"width":0.27222222,"height":0.09},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom","depth":14,"bounds":{"left":0.32152778,"top":0.52555555,"width":0.27222222,"height":0.09},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Anastasiia Furman","depth":13,"bounds":{"left":0.32152778,"top":0.64666665,"width":0.084027775,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Anastasiia Furman","depth":14,"bounds":{"left":0.32152778,"top":0.64666665,"width":0.084027775,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7. apríla 2026","depth":13,"bounds":{"left":0.4138889,"top":0.64666665,"width":0.061805554,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 12)","depth":13,"bounds":{"left":0.5708333,"top":0.6388889,"width":0.036111113,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.57708335,"top":0.65,"width":0.008333334,"height":0.013333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"12","depth":14,"bounds":{"left":0.58958334,"top":0.6488889,"width":0.009722223,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15","depth":12,"bounds":{"left":0.61527777,"top":0.72888887,"width":0.125,"height":0.13333334},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15","depth":12,"bounds":{"left":0.32152778,"top":0.72888887,"width":0.28541666,"height":0.112222224},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15","depth":13,"bounds":{"left":0.32152778,"top":0.7266667,"width":0.2361111,"height":0.11777778},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15","depth":14,"bounds":{"left":0.32152778,"top":0.7266667,"width":0.2361111,"height":0.11777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Tomáš Čorej","depth":13,"bounds":{"left":0.32152778,"top":0.8755556,"width":0.057638887,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tomáš Čorej","depth":14,"bounds":{"left":0.32152778,"top":0.8755556,"width":0.057638887,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5. apríla 2026","depth":13,"bounds":{"left":0.3875,"top":0.8755556,"width":0.0625,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 60)","depth":13,"bounds":{"left":0.56875,"top":0.86777776,"width":0.038194444,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.575,"top":0.8788889,"width":0.008333334,"height":0.013333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"60","depth":14,"bounds":{"left":0.5875,"top":0.87777776,"width":0.011805556,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KOMENTÁRE","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne","depth":12,"bounds":{"left":0.61527777,"top":0.9577778,"width":0.125,"height":0.0422222},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne","depth":12,"bounds":{"left":0.32152778,"top":0.9577778,"width":0.28541666,"height":0.0422222},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne","depth":13,"bounds":{"left":0.32152778,"top":0.95444447,"width":0.2847222,"height":0.045555532},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne","depth":14,"bounds":{"left":0.32152778,"top":0.95444447,"width":0.2847222,"height":0.045555532},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Tomáš Čorej","depth":13,"bounds":{"left":0.32152778,"top":1.0,"width":0.057638887,"height":-0.07666671},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tomáš Čorej","depth":14,"bounds":{"left":0.32152778,"top":1.0,"width":0.057638887,"height":-0.07666671},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. apríla 2026","depth":13,"bounds":{"left":0.3875,"top":1.0,"width":0.0625,"height":-0.07666671},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zobraziť diskusiu (počet príspevkov: 1)","depth":13,"bounds":{"left":0.57569444,"top":1.0,"width":0.03125,"height":-0.0688889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-8958961360662367365
|
3222068321267975174
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Close tab
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Preskočiť hlavnú navigáciu
Preskočiť hlavnú navigáciu
Denník N
Denník N
Vyhľadávaný výraz
Hľadaj
Hľadaj
Vypnúť reklamu
Vypnúť reklamu
SK
SK
HU
HU
CZ
CZ
EU
EU
Otvoriť hlavné menu
MENU
SVET
SVET
DONALD TRUMP
DONALD TRUMP
IRÁN
IRÁN
VIKTOR ORBÁN
VIKTOR ORBÁN
VOJNA NA UKRAJINE
VOJNA NA UKRAJINE
KONFLIKT NA BLÍZKOM VÝCHODE
KONFLIKT NA BLÍZKOM VÝCHODE
MAĎARSKÉ VOĽBY
MAĎARSKÉ VOĽBY
USA
USA
RUSKO
RUSKO
VLADIMIR PUTIN
VLADIMIR PUTIN
VÝVOJ BOJOV NA UKRAJINE
VÝVOJ BOJOV NA UKRAJINE
Posun na nasledujúce
Irán
Irán
ODOBERAŤ
ODOBERAŤ
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
36 hodín chaosu: Ako Trump hrozbou o vyhladení Iránu pokazil rokovania (reportáž denníka New York Times)
The New York Times
The New York Times
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 4)
4
KOMENTÁRE
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Trump si vylial zlosť na šéfovi NATO, Rutteho stratégia ustupovania prestala vychádzať
Soňa Weissová
Soňa Weissová
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 24)
24
KOMENTÁRE
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Irán je ako čierna skrinka, do ktorej nevidíme. Ťažké rokovania v Pakistane naznačia jeho budúcnosť
Denisa Ballová
Denisa Ballová
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 4)
4
KOMENTÁRE
Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Trumpov svet (vývoj v Bielom dome) Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Trumpov svet (vývoj v Bielom dome) Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Trumpov svet (445. deň): Melania Trump náhle prehovorila o Epsteinovi a nikto nevie prečo
Jiří Sobota
Jiří Sobota
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 29)
29
KOMENTÁRE
Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Svetový newsfilter Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Svetový newsfilter Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Svetový newsfilter: Poradcovia Trumpa mali pochybnosti o vojne, no nepovedali mu to do očí
Denník N
Denník N
a ďalší
10. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Energetická analytička: Dotovanie cien benzínu a verejnej dopravy nerieši hlavný problém, ktorý tu máme
Tomáš Zemko
Tomáš Zemko
9. apríla 2026
Zobraziť diskusiu (počet príspevkov: 4)
4
KOMENTÁRE
Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Graf dňa Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Graf dňa Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Graf dňa: Po optimizme zavládla na trhoch opäť skepsa. Akciové indexy stále strácajú
Tomáš Zemko
Tomáš Zemko
9. apríla 2026
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Dosiahol len otázne otvorenie prielivu, ktorý už bol pred jeho útokom otvorený. Trumpovo skóre v Iráne je mizivé
Jan Wirnitzer
Jan Wirnitzer
9. apríla 2026
Zobraziť diskusiu (počet príspevkov: 3)
3
KOMENTÁRE
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Vojna s Iránom stála USA desiatky miliárd dolárov, stratili lietadlá aj vzácne radary
Tomáš Vasilko
Tomáš Vasilko
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Čo obsahuje 10-bodový iránsky plán a budú s ním Spojené štáty súhlasiť? (kľúčové otázky a odpovede o prímerí)
Denisa Ballová
Denisa Ballová
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 9)
9
KOMENTÁRE
Petr Mlejnek
Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu
Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu
Iránska vojna zvnútra: moc revolučných gárd, slabý prezident a boj o riadenie konfliktu
Petr Mlejnek
Petr Mlejnek
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Ekonomický newsfilter Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Ekonomický newsfilter Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Ekonomický newsfilter: Trump zničí skôr západnú civilizáciu než Irán
Tomáš Zemko
Tomáš Zemko
8. apríla 2026
Zobraziť diskusiu (počet príspevkov: 3)
3
KOMENTÁRE
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Už sme boli potrestaní dosť, hovoria Iránci, ktorí sa boja ďalších útokov na elektrárne a mosty
Denisa Ballová
Denisa Ballová
7. apríla 2026
Zobraziť diskusiu (počet príspevkov: 3)
3
KOMENTÁRE
Dalibor Roháč
O kolektívnej nepripravenosti na asymetrickú dronovú vojnu
O kolektívnej nepripravenosti na asymetrickú dronovú vojnu
O kolektívnej nepripravenosti na asymetrickú dronovú vojnu
Dalibor Roháč
Dalibor Roháč
7. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)
1
KOMENTÁRE
Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Trumpov svet (vývoj v Bielom dome) Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Trumpov svet (vývoj v Bielom dome) Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Trumpov svet (442. deň): Boh žehnaj vojne. Trump čoraz viac spája vojnu v Iráne s kresťanstvom
Anastasiia Furman
Anastasiia Furman
7. apríla 2026
Zobraziť diskusiu (počet príspevkov: 12)
12
KOMENTÁRE
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Zastierací manéver CIA a zničené lietadlá za stovky miliónov. Ako Američania zachránili vojaka zo zostrelenej stíhačky F-15
Tomáš Čorej
Tomáš Čorej
5. apríla 2026
Zobraziť diskusiu (počet príspevkov: 60)
60
KOMENTÁRE
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Nečakaný mierotvorca medzi Bielym domom a Teheránom: ako chce Pakistan ukončiť vojnu v Iráne
Tomáš Čorej
Tomáš Čorej
3. apríla 2026
Zobraziť diskusiu (počet príspevkov: 1)...
|
NULL
|
|
1461
|
32
|
30
|
2026-04-11T17:30:03.757771+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928603757_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:30:03-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
336458342441323293
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:30:03-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1460
|
|
1460
|
32
|
29
|
2026-04-11T17:30:01.894673+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928601894_m1.jpg...
|
Firefox
|
Linkwarden – Get this Extension for 🦊 Firefox (en- Linkwarden – Get this Extension for 🦊 Firefox (en-US) — Personal...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Sync and save data
Sign In
Sign In
Personal
New ta Sync and save data
Sign In
Sign In
Personal
New tab
New window
New private window
Bookmarks
History
Downloads
Passwords
Extensions and themes
Print…
Save page as…
Find in page…
Translate page…
Zoom
Zoom out
100%
Zoom in
Full screen
Settings
More tools
Report broken site
Help
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Recommendations
Recommendations
Extensions
Extensions
Themes
Themes
Plugins
Plugins
Firefox Settings
Firefox Settings
Add-ons Support
Add-ons Support
Find more add-ons
bitwrden
bitwrden
Search addons.mozilla.org
Personalize Your Firefox
Personalize Your Firefox
Tools for all add-ons
Extensions and themes let you customize Firefox. They can boost privacy, enhance productivity, improve media, change the way Firefox looks, and so much more. These small software programs are often developed by a third party. Here’s a selection Firefox
recommends
recommends
for exceptional security, performance, and functionality.
Some of these recommendations are personalized. They are based on other extensions you’ve installed, profile preferences, and usage statistics.
Learn more
Learn more
Close
Crimson Merlot
Crimson Merlot
by
DV8
DV8
Install Theme
ClearURLs
ClearURLs
by
Kevin
Kevin
Add to Firefox
Remove tracking elements from web links.
Users: 516,099
automaticDark - Time-Based Theme Changer
automaticDark - Time-Based Theme Changer
by
simonkhzhang
simonkhzhang
Add to Firefox
Time-based theme changer. Make Firefox change colors as the time of day changes.
Users: 15,692
Rosy Sky
Rosy Sky
by
g1308_
g1308_
Install Theme
Time Tracker - Web Habit Builder
Time Tracker - Web Habit Builder
by
sheepie z
sheepie z
Add to Firefox
Track the time you spend on the web, analyze your habits, block distracting sites & more.
Users: 14,423
Download All Images
Download All Images
by
Joe Ertaba
Joe Ertaba
Add to Firefox
Save images easily—just grab them from your current tab.
Users: 104,720
Double Phantasy
Double Phantasy
by
Dave
Dave
Install Theme
Find more add-ons
Privacy Policy
Privacy Policy...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Sync and save data","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Sign In","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sign In","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Personal","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New tab","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New window","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New private window","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Bookmarks","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"History","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Downloads","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Passwords","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Extensions and themes","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Print…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Save page as…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Find in page…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Translate page…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Zoom","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zoom out","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"100%","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Zoom in","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Full screen","depth":3,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Settings","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More tools","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Report broken site","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Help","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Recommendations","depth":9,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Recommendations","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions","depth":9,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Themes","depth":9,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Plugins","depth":9,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Plugins","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Settings","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Settings","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Add-ons Support","depth":11,"bounds":{"left":0.0,"top":0.0,"width":0.17083333,"height":0.04222222},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Support","depth":13,"bounds":{"left":0.0,"top":0.0,"width":0.075,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Find more add-ons","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"bitwrden","depth":12,"value":"bitwrden","help_text":"","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"bitwrden","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search addons.mozilla.org","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Personalize Your Firefox","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Personalize Your Firefox","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Tools for all add-ons","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Extensions and themes let you customize Firefox. They can boost privacy, enhance productivity, improve media, change the way Firefox looks, and so much more. These small software programs are often developed by a third party. Here’s a selection Firefox","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"recommends","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"recommends","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"for exceptional security, performance, and functionality.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Some of these recommendations are personalized. They are based on other extensions you’ve installed, profile preferences, and usage statistics.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Crimson Merlot","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Crimson Merlot","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"DV8","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"DV8","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Install Theme","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"ClearURLs","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ClearURLs","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Kevin","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Kevin","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add to Firefox","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Remove tracking elements from web links.","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Users: 516,099","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"automaticDark - Time-Based Theme Changer","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"automaticDark - Time-Based Theme Changer","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"simonkhzhang","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"simonkhzhang","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add to Firefox","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Time-based theme changer. Make Firefox change colors as the time of day changes.","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Users: 15,692","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Rosy Sky","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rosy Sky","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"g1308_","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"g1308_","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Install Theme","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Time Tracker - Web Habit Builder","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Time Tracker - Web Habit Builder","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"sheepie z","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"sheepie z","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add to Firefox","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Track the time you spend on the web, analyze your habits, block distracting sites & more.","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Users: 14,423","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Download All Images","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Download All Images","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Joe Ertaba","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Joe Ertaba","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add to Firefox","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Save images easily—just grab them from your current tab.","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Users: 104,720","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Double Phantasy","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Double Phantasy","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Dave","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dave","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Install Theme","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Find more add-ons","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Privacy Policy","depth":11,"bounds":{"left":0.34861112,"top":0.0,"width":0.058333334,"height":0.017777778},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy Policy","depth":12,"bounds":{"left":0.34861112,"top":0.0,"width":0.058333334,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
4415098977594849275
|
3796289383356581388
|
click
|
accessibility
|
NULL
|
Sync and save data
Sign In
Sign In
Personal
New ta Sync and save data
Sign In
Sign In
Personal
New tab
New window
New private window
Bookmarks
History
Downloads
Passwords
Extensions and themes
Print…
Save page as…
Find in page…
Translate page…
Zoom
Zoom out
100%
Zoom in
Full screen
Settings
More tools
Report broken site
Help
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Recommendations
Recommendations
Extensions
Extensions
Themes
Themes
Plugins
Plugins
Firefox Settings
Firefox Settings
Add-ons Support
Add-ons Support
Find more add-ons
bitwrden
bitwrden
Search addons.mozilla.org
Personalize Your Firefox
Personalize Your Firefox
Tools for all add-ons
Extensions and themes let you customize Firefox. They can boost privacy, enhance productivity, improve media, change the way Firefox looks, and so much more. These small software programs are often developed by a third party. Here’s a selection Firefox
recommends
recommends
for exceptional security, performance, and functionality.
Some of these recommendations are personalized. They are based on other extensions you’ve installed, profile preferences, and usage statistics.
Learn more
Learn more
Close
Crimson Merlot
Crimson Merlot
by
DV8
DV8
Install Theme
ClearURLs
ClearURLs
by
Kevin
Kevin
Add to Firefox
Remove tracking elements from web links.
Users: 516,099
automaticDark - Time-Based Theme Changer
automaticDark - Time-Based Theme Changer
by
simonkhzhang
simonkhzhang
Add to Firefox
Time-based theme changer. Make Firefox change colors as the time of day changes.
Users: 15,692
Rosy Sky
Rosy Sky
by
g1308_
g1308_
Install Theme
Time Tracker - Web Habit Builder
Time Tracker - Web Habit Builder
by
sheepie z
sheepie z
Add to Firefox
Track the time you spend on the web, analyze your habits, block distracting sites & more.
Users: 14,423
Download All Images
Download All Images
by
Joe Ertaba
Joe Ertaba
Add to Firefox
Save images easily—just grab them from your current tab.
Users: 104,720
Double Phantasy
Double Phantasy
by
Dave
Dave
Install Theme
Find more add-ons
Privacy Policy
Privacy Policy...
|
NULL
|
|
1459
|
32
|
28
|
2026-04-11T17:29:59.165075+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928599165_m1.jpg...
|
Firefox
|
Linkwarden – Get this Extension for 🦊 Firefox (en- Linkwarden – Get this Extension for 🦊 Firefox (en-US) — Personal...
|
True
|
addons.mozilla.org/en-US/firefox/addon/linkwarden/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Sync and save data
Sign In
Sign In
Personal
New ta Sync and save data
Sign In
Sign In
Personal
New tab
New window
New private window
Bookmarks
History
Downloads
Passwords
Extensions and themes
Print…
Save page as…
Find in page…
Translate page…
Zoom
Zoom out
100%
Zoom in
Full screen
Settings
More tools
Report broken site
Help
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Remove
Remove
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites
Learn more
Learn more
More information
Add-on Links
Support site
Support site
Support Email
Support Email
Version
1.5.2
Size
206.89 KB
Last updated
18 days ago (Mar 24, 2026)
Related Categories
Bookmarks
Bookmarks
∙
Social & Communication
Social & Communication
License
MIT License
MIT License
Version History
See all versions
See all versions
Add to collection
Report this add-on
Report this add-on
Other popular extensions
Dark Reader Recommended Recommended
Dark Reader
Dark Reader
Recommended Recommended
Recommended
Recommended
1,311,453 users
1,311,453 users
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
407,741 users
407,741 users
Search by Image Recommended Recommended
Search by Image
Search by Image
Recommended Recommended
Recommended
Recommended
440,911 users
440,911 users
Tree Style Tab Recommended Recommended
Tree Style Tab
Tree Style Tab
Recommended Recommended
Recommended
Recommended
160,924 users
160,924 users
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Sync and save data","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Sign In","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sign In","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Personal","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New tab","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New window","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New private window","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Bookmarks","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"History","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Downloads","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Passwords","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Extensions and themes","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Print…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Save page as…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Find in page…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Translate page…","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Zoom","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Zoom out","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"100%","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Zoom in","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Full screen","depth":3,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Settings","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More tools","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Report broken site","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Help","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Firefox Browser Add-ons","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Browser Add-ons","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extensions","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Themes","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More…","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"More…","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":9,"help_text":"Learn how to create extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":9,"help_text":"Submit and manage extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Search","depth":9,"value":"Search","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Search","depth":10,"help_text":"","placeholder":"Find add-ons","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Search","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Linkwarden by Daniel31x13 , Jordan Higuera","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Linkwarden","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Daniel31x13","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Daniel31x13","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jordan Higuera","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jordan Higuera","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The browser extension for Linkwarden.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4.6 (35 reviews) 4.6 (35 reviews)","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Remove","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Remove","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Extension Metadata","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Extension Metadata","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Screenshots","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 4.6 by 35 reviewers","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in to rate this extension","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in to rate this extension","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"5","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"5","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 27 five-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"27","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 3 four-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 4 three-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"2","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read the 1 two-star review","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"1","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"There are no one-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"0","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"0","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 35 reviews","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Read all 35 reviews","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Permissions and data","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Required permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Required permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Read and modify bookmarks","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access browser tabs","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Optional permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Optional permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access your data for all websites","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":13,"bounds":{"left":0.20208333,"top":0.0,"width":0.05277778,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":14,"bounds":{"left":0.20208333,"top":0.0,"width":0.05277778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More information","depth":12,"bounds":{"left":0.20208333,"top":0.0,"width":0.09236111,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add-on Links","depth":13,"bounds":{"left":0.20208333,"top":0.045555554,"width":0.06111111,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Support site","depth":15,"bounds":{"left":0.20208333,"top":0.06888889,"width":0.056944445,"height":0.02},"help_text":"https://linkwarden.app/","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Support site","depth":16,"bounds":{"left":0.20208333,"top":0.06888889,"width":0.056944445,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Support Email","depth":15,"bounds":{"left":0.20208333,"top":0.09222222,"width":0.06527778,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Support Email","depth":16,"bounds":{"left":0.20208333,"top":0.09222222,"width":0.06527778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Version","depth":13,"bounds":{"left":0.20208333,"top":0.12888889,"width":0.034027778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.5.2","depth":13,"bounds":{"left":0.20208333,"top":0.15222222,"width":0.021527778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Size","depth":13,"bounds":{"left":0.49930555,"top":0.045555554,"width":0.019444445,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"206.89 KB","depth":13,"bounds":{"left":0.49930555,"top":0.06888889,"width":0.048611112,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last updated","depth":13,"bounds":{"left":0.49930555,"top":0.10555556,"width":0.060416665,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"18 days ago (Mar 24, 2026)","depth":13,"bounds":{"left":0.49930555,"top":0.12888889,"width":0.12777779,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Related Categories","depth":13,"bounds":{"left":0.49930555,"top":0.16555555,"width":0.0875,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bookmarks","depth":15,"bounds":{"left":0.49930555,"top":0.18888889,"width":0.052083332,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bookmarks","depth":16,"bounds":{"left":0.49930555,"top":0.18888889,"width":0.052083332,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"∙","depth":15,"bounds":{"left":0.55138886,"top":0.18888889,"width":0.007638889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Social & Communication","depth":15,"bounds":{"left":0.5590278,"top":0.18888889,"width":0.1125,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Social & Communication","depth":16,"bounds":{"left":0.5590278,"top":0.18888889,"width":0.1125,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"License","depth":13,"bounds":{"left":0.7965278,"top":0.045555554,"width":0.035416666,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"MIT License","depth":13,"bounds":{"left":0.7965278,"top":0.06888889,"width":0.05625,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"MIT License","depth":14,"bounds":{"left":0.7965278,"top":0.06888889,"width":0.05625,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Version History","depth":13,"bounds":{"left":0.7965278,"top":0.10555556,"width":0.07013889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"See all versions","depth":15,"bounds":{"left":0.7965278,"top":0.12888889,"width":0.072222225,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"See all versions","depth":16,"bounds":{"left":0.7965278,"top":0.12888889,"width":0.072222225,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add to collection","depth":13,"bounds":{"left":0.7965278,"top":0.16555555,"width":0.07777778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report this add-on","depth":13,"bounds":{"left":0.20208333,"top":0.25666666,"width":0.0875,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report this add-on","depth":14,"bounds":{"left":0.20208333,"top":0.25666666,"width":0.0875,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Other popular extensions","depth":10,"bounds":{"left":0.20208333,"top":0.3611111,"width":0.13611111,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Dark Reader Recommended Recommended","depth":11,"bounds":{"left":0.23819445,"top":0.4311111,"width":0.2326389,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Dark Reader","depth":12,"bounds":{"left":0.23819445,"top":0.43333334,"width":0.06527778,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dark Reader","depth":13,"bounds":{"left":0.23819445,"top":0.43333334,"width":0.06527778,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.31388888,"top":0.43666667,"width":0.07152778,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.31319445,"top":0.43555555,"width":0.079166666,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.3263889,"top":0.43555555,"width":0.059027776,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1,311,453 users","depth":11,"bounds":{"left":0.2361111,"top":0.45777777,"width":0.23472223,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,311,453 users","depth":12,"bounds":{"left":0.24722221,"top":0.45666668,"width":0.07013889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Grammar and Spell Checker - LanguageTool","depth":11,"bounds":{"left":0.5402778,"top":0.4311111,"width":0.23333333,"height":0.04222222},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Grammar and Spell Checker - LanguageTool","depth":12,"bounds":{"left":0.5402778,"top":0.4311111,"width":0.22777778,"height":0.04222222},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Grammar and Spell Checker - LanguageTool","depth":13,"bounds":{"left":0.5402778,"top":0.43,"width":0.15694444,"height":0.043333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"407,741 users","depth":11,"bounds":{"left":0.5381944,"top":0.47333333,"width":0.23541667,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"407,741 users","depth":12,"bounds":{"left":0.54930556,"top":0.4722222,"width":0.0625,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Search by Image Recommended Recommended","depth":11,"bounds":{"left":0.84305555,"top":0.4311111,"width":0.15694445,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Search by Image","depth":12,"bounds":{"left":0.84305555,"top":0.43333334,"width":0.088194445,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search by Image","depth":13,"bounds":{"left":0.84305555,"top":0.43333334,"width":0.088194445,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.94166666,"top":0.43666667,"width":0.058333337,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.9409722,"top":0.43555555,"width":0.05902779,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.95416665,"top":0.43555555,"width":0.04583335,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"440,911 users","depth":11,"bounds":{"left":0.8402778,"top":0.45777777,"width":0.15972221,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"440,911 users","depth":12,"bounds":{"left":0.8513889,"top":0.45666668,"width":0.06388889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Tree Style Tab Recommended Recommended","depth":11,"bounds":{"left":0.23819445,"top":0.5277778,"width":0.2326389,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Tree Style Tab","depth":12,"bounds":{"left":0.23819445,"top":0.5311111,"width":0.07638889,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tree Style Tab","depth":13,"bounds":{"left":0.23819445,"top":0.53,"width":0.07638889,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.325,"top":0.53333336,"width":0.07152778,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.32430556,"top":0.53333336,"width":0.07847222,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.3375,"top":0.53333336,"width":0.059027776,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"160,924 users","depth":11,"bounds":{"left":0.2361111,"top":0.55444443,"width":0.23472223,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"160,924 users","depth":12,"bounds":{"left":0.24722221,"top":0.55444443,"width":0.06458333,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Go to Mozilla's homepage","depth":9,"bounds":{"left":0.18541667,"top":0.7266667,"width":0.07777778,"height":0.043333333},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to Mozilla's homepage","depth":12,"bounds":{"left":0.18472221,"top":0.7277778,"width":0.011111111,"height":0.27222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Add-ons","depth":9,"bounds":{"left":0.41458333,"top":0.7266667,"width":0.22847222,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Add-ons","depth":10,"bounds":{"left":0.41458333,"top":0.72888887,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons","depth":11,"bounds":{"left":0.41458333,"top":0.72888887,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"About","depth":11,"bounds":{"left":0.41458333,"top":0.77,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"About","depth":12,"bounds":{"left":0.41458333,"top":0.77,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":11,"bounds":{"left":0.41458333,"top":0.8111111,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":12,"bounds":{"left":0.41458333,"top":0.8111111,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":11,"bounds":{"left":0.41458333,"top":0.8522222,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":12,"bounds":{"left":0.41458333,"top":0.8522222,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":11,"bounds":{"left":0.41458333,"top":0.8933333,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":12,"bounds":{"left":0.41458333,"top":0.8933333,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Policies","depth":11,"bounds":{"left":0.41458333,"top":0.9344444,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Policies","depth":12,"bounds":{"left":0.41458333,"top":0.9344444,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Community Blog","depth":11,"bounds":{"left":0.41458333,"top":0.97555554,"width":0.09652778,"height":0.02444446},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Community Blog","depth":12,"bounds":{"left":0.41458333,"top":0.97555554,"width":0.09652778,"height":0.02444446},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Forum","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.036805555,"height":-0.01666665},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Forum","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.036805555,"height":-0.01666665},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report a bug","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.07638889,"height":-0.057777762},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report a bug","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.07638889,"height":-0.057777762},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Review Guide","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.07986111,"height":-0.098888874},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Review Guide","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.07986111,"height":-0.098888874},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Browsers","depth":9,"bounds":{"left":0.64305556,"top":0.7266667,"width":0.22916667,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browsers","depth":10,"bounds":{"left":0.64305556,"top":0.72888887,"width":0.056944445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop","depth":11,"bounds":{"left":0.64305556,"top":0.77,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop","depth":12,"bounds":{"left":0.64305556,"top":0.77,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Mobile","depth":11,"bounds":{"left":0.64305556,"top":0.8111111,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile","depth":12,"bounds":{"left":0.64305556,"top":0.8111111,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Enterprise","depth":11,"bounds":{"left":0.64305556,"top":0.8522222,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enterprise","depth":12,"bounds":{"left":0.64305556,"top":0.8522222,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Products","depth":9,"bounds":{"left":0.87222224,"top":0.7266667,"width":0.12777776,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Products","depth":10,"bounds":{"left":0.87222224,"top":0.72888887,"width":0.054166667,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Browsers","depth":11,"bounds":{"left":0.87222224,"top":0.77,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browsers","depth":12,"bounds":{"left":0.87222224,"top":0.77,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VPN","depth":11,"bounds":{"left":0.87222224,"top":0.8111111,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VPN","depth":12,"bounds":{"left":0.87222224,"top":0.8111111,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Relay","depth":11,"bounds":{"left":0.87222224,"top":0.8522222,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Relay","depth":12,"bounds":{"left":0.87222224,"top":0.8522222,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Monitor","depth":11,"bounds":{"left":0.87222224,"top":0.8933333,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Monitor","depth":12,"bounds":{"left":0.87222224,"top":0.8933333,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pocket","depth":11,"bounds":{"left":0.87222224,"top":0.9344444,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pocket","depth":12,"bounds":{"left":0.87222224,"top":0.9344444,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bluesky (@firefox.com)","depth":11,"bounds":{"left":0.87222224,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bluesky (@firefox.com)","depth":14,"bounds":{"left":0.8715278,"top":1.0,"width":0.011111111,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Instagram (Firefox)","depth":11,"bounds":{"left":0.8972222,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Instagram (Firefox)","depth":14,"bounds":{"left":0.89652777,"top":1.0,"width":0.010416667,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"YouTube (firefoxchannel)","depth":11,"bounds":{"left":0.9222222,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"YouTube (firefoxchannel)","depth":14,"bounds":{"left":0.9215278,"top":1.0,"width":0.007638889,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Cookies","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Cookies","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Legal","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Legal","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Except where otherwise","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"noted","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"noted","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", content on this site is licensed under the","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Creative Commons Attribution Share-Alike License v3.0","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Creative Commons Attribution Share-Alike License v3.0","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or any later version.","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Change language","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
7500419818748546179
|
-5337494232929759522
|
click
|
accessibility
|
NULL
|
Sync and save data
Sign In
Sign In
Personal
New ta Sync and save data
Sign In
Sign In
Personal
New tab
New window
New private window
Bookmarks
History
Downloads
Passwords
Extensions and themes
Print…
Save page as…
Find in page…
Translate page…
Zoom
Zoom out
100%
Zoom in
Full screen
Settings
More tools
Report broken site
Help
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Remove
Remove
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites
Learn more
Learn more
More information
Add-on Links
Support site
Support site
Support Email
Support Email
Version
1.5.2
Size
206.89 KB
Last updated
18 days ago (Mar 24, 2026)
Related Categories
Bookmarks
Bookmarks
∙
Social & Communication
Social & Communication
License
MIT License
MIT License
Version History
See all versions
See all versions
Add to collection
Report this add-on
Report this add-on
Other popular extensions
Dark Reader Recommended Recommended
Dark Reader
Dark Reader
Recommended Recommended
Recommended
Recommended
1,311,453 users
1,311,453 users
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
407,741 users
407,741 users
Search by Image Recommended Recommended
Search by Image
Search by Image
Recommended Recommended
Recommended
Recommended
440,911 users
440,911 users
Tree Style Tab Recommended Recommended
Tree Style Tab
Tree Style Tab
Recommended Recommended
Recommended
Recommended
160,924 users
160,924 users
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
1458
|
|
1458
|
32
|
27
|
2026-04-11T17:29:56.518672+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928596518_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:29:56-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-3586394165700981304
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:29:56-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
|
1457
|
32
|
26
|
2026-04-11T17:29:55.241921+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928595241_m1.jpg...
|
Firefox
|
Linkwarden – Get this Extension for 🦊 Firefox (en- Linkwarden – Get this Extension for 🦊 Firefox (en-US) — Personal...
|
True
|
addons.mozilla.org/en-US/firefox/addon/linkwarden/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Add
Linkwarden
Required permissions:
Required perm Add
Linkwarden
Required permissions:
Required permissions:
Access your data for all websites
Read and modify bookmarks
Access browser tabs
Optional settings:
Optional settings:
Allow extension to run in private windows
Allow extension to run in private windows
Learn more
Learn more
Cancel
Add
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Remove
Remove
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Add","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Linkwarden","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Required permissions:","depth":3,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Required permissions:","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access your data for all websites","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Read and modify bookmarks","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access browser tabs","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Optional settings:","depth":3,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Optional settings:","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Allow extension to run in private windows","depth":9,"help_text":"","role_description":"checkbox","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Allow extension to run in private windows","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":3,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":4,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Cancel","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Add","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Firefox Browser Add-ons","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Browser Add-ons","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extensions","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Themes","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More…","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"More…","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":9,"help_text":"Learn how to create extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":9,"help_text":"Submit and manage extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Search","depth":9,"value":"Search","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Search","depth":10,"help_text":"","placeholder":"Find add-ons","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Search","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Linkwarden by Daniel31x13 , Jordan Higuera","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Linkwarden","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Daniel31x13","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Daniel31x13","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jordan Higuera","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jordan Higuera","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The browser extension for Linkwarden.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4.6 (35 reviews) 4.6 (35 reviews)","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Remove","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Remove","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Extension Metadata","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Extension Metadata","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Screenshots","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 4.6 by 35 reviewers","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in to rate this extension","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in to rate this extension","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"5","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"5","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 27 five-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"27","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 3 four-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 4 three-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"2","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read the 1 two-star review","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"1","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"There are no one-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"0","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"0","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 35 reviews","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Read all 35 reviews","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Permissions and data","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Required permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Required permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Read and modify bookmarks","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access browser tabs","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Optional permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Optional permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access your data for all websites","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
2236910719486679195
|
-7643626308474630450
|
click
|
accessibility
|
NULL
|
Add
Linkwarden
Required permissions:
Required perm Add
Linkwarden
Required permissions:
Required permissions:
Access your data for all websites
Read and modify bookmarks
Access browser tabs
Optional settings:
Optional settings:
Allow extension to run in private windows
Allow extension to run in private windows
Learn more
Learn more
Cancel
Add
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Remove
Remove
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites...
|
1456
|
|
1456
|
32
|
25
|
2026-04-11T17:29:46.555323+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928586555_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:29:46-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-5141584208113579176
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:29:46-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
|
1455
|
32
|
24
|
2026-04-11T17:29:44.941055+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928584941_m1.jpg...
|
Firefox
|
Linkwarden – Get this Extension for 🦊 Firefox (en- Linkwarden – Get this Extension for 🦊 Firefox (en-US) — Personal...
|
True
|
addons.mozilla.org/en-US/firefox/addon/linkwarden/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Add to Firefox
Add to Firefox
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites
Learn more
Learn more
More information
Add-on Links
Support site
Support site
Support Email
Support Email
Version
1.5.2
Size
206.89 KB
Last updated
18 days ago (Mar 24, 2026)
Related Categories
Bookmarks
Bookmarks
∙
Social & Communication
Social & Communication
License
MIT License
MIT License
Version History
See all versions
See all versions
Add to collection
Report this add-on
Report this add-on
Other popular extensions
Dark Reader Recommended Recommended
Dark Reader
Dark Reader
Recommended Recommended
Recommended
Recommended
1,311,453 users
1,311,453 users
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
407,741 users
407,741 users
Search by Image Recommended Recommended
Search by Image
Search by Image
Recommended Recommended
Recommended
Recommended
440,911 users
440,911 users
Tree Style Tab Recommended Recommended
Tree Style Tab
Tree Style Tab
Recommended Recommended
Recommended
Recommended
160,924 users
160,924 users
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language
Close...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Firefox Browser Add-ons","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Browser Add-ons","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extensions","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Themes","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More…","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"More…","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":9,"help_text":"Learn how to create extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":9,"help_text":"Submit and manage extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Search","depth":9,"value":"Search","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Search","depth":10,"help_text":"","placeholder":"Find add-ons","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Search","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Linkwarden by Daniel31x13 , Jordan Higuera","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Linkwarden","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Daniel31x13","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Daniel31x13","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jordan Higuera","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jordan Higuera","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The browser extension for Linkwarden.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4.6 (35 reviews) 4.6 (35 reviews)","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Add to Firefox","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add to Firefox","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Extension Metadata","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Extension Metadata","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Screenshots","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 4.6 by 35 reviewers","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in to rate this extension","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in to rate this extension","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"5","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"5","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 27 five-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"27","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 3 four-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 4 three-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"2","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read the 1 two-star review","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"1","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"There are no one-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"0","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"0","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 35 reviews","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Read all 35 reviews","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Permissions and data","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Required permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Required permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Read and modify bookmarks","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access browser tabs","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Optional permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Optional permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access your data for all websites","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":13,"bounds":{"left":0.20208333,"top":0.0,"width":0.05277778,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":14,"bounds":{"left":0.20208333,"top":0.0,"width":0.05277778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More information","depth":12,"bounds":{"left":0.20208333,"top":0.0,"width":0.09236111,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add-on Links","depth":13,"bounds":{"left":0.20208333,"top":0.045555554,"width":0.06111111,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Support site","depth":15,"bounds":{"left":0.20208333,"top":0.06888889,"width":0.056944445,"height":0.02},"help_text":"https://linkwarden.app/","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Support site","depth":16,"bounds":{"left":0.20208333,"top":0.06888889,"width":0.056944445,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Support Email","depth":15,"bounds":{"left":0.20208333,"top":0.09222222,"width":0.06527778,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Support Email","depth":16,"bounds":{"left":0.20208333,"top":0.09222222,"width":0.06527778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Version","depth":13,"bounds":{"left":0.20208333,"top":0.12888889,"width":0.034027778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.5.2","depth":13,"bounds":{"left":0.20208333,"top":0.15222222,"width":0.021527778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Size","depth":13,"bounds":{"left":0.49930555,"top":0.045555554,"width":0.019444445,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"206.89 KB","depth":13,"bounds":{"left":0.49930555,"top":0.06888889,"width":0.048611112,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last updated","depth":13,"bounds":{"left":0.49930555,"top":0.10555556,"width":0.060416665,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"18 days ago (Mar 24, 2026)","depth":13,"bounds":{"left":0.49930555,"top":0.12888889,"width":0.12777779,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Related Categories","depth":13,"bounds":{"left":0.49930555,"top":0.16555555,"width":0.0875,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bookmarks","depth":15,"bounds":{"left":0.49930555,"top":0.18888889,"width":0.052083332,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bookmarks","depth":16,"bounds":{"left":0.49930555,"top":0.18888889,"width":0.052083332,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"∙","depth":15,"bounds":{"left":0.55138886,"top":0.18888889,"width":0.007638889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Social & Communication","depth":15,"bounds":{"left":0.5590278,"top":0.18888889,"width":0.1125,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Social & Communication","depth":16,"bounds":{"left":0.5590278,"top":0.18888889,"width":0.1125,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"License","depth":13,"bounds":{"left":0.7965278,"top":0.045555554,"width":0.035416666,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"MIT License","depth":13,"bounds":{"left":0.7965278,"top":0.06888889,"width":0.05625,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"MIT License","depth":14,"bounds":{"left":0.7965278,"top":0.06888889,"width":0.05625,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Version History","depth":13,"bounds":{"left":0.7965278,"top":0.10555556,"width":0.07013889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"See all versions","depth":15,"bounds":{"left":0.7965278,"top":0.12888889,"width":0.072222225,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"See all versions","depth":16,"bounds":{"left":0.7965278,"top":0.12888889,"width":0.072222225,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add to collection","depth":13,"bounds":{"left":0.7965278,"top":0.16555555,"width":0.07777778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report this add-on","depth":13,"bounds":{"left":0.20208333,"top":0.25666666,"width":0.0875,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report this add-on","depth":14,"bounds":{"left":0.20208333,"top":0.25666666,"width":0.0875,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Other popular extensions","depth":10,"bounds":{"left":0.20208333,"top":0.3611111,"width":0.13611111,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Dark Reader Recommended Recommended","depth":11,"bounds":{"left":0.23819445,"top":0.4311111,"width":0.2326389,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Dark Reader","depth":12,"bounds":{"left":0.23819445,"top":0.43333334,"width":0.06527778,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dark Reader","depth":13,"bounds":{"left":0.23819445,"top":0.43333334,"width":0.06527778,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.31388888,"top":0.43666667,"width":0.07152778,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.31319445,"top":0.43555555,"width":0.079166666,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.3263889,"top":0.43555555,"width":0.059027776,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1,311,453 users","depth":11,"bounds":{"left":0.2361111,"top":0.45777777,"width":0.23472223,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,311,453 users","depth":12,"bounds":{"left":0.24722221,"top":0.45666668,"width":0.07013889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Grammar and Spell Checker - LanguageTool","depth":11,"bounds":{"left":0.5402778,"top":0.4311111,"width":0.23333333,"height":0.04222222},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Grammar and Spell Checker - LanguageTool","depth":12,"bounds":{"left":0.5402778,"top":0.4311111,"width":0.22777778,"height":0.04222222},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Grammar and Spell Checker - LanguageTool","depth":13,"bounds":{"left":0.5402778,"top":0.43,"width":0.15694444,"height":0.043333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"407,741 users","depth":11,"bounds":{"left":0.5381944,"top":0.47333333,"width":0.23541667,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"407,741 users","depth":12,"bounds":{"left":0.54930556,"top":0.4722222,"width":0.0625,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Search by Image Recommended Recommended","depth":11,"bounds":{"left":0.84305555,"top":0.4311111,"width":0.15694445,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Search by Image","depth":12,"bounds":{"left":0.84305555,"top":0.43333334,"width":0.088194445,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search by Image","depth":13,"bounds":{"left":0.84305555,"top":0.43333334,"width":0.088194445,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.94166666,"top":0.43666667,"width":0.058333337,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.9409722,"top":0.43555555,"width":0.05902779,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.95416665,"top":0.43555555,"width":0.04583335,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"440,911 users","depth":11,"bounds":{"left":0.8402778,"top":0.45777777,"width":0.15972221,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"440,911 users","depth":12,"bounds":{"left":0.8513889,"top":0.45666668,"width":0.06388889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Tree Style Tab Recommended Recommended","depth":11,"bounds":{"left":0.23819445,"top":0.5277778,"width":0.2326389,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Tree Style Tab","depth":12,"bounds":{"left":0.23819445,"top":0.5311111,"width":0.07638889,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tree Style Tab","depth":13,"bounds":{"left":0.23819445,"top":0.53,"width":0.07638889,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.325,"top":0.53333336,"width":0.07152778,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.32430556,"top":0.53333336,"width":0.07847222,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.3375,"top":0.53333336,"width":0.059027776,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"160,924 users","depth":11,"bounds":{"left":0.2361111,"top":0.55444443,"width":0.23472223,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"160,924 users","depth":12,"bounds":{"left":0.24722221,"top":0.55444443,"width":0.06458333,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Go to Mozilla's homepage","depth":9,"bounds":{"left":0.18541667,"top":0.7266667,"width":0.07777778,"height":0.043333333},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to Mozilla's homepage","depth":12,"bounds":{"left":0.18472221,"top":0.7277778,"width":0.011111111,"height":0.27222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Add-ons","depth":9,"bounds":{"left":0.41458333,"top":0.7266667,"width":0.22847222,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Add-ons","depth":10,"bounds":{"left":0.41458333,"top":0.72888887,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons","depth":11,"bounds":{"left":0.41458333,"top":0.72888887,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"About","depth":11,"bounds":{"left":0.41458333,"top":0.77,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"About","depth":12,"bounds":{"left":0.41458333,"top":0.77,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":11,"bounds":{"left":0.41458333,"top":0.8111111,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":12,"bounds":{"left":0.41458333,"top":0.8111111,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":11,"bounds":{"left":0.41458333,"top":0.8522222,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":12,"bounds":{"left":0.41458333,"top":0.8522222,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":11,"bounds":{"left":0.41458333,"top":0.8933333,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":12,"bounds":{"left":0.41458333,"top":0.8933333,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Policies","depth":11,"bounds":{"left":0.41458333,"top":0.9344444,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Policies","depth":12,"bounds":{"left":0.41458333,"top":0.9344444,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Community Blog","depth":11,"bounds":{"left":0.41458333,"top":0.97555554,"width":0.09652778,"height":0.02444446},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Community Blog","depth":12,"bounds":{"left":0.41458333,"top":0.97555554,"width":0.09652778,"height":0.02444446},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Forum","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.036805555,"height":-0.01666665},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Forum","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.036805555,"height":-0.01666665},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report a bug","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.07638889,"height":-0.057777762},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report a bug","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.07638889,"height":-0.057777762},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Review Guide","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.07986111,"height":-0.098888874},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Review Guide","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.07986111,"height":-0.098888874},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Browsers","depth":9,"bounds":{"left":0.64305556,"top":0.7266667,"width":0.22916667,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browsers","depth":10,"bounds":{"left":0.64305556,"top":0.72888887,"width":0.056944445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop","depth":11,"bounds":{"left":0.64305556,"top":0.77,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop","depth":12,"bounds":{"left":0.64305556,"top":0.77,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Mobile","depth":11,"bounds":{"left":0.64305556,"top":0.8111111,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile","depth":12,"bounds":{"left":0.64305556,"top":0.8111111,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Enterprise","depth":11,"bounds":{"left":0.64305556,"top":0.8522222,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enterprise","depth":12,"bounds":{"left":0.64305556,"top":0.8522222,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Products","depth":9,"bounds":{"left":0.87222224,"top":0.7266667,"width":0.12777776,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Products","depth":10,"bounds":{"left":0.87222224,"top":0.72888887,"width":0.054166667,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Browsers","depth":11,"bounds":{"left":0.87222224,"top":0.77,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browsers","depth":12,"bounds":{"left":0.87222224,"top":0.77,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VPN","depth":11,"bounds":{"left":0.87222224,"top":0.8111111,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VPN","depth":12,"bounds":{"left":0.87222224,"top":0.8111111,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Relay","depth":11,"bounds":{"left":0.87222224,"top":0.8522222,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Relay","depth":12,"bounds":{"left":0.87222224,"top":0.8522222,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Monitor","depth":11,"bounds":{"left":0.87222224,"top":0.8933333,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Monitor","depth":12,"bounds":{"left":0.87222224,"top":0.8933333,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pocket","depth":11,"bounds":{"left":0.87222224,"top":0.9344444,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pocket","depth":12,"bounds":{"left":0.87222224,"top":0.9344444,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bluesky (@firefox.com)","depth":11,"bounds":{"left":0.87222224,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bluesky (@firefox.com)","depth":14,"bounds":{"left":0.8715278,"top":1.0,"width":0.011111111,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Instagram (Firefox)","depth":11,"bounds":{"left":0.8972222,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Instagram (Firefox)","depth":14,"bounds":{"left":0.89652777,"top":1.0,"width":0.010416667,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"YouTube (firefoxchannel)","depth":11,"bounds":{"left":0.9222222,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"YouTube (firefoxchannel)","depth":14,"bounds":{"left":0.9215278,"top":1.0,"width":0.007638889,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Cookies","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Cookies","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Legal","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Legal","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Except where otherwise","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"noted","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"noted","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", content on this site is licensed under the","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Creative Commons Attribution Share-Alike License v3.0","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Creative Commons Attribution Share-Alike License v3.0","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or any later version.","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Change language","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-4800357753902749725
|
-5337494230782013730
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Add to Firefox
Add to Firefox
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites
Learn more
Learn more
More information
Add-on Links
Support site
Support site
Support Email
Support Email
Version
1.5.2
Size
206.89 KB
Last updated
18 days ago (Mar 24, 2026)
Related Categories
Bookmarks
Bookmarks
∙
Social & Communication
Social & Communication
License
MIT License
MIT License
Version History
See all versions
See all versions
Add to collection
Report this add-on
Report this add-on
Other popular extensions
Dark Reader Recommended Recommended
Dark Reader
Dark Reader
Recommended Recommended
Recommended
Recommended
1,311,453 users
1,311,453 users
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
407,741 users
407,741 users
Search by Image Recommended Recommended
Search by Image
Search by Image
Recommended Recommended
Recommended
Recommended
440,911 users
440,911 users
Tree Style Tab Recommended Recommended
Tree Style Tab
Tree Style Tab
Recommended Recommended
Recommended
Recommended
160,924 users
160,924 users
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language
Close...
|
1454
|
|
1454
|
32
|
23
|
2026-04-11T17:29:42.514687+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928582514_m1.jpg...
|
Firefox
|
Linkwarden – Get this Extension for 🦊 Firefox (en- Linkwarden – Get this Extension for 🦊 Firefox (en-US) — Personal...
|
True
|
addons.mozilla.org/en-US/firefox/addon/linkwarden/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Add to Firefox
Add to Firefox
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites
Learn more
Learn more
More information
Add-on Links
Support site
Support site
Support Email
Support Email
Version
1.5.2
Size
206.89 KB
Last updated
18 days ago (Mar 24, 2026)
Related Categories
Bookmarks
Bookmarks
∙
Social & Communication
Social & Communication
License
MIT License
MIT License
Version History
See all versions
See all versions
Add to collection
Report this add-on
Report this add-on
Other popular extensions
Dark Reader Recommended Recommended
Dark Reader
Dark Reader
Recommended Recommended
Recommended
Recommended
1,311,453 users
1,311,453 users
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
407,741 users
407,741 users
Search by Image Recommended Recommended
Search by Image
Search by Image
Recommended Recommended
Recommended
Recommended
440,911 users
440,911 users
Tree Style Tab Recommended Recommended
Tree Style Tab
Tree Style Tab
Recommended Recommended
Recommended
Recommended
160,924 users
160,924 users
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Linkwarden – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Firefox Browser Add-ons","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Browser Add-ons","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extensions","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Themes","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More…","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"More…","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":9,"help_text":"Learn how to create extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":9,"help_text":"Submit and manage extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Search","depth":9,"value":"Search","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Search","depth":10,"help_text":"","placeholder":"Find add-ons","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Search","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Linkwarden by Daniel31x13 , Jordan Higuera","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Linkwarden","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"by","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Daniel31x13","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Daniel31x13","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jordan Higuera","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jordan Higuera","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The browser extension for Linkwarden.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4.6 (35 reviews) 4.6 (35 reviews)","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4.6 (35 reviews)","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2,613 Users","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Add to Firefox","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add to Firefox","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Extension Metadata","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Extension Metadata","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Screenshots","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 4.6 by 35 reviewers","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in to rate this extension","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in to rate this extension","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"5","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"5","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 27 five-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"27","depth":11,"help_text":"Read all 27 five-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 3 four-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 3 four-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"3","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 4 three-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"4","depth":11,"help_text":"Read all 4 three-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"2","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read the 1 two-star review","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"1","depth":11,"help_text":"Read the 1 two-star review","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"There are no one-star reviews","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"0","depth":11,"help_text":"There are no one-star reviews","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"0","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Read all 35 reviews","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Read all 35 reviews","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Permissions and data","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Required permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Required permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Read and modify bookmarks","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access browser tabs","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Optional permissions:","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Optional permissions:","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Access your data for all websites","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Learn more","depth":13,"bounds":{"left":0.20208333,"top":0.0,"width":0.05277778,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Learn more","depth":14,"bounds":{"left":0.20208333,"top":0.0,"width":0.05277778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More information","depth":12,"bounds":{"left":0.20208333,"top":0.0,"width":0.09236111,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add-on Links","depth":13,"bounds":{"left":0.20208333,"top":0.045555554,"width":0.06111111,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Support site","depth":15,"bounds":{"left":0.20208333,"top":0.06888889,"width":0.056944445,"height":0.02},"help_text":"https://linkwarden.app/","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Support site","depth":16,"bounds":{"left":0.20208333,"top":0.06888889,"width":0.056944445,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Support Email","depth":15,"bounds":{"left":0.20208333,"top":0.09222222,"width":0.06527778,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Support Email","depth":16,"bounds":{"left":0.20208333,"top":0.09222222,"width":0.06527778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Version","depth":13,"bounds":{"left":0.20208333,"top":0.12888889,"width":0.034027778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.5.2","depth":13,"bounds":{"left":0.20208333,"top":0.15222222,"width":0.021527778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Size","depth":13,"bounds":{"left":0.49930555,"top":0.045555554,"width":0.019444445,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"206.89 KB","depth":13,"bounds":{"left":0.49930555,"top":0.06888889,"width":0.048611112,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last updated","depth":13,"bounds":{"left":0.49930555,"top":0.10555556,"width":0.060416665,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"18 days ago (Mar 24, 2026)","depth":13,"bounds":{"left":0.49930555,"top":0.12888889,"width":0.12777779,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Related Categories","depth":13,"bounds":{"left":0.49930555,"top":0.16555555,"width":0.0875,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bookmarks","depth":15,"bounds":{"left":0.49930555,"top":0.18888889,"width":0.052083332,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bookmarks","depth":16,"bounds":{"left":0.49930555,"top":0.18888889,"width":0.052083332,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"∙","depth":15,"bounds":{"left":0.55138886,"top":0.18888889,"width":0.007638889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Social & Communication","depth":15,"bounds":{"left":0.5590278,"top":0.18888889,"width":0.1125,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Social & Communication","depth":16,"bounds":{"left":0.5590278,"top":0.18888889,"width":0.1125,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"License","depth":13,"bounds":{"left":0.7965278,"top":0.045555554,"width":0.035416666,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"MIT License","depth":13,"bounds":{"left":0.7965278,"top":0.06888889,"width":0.05625,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"MIT License","depth":14,"bounds":{"left":0.7965278,"top":0.06888889,"width":0.05625,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Version History","depth":13,"bounds":{"left":0.7965278,"top":0.10555556,"width":0.07013889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"See all versions","depth":15,"bounds":{"left":0.7965278,"top":0.12888889,"width":0.072222225,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"See all versions","depth":16,"bounds":{"left":0.7965278,"top":0.12888889,"width":0.072222225,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add to collection","depth":13,"bounds":{"left":0.7965278,"top":0.16555555,"width":0.07777778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report this add-on","depth":13,"bounds":{"left":0.20208333,"top":0.25666666,"width":0.0875,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report this add-on","depth":14,"bounds":{"left":0.20208333,"top":0.25666666,"width":0.0875,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Other popular extensions","depth":10,"bounds":{"left":0.20208333,"top":0.3611111,"width":0.13611111,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Dark Reader Recommended Recommended","depth":11,"bounds":{"left":0.23819445,"top":0.4311111,"width":0.2326389,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Dark Reader","depth":12,"bounds":{"left":0.23819445,"top":0.43333334,"width":0.06527778,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dark Reader","depth":13,"bounds":{"left":0.23819445,"top":0.43333334,"width":0.06527778,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.31388888,"top":0.43666667,"width":0.07152778,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.31319445,"top":0.43555555,"width":0.079166666,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.3263889,"top":0.43555555,"width":0.059027776,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1,311,453 users","depth":11,"bounds":{"left":0.2361111,"top":0.45777777,"width":0.23472223,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,311,453 users","depth":12,"bounds":{"left":0.24722221,"top":0.45666668,"width":0.07013889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Grammar and Spell Checker - LanguageTool","depth":11,"bounds":{"left":0.5402778,"top":0.4311111,"width":0.23333333,"height":0.04222222},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Grammar and Spell Checker - LanguageTool","depth":12,"bounds":{"left":0.5402778,"top":0.4311111,"width":0.22777778,"height":0.04222222},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Grammar and Spell Checker - LanguageTool","depth":13,"bounds":{"left":0.5402778,"top":0.43,"width":0.15694444,"height":0.043333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"407,741 users","depth":11,"bounds":{"left":0.5381944,"top":0.47333333,"width":0.23541667,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"407,741 users","depth":12,"bounds":{"left":0.54930556,"top":0.4722222,"width":0.0625,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Search by Image Recommended Recommended","depth":11,"bounds":{"left":0.84305555,"top":0.4311111,"width":0.15694445,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Search by Image","depth":12,"bounds":{"left":0.84305555,"top":0.43333334,"width":0.088194445,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search by Image","depth":13,"bounds":{"left":0.84305555,"top":0.43333334,"width":0.088194445,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.94166666,"top":0.43666667,"width":0.058333337,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.9409722,"top":0.43555555,"width":0.05902779,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.95416665,"top":0.43555555,"width":0.04583335,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"440,911 users","depth":11,"bounds":{"left":0.8402778,"top":0.45777777,"width":0.15972221,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"440,911 users","depth":12,"bounds":{"left":0.8513889,"top":0.45666668,"width":0.06388889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Tree Style Tab Recommended Recommended","depth":11,"bounds":{"left":0.23819445,"top":0.5277778,"width":0.2326389,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Tree Style Tab","depth":12,"bounds":{"left":0.23819445,"top":0.5311111,"width":0.07638889,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tree Style Tab","depth":13,"bounds":{"left":0.23819445,"top":0.53,"width":0.07638889,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"bounds":{"left":0.325,"top":0.53333336,"width":0.07152778,"height":0.015555556},"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.32430556,"top":0.53333336,"width":0.07847222,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"bounds":{"left":0.3375,"top":0.53333336,"width":0.059027776,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"160,924 users","depth":11,"bounds":{"left":0.2361111,"top":0.55444443,"width":0.23472223,"height":0.026666667},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"160,924 users","depth":12,"bounds":{"left":0.24722221,"top":0.55444443,"width":0.06458333,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Go to Mozilla's homepage","depth":9,"bounds":{"left":0.18541667,"top":0.7266667,"width":0.07777778,"height":0.043333333},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to Mozilla's homepage","depth":12,"bounds":{"left":0.18472221,"top":0.7277778,"width":0.011111111,"height":0.27222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Add-ons","depth":9,"bounds":{"left":0.41458333,"top":0.7266667,"width":0.22847222,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Add-ons","depth":10,"bounds":{"left":0.41458333,"top":0.72888887,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons","depth":11,"bounds":{"left":0.41458333,"top":0.72888887,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"About","depth":11,"bounds":{"left":0.41458333,"top":0.77,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"About","depth":12,"bounds":{"left":0.41458333,"top":0.77,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":11,"bounds":{"left":0.41458333,"top":0.8111111,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":12,"bounds":{"left":0.41458333,"top":0.8111111,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":11,"bounds":{"left":0.41458333,"top":0.8522222,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":12,"bounds":{"left":0.41458333,"top":0.8522222,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":11,"bounds":{"left":0.41458333,"top":0.8933333,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":12,"bounds":{"left":0.41458333,"top":0.8933333,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Policies","depth":11,"bounds":{"left":0.41458333,"top":0.9344444,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Policies","depth":12,"bounds":{"left":0.41458333,"top":0.9344444,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Community Blog","depth":11,"bounds":{"left":0.41458333,"top":0.97555554,"width":0.09652778,"height":0.02444446},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Community Blog","depth":12,"bounds":{"left":0.41458333,"top":0.97555554,"width":0.09652778,"height":0.02444446},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Forum","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.036805555,"height":-0.01666665},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Forum","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.036805555,"height":-0.01666665},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report a bug","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.07638889,"height":-0.057777762},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report a bug","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.07638889,"height":-0.057777762},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Review Guide","depth":11,"bounds":{"left":0.41458333,"top":1.0,"width":0.07986111,"height":-0.098888874},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Review Guide","depth":12,"bounds":{"left":0.41458333,"top":1.0,"width":0.07986111,"height":-0.098888874},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Browsers","depth":9,"bounds":{"left":0.64305556,"top":0.7266667,"width":0.22916667,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browsers","depth":10,"bounds":{"left":0.64305556,"top":0.72888887,"width":0.056944445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop","depth":11,"bounds":{"left":0.64305556,"top":0.77,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop","depth":12,"bounds":{"left":0.64305556,"top":0.77,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Mobile","depth":11,"bounds":{"left":0.64305556,"top":0.8111111,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile","depth":12,"bounds":{"left":0.64305556,"top":0.8111111,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Enterprise","depth":11,"bounds":{"left":0.64305556,"top":0.8522222,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enterprise","depth":12,"bounds":{"left":0.64305556,"top":0.8522222,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Products","depth":9,"bounds":{"left":0.87222224,"top":0.7266667,"width":0.12777776,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Products","depth":10,"bounds":{"left":0.87222224,"top":0.72888887,"width":0.054166667,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Browsers","depth":11,"bounds":{"left":0.87222224,"top":0.77,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browsers","depth":12,"bounds":{"left":0.87222224,"top":0.77,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VPN","depth":11,"bounds":{"left":0.87222224,"top":0.8111111,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VPN","depth":12,"bounds":{"left":0.87222224,"top":0.8111111,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Relay","depth":11,"bounds":{"left":0.87222224,"top":0.8522222,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Relay","depth":12,"bounds":{"left":0.87222224,"top":0.8522222,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Monitor","depth":11,"bounds":{"left":0.87222224,"top":0.8933333,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Monitor","depth":12,"bounds":{"left":0.87222224,"top":0.8933333,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pocket","depth":11,"bounds":{"left":0.87222224,"top":0.9344444,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pocket","depth":12,"bounds":{"left":0.87222224,"top":0.9344444,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bluesky (@firefox.com)","depth":11,"bounds":{"left":0.87222224,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bluesky (@firefox.com)","depth":14,"bounds":{"left":0.8715278,"top":1.0,"width":0.011111111,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Instagram (Firefox)","depth":11,"bounds":{"left":0.8972222,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Instagram (Firefox)","depth":14,"bounds":{"left":0.89652777,"top":1.0,"width":0.010416667,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"YouTube (firefoxchannel)","depth":11,"bounds":{"left":0.9222222,"top":1.0,"width":0.011111111,"height":-0.008888841},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"YouTube (firefoxchannel)","depth":14,"bounds":{"left":0.9215278,"top":1.0,"width":0.007638889,"height":-0.012222171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Cookies","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Cookies","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Legal","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Legal","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Except where otherwise","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"noted","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"noted","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", content on this site is licensed under the","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Creative Commons Attribution Share-Alike License v3.0","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Creative Commons Attribution Share-Alike License v3.0","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or any later version.","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Change language","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-8924617497646557957
|
-5337494230781751586
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Linkwarden – Get this Extension for 🦊 Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
This add-on is not actively monitored for security by Mozilla. Make sure you trust it before installing.
Learn more
Learn more
Linkwarden by Daniel31x13 , Jordan Higuera
Linkwarden
by
Daniel31x13
Daniel31x13
,
Jordan Higuera
Jordan Higuera
The browser extension for Linkwarden.
4.6 (35 reviews) 4.6 (35 reviews)
4.6 (35 reviews)
4.6 (35 reviews)
2,613 Users
2,613 Users
Add to Firefox
Add to Firefox
Extension Metadata
Extension Metadata
Screenshots
Rated 4.6 by 35 reviewers
Log in to rate this extension
Log in to rate this extension
There are no ratings yet
5
5
Read all 27 five-star reviews
27
27
4
4
Read all 3 four-star reviews
3
3
3
3
Read all 4 three-star reviews
4
4
2
2
Read the 1 two-star review
1
1
1
1
There are no one-star reviews
0
0
Read all 35 reviews
Read all 35 reviews
Permissions and data
Required permissions:
Required permissions:
Read and modify bookmarks
Access browser tabs
Optional permissions:
Optional permissions:
Access your data for all websites
Learn more
Learn more
More information
Add-on Links
Support site
Support site
Support Email
Support Email
Version
1.5.2
Size
206.89 KB
Last updated
18 days ago (Mar 24, 2026)
Related Categories
Bookmarks
Bookmarks
∙
Social & Communication
Social & Communication
License
MIT License
MIT License
Version History
See all versions
See all versions
Add to collection
Report this add-on
Report this add-on
Other popular extensions
Dark Reader Recommended Recommended
Dark Reader
Dark Reader
Recommended Recommended
Recommended
Recommended
1,311,453 users
1,311,453 users
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
Grammar and Spell Checker - LanguageTool
407,741 users
407,741 users
Search by Image Recommended Recommended
Search by Image
Search by Image
Recommended Recommended
Recommended
Recommended
440,911 users
440,911 users
Tree Style Tab Recommended Recommended
Tree Style Tab
Tree Style Tab
Recommended Recommended
Recommended
Recommended
160,924 users
160,924 users
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
NULL
|
|
1453
|
32
|
22
|
2026-04-11T17:29:38.702208+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928578702_m1.jpg...
|
Firefox
|
Extensions – Add-ons for Firefox (en-US) — Persona Extensions – Add-ons for Firefox (en-US) — Personal...
|
True
|
addons.mozilla.org/en-US/firefox/addon/linkwarden/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
Extension Metadata
Extension Metadata
This add-on cannot be rated because no versions have been published.
More information
Last updated
Add to collection
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Firefox Browser Add-ons","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Browser Add-ons","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extensions","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Themes","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More…","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"More…","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":9,"help_text":"Learn how to create extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":9,"help_text":"Submit and manage extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Search","depth":9,"value":"Search","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Search","depth":10,"help_text":"","placeholder":"Find add-ons","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Search","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Extension Metadata","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Extension Metadata","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This add-on cannot be rated because no versions have been published.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More information","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last updated","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add to collection","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Go to Mozilla's homepage","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to Mozilla's homepage","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Add-ons","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Add-ons","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"About","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"About","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Policies","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Policies","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Community Blog","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Community Blog","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Forum","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Forum","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report a bug","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report a bug","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Review Guide","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Review Guide","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Browsers","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browsers","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Mobile","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Enterprise","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enterprise","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Products","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Products","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Browsers","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browsers","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VPN","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VPN","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Relay","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Relay","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Monitor","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Monitor","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pocket","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pocket","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bluesky (@firefox.com)","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bluesky (@firefox.com)","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Instagram (Firefox)","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Instagram (Firefox)","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"YouTube (firefoxchannel)","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"YouTube (firefoxchannel)","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Cookies","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Cookies","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Legal","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Legal","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Except where otherwise","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"noted","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"noted","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", content on this site is licensed under the","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Creative Commons Attribution Share-Alike License v3.0","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Creative Commons Attribution Share-Alike License v3.0","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or any later version.","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Change language","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
4691810669754676493
|
3885516126344367694
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
Extension Metadata
Extension Metadata
This add-on cannot be rated because no versions have been published.
More information
Last updated
Add to collection
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
1452
|
|
1452
|
32
|
21
|
2026-04-11T17:29:33.323693+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928573323_m1.jpg...
|
Firefox
|
Extensions – Add-ons for Firefox (en-US) — Persona Extensions – Add-ons for Firefox (en-US) — Personal...
|
True
|
addons.mozilla.org/en-US/firefox/extensions/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
Extensions
Extensions
Explore powerful tools and features to customize Firefox and make the browser all your own.
Categories
Alerts & Updates
Alerts & Updates
Appearance
Appearance
Bookmarks
Bookmarks
Download Management
Download Management
Feeds, News & Blogging
Feeds, News & Blogging
Games & Entertainment
Games & Entertainment
Language Support
Language Support
Other
Other
Photos, Music & Videos
Photos, Music & Videos
Privacy & Security
Privacy & Security
Search Tools
Search Tools
Shopping
Shopping
Social & Communication
Social & Communication
Tabs
Tabs
Web Development
Web Development
Recommended extensions
SingleFile
SingleFile
SingleFile
82,401 users
82,401 users
DuckDuckGo Search & Tracker Protection
DuckDuckGo Search & Tracker Protection
DuckDuckGo Search & Tracker Protection
1,159,712 users
1,159,712 users
Disconnect
Disconnect
Disconnect
66,417 users
66,417 users
Chrome Mask
Chrome Mask
Chrome Mask
50,236 users
50,236 users
See more recommended extensions
See more recommended extensions
Top rated extensions
SteamDB
SteamDB
SteamDB
101,356 users
101,356 users
LeechBlock NG
LeechBlock NG
LeechBlock NG
111,791 users
111,791 users
SponsorBlock - Skip Sponsorships on YouTube
SponsorBlock - Skip Sponsorships on YouTube
SponsorBlock - Skip Sponsorships on YouTube
736,901 users
736,901 users
Privacy Badger
Privacy Badger
Privacy Badger
1,890,491 users
1,890,491 users
See more top rated extensions
See more top rated extensions
Trending extensions
automaticDark - Time-Based Theme Changer
automaticDark - Time-Based Theme Changer
automaticDark - Time-Based Theme Changer
16,235 users
16,235 users
Time Tracker - Web Habit Builder
Time Tracker - Web Habit Builder
Time Tracker - Web Habit Builder
14,833 users
14,833 users
Download All Images
Download All Images
Download All Images
106,194 users
106,194 users
ClearURLs
ClearURLs
ClearURLs
520,769 users
520,769 users
See more trending extensions
See more trending extensions
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Firefox Browser Add-ons","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Browser Add-ons","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extensions","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Themes","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More…","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"More…","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":9,"help_text":"Learn how to create extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":9,"help_text":"Submit and manage extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Search","depth":9,"value":"Search","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Search","depth":10,"help_text":"","placeholder":"Find add-ons","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":true,"is_selected":false},{"role":"AXButton","text":"Search","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Extensions","depth":7,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Extensions","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Explore powerful tools and features to customize Firefox and make the browser all your own.","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Categories","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Alerts & Updates","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Alerts & Updates","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Appearance","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Appearance","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bookmarks","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bookmarks","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Download Management","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Download Management","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Feeds, News & Blogging","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feeds, News & Blogging","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Games & Entertainment","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Games & Entertainment","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Language Support","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Language Support","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Other","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Other","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Photos, Music & Videos","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Photos, Music & Videos","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy & Security","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy & Security","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Search Tools","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search Tools","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Shopping","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shopping","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Social & Communication","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Social & Communication","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Tabs","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Tabs","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Web Development","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Web Development","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended extensions","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"SingleFile","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"SingleFile","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SingleFile","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"82,401 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"82,401 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"DuckDuckGo Search & Tracker Protection","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"DuckDuckGo Search & Tracker Protection","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"DuckDuckGo Search & Tracker Protection","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1,159,712 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,159,712 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Disconnect","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Disconnect","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Disconnect","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"66,417 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"66,417 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Chrome Mask","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Chrome Mask","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Chrome Mask","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"50,236 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"50,236 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"See more recommended extensions","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"See more recommended extensions","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Top rated extensions","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"SteamDB","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"SteamDB","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SteamDB","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"101,356 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"101,356 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"LeechBlock NG","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"LeechBlock NG","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"LeechBlock NG","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"111,791 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"111,791 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"SponsorBlock - Skip Sponsorships on YouTube","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"SponsorBlock - Skip Sponsorships on YouTube","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SponsorBlock - Skip Sponsorships on YouTube","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"736,901 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"736,901 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Privacy Badger","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy Badger","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy Badger","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1,890,491 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,890,491 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"See more top rated extensions","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"See more top rated extensions","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Trending extensions","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"automaticDark - Time-Based Theme Changer","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"automaticDark - Time-Based Theme Changer","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"automaticDark - Time-Based Theme Changer","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"16,235 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"16,235 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Time Tracker - Web Habit Builder","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Time Tracker - Web Habit Builder","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Time Tracker - Web Habit Builder","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"14,833 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"14,833 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Download All Images","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Download All Images","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Download All Images","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"106,194 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"106,194 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"ClearURLs","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"ClearURLs","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"ClearURLs","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"520,769 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"520,769 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"See more trending extensions","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"See more trending extensions","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Go to Mozilla's homepage","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to Mozilla's homepage","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Add-ons","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Add-ons","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"About","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"About","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":11,"bounds":{"left":0.41458333,"top":0.0,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":12,"bounds":{"left":0.41458333,"top":0.0,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":11,"bounds":{"left":0.41458333,"top":0.0,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":12,"bounds":{"left":0.41458333,"top":0.0,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":11,"bounds":{"left":0.41458333,"top":0.02111111,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":12,"bounds":{"left":0.41458333,"top":0.02111111,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Policies","depth":11,"bounds":{"left":0.41458333,"top":0.062222224,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Policies","depth":12,"bounds":{"left":0.41458333,"top":0.062222224,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Community Blog","depth":11,"bounds":{"left":0.41458333,"top":0.10333333,"width":0.09652778,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Community Blog","depth":12,"bounds":{"left":0.41458333,"top":0.10333333,"width":0.09652778,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Forum","depth":11,"bounds":{"left":0.41458333,"top":0.14444445,"width":0.036805555,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Forum","depth":12,"bounds":{"left":0.41458333,"top":0.14444445,"width":0.036805555,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report a bug","depth":11,"bounds":{"left":0.41458333,"top":0.18555556,"width":0.07638889,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report a bug","depth":12,"bounds":{"left":0.41458333,"top":0.18555556,"width":0.07638889,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Review Guide","depth":11,"bounds":{"left":0.41458333,"top":0.22666667,"width":0.07986111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Review Guide","depth":12,"bounds":{"left":0.41458333,"top":0.22666667,"width":0.07986111,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Browsers","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browsers","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Mobile","depth":11,"bounds":{"left":0.64305556,"top":0.0,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile","depth":12,"bounds":{"left":0.64305556,"top":0.0,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Enterprise","depth":11,"bounds":{"left":0.64305556,"top":0.0,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enterprise","depth":12,"bounds":{"left":0.64305556,"top":0.0,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Products","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Products","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Browsers","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browsers","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VPN","depth":11,"bounds":{"left":0.87222224,"top":0.0,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VPN","depth":12,"bounds":{"left":0.87222224,"top":0.0,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Relay","depth":11,"bounds":{"left":0.87222224,"top":0.0,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Relay","depth":12,"bounds":{"left":0.87222224,"top":0.0,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Monitor","depth":11,"bounds":{"left":0.87222224,"top":0.02111111,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Monitor","depth":12,"bounds":{"left":0.87222224,"top":0.02111111,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pocket","depth":11,"bounds":{"left":0.87222224,"top":0.062222224,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pocket","depth":12,"bounds":{"left":0.87222224,"top":0.062222224,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bluesky (@firefox.com)","depth":11,"bounds":{"left":0.87222224,"top":0.13666667,"width":0.011111111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bluesky (@firefox.com)","depth":14,"bounds":{"left":0.8715278,"top":0.14,"width":0.011111111,"height":0.6255556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Instagram (Firefox)","depth":11,"bounds":{"left":0.8972222,"top":0.13666667,"width":0.011111111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Instagram (Firefox)","depth":14,"bounds":{"left":0.89652777,"top":0.14,"width":0.010416667,"height":0.53555554},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"YouTube (firefoxchannel)","depth":11,"bounds":{"left":0.9222222,"top":0.13666667,"width":0.011111111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"YouTube (firefoxchannel)","depth":14,"bounds":{"left":0.9215278,"top":0.14,"width":0.007638889,"height":0.6855556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy","depth":10,"bounds":{"left":0.18541667,"top":0.30333334,"width":0.033333335,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy","depth":11,"bounds":{"left":0.18541667,"top":0.30333334,"width":0.033333335,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Cookies","depth":10,"bounds":{"left":0.23958333,"top":0.30333334,"width":0.0375,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Cookies","depth":11,"bounds":{"left":0.23958333,"top":0.30333334,"width":0.0375,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Legal","depth":10,"bounds":{"left":0.29791668,"top":0.30333334,"width":0.025,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Legal","depth":11,"bounds":{"left":0.29791668,"top":0.30333334,"width":0.025,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Except where otherwise","depth":9,"bounds":{"left":0.18541667,"top":0.3611111,"width":0.11319444,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"noted","depth":9,"bounds":{"left":0.2986111,"top":0.3611111,"width":0.027083334,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"noted","depth":10,"bounds":{"left":0.2986111,"top":0.3611111,"width":0.027083334,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", content on this site is licensed under the","depth":9,"bounds":{"left":0.32569444,"top":0.3611111,"width":0.19444445,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Creative Commons Attribution Share-Alike License v3.0","depth":9,"bounds":{"left":0.52013886,"top":0.3611111,"width":0.25486112,"height":0.02},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Creative Commons Attribution Share-Alike License v3.0","depth":10,"bounds":{"left":0.52013886,"top":0.3611111,"width":0.25486112,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or any later version.","depth":9,"bounds":{"left":0.18541667,"top":0.3611111,"width":0.64375,"height":0.043333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Change language","depth":10,"bounds":{"left":0.87222224,"top":0.35555556,"width":0.07986111,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
5863032855403197618
|
3869543555290386252
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
Search
Search
Search
Search
Extensions
Extensions
Explore powerful tools and features to customize Firefox and make the browser all your own.
Categories
Alerts & Updates
Alerts & Updates
Appearance
Appearance
Bookmarks
Bookmarks
Download Management
Download Management
Feeds, News & Blogging
Feeds, News & Blogging
Games & Entertainment
Games & Entertainment
Language Support
Language Support
Other
Other
Photos, Music & Videos
Photos, Music & Videos
Privacy & Security
Privacy & Security
Search Tools
Search Tools
Shopping
Shopping
Social & Communication
Social & Communication
Tabs
Tabs
Web Development
Web Development
Recommended extensions
SingleFile
SingleFile
SingleFile
82,401 users
82,401 users
DuckDuckGo Search & Tracker Protection
DuckDuckGo Search & Tracker Protection
DuckDuckGo Search & Tracker Protection
1,159,712 users
1,159,712 users
Disconnect
Disconnect
Disconnect
66,417 users
66,417 users
Chrome Mask
Chrome Mask
Chrome Mask
50,236 users
50,236 users
See more recommended extensions
See more recommended extensions
Top rated extensions
SteamDB
SteamDB
SteamDB
101,356 users
101,356 users
LeechBlock NG
LeechBlock NG
LeechBlock NG
111,791 users
111,791 users
SponsorBlock - Skip Sponsorships on YouTube
SponsorBlock - Skip Sponsorships on YouTube
SponsorBlock - Skip Sponsorships on YouTube
736,901 users
736,901 users
Privacy Badger
Privacy Badger
Privacy Badger
1,890,491 users
1,890,491 users
See more top rated extensions
See more top rated extensions
Trending extensions
automaticDark - Time-Based Theme Changer
automaticDark - Time-Based Theme Changer
automaticDark - Time-Based Theme Changer
16,235 users
16,235 users
Time Tracker - Web Habit Builder
Time Tracker - Web Habit Builder
Time Tracker - Web Habit Builder
14,833 users
14,833 users
Download All Images
Download All Images
Download All Images
106,194 users
106,194 users
ClearURLs
ClearURLs
ClearURLs
520,769 users
520,769 users
See more trending extensions
See more trending extensions
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
NULL
|
|
1451
|
32
|
20
|
2026-04-11T17:29:22.532069+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928562532_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:29:22-zsh181• *8|monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-1657353186981939931
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:29:22-zsh181• *8|monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1450
|
|
1450
|
32
|
19
|
2026-04-11T17:29:20.768288+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928560768_m1.jpg...
|
Firefox
|
Search results for "bitwarden" – Add-ons f Search results for "bitwarden" – Add-ons for Firefox (en-US) — Personal...
|
True
|
addons.mozilla.org/en-US/firefox/search/?q=bitward addons.mozilla.org/en-US/firefox/search/?q=bitwarden...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
bitwarden
bitwarden
bitwarden
Search
Search
11 results found for "bitwarden"
11 results found for "bitwarden"
Filter results
Filter results
Sort by
Add-on Type
Badging
Search results
Bitwarden Password Manager Recommended Recommended
Bitwarden Password Manager
Bitwarden Password Manager
Recommended Recommended
Recommended
Recommended
At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.
Rated 4.6 out of 5
Bitwarden Inc.
Bitwarden Inc.
965,377 users
965,377 users
Scout Vault
Scout Vault
Scout Vault
At home, at work, or on the go, Scout Vault easily secures all your passwords, passkeys, and sensitive information
There are no ratings yet
Scout Secured
Scout Secured
4 users
4 users
Sunsetvault
Sunsetvault
Sunsetvault
Sunsetvault is an exclusive build of the popular open source password manager, designed for internal use by SETG.
Rated 5 out of 5
Sunset
Sunset
12 users
12 users
Twake Pass
Twake Pass
Twake Pass
Twake Pass, the French and open source password manager application of your Twake Workplace that makes you the Internet easier by helping you to create, save and autofill strong passwords for all your accounts so you can browse securely online.
Rated 4.7 out of 5
Twake Workplace
Twake Workplace
1,045 users
1,045 users
Heimlane Vault
Heimlane Vault
Heimlane Vault
Password manager extension that securely stores, manages, and auto-fills login credentials for websites. Provides password generation and secure sharing capabilities.
There are no ratings yet
Heimlane
Heimlane
15 users
15 users
SAASPASS Password Manager & Authenticator 2FA SSO
SAASPASS Password Manager & Authenticator 2FA SSO
SAASPASS Password Manager & Authenticator 2FA SSO
SAASPASS is a free Password Manager & Authenticator [BACKUP_CODE]. There is NO need for a desktop application. You can use the Authenticator code generator & SAASPASS can autofill & autologin as well with single sign-on SSO
Rated 4.2 out of 5
SAASPASS
SAASPASS
260 users
260 users
Password Autofill Defocus
Password Autofill Defocus
Password Autofill Defocus
Defocuses password fields after auto-fill on HTTP pages, preventing Firefox's insecure login warning from blocking the login button.
There are no ratings yet
Nealon
Nealon
2 users
2 users
Authier
Authier
Authier
Authier chrome extension
There are no ratings yet
capaj
capaj
16 users
16 users
idrinth/walled-secrets
idrinth/walled-secrets
idrinth/walled-secrets
A small open source password manager.
Rated 5 out of 5
Björn 'Idrinth' Büttner
Björn 'Idrinth' Büttner
1 user
1 user
VivoKey Vault
VivoKey Vault
VivoKey Vault
A secure password manager, backed by the VivoKey Cryptobionic Identity.
There are no ratings yet
Riley Gall
Riley Gall
2 users
2 users
Overconfidence Password Generator
Overconfidence Password Generator
Overconfidence Password Generator
Create random, strong passwords that are extremely hard to crack and impossible to guess. Example: "nny考是込날ђປpማêầ누ျតस!%v"
There are no ratings yet
CRW1O1
CRW1O1
1 user
1 user
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Search results for \"bitwarden\" – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Search results for \"bitwarden\" – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Firefox Browser Add-ons","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Browser Add-ons","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extensions","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Themes","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Themes","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More…","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"More…","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":9,"help_text":"Learn how to create extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":9,"help_text":"Submit and manage extensions and themes","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Log in","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Log in","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"bitwarden","depth":9,"value":"bitwarden","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"bitwarden","depth":10,"value":"bitwarden","help_text":"","placeholder":"Find add-ons","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"bitwarden","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"11 results found for \"bitwarden\"","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"11 results found for \"bitwarden\"","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Filter results","depth":9,"help_text":"Toggle contents","role_description":"switch","subrole":"AXSwitch","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Filter results","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sort by","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Add-on Type","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Badging","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search results","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Bitwarden Password Manager Recommended Recommended","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Bitwarden Password Manager","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Recommended Recommended","depth":12,"help_text":"Firefox only recommends add-ons that meet our standards for security and performance.","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recommended","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recommended","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 4.6 out of 5","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Bitwarden Inc.","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Bitwarden Inc.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"965,377 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"965,377 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Scout Vault","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Scout Vault","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Scout Vault","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"At home, at work, or on the go, Scout Vault easily secures all your passwords, passkeys, and sensitive information","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Scout Secured","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Scout Secured","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"4 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Sunsetvault","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Sunsetvault","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sunsetvault","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sunsetvault is an exclusive build of the popular open source password manager, designed for internal use by SETG.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 5 out of 5","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Sunset","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sunset","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"12 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"12 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Twake Pass","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Twake Pass","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Twake Pass","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Twake Pass, the French and open source password manager application of your Twake Workplace that makes you the Internet easier by helping you to create, save and autofill strong passwords for all your accounts so you can browse securely online.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 4.7 out of 5","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Twake Workplace","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Twake Workplace","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1,045 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,045 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Heimlane Vault","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Heimlane Vault","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Heimlane Vault","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Password manager extension that securely stores, manages, and auto-fills login credentials for websites. Provides password generation and secure sharing capabilities.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Heimlane","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Heimlane","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"15 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"15 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"SAASPASS Password Manager & Authenticator 2FA SSO","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"SAASPASS Password Manager & Authenticator 2FA SSO","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SAASPASS Password Manager & Authenticator 2FA SSO","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SAASPASS is a free Password Manager & Authenticator 2FA code generator. There is NO need for a desktop application. You can use the Authenticator code generator & SAASPASS can autofill & autologin as well with single sign-on SSO","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 4.2 out of 5","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"SAASPASS","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SAASPASS","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"260 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"260 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Password Autofill Defocus","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Password Autofill Defocus","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Password Autofill Defocus","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Defocuses password fields after auto-fill on HTTP pages, preventing Firefox's insecure login warning from blocking the login button.","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Nealon","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nealon","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2 users","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 users","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Authier","depth":11,"bounds":{"left":0.5763889,"top":0.0,"width":0.41458333,"height":0.02111111},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Authier","depth":12,"bounds":{"left":0.5763889,"top":0.0,"width":0.03888889,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Authier","depth":13,"bounds":{"left":0.5763889,"top":0.0,"width":0.03888889,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authier chrome extension","depth":12,"bounds":{"left":0.5763889,"top":0.0,"width":0.11805555,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":13,"bounds":{"left":0.57569444,"top":0.0,"width":0.00625,"height":0.46333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"capaj","depth":11,"bounds":{"left":0.63958335,"top":0.0,"width":0.025,"height":0.018888889},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"capaj","depth":12,"bounds":{"left":0.63958335,"top":0.0,"width":0.025,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"16 users","depth":11,"bounds":{"left":0.99791664,"top":0.0,"width":0.0020833611,"height":0.06333333},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"16 users","depth":12,"bounds":{"left":1.0,"top":0.0,"width":-0.0090277195,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"idrinth/walled-secrets","depth":11,"bounds":{"left":0.5763889,"top":0.05111111,"width":0.4236111,"height":0.02111111},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"idrinth/walled-secrets","depth":12,"bounds":{"left":0.5763889,"top":0.05111111,"width":0.11736111,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"idrinth/walled-secrets","depth":13,"bounds":{"left":0.5763889,"top":0.05,"width":0.11736111,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"A small open source password manager.","depth":12,"bounds":{"left":0.5763889,"top":0.07333333,"width":0.18541667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Rated 5 out of 5","depth":13,"bounds":{"left":0.57569444,"top":0.09555556,"width":0.00625,"height":0.27666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Björn 'Idrinth' Büttner","depth":11,"bounds":{"left":0.63958335,"top":0.09555556,"width":0.097222224,"height":0.018888889},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Björn 'Idrinth' Büttner","depth":12,"bounds":{"left":0.63958335,"top":0.094444446,"width":0.097222224,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1 user","depth":11,"bounds":{"left":1.0,"top":0.05111111,"width":-0.0090277195,"height":0.06333333},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1 user","depth":12,"bounds":{"left":1.0,"top":0.05,"width":-0.02013886,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"VivoKey Vault","depth":11,"bounds":{"left":0.5763889,"top":0.16888888,"width":0.41944444,"height":0.02111111},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"VivoKey Vault","depth":12,"bounds":{"left":0.5763889,"top":0.16888888,"width":0.072916664,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VivoKey Vault","depth":13,"bounds":{"left":0.5763889,"top":0.16777778,"width":0.072916664,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"A secure password manager, backed by the VivoKey Cryptobionic Identity.","depth":12,"bounds":{"left":0.5763889,"top":0.19111112,"width":0.30208334,"height":0.043333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":13,"bounds":{"left":0.57569444,"top":0.23666666,"width":0.00625,"height":0.46333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Riley Gall","depth":11,"bounds":{"left":0.63958335,"top":0.23666666,"width":0.041666668,"height":0.018888889},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Riley Gall","depth":12,"bounds":{"left":0.63958335,"top":0.23555556,"width":0.041666668,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2 users","depth":11,"bounds":{"left":1.0,"top":0.16888888,"width":-0.0027778149,"height":0.086666666},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 users","depth":12,"bounds":{"left":1.0,"top":0.16777778,"width":-0.013888836,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Overconfidence Password Generator","depth":11,"bounds":{"left":0.5763889,"top":0.31,"width":0.4236111,"height":0.02111111},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Overconfidence Password Generator","depth":12,"bounds":{"left":0.5763889,"top":0.31,"width":0.19652778,"height":0.02111111},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Overconfidence Password Generator","depth":13,"bounds":{"left":0.5763889,"top":0.30888888,"width":0.19652778,"height":0.022222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Create random, strong passwords that are extremely hard to crack and impossible to guess. Example: \"nny考是込날ђປpማêầ누ျតस!%v\"","depth":12,"bounds":{"left":0.5763889,"top":0.33222222,"width":0.30625,"height":0.053333335},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"There are no ratings yet","depth":13,"bounds":{"left":0.57569444,"top":0.37777779,"width":0.00625,"height":0.46333334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"CRW1O1","depth":11,"bounds":{"left":0.63958335,"top":0.37777779,"width":0.03888889,"height":0.018888889},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"CRW1O1","depth":12,"bounds":{"left":0.63958335,"top":0.37666667,"width":0.03888889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1 user","depth":11,"bounds":{"left":1.0,"top":0.31,"width":-0.0090277195,"height":0.086666666},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1 user","depth":12,"bounds":{"left":1.0,"top":0.30888888,"width":-0.02013886,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Go to Mozilla's homepage","depth":9,"bounds":{"left":0.18541667,"top":0.5411111,"width":0.07777778,"height":0.043333333},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to Mozilla's homepage","depth":12,"bounds":{"left":0.18472221,"top":0.5422222,"width":0.011111111,"height":0.4577778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Add-ons","depth":9,"bounds":{"left":0.41458333,"top":0.5411111,"width":0.22847222,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"Add-ons","depth":10,"bounds":{"left":0.41458333,"top":0.54333335,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons","depth":11,"bounds":{"left":0.41458333,"top":0.54333335,"width":0.052083332,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"About","depth":11,"bounds":{"left":0.41458333,"top":0.58444446,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"About","depth":12,"bounds":{"left":0.41458333,"top":0.58444446,"width":0.035416666,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Firefox Add-ons Blog","depth":11,"bounds":{"left":0.41458333,"top":0.6255556,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Firefox Add-ons Blog","depth":12,"bounds":{"left":0.41458333,"top":0.6255556,"width":0.124305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Extension Workshop","depth":11,"bounds":{"left":0.41458333,"top":0.6666667,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extension Workshop","depth":12,"bounds":{"left":0.41458333,"top":0.6666667,"width":0.12013889,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Hub","depth":11,"bounds":{"left":0.41458333,"top":0.7077778,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Hub","depth":12,"bounds":{"left":0.41458333,"top":0.7077778,"width":0.0875,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Developer Policies","depth":11,"bounds":{"left":0.41458333,"top":0.7488889,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developer Policies","depth":12,"bounds":{"left":0.41458333,"top":0.7488889,"width":0.108333334,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Community Blog","depth":11,"bounds":{"left":0.41458333,"top":0.79,"width":0.09652778,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Community Blog","depth":12,"bounds":{"left":0.41458333,"top":0.79,"width":0.09652778,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Forum","depth":11,"bounds":{"left":0.41458333,"top":0.83111113,"width":0.036805555,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Forum","depth":12,"bounds":{"left":0.41458333,"top":0.83111113,"width":0.036805555,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Report a bug","depth":11,"bounds":{"left":0.41458333,"top":0.87222224,"width":0.07638889,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Report a bug","depth":12,"bounds":{"left":0.41458333,"top":0.87222224,"width":0.07638889,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Review Guide","depth":11,"bounds":{"left":0.41458333,"top":0.91333336,"width":0.07986111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Review Guide","depth":12,"bounds":{"left":0.41458333,"top":0.91333336,"width":0.07986111,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Browsers","depth":9,"bounds":{"left":0.64305556,"top":0.5411111,"width":0.22916667,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browsers","depth":10,"bounds":{"left":0.64305556,"top":0.54333335,"width":0.056944445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop","depth":11,"bounds":{"left":0.64305556,"top":0.58444446,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop","depth":12,"bounds":{"left":0.64305556,"top":0.58444446,"width":0.049305554,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Mobile","depth":11,"bounds":{"left":0.64305556,"top":0.6255556,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile","depth":12,"bounds":{"left":0.64305556,"top":0.6255556,"width":0.039583333,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Enterprise","depth":11,"bounds":{"left":0.64305556,"top":0.6666667,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enterprise","depth":12,"bounds":{"left":0.64305556,"top":0.6666667,"width":0.060416665,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Products","depth":9,"bounds":{"left":0.87222224,"top":0.5411111,"width":0.12777776,"height":0.03},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Products","depth":10,"bounds":{"left":0.87222224,"top":0.54333335,"width":0.054166667,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Browsers","depth":11,"bounds":{"left":0.87222224,"top":0.58444446,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browsers","depth":12,"bounds":{"left":0.87222224,"top":0.58444446,"width":0.05486111,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"VPN","depth":11,"bounds":{"left":0.87222224,"top":0.6255556,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"VPN","depth":12,"bounds":{"left":0.87222224,"top":0.6255556,"width":0.025694445,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Relay","depth":11,"bounds":{"left":0.87222224,"top":0.6666667,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Relay","depth":12,"bounds":{"left":0.87222224,"top":0.6666667,"width":0.03125,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Monitor","depth":11,"bounds":{"left":0.87222224,"top":0.7077778,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Monitor","depth":12,"bounds":{"left":0.87222224,"top":0.7077778,"width":0.045138888,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pocket","depth":11,"bounds":{"left":0.87222224,"top":0.7488889,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pocket","depth":12,"bounds":{"left":0.87222224,"top":0.7488889,"width":0.04027778,"height":0.025555555},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Bluesky (@firefox.com)","depth":11,"bounds":{"left":0.87222224,"top":0.8233333,"width":0.011111111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bluesky (@firefox.com)","depth":14,"bounds":{"left":0.8715278,"top":0.82666665,"width":0.011111111,"height":0.17333335},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Instagram (Firefox)","depth":11,"bounds":{"left":0.8972222,"top":0.8233333,"width":0.011111111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Instagram (Firefox)","depth":14,"bounds":{"left":0.89652777,"top":0.82666665,"width":0.010416667,"height":0.17333335},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"YouTube (firefoxchannel)","depth":11,"bounds":{"left":0.9222222,"top":0.8233333,"width":0.011111111,"height":0.025555555},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"YouTube (firefoxchannel)","depth":14,"bounds":{"left":0.9215278,"top":0.82666665,"width":0.007638889,"height":0.17333335},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Privacy","depth":10,"bounds":{"left":0.18541667,"top":0.99,"width":0.033333335,"height":0.00999999},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Privacy","depth":11,"bounds":{"left":0.18541667,"top":0.99,"width":0.033333335,"height":0.00999999},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Cookies","depth":10,"bounds":{"left":0.23958333,"top":0.99,"width":0.0375,"height":0.00999999},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Cookies","depth":11,"bounds":{"left":0.23958333,"top":0.99,"width":0.0375,"height":0.00999999},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Legal","depth":10,"bounds":{"left":0.29791668,"top":0.99,"width":0.025,"height":0.00999999},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Legal","depth":11,"bounds":{"left":0.29791668,"top":0.99,"width":0.025,"height":0.00999999},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Except where otherwise","depth":9,"bounds":{"left":0.18541667,"top":1.0,"width":0.11319444,"height":-0.046666622},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"noted","depth":9,"bounds":{"left":0.2986111,"top":1.0,"width":0.027083334,"height":-0.046666622},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"noted","depth":10,"bounds":{"left":0.2986111,"top":1.0,"width":0.027083334,"height":-0.046666622},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", content on this site is licensed under the","depth":9,"bounds":{"left":0.32569444,"top":1.0,"width":0.19444445,"height":-0.046666622},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Creative Commons Attribution Share-Alike License v3.0","depth":9,"bounds":{"left":0.52013886,"top":1.0,"width":0.25486112,"height":-0.046666622},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Creative Commons Attribution Share-Alike License v3.0","depth":10,"bounds":{"left":0.52013886,"top":1.0,"width":0.25486112,"height":-0.046666622},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or any later version.","depth":9,"bounds":{"left":0.18541667,"top":1.0,"width":0.64375,"height":-0.046666622},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Change language","depth":10,"bounds":{"left":0.87222224,"top":1.0,"width":0.07986111,"height":-0.04222226},"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
2233834825024307310
|
-5404837093853754236
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Close tab
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Firefox Browser Add-ons
Firefox Browser Add-ons
Extensions
Extensions
Themes
Themes
More…
More…
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Log in
Log in
Search
bitwarden
bitwarden
bitwarden
Search
Search
11 results found for "bitwarden"
11 results found for "bitwarden"
Filter results
Filter results
Sort by
Add-on Type
Badging
Search results
Bitwarden Password Manager Recommended Recommended
Bitwarden Password Manager
Bitwarden Password Manager
Recommended Recommended
Recommended
Recommended
At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.
Rated 4.6 out of 5
Bitwarden Inc.
Bitwarden Inc.
965,377 users
965,377 users
Scout Vault
Scout Vault
Scout Vault
At home, at work, or on the go, Scout Vault easily secures all your passwords, passkeys, and sensitive information
There are no ratings yet
Scout Secured
Scout Secured
4 users
4 users
Sunsetvault
Sunsetvault
Sunsetvault
Sunsetvault is an exclusive build of the popular open source password manager, designed for internal use by SETG.
Rated 5 out of 5
Sunset
Sunset
12 users
12 users
Twake Pass
Twake Pass
Twake Pass
Twake Pass, the French and open source password manager application of your Twake Workplace that makes you the Internet easier by helping you to create, save and autofill strong passwords for all your accounts so you can browse securely online.
Rated 4.7 out of 5
Twake Workplace
Twake Workplace
1,045 users
1,045 users
Heimlane Vault
Heimlane Vault
Heimlane Vault
Password manager extension that securely stores, manages, and auto-fills login credentials for websites. Provides password generation and secure sharing capabilities.
There are no ratings yet
Heimlane
Heimlane
15 users
15 users
SAASPASS Password Manager & Authenticator 2FA SSO
SAASPASS Password Manager & Authenticator 2FA SSO
SAASPASS Password Manager & Authenticator 2FA SSO
SAASPASS is a free Password Manager & Authenticator [BACKUP_CODE]. There is NO need for a desktop application. You can use the Authenticator code generator & SAASPASS can autofill & autologin as well with single sign-on SSO
Rated 4.2 out of 5
SAASPASS
SAASPASS
260 users
260 users
Password Autofill Defocus
Password Autofill Defocus
Password Autofill Defocus
Defocuses password fields after auto-fill on HTTP pages, preventing Firefox's insecure login warning from blocking the login button.
There are no ratings yet
Nealon
Nealon
2 users
2 users
Authier
Authier
Authier
Authier chrome extension
There are no ratings yet
capaj
capaj
16 users
16 users
idrinth/walled-secrets
idrinth/walled-secrets
idrinth/walled-secrets
A small open source password manager.
Rated 5 out of 5
Björn 'Idrinth' Büttner
Björn 'Idrinth' Büttner
1 user
1 user
VivoKey Vault
VivoKey Vault
VivoKey Vault
A secure password manager, backed by the VivoKey Cryptobionic Identity.
There are no ratings yet
Riley Gall
Riley Gall
2 users
2 users
Overconfidence Password Generator
Overconfidence Password Generator
Overconfidence Password Generator
Create random, strong passwords that are extremely hard to crack and impossible to guess. Example: "nny考是込날ђປpማêầ누ျតस!%v"
There are no ratings yet
CRW1O1
CRW1O1
1 user
1 user
Go to Mozilla's homepage
Go to Mozilla's homepage
Add-ons
Add-ons
Add-ons
About
About
Firefox Add-ons Blog
Firefox Add-ons Blog
Extension Workshop
Extension Workshop
Developer Hub
Developer Hub
Developer Policies
Developer Policies
Community Blog
Community Blog
Forum
Forum
Report a bug
Report a bug
Review Guide
Review Guide
Browsers
Browsers
Desktop
Desktop
Mobile
Mobile
Enterprise
Enterprise
Products
Products
Browsers
Browsers
VPN
VPN
Relay
Relay
Monitor
Monitor
Pocket
Pocket
Bluesky (@firefox.com)
Bluesky (@firefox.com)
Instagram (Firefox)
Instagram (Firefox)
YouTube (firefoxchannel)
YouTube (firefoxchannel)
Privacy
Privacy
Cookies
Cookies
Legal
Legal
Except where otherwise
noted
noted
, content on this site is licensed under the
Creative Commons Attribution Share-Alike License v3.0
Creative Commons Attribution Share-Alike License v3.0
or any later version.
Change language...
|
NULL
|
|
1449
|
32
|
18
|
2026-04-11T17:29:18.313309+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928558313_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:29:18-zsh181• *8|monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
8265903461351487404
|
NULL
|
visual_change
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:29:18-zsh181• *8|monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1448
|
|
1448
|
32
|
17
|
2026-04-11T17:29:17.606275+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928557606_m1.jpg...
|
Firefox
|
Screenpipe Dashboard — Personal
|
True
|
http://100.73.206.126:8766
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Close tab
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Search results for "bitwarden" – Add-ons for Firefox (en-US)
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Screenpipe
Screenpipe
Activity
Search
Audio
Work Report
AI Summary
Date
11
/
04
/
2026
Calendar
TOTAL SPAN
1.0h
14:52 → 15:54
ACTIVE
1.0h
BREAKS
0 breaks · 0.0h
SESSIONS
1
62m
FRAMES
612
APPS
6
UI EVENTS
1344
AUDIO
0
ACTIVE PERIOD
14:52 → 15:54
TIME PER APP
— CLICK TO FILTER RIGHT PANEL
iTerm2
5.2m
Firefox
1.5m
Finder
0.6m
QuickTime Player
0.5m
NetAuthAgent
0.1m
Raycast
0.1m
Websites
Windows
UI Events
nas.lakylak.xyz/desktop/#/
0.7m
play.hbomax.com/video/watch/6b742798-e5fa-4f90-8beb-74c328dc5028/65d19c63-e85e-4392-8594-f0f2e0a1dc2d
0.1m
nas.lakylak.xyz/desktop/#/login/account
0.1m
login.tailscale.com/login?next_url=%2Fadmin
0.1m
dennikn.sk/5263377/lod-orion-uspesne-pristala-na-zemi-desat-veci-ktore-si-pamatat-na-misii-artemis-ii/
0.1m
http://localhost:3030
0m
addons.mozilla.org/en-US/firefox/addon/vimium-ff/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search
0m
accounts.google.com/v3/signin/accountchooser?access_type=offline&client_id=674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com&prompt=select_account&redirect_uri=https%3A%2F%2Flogin.tailscale.com%2Fa%2Foauth_response&response_type=code&scope=openid+email+profile&state=tc-ftYwP8oB0urJfQZeh42KPfA&dsh=S1949714251%3A1775911302074699&o2v=2&service=lso&flowName=GeneralOAuthFlow&opparams=%253F&continue=https%3A%2F%2Faccounts.google.com%2Fsignin%2Foauth%2Fconsent%3Fauthuser%3Dunknown%26part%3DAJi8hAOOoeMJ9h1ZWE-Aau2rWQdjwJ-4Enpv6Ci-0hiDSq5jW9TsVTl1btOELm96_EZaQdgfRwBdj1zZGB_CYauRC0fkTqY_cZ35H_NcJOU2_4Ax0Gtto-fKf-nDgh4daUIBqdTh68bjSxOce25-KnK0XYdHV8mzb69bNhDZHVDj_gb42yLS6CaLOtRfvBqDUDqR3SzLMQPfBkXHhJz6IM_7wq52TLTL563ihzeGQBECsxCnD1Yk27REW5r_eZ0TNXkkAX7P1hB8gAkBKuZVXHltsgP7jvfc8R1fsuXURwjdV8kBOMOg3Reld6-0t5nB9qwgvuYCJsuo0Q7IMz9o-t41651a_Wsc0FnlTxFC_0DA-eKXtSlPZHRy9BW_O_UgliMh8Y9cnLpKZ4r9A_LwCm6flXHR1tZWe_NA_YGhQ9EYSdByTuW1hMcIXXh1Alssc0Sb9BtxZ5mPnlLjTXaWd8LEtCrsHv1HQQ%26flowName%3DGeneralOAuthFlow%26as%3DS1949714251%253A1775911302074699%26client_id%3D674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com%26requestPath%3D%252Fsignin%252Foauth%252Fconsent%23&app_domain=https%3A%2F%2Flogin.tailscale.com
0m...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Search results for \"bitwarden\" – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search results for \"bitwarden\" – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Screenpipe","depth":7,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Screenpipe","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Activity","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Search","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Audio","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Work Report","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Summary","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Date","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"11","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"04","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2026","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Calendar","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"TOTAL SPAN","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.0h","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"14:52 → 15:54","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ACTIVE","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.0h","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"BREAKS","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0 breaks · 0.0h","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SESSIONS","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"62m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"FRAMES","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"612","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"APPS","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"6","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"UI EVENTS","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1344","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AUDIO","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ACTIVE PERIOD","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"14:52 → 15:54","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TIME PER APP","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"— CLICK TO FILTER RIGHT PANEL","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"iTerm2","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5.2m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Firefox","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.5m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Finder","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.6m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"QuickTime Player","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.5m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NetAuthAgent","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Raycast","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Websites","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Windows","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"UI Events","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"nas.lakylak.xyz/desktop/#/","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.7m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"play.hbomax.com/video/watch/6b742798-e5fa-4f90-8beb-74c328dc5028/65d19c63-e85e-4392-8594-f0f2e0a1dc2d","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"nas.lakylak.xyz/desktop/#/login/account","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"login.tailscale.com/login?next_url=%2Fadmin","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dennikn.sk/5263377/lod-orion-uspesne-pristala-na-zemi-desat-veci-ktore-si-pamatat-na-misii-artemis-ii/","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"http://localhost:3030","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"addons.mozilla.org/en-US/firefox/addon/vimium-ff/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"accounts.google.com/v3/signin/accountchooser?access_type=offline&client_id=674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com&prompt=select_account&redirect_uri=https%3A%2F%2Flogin.tailscale.com%2Fa%2Foauth_response&response_type=code&scope=openid+email+profile&state=tc-ftYwP8oB0urJfQZeh42KPfA&dsh=S1949714251%3A1775911302074699&o2v=2&service=lso&flowName=GeneralOAuthFlow&opparams=%253F&continue=https%3A%2F%2Faccounts.google.com%2Fsignin%2Foauth%2Fconsent%3Fauthuser%3Dunknown%26part%3DAJi8hAOOoeMJ9h1ZWE-Aau2rWQdjwJ-4Enpv6Ci-0hiDSq5jW9TsVTl1btOELm96_EZaQdgfRwBdj1zZGB_CYauRC0fkTqY_cZ35H_NcJOU2_4Ax0Gtto-fKf-nDgh4daUIBqdTh68bjSxOce25-KnK0XYdHV8mzb69bNhDZHVDj_gb42yLS6CaLOtRfvBqDUDqR3SzLMQPfBkXHhJz6IM_7wq52TLTL563ihzeGQBECsxCnD1Yk27REW5r_eZ0TNXkkAX7P1hB8gAkBKuZVXHltsgP7jvfc8R1fsuXURwjdV8kBOMOg3Reld6-0t5nB9qwgvuYCJsuo0Q7IMz9o-t41651a_Wsc0FnlTxFC_0DA-eKXtSlPZHRy9BW_O_UgliMh8Y9cnLpKZ4r9A_LwCm6flXHR1tZWe_NA_YGhQ9EYSdByTuW1hMcIXXh1Alssc0Sb9BtxZ5mPnlLjTXaWd8LEtCrsHv1HQQ%26flowName%3DGeneralOAuthFlow%26as%3DS1949714251%253A1775911302074699%26client_id%3D674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com%26requestPath%3D%252Fsignin%252Foauth%252Fconsent%23&app_domain=https%3A%2F%2Flogin.tailscale.com","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-817909191078697115
|
-5356146286853993268
|
app_switch
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Close tab
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Search results for "bitwarden" – Add-ons for Firefox (en-US)
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Screenpipe
Screenpipe
Activity
Search
Audio
Work Report
AI Summary
Date
11
/
04
/
2026
Calendar
TOTAL SPAN
1.0h
14:52 → 15:54
ACTIVE
1.0h
BREAKS
0 breaks · 0.0h
SESSIONS
1
62m
FRAMES
612
APPS
6
UI EVENTS
1344
AUDIO
0
ACTIVE PERIOD
14:52 → 15:54
TIME PER APP
— CLICK TO FILTER RIGHT PANEL
iTerm2
5.2m
Firefox
1.5m
Finder
0.6m
QuickTime Player
0.5m
NetAuthAgent
0.1m
Raycast
0.1m
Websites
Windows
UI Events
nas.lakylak.xyz/desktop/#/
0.7m
play.hbomax.com/video/watch/6b742798-e5fa-4f90-8beb-74c328dc5028/65d19c63-e85e-4392-8594-f0f2e0a1dc2d
0.1m
nas.lakylak.xyz/desktop/#/login/account
0.1m
login.tailscale.com/login?next_url=%2Fadmin
0.1m
dennikn.sk/5263377/lod-orion-uspesne-pristala-na-zemi-desat-veci-ktore-si-pamatat-na-misii-artemis-ii/
0.1m
http://localhost:3030
0m
addons.mozilla.org/en-US/firefox/addon/vimium-ff/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search
0m
accounts.google.com/v3/signin/accountchooser?access_type=offline&client_id=674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com&prompt=select_account&redirect_uri=https%3A%2F%2Flogin.tailscale.com%2Fa%2Foauth_response&response_type=code&scope=openid+email+profile&state=tc-ftYwP8oB0urJfQZeh42KPfA&dsh=S1949714251%3A1775911302074699&o2v=2&service=lso&flowName=GeneralOAuthFlow&opparams=%253F&continue=https%3A%2F%2Faccounts.google.com%2Fsignin%2Foauth%2Fconsent%3Fauthuser%3Dunknown%26part%3DAJi8hAOOoeMJ9h1ZWE-Aau2rWQdjwJ-4Enpv6Ci-0hiDSq5jW9TsVTl1btOELm96_EZaQdgfRwBdj1zZGB_CYauRC0fkTqY_cZ35H_NcJOU2_4Ax0Gtto-fKf-nDgh4daUIBqdTh68bjSxOce25-KnK0XYdHV8mzb69bNhDZHVDj_gb42yLS6CaLOtRfvBqDUDqR3SzLMQPfBkXHhJz6IM_7wq52TLTL563ihzeGQBECsxCnD1Yk27REW5r_eZ0TNXkkAX7P1hB8gAkBKuZVXHltsgP7jvfc8R1fsuXURwjdV8kBOMOg3Reld6-0t5nB9qwgvuYCJsuo0Q7IMz9o-t41651a_Wsc0FnlTxFC_0DA-eKXtSlPZHRy9BW_O_UgliMh8Y9cnLpKZ4r9A_LwCm6flXHR1tZWe_NA_YGhQ9EYSdByTuW1hMcIXXh1Alssc0Sb9BtxZ5mPnlLjTXaWd8LEtCrsHv1HQQ%26flowName%3DGeneralOAuthFlow%26as%3DS1949714251%253A1775911302074699%26client_id%3D674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com%26requestPath%3D%252Fsignin%252Foauth%252Fconsent%23&app_domain=https%3A%2F%2Flogin.tailscale.com
0m...
|
NULL
|
|
1447
|
32
|
16
|
2026-04-11T17:29:11.030056+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928551030_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"__init__.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"activity.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"audio.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"ocr.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"report.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"summary.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXStaticText","text":"","depth":29,"role_description":"text"},{"role":"AXTextArea","text":"report.py, preview, Editor Group 1","depth":28,"role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"expanded","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"}]...
|
-1760183978761483615
|
379311784822278753
|
idle
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
report.py, preview, Editor Group 1
report.py, preview, Editor Group 1
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages...
|
1446
|
|
1446
|
32
|
15
|
2026-04-11T17:28:40.557838+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928520557_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:28:40-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
6441165755985111515
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:28:40-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
|
1445
|
32
|
14
|
2026-04-11T17:28:38.089568+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928518089_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:28:37-zshT81• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
8694985997360471120
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:28:37-zshT81• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1444
|
|
1444
|
32
|
13
|
2026-04-11T17:28:34.258408+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928514258_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"__init__.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"activity.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"audio.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"ocr.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"report.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"summary.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"expanded","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-4302382107043214347
|
3198478960193274374
|
click
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
__init__.py
activity.py
audio.py
ocr.py
report.py
summary.py
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
expanded
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking...
|
NULL
|
|
1443
|
32
|
12
|
2026-04-11T17:28:31.922572+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928511922_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:28:31-zshT81• *8|monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-5295118852529811740
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• хз-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:28:31-zshT81• *8|monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1442
|
|
1442
|
32
|
11
|
2026-04-11T17:28:28.434089+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928508434_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"}]...
|
4182593983178566583
|
3198426183633043462
|
click
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null...
|
NULL
|
|
1441
|
32
|
10
|
2026-04-11T17:28:19.581012+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928499581_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:28:19-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-778357031623456719
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:28:19-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1440
|
|
1440
|
32
|
9
|
2026-04-11T17:28:03.101581+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928483101_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:28:02-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-5408667119352649167
|
NULL
|
clipboard
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall100% <7-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zshO 87Sat 11 Apr 20:28:02-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
|
1439
|
32
|
8
|
2026-04-11T17:27:57.255047+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928477255_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• 285-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:57-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
6430081080579796635
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpall-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• 285-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:57-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1438
|
|
1438
|
32
|
7
|
2026-04-11T17:27:55.966742+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928475966_m1.jpg...
|
Code
|
#!/bin/bash • Untitled-1 — screenpipe [SSH: 100.73 #!/bin/bash • Untitled-1 — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Shell Script
Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions
LF
UTF-8
Spaces: 2
Ln 66, Col 1 (2524 selected)
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at [URL_WITH_CREDENTIALS] (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
.bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }
.bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }
.bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }
td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: rgba(255,255,255,0.02); }
.timeline-wrap { overflow-x: auto; }
.timeline-grid { display: grid; gap: 2px; min-width: 600px; }
.tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }
.tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }
.tl-cell {
flex: 1;
height: 20px;
border-radius: 3px;
background: var(--border);
position: relative;
cursor: default;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: white;
}
.summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
}
button {
background: var(--accent);
color: white;
border: none;
padding: 8px 18px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
button:hover { opacity: 0.85; }
button:disabled { opacity: 0.4; cursor: default; }
.summary-output {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
white-space: pre-wrap;
font-family: inherit;
line-height: 1.6;
min-height: 200px;
color: var(--text);
}
.summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }
.summary-output ul { padding-left: 20px; }
.summary-output strong { color: var(--accent2); }
.search-bar { display: flex; gap: 8px; margin-bottom: 16px; }
.search-bar input[type="text"] { flex: 1; }
.search-bar select { }
.result-item {
border-bottom: 1px solid var(--border);
padding: 10px 4px;
}
.result-item:last-child { border-bottom: none; }
.result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.result-snippet { font-size: 13px; }
.result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }
.audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }
.audio-item:last-child { border-bottom: none; }
.audio-badge {
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
font-wei...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Shell Script","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"LF","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"UTF-8","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Spaces: 2","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Ln 66, Col 1 (2524 selected)","depth":16,"bounds":{"left":0.98680556,"top":0.0,"width":0.013194442,"height":0.024444444},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"bounds":{"left":0.8736111,"top":0.0,"width":0.11180556,"height":0.024444444},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n[rerun: b4]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bootstrap pip then install packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n/usr/bin/python3: No module named ensurepip\n[rerun: b5]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip via apt","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n[rerun: b6]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip with sudo","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Reason:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"lets make it in docker, not to install it in nas directly","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Good call. Let me check what Docker setup you already have and build a proper containerized app.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check existing docker-compose for reference","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"consume\ndata\ndb\ndocker-compose.yml\nexport\nmcp-server\nmedia\nredis\ntrash\nservices:\n redis:\n image: redis:8\n command:\n - /bin/sh\n - -c\n - redis-server --requirepass redispass\n container_name: PaperlessNGX-REDIS\n hostname: paper-redis\n security_opt:\n - no-new-privileges:true\n read_only: true\n user: 1000:10\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli ping || exit 1\"]\n volumes:\n - /volume2/docker/paperlessngx/redis:/data:rw\n environment:\n TZ: Europe/Sofia\n restart: always\n\n db:\n image: postgres:17\n container_name: PaperlessNGX-DB\n hostname: paper-db\n mem_limit: 1g\n security_opt:\n - no-new-privileges:true\n healthcheck:\n test: [\"CMD\", \"pg_isready\", \"-q\", \"-d\", \"paperless\", \"-U\", \"paperlessuser\"]\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Docker availability","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"docker --version && docker compose version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Docker version 26.1.0, build 9714adc\nDocker Compose version v2.26.1\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Create app directory structure","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"[rerun: b9]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Now let me build everything. I'll create all the files:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"requirements.txt","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"requirements.txt","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"6 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"fastapi==0.115.12\nuvicorn[standard]==0.34.0\nclick==8.1.8\nanthropic==0.49.0\npython-dotenv==1.1.0","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Dockerfile","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Dockerfile","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"13 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"FROM python:3.12-slim\n\nWORKDIR /app\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY . .\n\nEXPOSE 8000\n\nCMD [\"uvicorn\", \"main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"db.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"db.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"28 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"import sqlite3\nfrom contextlib import contextmanager\nfrom datetime import date, datetime, timedelta, timezone\nfrom typing import Generator\n\nDB_PATH = \"/data/db.sqlite\"\n\n\n@contextmanager\ndef get_conn() -> Generator[sqlite3.Connection, None, None]:\n conn = sqlite3.connect(f\"file:{DB_PATH}?mode=ro\", uri=True)\n conn.row_factory = sqlite3.Row\n try:\n yield conn\n finally:\n conn.close()\n\n\ndef date_range(for_date: date) -> tuple[str, str]:\n \"\"\"Return ISO timestamps for start/end of a given date (UTC).\"\"\"\n start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)\n end = start + timedelta(days=1)\n return start.isoformat(), end.isoformat()\n\n\ndef today() -> date:\n return datetime.now(timezone.utc).date()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"__init__.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"__init__.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"1 line","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"activity.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"activity.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"123 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"App usage and window focus time consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef app_time(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per app for a given date, sorted descending.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n o.app_name,\n COUNT(*) as frame_count,\n -- each frame ~ 2s at 0.5fps; convert to minutes\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY o.app_name\n ORDER BY frame_count DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per window/tab, optionally filtered by app.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n o.app_name,\n o.window_name,\n COUNT(*) as frame_count,\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n \"\"\"\n params: list = [start, end]\n if app:\n query += \" AND o.app_name = ?\"\n params.append(app)\n query += \" GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:\n \"\"\"Return activity bucketed into N-minute intervals.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = interval_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n COUNT(*) as frames\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY bucket, o.app_name\n ORDER BY bucket, frames DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef summary_stats(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Return high-level stats for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n frames_row = conn.execute(\n \"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last \"\n \"FROM frames WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n app_count = conn.execute(\n \"\"\"\n SELECT COUNT(DISTINCT o.app_name) as n\n FROM frames f JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''\n \"\"\",\n (start, end),\n ).fetchone()\n\n audio_count = conn.execute(\n \"SELECT COUNT(*) as n FROM audio_transcriptions \"\n \"WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n return {\n \"date\": str(d),\n \"total_frames\": frames_row[\"n\"],\n \"first_seen\": frames_row[\"first\"],\n \"last_seen\": frames_row[\"last\"],\n \"unique_apps\": app_count[\"n\"],\n \"audio_segments\": audio_count[\"n\"],\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"ocr.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"ocr.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"92 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"OCR text search and aggregation consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:\n \"\"\"Full-text search OCR content.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet\n FROM ocr_text_fts\n JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid\n JOIN frames f ON o.frame_id = f.id\n WHERE ocr_text_fts MATCH ?\n AND f.timestamp >= ? AND f.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:\n \"\"\"Return all OCR text for a date, optionally only focused windows.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n o.text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n \"\"\"\n params: list = [start, end]\n if focused_only:\n query += \" AND o.focused = TRUE\"\n query += \" ORDER BY f.timestamp\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:\n \"\"\"\n Return OCR text deduplicated by time chunk + app to reduce LLM token usage.\n Groups frames into N-minute buckets and returns one representative text per app per bucket.\n \"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = chunk_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n o.window_name,\n substr(o.text, 1, 500) as text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n GROUP BY bucket, o.app_name\n ORDER BY bucket\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"audio.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"audio.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"61 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Audio transcription consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return all audio transcriptions for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT timestamp, transcription, device, is_input_device, speaker_id\n FROM audio_transcriptions\n WHERE timestamp >= ? AND timestamp < ?\n AND length(transcription) > 2\n ORDER BY timestamp\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:\n \"\"\"Search audio transcriptions.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n a.timestamp,\n a.device,\n snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet\n FROM audio_transcriptions_fts\n JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id\n WHERE audio_transcriptions_fts MATCH ?\n AND a.timestamp >= ? AND a.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef formatted_transcript(for_date: date | None = None) -> str:\n \"\"\"Return audio as a readable transcript string.\"\"\"\n rows = transcriptions(for_date)\n if not rows:\n return \"(no audio transcriptions)\"\n lines = []\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n lines.append(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n return \"\\n\".join(lines)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"summary.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"summary.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"162 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"AI summary generator using Claude.\"\"\"\nimport os\nfrom datetime import date\nfrom typing import Any\n\nfrom consumers.activity import app_time, summary_stats\nfrom consumers.ocr import deduplicated_text\nfrom consumers.audio import formatted_transcript\n\n\nSUMMARY_TYPES = {\n \"day_recap\": {\n \"title\": \"Day Recap\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a concise day recap using EXACTLY this format:\n\n## Summary\nOne sentence: what I mainly worked on today.\n\n## Accomplishments\n- Top 3 things I finished, with approximate times\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things seen or heard, with timestamps\n\n## Unfinished Work\n- What to continue tomorrow\n\n## Patterns\n- Apps used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with:\n**Next step:** [most important thing to continue]\"\"\",\n },\n \"standup\": {\n \"title\": \"Standup Update\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a brief standup update:\n\n**Yesterday:** What I worked on (2-3 bullets)\n**Today:** What I plan to continue (1-2 bullets)\n**Blockers:** Any issues observed (or \"None\")\n\nKeep it under 150 words. Only report what's verifiable from the data.\"\"\",\n },\n \"focus_time\": {\n \"title\": \"Focus & Distraction Analysis\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\nAnalyze focus vs distraction patterns:\n\n## Deep Work Periods\n- When and how long were sustained focus sessions?\n- What was being worked on?\n\n## Distractions\n- Which apps/sites were used recreationally?\n- How much time was lost to context switching?\n\n## Recommendations\n- 2-3 concrete suggestions to improve focus tomorrow\n\nBe specific with times and app names.\"\"\",\n },\n}\n\n\ndef _build_context(for_date: date) -> dict[str, str]:\n apps = app_time(for_date)\n app_lines = \"\\n\".join(f\"- {a['app_name']}: {a['minutes']} min\" for a in apps[:20])\n\n ocr_rows = deduplicated_text(for_date, chunk_minutes=5)\n ocr_lines = \"\\n\".join(\n f\"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}\"\n for r in ocr_rows[:100]\n )\n\n audio = formatted_transcript(for_date)\n\n return {\n \"app_time\": app_lines or \"(no app data)\",\n \"ocr_text\": ocr_lines or \"(no screen data)\",\n \"audio\": audio[:3000] if len(audio) > 3000 else audio,\n }\n\n\ndef generate(\n summary_type: str = \"day_recap\",\n for_date: date | None = None,\n api_key: str | None = None,\n) -> dict[str, Any]:\n \"\"\"Generate an AI summary. Returns dict with 'title', 'content', 'date'.\"\"\"\n import anthropic\n\n d = for_date or date.today()\n key = api_key or os.environ.get(\"ANTHROPIC_API_KEY\", \"\")\n if not key:\n return {\n \"title\": \"Error\",\n \"content\": \"ANTHROPIC_API_KEY not set. Add it to your .env file.\",\n \"date\": str(d),\n }\n\n spec = SUMMARY_TYPES.get(summary_type)\n if not spec:\n return {\"title\": \"Error\", \"content\": f\"Unknown summary type: {summary_type}\", \"date\": str(d)}\n\n stats = summary_stats(d)\n if stats[\"total_frames\"] == 0:\n return {\n \"title\": spec[\"title\"],\n \"content\": f\"No screen data found for {d}.\",\n \"date\": str(d),\n }\n\n ctx = _build_context(d)\n prompt = spec[\"prompt\"].format(**ctx)\n\n client = anthropic.Anthropic(api_key=key)\n message = client.messages.create(\n model=\"claude-opus-4-6\",\n max_tokens=1024,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n )\n\n return {\n \"title\": spec[\"title\"],\n \"content\": message.content[0].text,\n \"date\": str(d),\n \"stats\": stats,\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the FastAPI backend and CLI:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"main.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"main.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"89 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Screenpipe dashboard — FastAPI web UI.\"\"\"\nimport os\nfrom datetime import date, datetime\nfrom typing import Any\n\nfrom dotenv import load_dotenv\nfrom fastapi import FastAPI, Query\nfrom fastapi.responses import HTMLResponse, JSONResponse\nfrom fastapi.staticfiles import StaticFiles\n\nload_dotenv()\n\nfrom consumers.activity import app_time, window_time, timeline, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions, search as audio_search\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\napp = FastAPI(title=\"Screenpipe Dashboard\")\napp.mount(\"/static\", StaticFiles(directory=\"static\"), name=\"static\")\n\n\ndef _parse_date(d: str | None) -> date | None:\n if not d:\n return None\n try:\n return date.fromisoformat(d)\n except ValueError:\n return None\n\n\n@app.get(\"/\", response_class=HTMLResponse)\nasync def index():\n with open(\"static/index.html\") as f:\n return f.read()\n\n\n# ── Activity ──────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/stats\")\nasync def stats(d: str | None = Query(None)):\n return summary_stats(_parse_date(d))\n\n\n@app.get(\"/api/activity/apps\")\nasync def activity_apps(d: str | None = Query(None)):\n return app_time(_parse_date(d))\n\n\n@app.get(\"/api/activity/windows\")\nasync def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):\n return window_time(_parse_date(d), app_name)\n\n\n@app.get(\"/api/activity/timeline\")\nasync def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):\n return timeline(_parse_date(d), interval)\n\n\n# ── Search ───────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/search/ocr\")\nasync def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):\n return ocr_search(q, _parse_date(d), limit)\n\n\n@app.get(\"/api/search/audio\")\nasync def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):\n return audio_search(q, _parse_date(d), limit)\n\n\n# ── Audio ─────────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/audio\")\nasync def audio(d: str | None = Query(None)):\n return transcriptions(_parse_date(d))\n\n\n# ── Summaries ─────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/summary/types\")\nasync def summary_types():\n return [{\"id\": k, \"title\": v[\"title\"]} for k, v in SUMMARY_TYPES.items()]\n\n\n@app.post(\"/api/summary/generate\")\nasync def generate_summary(type: str = \"day_recap\", d: str | None = None):\n result = gen_summary(type, _parse_date(d))\n return result","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"cli.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"cli.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"131 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"#!/usr/bin/env python3\n\"\"\"Screenpipe CLI — run summaries and reports on demand.\"\"\"\nimport os\nimport sys\nfrom datetime import date, timedelta\n\nimport click\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nsys.path.insert(0, os.path.dirname(__file__))\n\nfrom consumers.activity import app_time, window_time, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\n\ndef _resolve_date(d_str: str | None) -> date | None:\n if not d_str:\n return None\n if d_str == \"yesterday\":\n return date.today() - timedelta(days=1)\n if d_str == \"today\":\n return date.today()\n return date.fromisoformat(d_str)\n\n\n@click.group()\ndef cli():\n \"\"\"Screenpipe data explorer and summariser.\"\"\"\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--type\", \"-t\", \"summary_type\", default=\"day_recap\",\n type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)\ndef summary(d, summary_type):\n \"\"\"Generate an AI summary for a date (default: today).\"\"\"\n target = _resolve_date(d)\n click.echo(f\"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...\")\n result = gen_summary(summary_type, target)\n click.echo(f\"\\n{'─'*60}\")\n click.echo(f\" {result['title']} — {result['date']}\")\n click.echo(f\"{'─'*60}\\n\")\n click.echo(result[\"content\"])\n if \"stats\" in result:\n s = result[\"stats\"]\n click.echo(f\"\\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, \"\n f\"{s['audio_segments']} audio segments]\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--app\", \"-a\", default=None, help=\"Filter by app name\")\n@click.option(\"--windows\", \"-w\", is_flag=True, help=\"Show window breakdown instead of apps\")\ndef activity(d, app, windows):\n \"\"\"Show time spent per app or window.\"\"\"\n target = _resolve_date(d)\n label = str(target or date.today())\n\n if windows or app:\n rows = window_time(target, app)\n click.echo(f\"\\nWindow time — {label}\" + (f\" [{app}]\" if app else \"\"))\n click.echo(f\"{'App':<25} {'Window':<40} {'Min':>6}\")\n click.echo(\"─\" * 75)\n for r in rows:\n click.echo(f\"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}\")\n else:\n rows = app_time(target)\n stats = summary_stats(target)\n click.echo(f\"\\nApp time — {label}\")\n click.echo(f\"{'App':<30} {'Minutes':>8}\")\n click.echo(\"─\" * 40)\n for r in rows:\n click.echo(f\"{r['app_name']:<30} {r['minutes']:>8}\")\n click.echo(f\"\\nTotal frames: {stats['total_frames']} | \"\n f\"Active: {stats['first_seen']} → {stats['last_seen']}\")\n\n\n@cli.command()\n@click.argument(\"query\")\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--audio\", is_flag=True, help=\"Search audio instead of screen text\")\n@click.option(\"--limit\", \"-n\", default=20, show_default=True)\ndef search(query, d, audio, limit):\n \"\"\"Search OCR screen text or audio transcriptions.\"\"\"\n from consumers.audio import search as audio_search_fn\n\n target = _resolve_date(d)\n if audio:\n rows = audio_search_fn(query, target, limit)\n click.echo(f\"\\nAudio results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['snippet']}\")\n else:\n rows = ocr_search(query, target, limit)\n click.echo(f\"\\nOCR results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}\")\n click.echo(f\" {r['snippet']}\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\ndef audio(d):\n \"\"\"Show audio transcriptions for a date.\"\"\"\n target = _resolve_date(d)\n rows = transcriptions(target)\n if not rows:\n click.echo(\"No audio transcriptions found.\")\n return\n click.echo(f\"\\nAudio transcriptions — {target or date.today()}\")\n click.echo(\"─\" * 60)\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n click.echo(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n\n\n@cli.command()\ndef serve():\n \"\"\"Start the web UI (runs uvicorn on port 8000).\"\"\"\n import uvicorn\n click.echo(\"Starting Screenpipe dashboard at http://localhost:8000\")\n uvicorn.run(\"main:app\", host=\"0.0.0.0\", port=8000, reload=False)\n\n\nif __name__ == \"__main__\":\n cli()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the frontend:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"index.html","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"index.html","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"438 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Screenpipe Dashboard</title>\n<style>\n :root {\n --bg: #0f0f11;\n --surface: #1a1a1f;\n --border: #2a2a35;\n --accent: #7c6fcd;\n --accent2: #4fa3e0;\n --text: #e0e0e8;\n --muted: #888899;\n --green: #4caf88;\n --red: #e06060;\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body { background: var(--bg); color: var(--text); font-family: 'Segoe UI', system-ui, sans-serif; font-size: 14px; }\n\n header {\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n gap: 16px;\n position: sticky;\n top: 0;\n z-index: 100;\n }\n header h1 { font-size: 16px; font-weight: 600; color: var(--accent); }\n header .spacer { flex: 1; }\n\n .date-picker { display: flex; align-items: center; gap: 8px; }\n .date-picker label { color: var(--muted); font-size: 12px; }\n input[type=\"date\"] {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 13px;\n }\n input[type=\"text\"] {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 13px;\n width: 200px;\n }\n input[type=\"text\"]:focus, input[type=\"date\"]:focus { outline: 1px solid var(--accent); }\n\n .tabs { display: flex; gap: 4px; }\n .tab {\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n color: var(--muted);\n font-size: 13px;\n border: 1px solid transparent;\n background: none;\n }\n .tab.active { background: var(--accent); color: white; }\n .tab:hover:not(.active) { background: var(--border); color: var(--text); }\n\n main { padding: 20px 24px; max-width: 1200px; margin: 0 auto; }\n\n .page { display: none; }\n .page.active { display: block; }\n\n .stats-bar {\n display: flex;\n gap: 16px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n }\n .stat-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 12px 16px;\n flex: 1;\n min-width: 150px;\n }\n .stat-card .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }\n .stat-card .value { font-size: 22px; font-weight: 700; color: var(--accent); margin-top: 4px; }\n .stat-card .sub { color: var(--muted); font-size: 11px; margin-top: 2px; }\n\n .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n @media (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }\n\n .card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 10px;\n padding: 16px;\n }\n .card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }\n\n .bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }\n .bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }\n .bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }\n .bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }\n .bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }\n\n table { width: 100%; border-collapse: collapse; font-size: 13px; }\n th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }\n td { padding: 6px 8px; border-bottom: 1px solid var(--border); }\n tr:last-child td { border-bottom: none; }\n tr:hover td { background: rgba(255,255,255,0.02); }\n\n .timeline-wrap { overflow-x: auto; }\n .timeline-grid { display: grid; gap: 2px; min-width: 600px; }\n .tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }\n .tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }\n .tl-cell {\n flex: 1;\n height: 20px;\n border-radius: 3px;\n background: var(--border);\n position: relative;\n cursor: default;\n font-size: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n color: white;\n }\n\n .summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }\n select {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 13px;\n }\n button {\n background: var(--accent);\n color: white;\n border: none;\n padding: 8px 18px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n }\n button:hover { opacity: 0.85; }\n button:disabled { opacity: 0.4; cursor: default; }\n\n .summary-output {\n background: var(--bg);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 20px;\n white-space: pre-wrap;\n font-family: inherit;\n line-height: 1.6;\n min-height: 200px;\n color: var(--text);\n }\n .summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }\n .summary-output ul { padding-left: 20px; }\n .summary-output strong { color: var(--accent2); }\n\n .search-bar { display: flex; gap: 8px; margin-bottom: 16px; }\n .search-bar input[type=\"text\"] { flex: 1; }\n .search-bar select { }\n\n .result-item {\n border-bottom: 1px solid var(--border);\n padding: 10px 4px;\n }\n .result-item:last-child { border-bottom: none; }\n .result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }\n .result-snippet { font-size: 13px; }\n .result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }\n\n .audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }\n .audio-item:last-child { border-bottom: none; }\n .audio-badge {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 4px;\n flex-shrink: 0;\n font-weight: 600;\n }\n .audio-badge.mic { background: rgba(76,175,136,0.2); color: var(--green); }\n .audio-badge.sys { background: rgba(74,163,224,0.2); color: var(--accent2); }\n .audio-ts { color: var(--muted); font-size: 11px; flex-shrink: 0; }\n\n .spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.7s linear infinite; vertical-align: middle; margin-right: 6px; }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n .empty { color: var(--muted); text-align: center; padding: 40px; font-size: 13px; }\n</style>\n</head>\n<body>\n\n<header>\n <h1>Screenpipe</h1>\n <div class=\"tabs\">\n <button class=\"tab active\" onclick=\"showPage('activity')\">Activity</button>\n <button class=\"tab\" onclick=\"showPage('search')\">Search</button>\n <button class=\"tab\" onclick=\"showPage('audio')\">Audio</button>\n <button class=\"tab\" onclick=\"showPage('summary')\">Summary</button>\n </div>\n <div class=\"spacer\"></div>\n <div class=\"date-picker\">\n <label>Date</label>\n <input type=\"date\" id=\"datePicker\" onchange=\"refresh()\">\n </div>\n</header>\n\n<main>\n\n<!-- ── Activity ─────────────────────────────────────────────── -->\n<div class=\"page active\" id=\"page-activity\">\n <div class=\"stats-bar\" id=\"statsBar\">\n <div class=\"stat-card\"><div class=\"label\">Frames</div><div class=\"value\" id=\"st-frames\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Apps</div><div class=\"value\" id=\"st-apps\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Audio Segs</div><div class=\"value\" id=\"st-audio\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Active</div><div class=\"value\" id=\"st-first\" style=\"font-size:13px;margin-top:6px\">–</div><div class=\"sub\" id=\"st-last\">–</div></div>\n </div>\n <div class=\"grid-2\">\n <div class=\"card\">\n <h2>Time per App</h2>\n <div id=\"appBars\"><div class=\"empty\">Loading...</div></div>\n </div>\n <div class=\"card\">\n <h2>Windows</h2>\n <div id=\"windowTable\"><div class=\"empty\">Click an app bar to filter</div></div>\n </div>\n </div>\n</div>\n\n<!-- ── Search ───────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-search\">\n <div class=\"search-bar\">\n <input type=\"text\" id=\"searchQuery\" placeholder=\"Search screen text...\" onkeydown=\"if(event.key==='Enter') runSearch()\">\n <select id=\"searchTarget\">\n <option value=\"ocr\">Screen (OCR)</option>\n <option value=\"audio\">Audio</option>\n </select>\n <button onclick=\"runSearch()\">Search</button>\n </div>\n <div class=\"card\">\n <div id=\"searchResults\"><div class=\"empty\">Enter a query above to search screen text or audio transcriptions.</div></div>\n </div>\n</div>\n\n<!-- ── Audio ────────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-audio\">\n <div class=\"card\">\n <h2>Audio Transcriptions</h2>\n <div id=\"audioList\"><div class=\"empty\">Loading...</div></div>\n </div>\n</div>\n\n<!-- ── Summary ──────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-summary\">\n <div class=\"summary-controls\">\n <select id=\"summaryType\">\n <option value=\"day_recap\">Day Recap</option>\n <option value=\"standup\">Standup Update</option>\n <option value=\"focus_time\">Focus Analysis</option>\n </select>\n <button id=\"genBtn\" onclick=\"generateSummary()\">Generate</button>\n </div>\n <div class=\"summary-output\" id=\"summaryOutput\">\n Select a summary type and click Generate. Requires ANTHROPIC_API_KEY in your environment.\n </div>\n</div>\n\n</main>\n\n<script>\nconst API = '';\nlet currentPage = 'activity';\nlet currentApp = null;\n\n// ── Date ────────────────────────────────────────────────────────\nconst dp = document.getElementById('datePicker');\ndp.value = new Date().toISOString().slice(0, 10);\nfunction getDate() { return dp.value; }\n\nfunction refresh() {\n if (currentPage === 'activity') loadActivity();\n if (currentPage === 'audio') loadAudio();\n}\n\n// ── Page nav ────────────────────────────────────────────────────\nfunction showPage(name) {\n document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));\n document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));\n document.getElementById('page-' + name).classList.add('active');\n document.querySelectorAll('.tab')[['activity','search','audio','summary'].indexOf(name)].classList.add('active');\n currentPage = name;\n if (name === 'activity') loadActivity();\n if (name === 'audio') loadAudio();\n}\n\n// ── Activity ────────────────────────────────────────────────────\nasync function loadActivity() {\n const d = getDate();\n const [stats, apps] = await Promise.all([\n fetch(`${API}/api/stats?d=${d}`).then(r => r.json()),\n fetch(`${API}/api/activity/apps?d=${d}`).then(r => r.json()),\n ]);\n\n document.getElementById('st-frames').textContent = stats.total_frames ?? '–';\n document.getElementById('st-apps').textContent = stats.unique_apps ?? '–';\n document.getElementById('st-audio').textContent = stats.audio_segments ?? '–';\n document.getElementById('st-first').textContent = stats.first_seen ? fmtTime(stats.first_seen) : '–';\n document.getElementById('st-last').textContent = stats.last_seen ? '→ ' + fmtTime(stats.last_seen) : '';\n\n const max = apps[0]?.minutes || 1;\n const barsEl = document.getElementById('appBars');\n if (!apps.length) { barsEl.innerHTML = '<div class=\"empty\">No data</div>'; return; }\n\n barsEl.innerHTML = apps.slice(0, 20).map(a => `\n <div class=\"bar-item\" onclick=\"loadWindows('${esc(a.app_name)}')\" style=\"cursor:pointer\" title=\"Click to filter windows\">\n <div class=\"name\" title=\"${esc(a.app_name)}\">${esc(a.app_name)}</div>\n <div class=\"bar-wrap\"><div class=\"bar-fill\" style=\"width:${(a.minutes/max*100).toFixed(1)}%\"></div></div>\n <div class=\"mins\">${a.minutes}m</div>\n </div>`).join('');\n\n loadWindows(null);\n}\n\nasync function loadWindows(app) {\n currentApp = app;\n const d = getDate();\n const url = app ? `/api/activity/windows?d=${d}&app_name=${encodeURIComponent(app)}` : `/api/activity/windows?d=${d}`;\n const rows = await fetch(url).then(r => r.json());\n const el = document.getElementById('windowTable');\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No data</div>'; return; }\n el.innerHTML = `<table>\n <thead><tr><th>App</th><th>Window</th><th>Min</th></tr></thead>\n <tbody>${rows.slice(0,30).map(r => `\n <tr><td>${esc(r.app_name)}</td><td title=\"${esc(r.window_name||'')}\">${esc((r.window_name||'').slice(0,50))}</td><td>${r.minutes}</td></tr>\n `).join('')}</tbody>\n </table>`;\n}\n\n// ── Search ──────────────────────────────────────────────────────\nasync function runSearch() {\n const q = document.getElementById('searchQuery').value.trim();\n if (!q) return;\n const target = document.getElementById('searchTarget').value;\n const d = getDate();\n const el = document.getElementById('searchResults');\n el.innerHTML = '<div class=\"empty\"><span class=\"spinner\"></span>Searching...</div>';\n\n const url = target === 'audio'\n ? `/api/search/audio?q=${encodeURIComponent(q)}&d=${d}`\n : `/api/search/ocr?q=${encodeURIComponent(q)}&d=${d}`;\n\n const rows = await fetch(url).then(r => r.json()).catch(() => []);\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No results found.</div>'; return; }\n\n el.innerHTML = rows.map(r => `\n <div class=\"result-item\">\n <div class=\"result-meta\">${r.timestamp ? fmtTime(r.timestamp) : ''} · ${esc(r.app_name||'')} ${r.window_name ? '/ ' + esc(r.window_name) : ''}</div>\n <div class=\"result-snippet\">${highlightSnippet(r.snippet||'')}</div>\n </div>`).join('');\n}\n\nfunction highlightSnippet(s) {\n return esc(s).replace(/\\[/g, '<mark>').replace(/\\]/g, '</mark>');\n}\n\n// ── Audio ───────────────────────────────────────────────────────\nasync function loadAudio() {\n const d = getDate();\n const rows = await fetch(`${API}/api/audio?d=${d}`).then(r => r.json());\n const el = document.getElementById('audioList');\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No audio transcriptions for this date.</div>'; return; }\n el.innerHTML = rows.map(r => `\n <div class=\"audio-item\">\n <span class=\"audio-ts\">${fmtTime(r.timestamp)}</span>\n <span class=\"audio-badge ${r.is_input_device ? 'mic' : 'sys'}\">${r.is_input_device ? 'MIC' : 'SYS'}</span>\n <span>${esc(r.transcription)}</span>\n </div>`).join('');\n}\n\n// ── Summary ─────────────────────────────────────────────────────\nasync function generateSummary() {\n const type = document.getElementById('summaryType').value;\n const d = getDate();\n const btn = document.getElementById('genBtn');\n const out = document.getElementById('summaryOutput');\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span>Generating...';\n out.textContent = 'Sending data to Claude...';\n\n const res = await fetch(`/api/summary/generate?type=${type}&d=${d}`, { method: 'POST' })\n .then(r => r.json()).catch(e => ({ content: 'Error: ' + e.message }));\n\n out.innerHTML = renderMarkdown(res.content || res.error || 'No content');\n btn.disabled = false;\n btn.textContent = 'Generate';\n}\n\n// ── Helpers ─────────────────────────────────────────────────────\nfunction esc(s) {\n return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');\n}\n\nfunction fmtTime(ts) {\n if (!ts) return '';\n try {\n const d = new Date(ts);\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n } catch { return ts; }\n}\n\nfunction renderMarkdown(text) {\n return esc(text)\n .replace(/^## (.+)$/gm, '<h2>$1</h2>')\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/^- (.+)$/gm, '<li>$1</li>')\n .replace(/(<li>.*<\\/li>)/s, '<ul>$1</ul>')\n .replace(/\\n/g, '<br>');\n}\n\n// Init\nloadActivity();\n</script>\n</body>\n</html>","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the Docker Compose file:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"docker-compose.yml","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"docker-compose.yml","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"14 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"services:\n screenpipe-app:\n build: .\n container_name: screenpipe-app\n restart: unless-stopped\n ports:\n - \"8765:8000\"\n volumes:\n - /volume1/screenpipe/db.sqlite:/data/db.sqlite:ro\n env_file:\n - .env\n environment:\n TZ: Europe/Sofia","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":".env","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":".env","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"2 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ANTHROPIC_API_KEY=your_key_here","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Now let's build and test:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Build Docker image","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cd /volume1/screenpipe/app && docker compose build 2>&1","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\npermission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head \"http://%2Fvar%2Frun%2Fdocker.sock/_ping\": dial unix /var/run/docker.sock: connect: permission denied\n[rerun: b10]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Build Docker image with sudo","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sudo docker compose -f /volume1/screenpipe/app/docker-compose.yml build 2>&1","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\nsudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper\nsudo: a password is required\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check current user and groups","depth":23,"role_description":"text"}]...
|
-8628379668759659158
|
-8187121230352777210
|
clipboard
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Shell Script
Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions
LF
UTF-8
Spaces: 2
Ln 66, Col 1 (2524 selected)
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at [URL_WITH_CREDENTIALS] (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
.bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }
.bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }
.bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }
td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: rgba(255,255,255,0.02); }
.timeline-wrap { overflow-x: auto; }
.timeline-grid { display: grid; gap: 2px; min-width: 600px; }
.tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }
.tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }
.tl-cell {
flex: 1;
height: 20px;
border-radius: 3px;
background: var(--border);
position: relative;
cursor: default;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: white;
}
.summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
}
button {
background: var(--accent);
color: white;
border: none;
padding: 8px 18px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
button:hover { opacity: 0.85; }
button:disabled { opacity: 0.4; cursor: default; }
.summary-output {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
white-space: pre-wrap;
font-family: inherit;
line-height: 1.6;
min-height: 200px;
color: var(--text);
}
.summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }
.summary-output ul { padding-left: 20px; }
.summary-output strong { color: var(--accent2); }
.search-bar { display: flex; gap: 8px; margin-bottom: 16px; }
.search-bar input[type="text"] { flex: 1; }
.search-bar select { }
.result-item {
border-bottom: 1px solid var(--border);
padding: 10px 4px;
}
.result-item:last-child { border-bottom: none; }
.result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.result-snippet { font-size: 13px; }
.result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }
.audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }
.audio-item:last-child { border-bottom: none; }
.audio-badge {
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
font-wei...
|
NULL
|
|
1437
|
32
|
6
|
2026-04-11T17:27:54.190128+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928474190_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:54-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
-2159165616830042629
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:54-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1436
|
|
1436
|
32
|
5
|
2026-04-11T17:27:53.652081+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928473652_m1.jpg...
|
Code
|
#!/bin/bash • Untitled-1 — screenpipe [SSH: 100.73 #!/bin/bash • Untitled-1 — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Shell Script
Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions
LF
UTF-8
Spaces: 2
Ln 16, Col 1
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at [URL_WITH_CREDENTIALS] (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
.bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }
.bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }
.bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }
td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: rgba(255,255,255,0.02); }
.timeline-wrap { overflow-x: auto; }
.timeline-grid { display: grid; gap: 2px; min-width: 600px; }
.tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }
.tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }
.tl-cell {
flex: 1;
height: 20px;
border-radius: 3px;
background: var(--border);
position: relative;
cursor: default;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: white;
}
.summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
}
button {
background: var(--accent);
color: white;
border: none;
padding: 8px 18px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
button:hover { opacity: 0.85; }
button:disabled { opacity: 0.4; cursor: default; }
.summary-output {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
white-space: pre-wrap;
font-family: inherit;
line-height: 1.6;
min-height: 200px;
color: var(--text);
}
.summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }
.summary-output ul { padding-left: 20px; }
.summary-output strong { color: var(--accent2); }
.search-bar { display: flex; gap: 8px; margin-bottom: 16px; }
.search-bar input[type="text"] { flex: 1; }
.search-bar select { }
.result-item {
border-bottom: 1px solid var(--border);
padding: 10px 4px;
}
.result-item:last-child { border-bottom: none; }
.result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.result-snippet { font-size: 13px; }
.result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }
.audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }
.audio-item:last-child { border-bottom: none; }
.audio-badge {
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
font-weight: 600;
}
...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Shell Script","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"LF","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"UTF-8","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Spaces: 2","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Ln 16, Col 1","depth":16,"bounds":{"left":1.0,"top":0.0,"width":-0.052777767,"height":0.024444444},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"bounds":{"left":0.93958336,"top":0.0,"width":0.06041664,"height":0.024444444},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n[rerun: b4]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bootstrap pip then install packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n/usr/bin/python3: No module named ensurepip\n[rerun: b5]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip via apt","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n[rerun: b6]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip with sudo","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Reason:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"lets make it in docker, not to install it in nas directly","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Good call. Let me check what Docker setup you already have and build a proper containerized app.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check existing docker-compose for reference","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"consume\ndata\ndb\ndocker-compose.yml\nexport\nmcp-server\nmedia\nredis\ntrash\nservices:\n redis:\n image: redis:8\n command:\n - /bin/sh\n - -c\n - redis-server --requirepass redispass\n container_name: PaperlessNGX-REDIS\n hostname: paper-redis\n security_opt:\n - no-new-privileges:true\n read_only: true\n user: 1000:10\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli ping || exit 1\"]\n volumes:\n - /volume2/docker/paperlessngx/redis:/data:rw\n environment:\n TZ: Europe/Sofia\n restart: always\n\n db:\n image: postgres:17\n container_name: PaperlessNGX-DB\n hostname: paper-db\n mem_limit: 1g\n security_opt:\n - no-new-privileges:true\n healthcheck:\n test: [\"CMD\", \"pg_isready\", \"-q\", \"-d\", \"paperless\", \"-U\", \"paperlessuser\"]\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Docker availability","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"docker --version && docker compose version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Docker version 26.1.0, build 9714adc\nDocker Compose version v2.26.1\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Create app directory structure","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"[rerun: b9]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Now let me build everything. I'll create all the files:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"requirements.txt","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"requirements.txt","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"6 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"fastapi==0.115.12\nuvicorn[standard]==0.34.0\nclick==8.1.8\nanthropic==0.49.0\npython-dotenv==1.1.0","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Dockerfile","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Dockerfile","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"13 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"FROM python:3.12-slim\n\nWORKDIR /app\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY . .\n\nEXPOSE 8000\n\nCMD [\"uvicorn\", \"main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"db.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"db.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"28 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"import sqlite3\nfrom contextlib import contextmanager\nfrom datetime import date, datetime, timedelta, timezone\nfrom typing import Generator\n\nDB_PATH = \"/data/db.sqlite\"\n\n\n@contextmanager\ndef get_conn() -> Generator[sqlite3.Connection, None, None]:\n conn = sqlite3.connect(f\"file:{DB_PATH}?mode=ro\", uri=True)\n conn.row_factory = sqlite3.Row\n try:\n yield conn\n finally:\n conn.close()\n\n\ndef date_range(for_date: date) -> tuple[str, str]:\n \"\"\"Return ISO timestamps for start/end of a given date (UTC).\"\"\"\n start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)\n end = start + timedelta(days=1)\n return start.isoformat(), end.isoformat()\n\n\ndef today() -> date:\n return datetime.now(timezone.utc).date()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"__init__.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"__init__.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"1 line","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"activity.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"activity.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"123 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"App usage and window focus time consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef app_time(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per app for a given date, sorted descending.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n o.app_name,\n COUNT(*) as frame_count,\n -- each frame ~ 2s at 0.5fps; convert to minutes\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY o.app_name\n ORDER BY frame_count DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per window/tab, optionally filtered by app.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n o.app_name,\n o.window_name,\n COUNT(*) as frame_count,\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n \"\"\"\n params: list = [start, end]\n if app:\n query += \" AND o.app_name = ?\"\n params.append(app)\n query += \" GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:\n \"\"\"Return activity bucketed into N-minute intervals.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = interval_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n COUNT(*) as frames\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY bucket, o.app_name\n ORDER BY bucket, frames DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef summary_stats(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Return high-level stats for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n frames_row = conn.execute(\n \"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last \"\n \"FROM frames WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n app_count = conn.execute(\n \"\"\"\n SELECT COUNT(DISTINCT o.app_name) as n\n FROM frames f JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''\n \"\"\",\n (start, end),\n ).fetchone()\n\n audio_count = conn.execute(\n \"SELECT COUNT(*) as n FROM audio_transcriptions \"\n \"WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n return {\n \"date\": str(d),\n \"total_frames\": frames_row[\"n\"],\n \"first_seen\": frames_row[\"first\"],\n \"last_seen\": frames_row[\"last\"],\n \"unique_apps\": app_count[\"n\"],\n \"audio_segments\": audio_count[\"n\"],\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"ocr.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"ocr.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"92 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"OCR text search and aggregation consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:\n \"\"\"Full-text search OCR content.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet\n FROM ocr_text_fts\n JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid\n JOIN frames f ON o.frame_id = f.id\n WHERE ocr_text_fts MATCH ?\n AND f.timestamp >= ? AND f.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:\n \"\"\"Return all OCR text for a date, optionally only focused windows.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n o.text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n \"\"\"\n params: list = [start, end]\n if focused_only:\n query += \" AND o.focused = TRUE\"\n query += \" ORDER BY f.timestamp\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:\n \"\"\"\n Return OCR text deduplicated by time chunk + app to reduce LLM token usage.\n Groups frames into N-minute buckets and returns one representative text per app per bucket.\n \"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = chunk_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n o.window_name,\n substr(o.text, 1, 500) as text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n GROUP BY bucket, o.app_name\n ORDER BY bucket\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"audio.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"audio.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"61 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Audio transcription consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return all audio transcriptions for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT timestamp, transcription, device, is_input_device, speaker_id\n FROM audio_transcriptions\n WHERE timestamp >= ? AND timestamp < ?\n AND length(transcription) > 2\n ORDER BY timestamp\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:\n \"\"\"Search audio transcriptions.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n a.timestamp,\n a.device,\n snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet\n FROM audio_transcriptions_fts\n JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id\n WHERE audio_transcriptions_fts MATCH ?\n AND a.timestamp >= ? AND a.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef formatted_transcript(for_date: date | None = None) -> str:\n \"\"\"Return audio as a readable transcript string.\"\"\"\n rows = transcriptions(for_date)\n if not rows:\n return \"(no audio transcriptions)\"\n lines = []\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n lines.append(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n return \"\\n\".join(lines)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"summary.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"summary.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"162 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"AI summary generator using Claude.\"\"\"\nimport os\nfrom datetime import date\nfrom typing import Any\n\nfrom consumers.activity import app_time, summary_stats\nfrom consumers.ocr import deduplicated_text\nfrom consumers.audio import formatted_transcript\n\n\nSUMMARY_TYPES = {\n \"day_recap\": {\n \"title\": \"Day Recap\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a concise day recap using EXACTLY this format:\n\n## Summary\nOne sentence: what I mainly worked on today.\n\n## Accomplishments\n- Top 3 things I finished, with approximate times\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things seen or heard, with timestamps\n\n## Unfinished Work\n- What to continue tomorrow\n\n## Patterns\n- Apps used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with:\n**Next step:** [most important thing to continue]\"\"\",\n },\n \"standup\": {\n \"title\": \"Standup Update\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a brief standup update:\n\n**Yesterday:** What I worked on (2-3 bullets)\n**Today:** What I plan to continue (1-2 bullets)\n**Blockers:** Any issues observed (or \"None\")\n\nKeep it under 150 words. Only report what's verifiable from the data.\"\"\",\n },\n \"focus_time\": {\n \"title\": \"Focus & Distraction Analysis\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\nAnalyze focus vs distraction patterns:\n\n## Deep Work Periods\n- When and how long were sustained focus sessions?\n- What was being worked on?\n\n## Distractions\n- Which apps/sites were used recreationally?\n- How much time was lost to context switching?\n\n## Recommendations\n- 2-3 concrete suggestions to improve focus tomorrow\n\nBe specific with times and app names.\"\"\",\n },\n}\n\n\ndef _build_context(for_date: date) -> dict[str, str]:\n apps = app_time(for_date)\n app_lines = \"\\n\".join(f\"- {a['app_name']}: {a['minutes']} min\" for a in apps[:20])\n\n ocr_rows = deduplicated_text(for_date, chunk_minutes=5)\n ocr_lines = \"\\n\".join(\n f\"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}\"\n for r in ocr_rows[:100]\n )\n\n audio = formatted_transcript(for_date)\n\n return {\n \"app_time\": app_lines or \"(no app data)\",\n \"ocr_text\": ocr_lines or \"(no screen data)\",\n \"audio\": audio[:3000] if len(audio) > 3000 else audio,\n }\n\n\ndef generate(\n summary_type: str = \"day_recap\",\n for_date: date | None = None,\n api_key: str | None = None,\n) -> dict[str, Any]:\n \"\"\"Generate an AI summary. Returns dict with 'title', 'content', 'date'.\"\"\"\n import anthropic\n\n d = for_date or date.today()\n key = api_key or os.environ.get(\"ANTHROPIC_API_KEY\", \"\")\n if not key:\n return {\n \"title\": \"Error\",\n \"content\": \"ANTHROPIC_API_KEY not set. Add it to your .env file.\",\n \"date\": str(d),\n }\n\n spec = SUMMARY_TYPES.get(summary_type)\n if not spec:\n return {\"title\": \"Error\", \"content\": f\"Unknown summary type: {summary_type}\", \"date\": str(d)}\n\n stats = summary_stats(d)\n if stats[\"total_frames\"] == 0:\n return {\n \"title\": spec[\"title\"],\n \"content\": f\"No screen data found for {d}.\",\n \"date\": str(d),\n }\n\n ctx = _build_context(d)\n prompt = spec[\"prompt\"].format(**ctx)\n\n client = anthropic.Anthropic(api_key=key)\n message = client.messages.create(\n model=\"claude-opus-4-6\",\n max_tokens=1024,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n )\n\n return {\n \"title\": spec[\"title\"],\n \"content\": message.content[0].text,\n \"date\": str(d),\n \"stats\": stats,\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the FastAPI backend and CLI:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"main.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"main.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"89 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Screenpipe dashboard — FastAPI web UI.\"\"\"\nimport os\nfrom datetime import date, datetime\nfrom typing import Any\n\nfrom dotenv import load_dotenv\nfrom fastapi import FastAPI, Query\nfrom fastapi.responses import HTMLResponse, JSONResponse\nfrom fastapi.staticfiles import StaticFiles\n\nload_dotenv()\n\nfrom consumers.activity import app_time, window_time, timeline, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions, search as audio_search\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\napp = FastAPI(title=\"Screenpipe Dashboard\")\napp.mount(\"/static\", StaticFiles(directory=\"static\"), name=\"static\")\n\n\ndef _parse_date(d: str | None) -> date | None:\n if not d:\n return None\n try:\n return date.fromisoformat(d)\n except ValueError:\n return None\n\n\n@app.get(\"/\", response_class=HTMLResponse)\nasync def index():\n with open(\"static/index.html\") as f:\n return f.read()\n\n\n# ── Activity ──────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/stats\")\nasync def stats(d: str | None = Query(None)):\n return summary_stats(_parse_date(d))\n\n\n@app.get(\"/api/activity/apps\")\nasync def activity_apps(d: str | None = Query(None)):\n return app_time(_parse_date(d))\n\n\n@app.get(\"/api/activity/windows\")\nasync def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):\n return window_time(_parse_date(d), app_name)\n\n\n@app.get(\"/api/activity/timeline\")\nasync def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):\n return timeline(_parse_date(d), interval)\n\n\n# ── Search ───────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/search/ocr\")\nasync def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):\n return ocr_search(q, _parse_date(d), limit)\n\n\n@app.get(\"/api/search/audio\")\nasync def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):\n return audio_search(q, _parse_date(d), limit)\n\n\n# ── Audio ─────────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/audio\")\nasync def audio(d: str | None = Query(None)):\n return transcriptions(_parse_date(d))\n\n\n# ── Summaries ─────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/summary/types\")\nasync def summary_types():\n return [{\"id\": k, \"title\": v[\"title\"]} for k, v in SUMMARY_TYPES.items()]\n\n\n@app.post(\"/api/summary/generate\")\nasync def generate_summary(type: str = \"day_recap\", d: str | None = None):\n result = gen_summary(type, _parse_date(d))\n return result","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"cli.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"cli.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"131 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"#!/usr/bin/env python3\n\"\"\"Screenpipe CLI — run summaries and reports on demand.\"\"\"\nimport os\nimport sys\nfrom datetime import date, timedelta\n\nimport click\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nsys.path.insert(0, os.path.dirname(__file__))\n\nfrom consumers.activity import app_time, window_time, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\n\ndef _resolve_date(d_str: str | None) -> date | None:\n if not d_str:\n return None\n if d_str == \"yesterday\":\n return date.today() - timedelta(days=1)\n if d_str == \"today\":\n return date.today()\n return date.fromisoformat(d_str)\n\n\n@click.group()\ndef cli():\n \"\"\"Screenpipe data explorer and summariser.\"\"\"\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--type\", \"-t\", \"summary_type\", default=\"day_recap\",\n type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)\ndef summary(d, summary_type):\n \"\"\"Generate an AI summary for a date (default: today).\"\"\"\n target = _resolve_date(d)\n click.echo(f\"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...\")\n result = gen_summary(summary_type, target)\n click.echo(f\"\\n{'─'*60}\")\n click.echo(f\" {result['title']} — {result['date']}\")\n click.echo(f\"{'─'*60}\\n\")\n click.echo(result[\"content\"])\n if \"stats\" in result:\n s = result[\"stats\"]\n click.echo(f\"\\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, \"\n f\"{s['audio_segments']} audio segments]\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--app\", \"-a\", default=None, help=\"Filter by app name\")\n@click.option(\"--windows\", \"-w\", is_flag=True, help=\"Show window breakdown instead of apps\")\ndef activity(d, app, windows):\n \"\"\"Show time spent per app or window.\"\"\"\n target = _resolve_date(d)\n label = str(target or date.today())\n\n if windows or app:\n rows = window_time(target, app)\n click.echo(f\"\\nWindow time — {label}\" + (f\" [{app}]\" if app else \"\"))\n click.echo(f\"{'App':<25} {'Window':<40} {'Min':>6}\")\n click.echo(\"─\" * 75)\n for r in rows:\n click.echo(f\"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}\")\n else:\n rows = app_time(target)\n stats = summary_stats(target)\n click.echo(f\"\\nApp time — {label}\")\n click.echo(f\"{'App':<30} {'Minutes':>8}\")\n click.echo(\"─\" * 40)\n for r in rows:\n click.echo(f\"{r['app_name']:<30} {r['minutes']:>8}\")\n click.echo(f\"\\nTotal frames: {stats['total_frames']} | \"\n f\"Active: {stats['first_seen']} → {stats['last_seen']}\")\n\n\n@cli.command()\n@click.argument(\"query\")\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--audio\", is_flag=True, help=\"Search audio instead of screen text\")\n@click.option(\"--limit\", \"-n\", default=20, show_default=True)\ndef search(query, d, audio, limit):\n \"\"\"Search OCR screen text or audio transcriptions.\"\"\"\n from consumers.audio import search as audio_search_fn\n\n target = _resolve_date(d)\n if audio:\n rows = audio_search_fn(query, target, limit)\n click.echo(f\"\\nAudio results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['snippet']}\")\n else:\n rows = ocr_search(query, target, limit)\n click.echo(f\"\\nOCR results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}\")\n click.echo(f\" {r['snippet']}\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\ndef audio(d):\n \"\"\"Show audio transcriptions for a date.\"\"\"\n target = _resolve_date(d)\n rows = transcriptions(target)\n if not rows:\n click.echo(\"No audio transcriptions found.\")\n return\n click.echo(f\"\\nAudio transcriptions — {target or date.today()}\")\n click.echo(\"─\" * 60)\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n click.echo(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n\n\n@cli.command()\ndef serve():\n \"\"\"Start the web UI (runs uvicorn on port 8000).\"\"\"\n import uvicorn\n click.echo(\"Starting Screenpipe dashboard at http://localhost:8000\")\n uvicorn.run(\"main:app\", host=\"0.0.0.0\", port=8000, reload=False)\n\n\nif __name__ == \"__main__\":\n cli()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the frontend:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"index.html","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"index.html","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"438 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Screenpipe Dashboard</title>\n<style>\n :root {\n --bg: #0f0f11;\n --surface: #1a1a1f;\n --border: #2a2a35;\n --accent: #7c6fcd;\n --accent2: #4fa3e0;\n --text: #e0e0e8;\n --muted: #888899;\n --green: #4caf88;\n --red: #e06060;\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body { background: var(--bg); color: var(--text); font-family: 'Segoe UI', system-ui, sans-serif; font-size: 14px; }\n\n header {\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n gap: 16px;\n position: sticky;\n top: 0;\n z-index: 100;\n }\n header h1 { font-size: 16px; font-weight: 600; color: var(--accent); }\n header .spacer { flex: 1; }\n\n .date-picker { display: flex; align-items: center; gap: 8px; }\n .date-picker label { color: var(--muted); font-size: 12px; }\n input[type=\"date\"] {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 13px;\n }\n input[type=\"text\"] {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 13px;\n width: 200px;\n }\n input[type=\"text\"]:focus, input[type=\"date\"]:focus { outline: 1px solid var(--accent); }\n\n .tabs { display: flex; gap: 4px; }\n .tab {\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n color: var(--muted);\n font-size: 13px;\n border: 1px solid transparent;\n background: none;\n }\n .tab.active { background: var(--accent); color: white; }\n .tab:hover:not(.active) { background: var(--border); color: var(--text); }\n\n main { padding: 20px 24px; max-width: 1200px; margin: 0 auto; }\n\n .page { display: none; }\n .page.active { display: block; }\n\n .stats-bar {\n display: flex;\n gap: 16px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n }\n .stat-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 12px 16px;\n flex: 1;\n min-width: 150px;\n }\n .stat-card .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }\n .stat-card .value { font-size: 22px; font-weight: 700; color: var(--accent); margin-top: 4px; }\n .stat-card .sub { color: var(--muted); font-size: 11px; margin-top: 2px; }\n\n .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n @media (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }\n\n .card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 10px;\n padding: 16px;\n }\n .card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }\n\n .bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }\n .bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }\n .bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }\n .bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }\n .bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }\n\n table { width: 100%; border-collapse: collapse; font-size: 13px; }\n th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }\n td { padding: 6px 8px; border-bottom: 1px solid var(--border); }\n tr:last-child td { border-bottom: none; }\n tr:hover td { background: rgba(255,255,255,0.02); }\n\n .timeline-wrap { overflow-x: auto; }\n .timeline-grid { display: grid; gap: 2px; min-width: 600px; }\n .tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }\n .tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }\n .tl-cell {\n flex: 1;\n height: 20px;\n border-radius: 3px;\n background: var(--border);\n position: relative;\n cursor: default;\n font-size: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n color: white;\n }\n\n .summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }\n select {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 13px;\n }\n button {\n background: var(--accent);\n color: white;\n border: none;\n padding: 8px 18px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n }\n button:hover { opacity: 0.85; }\n button:disabled { opacity: 0.4; cursor: default; }\n\n .summary-output {\n background: var(--bg);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 20px;\n white-space: pre-wrap;\n font-family: inherit;\n line-height: 1.6;\n min-height: 200px;\n color: var(--text);\n }\n .summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }\n .summary-output ul { padding-left: 20px; }\n .summary-output strong { color: var(--accent2); }\n\n .search-bar { display: flex; gap: 8px; margin-bottom: 16px; }\n .search-bar input[type=\"text\"] { flex: 1; }\n .search-bar select { }\n\n .result-item {\n border-bottom: 1px solid var(--border);\n padding: 10px 4px;\n }\n .result-item:last-child { border-bottom: none; }\n .result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }\n .result-snippet { font-size: 13px; }\n .result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }\n\n .audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }\n .audio-item:last-child { border-bottom: none; }\n .audio-badge {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 4px;\n flex-shrink: 0;\n font-weight: 600;\n }\n .audio-badge.mic { background: rgba(76,175,136,0.2); color: var(--green); }\n .audio-badge.sys { background: rgba(74,163,224,0.2); color: var(--accent2); }\n .audio-ts { color: var(--muted); font-size: 11px; flex-shrink: 0; }\n\n .spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.7s linear infinite; vertical-align: middle; margin-right: 6px; }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n .empty { color: var(--muted); text-align: center; padding: 40px; font-size: 13px; }\n</style>\n</head>\n<body>\n\n<header>\n <h1>Screenpipe</h1>\n <div class=\"tabs\">\n <button class=\"tab active\" onclick=\"showPage('activity')\">Activity</button>\n <button class=\"tab\" onclick=\"showPage('search')\">Search</button>\n <button class=\"tab\" onclick=\"showPage('audio')\">Audio</button>\n <button class=\"tab\" onclick=\"showPage('summary')\">Summary</button>\n </div>\n <div class=\"spacer\"></div>\n <div class=\"date-picker\">\n <label>Date</label>\n <input type=\"date\" id=\"datePicker\" onchange=\"refresh()\">\n </div>\n</header>\n\n<main>\n\n<!-- ── Activity ─────────────────────────────────────────────── -->\n<div class=\"page active\" id=\"page-activity\">\n <div class=\"stats-bar\" id=\"statsBar\">\n <div class=\"stat-card\"><div class=\"label\">Frames</div><div class=\"value\" id=\"st-frames\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Apps</div><div class=\"value\" id=\"st-apps\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Audio Segs</div><div class=\"value\" id=\"st-audio\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Active</div><div class=\"value\" id=\"st-first\" style=\"font-size:13px;margin-top:6px\">–</div><div class=\"sub\" id=\"st-last\">–</div></div>\n </div>\n <div class=\"grid-2\">\n <div class=\"card\">\n <h2>Time per App</h2>\n <div id=\"appBars\"><div class=\"empty\">Loading...</div></div>\n </div>\n <div class=\"card\">\n <h2>Windows</h2>\n <div id=\"windowTable\"><div class=\"empty\">Click an app bar to filter</div></div>\n </div>\n </div>\n</div>\n\n<!-- ── Search ───────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-search\">\n <div class=\"search-bar\">\n <input type=\"text\" id=\"searchQuery\" placeholder=\"Search screen text...\" onkeydown=\"if(event.key==='Enter') runSearch()\">\n <select id=\"searchTarget\">\n <option value=\"ocr\">Screen (OCR)</option>\n <option value=\"audio\">Audio</option>\n </select>\n <button onclick=\"runSearch()\">Search</button>\n </div>\n <div class=\"card\">\n <div id=\"searchResults\"><div class=\"empty\">Enter a query above to search screen text or audio transcriptions.</div></div>\n </div>\n</div>\n\n<!-- ── Audio ────────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-audio\">\n <div class=\"card\">\n <h2>Audio Transcriptions</h2>\n <div id=\"audioList\"><div class=\"empty\">Loading...</div></div>\n </div>\n</div>\n\n<!-- ── Summary ──────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-summary\">\n <div class=\"summary-controls\">\n <select id=\"summaryType\">\n <option value=\"day_recap\">Day Recap</option>\n <option value=\"standup\">Standup Update</option>\n <option value=\"focus_time\">Focus Analysis</option>\n </select>\n <button id=\"genBtn\" onclick=\"generateSummary()\">Generate</button>\n </div>\n <div class=\"summary-output\" id=\"summaryOutput\">\n Select a summary type and click Generate. Requires ANTHROPIC_API_KEY in your environment.\n </div>\n</div>\n\n</main>\n\n<script>\nconst API = '';\nlet currentPage = 'activity';\nlet currentApp = null;\n\n// ── Date ────────────────────────────────────────────────────────\nconst dp = document.getElementById('datePicker');\ndp.value = new Date().toISOString().slice(0, 10);\nfunction getDate() { return dp.value; }\n\nfunction refresh() {\n if (currentPage === 'activity') loadActivity();\n if (currentPage === 'audio') loadAudio();\n}\n\n// ── Page nav ────────────────────────────────────────────────────\nfunction showPage(name) {\n document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));\n document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));\n document.getElementById('page-' + name).classList.add('active');\n document.querySelectorAll('.tab')[['activity','search','audio','summary'].indexOf(name)].classList.add('active');\n currentPage = name;\n if (name === 'activity') loadActivity();\n if (name === 'audio') loadAudio();\n}\n\n// ── Activity ────────────────────────────────────────────────────\nasync function loadActivity() {\n const d = getDate();\n const [stats, apps] = await Promise.all([\n fetch(`${API}/api/stats?d=${d}`).then(r => r.json()),\n fetch(`${API}/api/activity/apps?d=${d}`).then(r => r.json()),\n ]);\n\n document.getElementById('st-frames').textContent = stats.total_frames ?? '–';\n document.getElementById('st-apps').textContent = stats.unique_apps ?? '–';\n document.getElementById('st-audio').textContent = stats.audio_segments ?? '–';\n document.getElementById('st-first').textContent = stats.first_seen ? fmtTime(stats.first_seen) : '–';\n document.getElementById('st-last').textContent = stats.last_seen ? '→ ' + fmtTime(stats.last_seen) : '';\n\n const max = apps[0]?.minutes || 1;\n const barsEl = document.getElementById('appBars');\n if (!apps.length) { barsEl.innerHTML = '<div class=\"empty\">No data</div>'; return; }\n\n barsEl.innerHTML = apps.slice(0, 20).map(a => `\n <div class=\"bar-item\" onclick=\"loadWindows('${esc(a.app_name)}')\" style=\"cursor:pointer\" title=\"Click to filter windows\">\n <div class=\"name\" title=\"${esc(a.app_name)}\">${esc(a.app_name)}</div>\n <div class=\"bar-wrap\"><div class=\"bar-fill\" style=\"width:${(a.minutes/max*100).toFixed(1)}%\"></div></div>\n <div class=\"mins\">${a.minutes}m</div>\n </div>`).join('');\n\n loadWindows(null);\n}\n\nasync function loadWindows(app) {\n currentApp = app;\n const d = getDate();\n const url = app ? `/api/activity/windows?d=${d}&app_name=${encodeURIComponent(app)}` : `/api/activity/windows?d=${d}`;\n const rows = await fetch(url).then(r => r.json());\n const el = document.getElementById('windowTable');\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No data</div>'; return; }\n el.innerHTML = `<table>\n <thead><tr><th>App</th><th>Window</th><th>Min</th></tr></thead>\n <tbody>${rows.slice(0,30).map(r => `\n <tr><td>${esc(r.app_name)}</td><td title=\"${esc(r.window_name||'')}\">${esc((r.window_name||'').slice(0,50))}</td><td>${r.minutes}</td></tr>\n `).join('')}</tbody>\n </table>`;\n}\n\n// ── Search ──────────────────────────────────────────────────────\nasync function runSearch() {\n const q = document.getElementById('searchQuery').value.trim();\n if (!q) return;\n const target = document.getElementById('searchTarget').value;\n const d = getDate();\n const el = document.getElementById('searchResults');\n el.innerHTML = '<div class=\"empty\"><span class=\"spinner\"></span>Searching...</div>';\n\n const url = target === 'audio'\n ? `/api/search/audio?q=${encodeURIComponent(q)}&d=${d}`\n : `/api/search/ocr?q=${encodeURIComponent(q)}&d=${d}`;\n\n const rows = await fetch(url).then(r => r.json()).catch(() => []);\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No results found.</div>'; return; }\n\n el.innerHTML = rows.map(r => `\n <div class=\"result-item\">\n <div class=\"result-meta\">${r.timestamp ? fmtTime(r.timestamp) : ''} · ${esc(r.app_name||'')} ${r.window_name ? '/ ' + esc(r.window_name) : ''}</div>\n <div class=\"result-snippet\">${highlightSnippet(r.snippet||'')}</div>\n </div>`).join('');\n}\n\nfunction highlightSnippet(s) {\n return esc(s).replace(/\\[/g, '<mark>').replace(/\\]/g, '</mark>');\n}\n\n// ── Audio ───────────────────────────────────────────────────────\nasync function loadAudio() {\n const d = getDate();\n const rows = await fetch(`${API}/api/audio?d=${d}`).then(r => r.json());\n const el = document.getElementById('audioList');\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No audio transcriptions for this date.</div>'; return; }\n el.innerHTML = rows.map(r => `\n <div class=\"audio-item\">\n <span class=\"audio-ts\">${fmtTime(r.timestamp)}</span>\n <span class=\"audio-badge ${r.is_input_device ? 'mic' : 'sys'}\">${r.is_input_device ? 'MIC' : 'SYS'}</span>\n <span>${esc(r.transcription)}</span>\n </div>`).join('');\n}\n\n// ── Summary ─────────────────────────────────────────────────────\nasync function generateSummary() {\n const type = document.getElementById('summaryType').value;\n const d = getDate();\n const btn = document.getElementById('genBtn');\n const out = document.getElementById('summaryOutput');\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span>Generating...';\n out.textContent = 'Sending data to Claude...';\n\n const res = await fetch(`/api/summary/generate?type=${type}&d=${d}`, { method: 'POST' })\n .then(r => r.json()).catch(e => ({ content: 'Error: ' + e.message }));\n\n out.innerHTML = renderMarkdown(res.content || res.error || 'No content');\n btn.disabled = false;\n btn.textContent = 'Generate';\n}\n\n// ── Helpers ─────────────────────────────────────────────────────\nfunction esc(s) {\n return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');\n}\n\nfunction fmtTime(ts) {\n if (!ts) return '';\n try {\n const d = new Date(ts);\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n } catch { return ts; }\n}\n\nfunction renderMarkdown(text) {\n return esc(text)\n .replace(/^## (.+)$/gm, '<h2>$1</h2>')\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/^- (.+)$/gm, '<li>$1</li>')\n .replace(/(<li>.*<\\/li>)/s, '<ul>$1</ul>')\n .replace(/\\n/g, '<br>');\n}\n\n// Init\nloadActivity();\n</script>\n</body>\n</html>","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the Docker Compose file:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"docker-compose.yml","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"docker-compose.yml","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"14 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"services:\n screenpipe-app:\n build: .\n container_name: screenpipe-app\n restart: unless-stopped\n ports:\n - \"8765:8000\"\n volumes:\n - /volume1/screenpipe/db.sqlite:/data/db.sqlite:ro\n env_file:\n - .env\n environment:\n TZ: Europe/Sofia","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":".env","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":".env","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"2 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ANTHROPIC_API_KEY=your_key_here","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Now let's build and test:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Build Docker image","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cd /volume1/screenpipe/app && docker compose build 2>&1","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\npermission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head \"http://%2Fvar%2Frun%2Fdocker.sock/_ping\": dial unix /var/run/docker.sock: connect: permission denied\n[rerun: b10]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Build Docker image with sudo","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sudo docker compose -f /volume1/screenpipe/app/docker-compose.yml build 2>&1","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\nsudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper\nsudo: a password is required\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check current user and groups","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"groups && id","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"admin users ughomeusers Media Social\nuid=1000(Adm1n) gid=10(admin) groups=10(admin),100(users),133(ughomeusers),1001(Media),1002(Social)\n[rerun: b12]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"The user isn't in the docker group. Let me verify the files look correct and check the overall structure:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List all created app files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"find /volume1/screenpipe/app -type f | sort","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/volume1/screenpipe/app/cli.py\n/volume1/screenpipe/app/consumers/activity.py\n/volume1/screenpipe/app/consumers/audio.py\n/volume1/screenpipe/app/consumers/__init__.py\n/volume1/screenpipe/app/consumers/ocr.py\n/volume1/screenpipe/app/consumers/summary.py\n/volume1/screenpipe/app/db.py\n/volume1/screenpipe/app/docker-compose.yml\n/volume1/screenpipe/app/Dockerfile\n/volume1/screenpipe/app/.env\n/volume1/screenpipe/app/main.py\n/volume1/screenpipe/app/requirements.txt\n/volume1/screenpipe/app/static/index.html\n[rerun: b13]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Everything is built. You'll need to run Docker as root (or add yourself to the docker group). Here's how to get it running:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"To build and start:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"# From DSM or an admin shell with docker access:\ncd /volume1/screenpipe/app\ndocker compose up -d --build","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Web UI:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"http://your-nas-ip:8765","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"CLI (inside container):","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"docker exec screenpipe-app python cli.py summary\ndocker exec screenpipe-app python cli.py summary --type standup\ndocker exec screenpipe-app python cli.py activity\ndocker exec screenpipe-app python cli.py search \"docker\"\ndocker exec screenpipe-app python cli.py audio","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"What's been built:","depth":23,"role_description":"text"}]...
|
6437832270600868115
|
-8187121230352777210
|
click
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Shell Script
Editor Language Status: $(copilot) No inline suggestion available, Inline suggestions
LF
UTF-8
Spaces: 2
Ln 16, Col 1
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at [URL_WITH_CREDENTIALS] (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
.bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }
.bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }
.bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }
td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: rgba(255,255,255,0.02); }
.timeline-wrap { overflow-x: auto; }
.timeline-grid { display: grid; gap: 2px; min-width: 600px; }
.tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }
.tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }
.tl-cell {
flex: 1;
height: 20px;
border-radius: 3px;
background: var(--border);
position: relative;
cursor: default;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: white;
}
.summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
}
button {
background: var(--accent);
color: white;
border: none;
padding: 8px 18px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
button:hover { opacity: 0.85; }
button:disabled { opacity: 0.4; cursor: default; }
.summary-output {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
white-space: pre-wrap;
font-family: inherit;
line-height: 1.6;
min-height: 200px;
color: var(--text);
}
.summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }
.summary-output ul { padding-left: 20px; }
.summary-output strong { color: var(--accent2); }
.search-bar { display: flex; gap: 8px; margin-bottom: 16px; }
.search-bar input[type="text"] { flex: 1; }
.search-bar select { }
.result-item {
border-bottom: 1px solid var(--border);
padding: 10px 4px;
}
.result-item:last-child { border-bottom: none; }
.result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.result-snippet { font-size: 13px; }
.result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }
.audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }
.audio-item:last-child { border-bottom: none; }
.audio-badge {
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
font-weight: 600;
}
...
|
NULL
|
|
1435
|
32
|
4
|
2026-04-11T17:27:51.591765+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928471591_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:51-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
2789195165247559633
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• ₴5|-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:51-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1434
|
|
1434
|
32
|
3
|
2026-04-11T17:27:49.864864+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928469864_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at [URL_WITH_CREDENTIALS] (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
.bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }
.bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }
.bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }
td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: rgba(255,255,255,0.02); }
.timeline-wrap { overflow-x: auto; }
.timeline-grid { display: grid; gap: 2px; min-width: 600px; }
.tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }
.tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }
.tl-cell {
flex: 1;
height: 20px;
border-radius: 3px;
background: var(--border);
position: relative;
cursor: default;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: white;
}
.summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
}
button {
background: var(--accent);
color: white;
border: none;
padding: 8px 18px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
button:hover { opacity: 0.85; }
button:disabled { opacity: 0.4; cursor: default; }
.summary-output {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
white-space: pre-wrap;
font-family: inherit;
line-height: 1.6;
min-height: 200px;
color: var(--text);
}
.summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }
.summary-output ul { padding-left: 20px; }
.summary-output strong { color: var(--accent2); }
.search-bar { display: flex; gap: 8px; margin-bottom: 16px; }
.search-bar input[type="text"] { flex: 1; }
.search-bar select { }
.result-item {
border-bottom: 1px solid var(--border);
padding: 10px 4px;
}
.result-item:last-child { border-bottom: none; }
.result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.result-snippet { font-size: 13px; }
.result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }
.audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }
.audio-item:last-child { border-bottom: none; }
.audio-badge {
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
font-weight: 600;
}
.audio-badge.mic { background: rgba(76,175,136,0.2); color: var(--green); }
.audio-badge.sys { background: rgba(74,163,224,0.2); ...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n[rerun: b4]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bootstrap pip then install packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n/usr/bin/python3: No module named ensurepip\n[rerun: b5]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip via apt","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n[rerun: b6]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip with sudo","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Reason:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"lets make it in docker, not to install it in nas directly","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Good call. Let me check what Docker setup you already have and build a proper containerized app.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check existing docker-compose for reference","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"consume\ndata\ndb\ndocker-compose.yml\nexport\nmcp-server\nmedia\nredis\ntrash\nservices:\n redis:\n image: redis:8\n command:\n - /bin/sh\n - -c\n - redis-server --requirepass redispass\n container_name: PaperlessNGX-REDIS\n hostname: paper-redis\n security_opt:\n - no-new-privileges:true\n read_only: true\n user: 1000:10\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli ping || exit 1\"]\n volumes:\n - /volume2/docker/paperlessngx/redis:/data:rw\n environment:\n TZ: Europe/Sofia\n restart: always\n\n db:\n image: postgres:17\n container_name: PaperlessNGX-DB\n hostname: paper-db\n mem_limit: 1g\n security_opt:\n - no-new-privileges:true\n healthcheck:\n test: [\"CMD\", \"pg_isready\", \"-q\", \"-d\", \"paperless\", \"-U\", \"paperlessuser\"]\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Docker availability","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"docker --version && docker compose version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Docker version 26.1.0, build 9714adc\nDocker Compose version v2.26.1\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Create app directory structure","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"[rerun: b9]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Now let me build everything. I'll create all the files:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"requirements.txt","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"requirements.txt","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"6 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"fastapi==0.115.12\nuvicorn[standard]==0.34.0\nclick==8.1.8\nanthropic==0.49.0\npython-dotenv==1.1.0","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Dockerfile","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Dockerfile","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"13 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"FROM python:3.12-slim\n\nWORKDIR /app\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY . .\n\nEXPOSE 8000\n\nCMD [\"uvicorn\", \"main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"db.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"db.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"28 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"import sqlite3\nfrom contextlib import contextmanager\nfrom datetime import date, datetime, timedelta, timezone\nfrom typing import Generator\n\nDB_PATH = \"/data/db.sqlite\"\n\n\n@contextmanager\ndef get_conn() -> Generator[sqlite3.Connection, None, None]:\n conn = sqlite3.connect(f\"file:{DB_PATH}?mode=ro\", uri=True)\n conn.row_factory = sqlite3.Row\n try:\n yield conn\n finally:\n conn.close()\n\n\ndef date_range(for_date: date) -> tuple[str, str]:\n \"\"\"Return ISO timestamps for start/end of a given date (UTC).\"\"\"\n start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)\n end = start + timedelta(days=1)\n return start.isoformat(), end.isoformat()\n\n\ndef today() -> date:\n return datetime.now(timezone.utc).date()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"__init__.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"__init__.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"1 line","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"activity.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"activity.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"123 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"App usage and window focus time consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef app_time(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per app for a given date, sorted descending.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n o.app_name,\n COUNT(*) as frame_count,\n -- each frame ~ 2s at 0.5fps; convert to minutes\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY o.app_name\n ORDER BY frame_count DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per window/tab, optionally filtered by app.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n o.app_name,\n o.window_name,\n COUNT(*) as frame_count,\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n \"\"\"\n params: list = [start, end]\n if app:\n query += \" AND o.app_name = ?\"\n params.append(app)\n query += \" GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:\n \"\"\"Return activity bucketed into N-minute intervals.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = interval_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n COUNT(*) as frames\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY bucket, o.app_name\n ORDER BY bucket, frames DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef summary_stats(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Return high-level stats for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n frames_row = conn.execute(\n \"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last \"\n \"FROM frames WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n app_count = conn.execute(\n \"\"\"\n SELECT COUNT(DISTINCT o.app_name) as n\n FROM frames f JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''\n \"\"\",\n (start, end),\n ).fetchone()\n\n audio_count = conn.execute(\n \"SELECT COUNT(*) as n FROM audio_transcriptions \"\n \"WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n return {\n \"date\": str(d),\n \"total_frames\": frames_row[\"n\"],\n \"first_seen\": frames_row[\"first\"],\n \"last_seen\": frames_row[\"last\"],\n \"unique_apps\": app_count[\"n\"],\n \"audio_segments\": audio_count[\"n\"],\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"ocr.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"ocr.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"92 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"OCR text search and aggregation consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:\n \"\"\"Full-text search OCR content.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet\n FROM ocr_text_fts\n JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid\n JOIN frames f ON o.frame_id = f.id\n WHERE ocr_text_fts MATCH ?\n AND f.timestamp >= ? AND f.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:\n \"\"\"Return all OCR text for a date, optionally only focused windows.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n o.text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n \"\"\"\n params: list = [start, end]\n if focused_only:\n query += \" AND o.focused = TRUE\"\n query += \" ORDER BY f.timestamp\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:\n \"\"\"\n Return OCR text deduplicated by time chunk + app to reduce LLM token usage.\n Groups frames into N-minute buckets and returns one representative text per app per bucket.\n \"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = chunk_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n o.window_name,\n substr(o.text, 1, 500) as text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n GROUP BY bucket, o.app_name\n ORDER BY bucket\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"audio.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"audio.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"61 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Audio transcription consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return all audio transcriptions for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT timestamp, transcription, device, is_input_device, speaker_id\n FROM audio_transcriptions\n WHERE timestamp >= ? AND timestamp < ?\n AND length(transcription) > 2\n ORDER BY timestamp\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:\n \"\"\"Search audio transcriptions.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n a.timestamp,\n a.device,\n snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet\n FROM audio_transcriptions_fts\n JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id\n WHERE audio_transcriptions_fts MATCH ?\n AND a.timestamp >= ? AND a.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef formatted_transcript(for_date: date | None = None) -> str:\n \"\"\"Return audio as a readable transcript string.\"\"\"\n rows = transcriptions(for_date)\n if not rows:\n return \"(no audio transcriptions)\"\n lines = []\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n lines.append(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n return \"\\n\".join(lines)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"summary.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"summary.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"162 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"AI summary generator using Claude.\"\"\"\nimport os\nfrom datetime import date\nfrom typing import Any\n\nfrom consumers.activity import app_time, summary_stats\nfrom consumers.ocr import deduplicated_text\nfrom consumers.audio import formatted_transcript\n\n\nSUMMARY_TYPES = {\n \"day_recap\": {\n \"title\": \"Day Recap\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a concise day recap using EXACTLY this format:\n\n## Summary\nOne sentence: what I mainly worked on today.\n\n## Accomplishments\n- Top 3 things I finished, with approximate times\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things seen or heard, with timestamps\n\n## Unfinished Work\n- What to continue tomorrow\n\n## Patterns\n- Apps used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with:\n**Next step:** [most important thing to continue]\"\"\",\n },\n \"standup\": {\n \"title\": \"Standup Update\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a brief standup update:\n\n**Yesterday:** What I worked on (2-3 bullets)\n**Today:** What I plan to continue (1-2 bullets)\n**Blockers:** Any issues observed (or \"None\")\n\nKeep it under 150 words. Only report what's verifiable from the data.\"\"\",\n },\n \"focus_time\": {\n \"title\": \"Focus & Distraction Analysis\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\nAnalyze focus vs distraction patterns:\n\n## Deep Work Periods\n- When and how long were sustained focus sessions?\n- What was being worked on?\n\n## Distractions\n- Which apps/sites were used recreationally?\n- How much time was lost to context switching?\n\n## Recommendations\n- 2-3 concrete suggestions to improve focus tomorrow\n\nBe specific with times and app names.\"\"\",\n },\n}\n\n\ndef _build_context(for_date: date) -> dict[str, str]:\n apps = app_time(for_date)\n app_lines = \"\\n\".join(f\"- {a['app_name']}: {a['minutes']} min\" for a in apps[:20])\n\n ocr_rows = deduplicated_text(for_date, chunk_minutes=5)\n ocr_lines = \"\\n\".join(\n f\"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}\"\n for r in ocr_rows[:100]\n )\n\n audio = formatted_transcript(for_date)\n\n return {\n \"app_time\": app_lines or \"(no app data)\",\n \"ocr_text\": ocr_lines or \"(no screen data)\",\n \"audio\": audio[:3000] if len(audio) > 3000 else audio,\n }\n\n\ndef generate(\n summary_type: str = \"day_recap\",\n for_date: date | None = None,\n api_key: str | None = None,\n) -> dict[str, Any]:\n \"\"\"Generate an AI summary. Returns dict with 'title', 'content', 'date'.\"\"\"\n import anthropic\n\n d = for_date or date.today()\n key = api_key or os.environ.get(\"ANTHROPIC_API_KEY\", \"\")\n if not key:\n return {\n \"title\": \"Error\",\n \"content\": \"ANTHROPIC_API_KEY not set. Add it to your .env file.\",\n \"date\": str(d),\n }\n\n spec = SUMMARY_TYPES.get(summary_type)\n if not spec:\n return {\"title\": \"Error\", \"content\": f\"Unknown summary type: {summary_type}\", \"date\": str(d)}\n\n stats = summary_stats(d)\n if stats[\"total_frames\"] == 0:\n return {\n \"title\": spec[\"title\"],\n \"content\": f\"No screen data found for {d}.\",\n \"date\": str(d),\n }\n\n ctx = _build_context(d)\n prompt = spec[\"prompt\"].format(**ctx)\n\n client = anthropic.Anthropic(api_key=key)\n message = client.messages.create(\n model=\"claude-opus-4-6\",\n max_tokens=1024,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n )\n\n return {\n \"title\": spec[\"title\"],\n \"content\": message.content[0].text,\n \"date\": str(d),\n \"stats\": stats,\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the FastAPI backend and CLI:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"main.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"main.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"89 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Screenpipe dashboard — FastAPI web UI.\"\"\"\nimport os\nfrom datetime import date, datetime\nfrom typing import Any\n\nfrom dotenv import load_dotenv\nfrom fastapi import FastAPI, Query\nfrom fastapi.responses import HTMLResponse, JSONResponse\nfrom fastapi.staticfiles import StaticFiles\n\nload_dotenv()\n\nfrom consumers.activity import app_time, window_time, timeline, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions, search as audio_search\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\napp = FastAPI(title=\"Screenpipe Dashboard\")\napp.mount(\"/static\", StaticFiles(directory=\"static\"), name=\"static\")\n\n\ndef _parse_date(d: str | None) -> date | None:\n if not d:\n return None\n try:\n return date.fromisoformat(d)\n except ValueError:\n return None\n\n\n@app.get(\"/\", response_class=HTMLResponse)\nasync def index():\n with open(\"static/index.html\") as f:\n return f.read()\n\n\n# ── Activity ──────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/stats\")\nasync def stats(d: str | None = Query(None)):\n return summary_stats(_parse_date(d))\n\n\n@app.get(\"/api/activity/apps\")\nasync def activity_apps(d: str | None = Query(None)):\n return app_time(_parse_date(d))\n\n\n@app.get(\"/api/activity/windows\")\nasync def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):\n return window_time(_parse_date(d), app_name)\n\n\n@app.get(\"/api/activity/timeline\")\nasync def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):\n return timeline(_parse_date(d), interval)\n\n\n# ── Search ───────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/search/ocr\")\nasync def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):\n return ocr_search(q, _parse_date(d), limit)\n\n\n@app.get(\"/api/search/audio\")\nasync def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):\n return audio_search(q, _parse_date(d), limit)\n\n\n# ── Audio ─────────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/audio\")\nasync def audio(d: str | None = Query(None)):\n return transcriptions(_parse_date(d))\n\n\n# ── Summaries ─────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/summary/types\")\nasync def summary_types():\n return [{\"id\": k, \"title\": v[\"title\"]} for k, v in SUMMARY_TYPES.items()]\n\n\n@app.post(\"/api/summary/generate\")\nasync def generate_summary(type: str = \"day_recap\", d: str | None = None):\n result = gen_summary(type, _parse_date(d))\n return result","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"cli.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"cli.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"131 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"#!/usr/bin/env python3\n\"\"\"Screenpipe CLI — run summaries and reports on demand.\"\"\"\nimport os\nimport sys\nfrom datetime import date, timedelta\n\nimport click\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nsys.path.insert(0, os.path.dirname(__file__))\n\nfrom consumers.activity import app_time, window_time, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\n\ndef _resolve_date(d_str: str | None) -> date | None:\n if not d_str:\n return None\n if d_str == \"yesterday\":\n return date.today() - timedelta(days=1)\n if d_str == \"today\":\n return date.today()\n return date.fromisoformat(d_str)\n\n\n@click.group()\ndef cli():\n \"\"\"Screenpipe data explorer and summariser.\"\"\"\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--type\", \"-t\", \"summary_type\", default=\"day_recap\",\n type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)\ndef summary(d, summary_type):\n \"\"\"Generate an AI summary for a date (default: today).\"\"\"\n target = _resolve_date(d)\n click.echo(f\"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...\")\n result = gen_summary(summary_type, target)\n click.echo(f\"\\n{'─'*60}\")\n click.echo(f\" {result['title']} — {result['date']}\")\n click.echo(f\"{'─'*60}\\n\")\n click.echo(result[\"content\"])\n if \"stats\" in result:\n s = result[\"stats\"]\n click.echo(f\"\\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, \"\n f\"{s['audio_segments']} audio segments]\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--app\", \"-a\", default=None, help=\"Filter by app name\")\n@click.option(\"--windows\", \"-w\", is_flag=True, help=\"Show window breakdown instead of apps\")\ndef activity(d, app, windows):\n \"\"\"Show time spent per app or window.\"\"\"\n target = _resolve_date(d)\n label = str(target or date.today())\n\n if windows or app:\n rows = window_time(target, app)\n click.echo(f\"\\nWindow time — {label}\" + (f\" [{app}]\" if app else \"\"))\n click.echo(f\"{'App':<25} {'Window':<40} {'Min':>6}\")\n click.echo(\"─\" * 75)\n for r in rows:\n click.echo(f\"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}\")\n else:\n rows = app_time(target)\n stats = summary_stats(target)\n click.echo(f\"\\nApp time — {label}\")\n click.echo(f\"{'App':<30} {'Minutes':>8}\")\n click.echo(\"─\" * 40)\n for r in rows:\n click.echo(f\"{r['app_name']:<30} {r['minutes']:>8}\")\n click.echo(f\"\\nTotal frames: {stats['total_frames']} | \"\n f\"Active: {stats['first_seen']} → {stats['last_seen']}\")\n\n\n@cli.command()\n@click.argument(\"query\")\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--audio\", is_flag=True, help=\"Search audio instead of screen text\")\n@click.option(\"--limit\", \"-n\", default=20, show_default=True)\ndef search(query, d, audio, limit):\n \"\"\"Search OCR screen text or audio transcriptions.\"\"\"\n from consumers.audio import search as audio_search_fn\n\n target = _resolve_date(d)\n if audio:\n rows = audio_search_fn(query, target, limit)\n click.echo(f\"\\nAudio results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['snippet']}\")\n else:\n rows = ocr_search(query, target, limit)\n click.echo(f\"\\nOCR results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}\")\n click.echo(f\" {r['snippet']}\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\ndef audio(d):\n \"\"\"Show audio transcriptions for a date.\"\"\"\n target = _resolve_date(d)\n rows = transcriptions(target)\n if not rows:\n click.echo(\"No audio transcriptions found.\")\n return\n click.echo(f\"\\nAudio transcriptions — {target or date.today()}\")\n click.echo(\"─\" * 60)\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n click.echo(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n\n\n@cli.command()\ndef serve():\n \"\"\"Start the web UI (runs uvicorn on port 8000).\"\"\"\n import uvicorn\n click.echo(\"Starting Screenpipe dashboard at http://localhost:8000\")\n uvicorn.run(\"main:app\", host=\"0.0.0.0\", port=8000, reload=False)\n\n\nif __name__ == \"__main__\":\n cli()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the frontend:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"index.html","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"index.html","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"438 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Screenpipe Dashboard</title>\n<style>\n :root {\n --bg: #0f0f11;\n --surface: #1a1a1f;\n --border: #2a2a35;\n --accent: #7c6fcd;\n --accent2: #4fa3e0;\n --text: #e0e0e8;\n --muted: #888899;\n --green: #4caf88;\n --red: #e06060;\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body { background: var(--bg); color: var(--text); font-family: 'Segoe UI', system-ui, sans-serif; font-size: 14px; }\n\n header {\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n padding: 12px 24px;\n display: flex;\n align-items: center;\n gap: 16px;\n position: sticky;\n top: 0;\n z-index: 100;\n }\n header h1 { font-size: 16px; font-weight: 600; color: var(--accent); }\n header .spacer { flex: 1; }\n\n .date-picker { display: flex; align-items: center; gap: 8px; }\n .date-picker label { color: var(--muted); font-size: 12px; }\n input[type=\"date\"] {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 13px;\n }\n input[type=\"text\"] {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 13px;\n width: 200px;\n }\n input[type=\"text\"]:focus, input[type=\"date\"]:focus { outline: 1px solid var(--accent); }\n\n .tabs { display: flex; gap: 4px; }\n .tab {\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n color: var(--muted);\n font-size: 13px;\n border: 1px solid transparent;\n background: none;\n }\n .tab.active { background: var(--accent); color: white; }\n .tab:hover:not(.active) { background: var(--border); color: var(--text); }\n\n main { padding: 20px 24px; max-width: 1200px; margin: 0 auto; }\n\n .page { display: none; }\n .page.active { display: block; }\n\n .stats-bar {\n display: flex;\n gap: 16px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n }\n .stat-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 12px 16px;\n flex: 1;\n min-width: 150px;\n }\n .stat-card .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }\n .stat-card .value { font-size: 22px; font-weight: 700; color: var(--accent); margin-top: 4px; }\n .stat-card .sub { color: var(--muted); font-size: 11px; margin-top: 2px; }\n\n .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n @media (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }\n\n .card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 10px;\n padding: 16px;\n }\n .card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }\n\n .bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }\n .bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }\n .bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }\n .bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }\n .bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }\n\n table { width: 100%; border-collapse: collapse; font-size: 13px; }\n th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }\n td { padding: 6px 8px; border-bottom: 1px solid var(--border); }\n tr:last-child td { border-bottom: none; }\n tr:hover td { background: rgba(255,255,255,0.02); }\n\n .timeline-wrap { overflow-x: auto; }\n .timeline-grid { display: grid; gap: 2px; min-width: 600px; }\n .tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }\n .tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }\n .tl-cell {\n flex: 1;\n height: 20px;\n border-radius: 3px;\n background: var(--border);\n position: relative;\n cursor: default;\n font-size: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n color: white;\n }\n\n .summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }\n select {\n background: var(--bg);\n border: 1px solid var(--border);\n color: var(--text);\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 13px;\n }\n button {\n background: var(--accent);\n color: white;\n border: none;\n padding: 8px 18px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n }\n button:hover { opacity: 0.85; }\n button:disabled { opacity: 0.4; cursor: default; }\n\n .summary-output {\n background: var(--bg);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 20px;\n white-space: pre-wrap;\n font-family: inherit;\n line-height: 1.6;\n min-height: 200px;\n color: var(--text);\n }\n .summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }\n .summary-output ul { padding-left: 20px; }\n .summary-output strong { color: var(--accent2); }\n\n .search-bar { display: flex; gap: 8px; margin-bottom: 16px; }\n .search-bar input[type=\"text\"] { flex: 1; }\n .search-bar select { }\n\n .result-item {\n border-bottom: 1px solid var(--border);\n padding: 10px 4px;\n }\n .result-item:last-child { border-bottom: none; }\n .result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }\n .result-snippet { font-size: 13px; }\n .result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }\n\n .audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }\n .audio-item:last-child { border-bottom: none; }\n .audio-badge {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 4px;\n flex-shrink: 0;\n font-weight: 600;\n }\n .audio-badge.mic { background: rgba(76,175,136,0.2); color: var(--green); }\n .audio-badge.sys { background: rgba(74,163,224,0.2); color: var(--accent2); }\n .audio-ts { color: var(--muted); font-size: 11px; flex-shrink: 0; }\n\n .spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.7s linear infinite; vertical-align: middle; margin-right: 6px; }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n .empty { color: var(--muted); text-align: center; padding: 40px; font-size: 13px; }\n</style>\n</head>\n<body>\n\n<header>\n <h1>Screenpipe</h1>\n <div class=\"tabs\">\n <button class=\"tab active\" onclick=\"showPage('activity')\">Activity</button>\n <button class=\"tab\" onclick=\"showPage('search')\">Search</button>\n <button class=\"tab\" onclick=\"showPage('audio')\">Audio</button>\n <button class=\"tab\" onclick=\"showPage('summary')\">Summary</button>\n </div>\n <div class=\"spacer\"></div>\n <div class=\"date-picker\">\n <label>Date</label>\n <input type=\"date\" id=\"datePicker\" onchange=\"refresh()\">\n </div>\n</header>\n\n<main>\n\n<!-- ── Activity ─────────────────────────────────────────────── -->\n<div class=\"page active\" id=\"page-activity\">\n <div class=\"stats-bar\" id=\"statsBar\">\n <div class=\"stat-card\"><div class=\"label\">Frames</div><div class=\"value\" id=\"st-frames\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Apps</div><div class=\"value\" id=\"st-apps\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Audio Segs</div><div class=\"value\" id=\"st-audio\">–</div></div>\n <div class=\"stat-card\"><div class=\"label\">Active</div><div class=\"value\" id=\"st-first\" style=\"font-size:13px;margin-top:6px\">–</div><div class=\"sub\" id=\"st-last\">–</div></div>\n </div>\n <div class=\"grid-2\">\n <div class=\"card\">\n <h2>Time per App</h2>\n <div id=\"appBars\"><div class=\"empty\">Loading...</div></div>\n </div>\n <div class=\"card\">\n <h2>Windows</h2>\n <div id=\"windowTable\"><div class=\"empty\">Click an app bar to filter</div></div>\n </div>\n </div>\n</div>\n\n<!-- ── Search ───────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-search\">\n <div class=\"search-bar\">\n <input type=\"text\" id=\"searchQuery\" placeholder=\"Search screen text...\" onkeydown=\"if(event.key==='Enter') runSearch()\">\n <select id=\"searchTarget\">\n <option value=\"ocr\">Screen (OCR)</option>\n <option value=\"audio\">Audio</option>\n </select>\n <button onclick=\"runSearch()\">Search</button>\n </div>\n <div class=\"card\">\n <div id=\"searchResults\"><div class=\"empty\">Enter a query above to search screen text or audio transcriptions.</div></div>\n </div>\n</div>\n\n<!-- ── Audio ────────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-audio\">\n <div class=\"card\">\n <h2>Audio Transcriptions</h2>\n <div id=\"audioList\"><div class=\"empty\">Loading...</div></div>\n </div>\n</div>\n\n<!-- ── Summary ──────────────────────────────────────────────── -->\n<div class=\"page\" id=\"page-summary\">\n <div class=\"summary-controls\">\n <select id=\"summaryType\">\n <option value=\"day_recap\">Day Recap</option>\n <option value=\"standup\">Standup Update</option>\n <option value=\"focus_time\">Focus Analysis</option>\n </select>\n <button id=\"genBtn\" onclick=\"generateSummary()\">Generate</button>\n </div>\n <div class=\"summary-output\" id=\"summaryOutput\">\n Select a summary type and click Generate. Requires ANTHROPIC_API_KEY in your environment.\n </div>\n</div>\n\n</main>\n\n<script>\nconst API = '';\nlet currentPage = 'activity';\nlet currentApp = null;\n\n// ── Date ────────────────────────────────────────────────────────\nconst dp = document.getElementById('datePicker');\ndp.value = new Date().toISOString().slice(0, 10);\nfunction getDate() { return dp.value; }\n\nfunction refresh() {\n if (currentPage === 'activity') loadActivity();\n if (currentPage === 'audio') loadAudio();\n}\n\n// ── Page nav ────────────────────────────────────────────────────\nfunction showPage(name) {\n document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));\n document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));\n document.getElementById('page-' + name).classList.add('active');\n document.querySelectorAll('.tab')[['activity','search','audio','summary'].indexOf(name)].classList.add('active');\n currentPage = name;\n if (name === 'activity') loadActivity();\n if (name === 'audio') loadAudio();\n}\n\n// ── Activity ────────────────────────────────────────────────────\nasync function loadActivity() {\n const d = getDate();\n const [stats, apps] = await Promise.all([\n fetch(`${API}/api/stats?d=${d}`).then(r => r.json()),\n fetch(`${API}/api/activity/apps?d=${d}`).then(r => r.json()),\n ]);\n\n document.getElementById('st-frames').textContent = stats.total_frames ?? '–';\n document.getElementById('st-apps').textContent = stats.unique_apps ?? '–';\n document.getElementById('st-audio').textContent = stats.audio_segments ?? '–';\n document.getElementById('st-first').textContent = stats.first_seen ? fmtTime(stats.first_seen) : '–';\n document.getElementById('st-last').textContent = stats.last_seen ? '→ ' + fmtTime(stats.last_seen) : '';\n\n const max = apps[0]?.minutes || 1;\n const barsEl = document.getElementById('appBars');\n if (!apps.length) { barsEl.innerHTML = '<div class=\"empty\">No data</div>'; return; }\n\n barsEl.innerHTML = apps.slice(0, 20).map(a => `\n <div class=\"bar-item\" onclick=\"loadWindows('${esc(a.app_name)}')\" style=\"cursor:pointer\" title=\"Click to filter windows\">\n <div class=\"name\" title=\"${esc(a.app_name)}\">${esc(a.app_name)}</div>\n <div class=\"bar-wrap\"><div class=\"bar-fill\" style=\"width:${(a.minutes/max*100).toFixed(1)}%\"></div></div>\n <div class=\"mins\">${a.minutes}m</div>\n </div>`).join('');\n\n loadWindows(null);\n}\n\nasync function loadWindows(app) {\n currentApp = app;\n const d = getDate();\n const url = app ? `/api/activity/windows?d=${d}&app_name=${encodeURIComponent(app)}` : `/api/activity/windows?d=${d}`;\n const rows = await fetch(url).then(r => r.json());\n const el = document.getElementById('windowTable');\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No data</div>'; return; }\n el.innerHTML = `<table>\n <thead><tr><th>App</th><th>Window</th><th>Min</th></tr></thead>\n <tbody>${rows.slice(0,30).map(r => `\n <tr><td>${esc(r.app_name)}</td><td title=\"${esc(r.window_name||'')}\">${esc((r.window_name||'').slice(0,50))}</td><td>${r.minutes}</td></tr>\n `).join('')}</tbody>\n </table>`;\n}\n\n// ── Search ──────────────────────────────────────────────────────\nasync function runSearch() {\n const q = document.getElementById('searchQuery').value.trim();\n if (!q) return;\n const target = document.getElementById('searchTarget').value;\n const d = getDate();\n const el = document.getElementById('searchResults');\n el.innerHTML = '<div class=\"empty\"><span class=\"spinner\"></span>Searching...</div>';\n\n const url = target === 'audio'\n ? `/api/search/audio?q=${encodeURIComponent(q)}&d=${d}`\n : `/api/search/ocr?q=${encodeURIComponent(q)}&d=${d}`;\n\n const rows = await fetch(url).then(r => r.json()).catch(() => []);\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No results found.</div>'; return; }\n\n el.innerHTML = rows.map(r => `\n <div class=\"result-item\">\n <div class=\"result-meta\">${r.timestamp ? fmtTime(r.timestamp) : ''} · ${esc(r.app_name||'')} ${r.window_name ? '/ ' + esc(r.window_name) : ''}</div>\n <div class=\"result-snippet\">${highlightSnippet(r.snippet||'')}</div>\n </div>`).join('');\n}\n\nfunction highlightSnippet(s) {\n return esc(s).replace(/\\[/g, '<mark>').replace(/\\]/g, '</mark>');\n}\n\n// ── Audio ───────────────────────────────────────────────────────\nasync function loadAudio() {\n const d = getDate();\n const rows = await fetch(`${API}/api/audio?d=${d}`).then(r => r.json());\n const el = document.getElementById('audioList');\n if (!rows.length) { el.innerHTML = '<div class=\"empty\">No audio transcriptions for this date.</div>'; return; }\n el.innerHTML = rows.map(r => `\n <div class=\"audio-item\">\n <span class=\"audio-ts\">${fmtTime(r.timestamp)}</span>\n <span class=\"audio-badge ${r.is_input_device ? 'mic' : 'sys'}\">${r.is_input_device ? 'MIC' : 'SYS'}</span>\n <span>${esc(r.transcription)}</span>\n </div>`).join('');\n}\n\n// ── Summary ─────────────────────────────────────────────────────\nasync function generateSummary() {\n const type = document.getElementById('summaryType').value;\n const d = getDate();\n const btn = document.getElementById('genBtn');\n const out = document.getElementById('summaryOutput');\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span>Generating...';\n out.textContent = 'Sending data to Claude...';\n\n const res = await fetch(`/api/summary/generate?type=${type}&d=${d}`, { method: 'POST' })\n .then(r => r.json()).catch(e => ({ content: 'Error: ' + e.message }));\n\n out.innerHTML = renderMarkdown(res.content || res.error || 'No content');\n btn.disabled = false;\n btn.textContent = 'Generate';\n}\n\n// ── Helpers ─────────────────────────────────────────────────────\nfunction esc(s) {\n return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');\n}\n\nfunction fmtTime(ts) {\n if (!ts) return '';\n try {\n const d = new Date(ts);\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n } catch { return ts; }\n}\n\nfunction renderMarkdown(text) {\n return esc(text)\n .replace(/^## (.+)$/gm, '<h2>$1</h2>')\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/^- (.+)$/gm, '<li>$1</li>')\n .replace(/(<li>.*<\\/li>)/s, '<ul>$1</ul>')\n .replace(/\\n/g, '<br>');\n}\n\n// Init\nloadActivity();\n</script>\n</body>\n</html>","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the Docker Compose file:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"}]...
|
-369780725636059547
|
-8187121230352777210
|
idle
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at [URL_WITH_CREDENTIALS] (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.card h2 { font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
.bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.bar-item .name { width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.bar-item .bar-wrap { flex: 1; background: var(--border); border-radius: 4px; height: 12px; overflow: hidden; }
.bar-item .bar-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }
.bar-item .mins { width: 50px; text-align: right; color: var(--muted); font-size: 12px; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; }
td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: rgba(255,255,255,0.02); }
.timeline-wrap { overflow-x: auto; }
.timeline-grid { display: grid; gap: 2px; min-width: 600px; }
.tl-row { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }
.tl-label { width: 70px; font-size: 11px; color: var(--muted); text-align: right; flex-shrink: 0; }
.tl-cell {
flex: 1;
height: 20px;
border-radius: 3px;
background: var(--border);
position: relative;
cursor: default;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: white;
}
.summary-controls { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
}
button {
background: var(--accent);
color: white;
border: none;
padding: 8px 18px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
button:hover { opacity: 0.85; }
button:disabled { opacity: 0.4; cursor: default; }
.summary-output {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
white-space: pre-wrap;
font-family: inherit;
line-height: 1.6;
min-height: 200px;
color: var(--text);
}
.summary-output h2 { font-size: 15px; color: var(--accent); margin: 12px 0 6px; }
.summary-output ul { padding-left: 20px; }
.summary-output strong { color: var(--accent2); }
.search-bar { display: flex; gap: 8px; margin-bottom: 16px; }
.search-bar input[type="text"] { flex: 1; }
.search-bar select { }
.result-item {
border-bottom: 1px solid var(--border);
padding: 10px 4px;
}
.result-item:last-child { border-bottom: none; }
.result-meta { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.result-snippet { font-size: 13px; }
.result-snippet mark { background: rgba(124,111,205,0.3); color: var(--accent); border-radius: 2px; }
.audio-item { padding: 8px 4px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }
.audio-item:last-child { border-bottom: none; }
.audio-badge {
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
font-weight: 600;
}
.audio-badge.mic { background: rgba(76,175,136,0.2); color: var(--green); }
.audio-badge.sys { background: rgba(74,163,224,0.2); ...
|
NULL
|
|
1433
|
32
|
2
|
2026-04-11T17:27:19.319606+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928439319_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• 285-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:19-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
NULL
|
3814282177527483642
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl-zshDOCKERdeepgram keylanguages• 881DEV (-zsh)not setall languagesО ₴2APP (-zsh)• *3-zsh• x4|-zsh• 285-zsh886-zsh100% <7O 87Sat 11 Apr 20:27:19-zsh181• *8monitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487547ZINFO screenpipe: starting UI event capture2026-04-11T20:26:05.353656ZWARNscreenpipe: pi agent install failed: bun not found - install from https://bun.sh2026-04-11T20:26:05.358540ZINFO2026-04-11T20:26:05.363864Zscreenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.378325ZINFO screenpipe_engine::ui_recorder: Starting UI event captureINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmingfrom DB(2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFO2026-04-11T20:26:051.384798ZINFOscreenpipe_engine::meeting_detector: meeting V2: detection loop started (base_interval=5s, profiles=12)screenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe onport 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::vision_manager::manager: Starting visionrecordingfor monitor 1 (1440x900)2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFO2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)2026-04-11T20:26:07.067446ZINFO2026-04-11T20:26:08.752985ZINFOscreenpipe-engine::eventdriven.capture: Sonteut dedup: skipping copture for monitoo 1 (hrs -9215443531147982392, triggeravisualchange)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:55.992688ZINFO2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05408691ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)screenpipe_engine::snapshot_compaction: snapshot compaction: found 61eligible frames2026-04-11T20:27:09.664908ZINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 61 frames, 9.4MB 3.3MB (2.8x), 61 JPEGs deleted...
|
1432
|
|
1432
|
32
|
1
|
2026-04-11T17:27:18.683634+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928438683_m1.jpg...
|
Code
|
Build screenpipe data UI… — screenpipe [SSH: 100.7 Build screenpipe data UI… — screenpipe [SSH: 100.73.206.126]...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at http://localhost:8000")
uvicorn.run("main:app", host="[IP_ADDRESS]", port=8000, reload=False)
if __name__ == "__main__":
cli()
Now the frontend:
Write...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Build screenpipe data UI…, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Build screenpipe data UI and summary app","depth":18,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Let me explore the screenpipe directory to understand what we're working with.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List screenpipe directory contents","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"data\ndb.sqlite\npipes\n#recycle\n[rerun: b1]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List data and pipes directories","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09\n2026-04-11\nai-habits\nday-recap\nmeeting-summary\nstandup-update\ntime-breakdown\nvideo-export\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"List tables in screenpipe SQLite database","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \".tables\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"_sqlx_migrations meetings \naudio_chunks memories \naudio_tags memories_fts \naudio_transcriptions memories_fts_config \naudio_transcriptions_fts memories_fts_data \naudio_transcriptions_fts_config memories_fts_docsize \naudio_transcriptions_fts_data memories_fts_idx \naudio_transcriptions_fts_docsize ocr_text \naudio_transcriptions_fts_idx pipe_executions \nelements pipe_scheduler_state \nelements_fts speaker_embeddings \nelements_fts_config speakers \nelements_fts_data tags \nelements_fts_docsize ui_events \nelements_fts_idx ui_events_fts \nframes ui_events_fts_config \nframes_fts ui_events_fts_data \nframes_fts_config ui_events_fts_docsize \nframes_fts_content ui_events_fts_idx \nframes_fts_data video_chunks \nframes_fts_docsize vision_tags \nframes_fts_idx\n[rerun: b3]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get ocr_text table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(ocr_text);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|frame_id|INTEGER|1||0\n1|text|TEXT|1||0\n2|text_json|TEXT|0||0\n3|app_name|TEXT|1|''|0\n4|ocr_engine|TEXT|1|'unknown'|0\n5|window_name|TEXT|0||0\n6|focused|BOOLEAN|0|FALSE|0\n7|text_length|INTEGER|0||0\n8|sync_id|TEXT|0||0\n9|synced_at|DATETIME|0||0\n[rerun: b4]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get audio_transcriptions table schema","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(audio_transcriptions);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|audio_chunk_id|INTEGER|1||0\n2|offset_index|INTEGER|1||0\n3|timestamp|TIMESTAMP|1||0\n4|transcription|TEXT|1||0\n5|device|TEXT|1|''|0\n6|is_input_device|BOOLEAN|1|TRUE|0\n7|speaker_id|INTEGER|0||0\n8|transcription_engine|TEXT|1|'Whisper'|0\n9|start_time|REAL|0||0\n10|end_time|REAL|0||0\n11|text_length|INTEGER|0||0\n12|sync_id|TEXT|0||0\n13|synced_at|DATETIME|0||0\n[rerun: b5]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Get frames, ui_events, video_chunks schemas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"0|id|INTEGER|0||1\n1|video_chunk_id|INTEGER|0|NULL|0\n2|offset_index|INTEGER|1|0|0\n3|timestamp|TIMESTAMP|1||0\n4|name|TEXT|0||0\n5|app_name|TEXT|0|NULL|0\n6|window_name|TEXT|0|NULL|0\n7|focused|BOOLEAN|0|NULL|0\n8|browser_url|TEXT|0|NULL|0\n9|device_name|TEXT|1|''|0\n10|sync_id|TEXT|0||0\n11|machine_id|TEXT|0||0\n12|synced_at|DATETIME|0||0\n13|snapshot_path|TEXT|0|NULL|0\n14|accessibility_text|TEXT|0|NULL|0\n15|accessibility_tree_json|TEXT|0|NULL|0\n16|content_hash|INTEGER|0|NULL|0\n17|simhash|INTEGER|0|NULL|0\n18|capture_trigger|TEXT|0|NULL|0\n19|text_source|TEXT|0|NULL|0\n20|cloud_blob_id|TEXT|0|NULL|0\n21|full_text|TEXT|0|NULL|0\n22|elements_ref_frame_id|INTEGER|0|NULL|0\n0|id|INTEGER|0||1\n1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0\n2|session_id|TEXT|0||0\n3|relative_ms|INTEGER|1|0|0\n4|event_type|TEXT|1||0\n5|x|INTEGER|0||0\n6|y|INTEGER|0||0\n7|delta_x|INTEGER|0||0\n8|delta_y|INTEGER|0||0\n9|button|INTEGER|0||0\n10|click_count|INTEGER|0||0\n11|key_code|INTEGER|0||0\n12|modifiers|INTEGER|0||0\n13|text_content|TEXT|0||0\n14|text_length|INTEGER|0||0\n15|app_name|TEXT|0||0\n16|app_pid|INTEGER|0||0\n17|window_title|TEXT|0||0\n18|browser_url|TEXT|0||0\n19|element_role|TEXT|0||0\n20|element_name|TEXT|0||0\n21|element_value|TEXT|0||0\n22|element_description|TEXT|0||0\n23|element_automation_id|TEXT|0||0\n24|element_bounds|TEXT|0||0\n25|frame_id|INTEGER|0||0\n26|sync_id|TEXT|0||0\n27|machine_id|TEXT|0||0\n28|synced_at|DATETIME|0||0\n0|id|INTEGER|0||1\n1|file_path|TEXT|1||0\n2|device_name|TEXT|1|''|0\n3|sync_id|TEXT|0||0\n4|machine_id|TEXT|0||0\n5|synced_at|DATETIME|0||0\n6|fps|REAL|1|0.5|0\n7|cloud_blob_id|TEXT|0|NULL|0\n[rerun: b6]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check data counts and date ranges","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;\" 2>/dev/null\nsqlite3 /volume1/screenpipe/db.sqlite \"SELECT COUNT(*) FROM ocr_text;\" 2>/dev/null","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00\n3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00\n666\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Explore existing pipes for patterns","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Read day-recap pipe documentation","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"cat /volume1/screenpipe/pipes/day-recap/pipe.md","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \"📋\"\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). \n\nRead screenpipe skill first.\n\nUse this exact format:\n\n## Summary\nOne sentence: what I mainly did today.\n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard — with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow — name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n[rerun: b9]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample app names from frames","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Safari\nActivity Monitor\nDia\niTerm2\nControl Centre\nUserNotificationCenter\nFinder\nClaude\nSlack\nFirefox\nRaycast\nNetAuthAgent\nQuickTime Player\n[rerun: b10]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample joined frame+ocr data for today","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE\n2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER\n2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p\n2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p\n2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi\n[rerun: b11]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Sample recent audio transcriptions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-09T17:02:52+00:00|.|System Audio\n2026-04-09T16:59:22+00:00|Class is technically...|System Audio\n2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio\n[rerun: b12]","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Good data to work with. Here's what we have:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"frames","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"+","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ocr_text","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Screen captures with OCR text, app name, window name (1135 frames so far)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"audio_transcriptions","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Voice/system audio transcripts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ui_events","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Mouse/keyboard events with app context","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"video_chunks","depth":24,"role_description":"text"},{"role":"AXStaticText","text":": Raw video files","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Before I start building, a few quick questions:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Tech stack","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"AI summaries","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"What summaries matter most?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Day recap (what apps/windows you used, key text seen)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"App time breakdown (time per app)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Audio/conversation recap","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Custom searchable timeline","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Here's the architecture I'm thinking:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/volume1/screenpipe/app/\n├── main.py # FastAPI web UI\n├── cli.py # Click CLI: `python cli.py day-recap`\n├── db.py # Read-only queries against db.sqlite\n├── consumers/\n│ ├── activity.py # App usage, time per window\n│ ├── ocr.py # OCR text aggregation + search\n│ ├── audio.py # Transcription queries\n│ └── summary.py # AI summary builder\n└── static/\n └── index.html # Single-page UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"CLI commands would look like:","depth":22,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":22,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"python cli.py day-recap # EOD summary\npython cli.py activity --date today # App time breakdown\npython cli.py search \"docker\" # Search OCR/audio text\npython cli.py serve # Launch web UI","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Does this direction work? Any preferences on the stack or which summaries to prioritize?","depth":22,"role_description":"text"},{"role":"AXButton","text":"Message actions","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"seesm ok","depth":24,"role_description":"text"},{"role":"AXButton","text":"Thinking","depth":22,"role_description":"disclosure triangle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Thinking","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Python and available packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 --version && pip3 list 2>/dev/null | grep -E \"fastapi|uvicorn|click|anthropic|httpx|jinja\"","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Python 3.11.2\n[rerun: b1]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Verify join works for today's data","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 /volume1/screenpipe/db.sqlite \"\nSELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len\nFROM frames f JOIN ocr_text o ON f.id = o.frame_id\nWHERE DATE(f.timestamp) = '2026-04-11'\nORDER BY f.timestamp DESC LIMIT 3;\n\"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"2026-04-11T12:54:42.597794+00:00|||2111\n2026-04-11T12:54:41.915916+00:00|||2110\n2026-04-11T12:54:39.912712+00:00|||2111\n[rerun: b2]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install required packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"/bin/bash: line 1: pip3: command not found\n[rerun: b3]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Find pip","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"which pip || which pip3 || python3 -m pip --version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n[rerun: b4]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bootstrap pip then install packages","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Exit code 1\n/usr/bin/python3: No module named ensurepip\n[rerun: b5]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip via apt","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)\nE: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?\n[rerun: b6]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Install pip with sudo","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Reason:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"lets make it in docker, not to install it in nas directly","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Good call. Let me check what Docker setup you already have and build a proper containerized app.","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check existing docker-compose for reference","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"consume\ndata\ndb\ndocker-compose.yml\nexport\nmcp-server\nmedia\nredis\ntrash\nservices:\n redis:\n image: redis:8\n command:\n - /bin/sh\n - -c\n - redis-server --requirepass redispass\n container_name: PaperlessNGX-REDIS\n hostname: paper-redis\n security_opt:\n - no-new-privileges:true\n read_only: true\n user: 1000:10\n healthcheck:\n test: [\"CMD-SHELL\", \"redis-cli ping || exit 1\"]\n volumes:\n - /volume2/docker/paperlessngx/redis:/data:rw\n environment:\n TZ: Europe/Sofia\n restart: always\n\n db:\n image: postgres:17\n container_name: PaperlessNGX-DB\n hostname: paper-db\n mem_limit: 1g\n security_opt:\n - no-new-privileges:true\n healthcheck:\n test: [\"CMD\", \"pg_isready\", \"-q\", \"-d\", \"paperless\", \"-U\", \"paperlessuser\"]\n[rerun: b7]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check Docker availability","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"docker --version && docker compose version 2>/dev/null","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Docker version 26.1.0, build 9714adc\nDocker Compose version v2.26.1\n[rerun: b8]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Bash","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Create app directory structure","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"IN","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy code to clipboard","depth":23,"help_text":"Copy code","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUT","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"[rerun: b9]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Now let me build everything. I'll create all the files:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"requirements.txt","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"requirements.txt","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"6 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"fastapi==0.115.12\nuvicorn[standard]==0.34.0\nclick==8.1.8\nanthropic==0.49.0\npython-dotenv==1.1.0","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Dockerfile","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Dockerfile","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"13 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"FROM python:3.12-slim\n\nWORKDIR /app\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY . .\n\nEXPOSE 8000\n\nCMD [\"uvicorn\", \"main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"db.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"db.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"28 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"import sqlite3\nfrom contextlib import contextmanager\nfrom datetime import date, datetime, timedelta, timezone\nfrom typing import Generator\n\nDB_PATH = \"/data/db.sqlite\"\n\n\n@contextmanager\ndef get_conn() -> Generator[sqlite3.Connection, None, None]:\n conn = sqlite3.connect(f\"file:{DB_PATH}?mode=ro\", uri=True)\n conn.row_factory = sqlite3.Row\n try:\n yield conn\n finally:\n conn.close()\n\n\ndef date_range(for_date: date) -> tuple[str, str]:\n \"\"\"Return ISO timestamps for start/end of a given date (UTC).\"\"\"\n start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)\n end = start + timedelta(days=1)\n return start.isoformat(), end.isoformat()\n\n\ndef today() -> date:\n return datetime.now(timezone.utc).date()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"__init__.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"__init__.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"1 line","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"activity.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"activity.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"123 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"App usage and window focus time consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef app_time(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per app for a given date, sorted descending.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n o.app_name,\n COUNT(*) as frame_count,\n -- each frame ~ 2s at 0.5fps; convert to minutes\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY o.app_name\n ORDER BY frame_count DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:\n \"\"\"Return minutes spent per window/tab, optionally filtered by app.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n o.app_name,\n o.window_name,\n COUNT(*) as frame_count,\n ROUND(COUNT(*) * 2.0 / 60, 1) as minutes\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n \"\"\"\n params: list = [start, end]\n if app:\n query += \" AND o.app_name = ?\"\n params.append(app)\n query += \" GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:\n \"\"\"Return activity bucketed into N-minute intervals.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = interval_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n COUNT(*) as frames\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n GROUP BY bucket, o.app_name\n ORDER BY bucket, frames DESC\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef summary_stats(for_date: date | None = None) -> dict[str, Any]:\n \"\"\"Return high-level stats for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n frames_row = conn.execute(\n \"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last \"\n \"FROM frames WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n app_count = conn.execute(\n \"\"\"\n SELECT COUNT(DISTINCT o.app_name) as n\n FROM frames f JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''\n \"\"\",\n (start, end),\n ).fetchone()\n\n audio_count = conn.execute(\n \"SELECT COUNT(*) as n FROM audio_transcriptions \"\n \"WHERE timestamp >= ? AND timestamp < ?\",\n (start, end),\n ).fetchone()\n\n return {\n \"date\": str(d),\n \"total_frames\": frames_row[\"n\"],\n \"first_seen\": frames_row[\"first\"],\n \"last_seen\": frames_row[\"last\"],\n \"unique_apps\": app_count[\"n\"],\n \"audio_segments\": audio_count[\"n\"],\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"ocr.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"ocr.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"92 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"OCR text search and aggregation consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:\n \"\"\"Full-text search OCR content.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet\n FROM ocr_text_fts\n JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid\n JOIN frames f ON o.frame_id = f.id\n WHERE ocr_text_fts MATCH ?\n AND f.timestamp >= ? AND f.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:\n \"\"\"Return all OCR text for a date, optionally only focused windows.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n query = \"\"\"\n SELECT\n f.timestamp,\n o.app_name,\n o.window_name,\n o.text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n \"\"\"\n params: list = [start, end]\n if focused_only:\n query += \" AND o.focused = TRUE\"\n query += \" ORDER BY f.timestamp\"\n\n with get_conn() as conn:\n rows = conn.execute(query, params).fetchall()\n return [dict(r) for r in rows]\n\n\ndef deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:\n \"\"\"\n Return OCR text deduplicated by time chunk + app to reduce LLM token usage.\n Groups frames into N-minute buckets and returns one representative text per app per bucket.\n \"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n interval_sec = chunk_minutes * 60\n\n with get_conn() as conn:\n rows = conn.execute(\n f\"\"\"\n SELECT\n datetime(\n (strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},\n 'unixepoch'\n ) as bucket,\n o.app_name,\n o.window_name,\n substr(o.text, 1, 500) as text\n FROM frames f\n JOIN ocr_text o ON f.id = o.frame_id\n WHERE f.timestamp >= ? AND f.timestamp < ?\n AND o.app_name != ''\n AND length(o.text) > 20\n GROUP BY bucket, o.app_name\n ORDER BY bucket\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"audio.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"audio.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"61 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Audio transcription consumer.\"\"\"\nfrom datetime import date\nfrom typing import Any\n\nfrom db import get_conn, date_range, today as _today\n\n\ndef transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:\n \"\"\"Return all audio transcriptions for a date.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT timestamp, transcription, device, is_input_device, speaker_id\n FROM audio_transcriptions\n WHERE timestamp >= ? AND timestamp < ?\n AND length(transcription) > 2\n ORDER BY timestamp\n \"\"\",\n (start, end),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:\n \"\"\"Search audio transcriptions.\"\"\"\n d = for_date or _today()\n start, end = date_range(d)\n\n with get_conn() as conn:\n rows = conn.execute(\n \"\"\"\n SELECT\n a.timestamp,\n a.device,\n snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet\n FROM audio_transcriptions_fts\n JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id\n WHERE audio_transcriptions_fts MATCH ?\n AND a.timestamp >= ? AND a.timestamp < ?\n ORDER BY rank\n LIMIT ?\n \"\"\",\n (query, start, end, limit),\n ).fetchall()\n return [dict(r) for r in rows]\n\n\ndef formatted_transcript(for_date: date | None = None) -> str:\n \"\"\"Return audio as a readable transcript string.\"\"\"\n rows = transcriptions(for_date)\n if not rows:\n return \"(no audio transcriptions)\"\n lines = []\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n lines.append(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n return \"\\n\".join(lines)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"summary.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"summary.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"162 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"AI summary generator using Claude.\"\"\"\nimport os\nfrom datetime import date\nfrom typing import Any\n\nfrom consumers.activity import app_time, summary_stats\nfrom consumers.ocr import deduplicated_text\nfrom consumers.audio import formatted_transcript\n\n\nSUMMARY_TYPES = {\n \"day_recap\": {\n \"title\": \"Day Recap\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a concise day recap using EXACTLY this format:\n\n## Summary\nOne sentence: what I mainly worked on today.\n\n## Accomplishments\n- Top 3 things I finished, with approximate times\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things seen or heard, with timestamps\n\n## Unfinished Work\n- What to continue tomorrow\n\n## Patterns\n- Apps used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with:\n**Next step:** [most important thing to continue]\"\"\",\n },\n \"standup\": {\n \"title\": \"Standup Update\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\n## Audio/speech:\n{audio}\n\nWrite a brief standup update:\n\n**Yesterday:** What I worked on (2-3 bullets)\n**Today:** What I plan to continue (1-2 bullets)\n**Blockers:** Any issues observed (or \"None\")\n\nKeep it under 150 words. Only report what's verifiable from the data.\"\"\",\n },\n \"focus_time\": {\n \"title\": \"Focus & Distraction Analysis\",\n \"prompt\": \"\"\"\\\nYou are analyzing a person's computer activity for the day.\n\n## App usage (minutes per app):\n{app_time}\n\n## Screen content (sampled every 5 minutes):\n{ocr_text}\n\nAnalyze focus vs distraction patterns:\n\n## Deep Work Periods\n- When and how long were sustained focus sessions?\n- What was being worked on?\n\n## Distractions\n- Which apps/sites were used recreationally?\n- How much time was lost to context switching?\n\n## Recommendations\n- 2-3 concrete suggestions to improve focus tomorrow\n\nBe specific with times and app names.\"\"\",\n },\n}\n\n\ndef _build_context(for_date: date) -> dict[str, str]:\n apps = app_time(for_date)\n app_lines = \"\\n\".join(f\"- {a['app_name']}: {a['minutes']} min\" for a in apps[:20])\n\n ocr_rows = deduplicated_text(for_date, chunk_minutes=5)\n ocr_lines = \"\\n\".join(\n f\"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}\"\n for r in ocr_rows[:100]\n )\n\n audio = formatted_transcript(for_date)\n\n return {\n \"app_time\": app_lines or \"(no app data)\",\n \"ocr_text\": ocr_lines or \"(no screen data)\",\n \"audio\": audio[:3000] if len(audio) > 3000 else audio,\n }\n\n\ndef generate(\n summary_type: str = \"day_recap\",\n for_date: date | None = None,\n api_key: str | None = None,\n) -> dict[str, Any]:\n \"\"\"Generate an AI summary. Returns dict with 'title', 'content', 'date'.\"\"\"\n import anthropic\n\n d = for_date or date.today()\n key = api_key or os.environ.get(\"ANTHROPIC_API_KEY\", \"\")\n if not key:\n return {\n \"title\": \"Error\",\n \"content\": \"ANTHROPIC_API_KEY not set. Add it to your .env file.\",\n \"date\": str(d),\n }\n\n spec = SUMMARY_TYPES.get(summary_type)\n if not spec:\n return {\"title\": \"Error\", \"content\": f\"Unknown summary type: {summary_type}\", \"date\": str(d)}\n\n stats = summary_stats(d)\n if stats[\"total_frames\"] == 0:\n return {\n \"title\": spec[\"title\"],\n \"content\": f\"No screen data found for {d}.\",\n \"date\": str(d),\n }\n\n ctx = _build_context(d)\n prompt = spec[\"prompt\"].format(**ctx)\n\n client = anthropic.Anthropic(api_key=key)\n message = client.messages.create(\n model=\"claude-opus-4-6\",\n max_tokens=1024,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n )\n\n return {\n \"title\": spec[\"title\"],\n \"content\": message.content[0].text,\n \"date\": str(d),\n \"stats\": stats,\n }","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the FastAPI backend and CLI:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"main.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"main.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"89 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"\"\"\"Screenpipe dashboard — FastAPI web UI.\"\"\"\nimport os\nfrom datetime import date, datetime\nfrom typing import Any\n\nfrom dotenv import load_dotenv\nfrom fastapi import FastAPI, Query\nfrom fastapi.responses import HTMLResponse, JSONResponse\nfrom fastapi.staticfiles import StaticFiles\n\nload_dotenv()\n\nfrom consumers.activity import app_time, window_time, timeline, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions, search as audio_search\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\napp = FastAPI(title=\"Screenpipe Dashboard\")\napp.mount(\"/static\", StaticFiles(directory=\"static\"), name=\"static\")\n\n\ndef _parse_date(d: str | None) -> date | None:\n if not d:\n return None\n try:\n return date.fromisoformat(d)\n except ValueError:\n return None\n\n\n@app.get(\"/\", response_class=HTMLResponse)\nasync def index():\n with open(\"static/index.html\") as f:\n return f.read()\n\n\n# ── Activity ──────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/stats\")\nasync def stats(d: str | None = Query(None)):\n return summary_stats(_parse_date(d))\n\n\n@app.get(\"/api/activity/apps\")\nasync def activity_apps(d: str | None = Query(None)):\n return app_time(_parse_date(d))\n\n\n@app.get(\"/api/activity/windows\")\nasync def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):\n return window_time(_parse_date(d), app_name)\n\n\n@app.get(\"/api/activity/timeline\")\nasync def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):\n return timeline(_parse_date(d), interval)\n\n\n# ── Search ───────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/search/ocr\")\nasync def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):\n return ocr_search(q, _parse_date(d), limit)\n\n\n@app.get(\"/api/search/audio\")\nasync def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):\n return audio_search(q, _parse_date(d), limit)\n\n\n# ── Audio ─────────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/audio\")\nasync def audio(d: str | None = Query(None)):\n return transcriptions(_parse_date(d))\n\n\n# ── Summaries ─────────────────────────────────────────────────────────────────\n\n@app.get(\"/api/summary/types\")\nasync def summary_types():\n return [{\"id\": k, \"title\": v[\"title\"]} for k, v in SUMMARY_TYPES.items()]\n\n\n@app.post(\"/api/summary/generate\")\nasync def generate_summary(type: str = \"day_recap\", d: str | None = None):\n result = gen_summary(type, _parse_date(d))\n return result","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"cli.py","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"cli.py","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"131 lines","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"#!/usr/bin/env python3\n\"\"\"Screenpipe CLI — run summaries and reports on demand.\"\"\"\nimport os\nimport sys\nfrom datetime import date, timedelta\n\nimport click\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nsys.path.insert(0, os.path.dirname(__file__))\n\nfrom consumers.activity import app_time, window_time, summary_stats\nfrom consumers.ocr import search as ocr_search\nfrom consumers.audio import transcriptions\nfrom consumers.summary import generate as gen_summary, SUMMARY_TYPES\n\n\ndef _resolve_date(d_str: str | None) -> date | None:\n if not d_str:\n return None\n if d_str == \"yesterday\":\n return date.today() - timedelta(days=1)\n if d_str == \"today\":\n return date.today()\n return date.fromisoformat(d_str)\n\n\n@click.group()\ndef cli():\n \"\"\"Screenpipe data explorer and summariser.\"\"\"\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--type\", \"-t\", \"summary_type\", default=\"day_recap\",\n type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)\ndef summary(d, summary_type):\n \"\"\"Generate an AI summary for a date (default: today).\"\"\"\n target = _resolve_date(d)\n click.echo(f\"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...\")\n result = gen_summary(summary_type, target)\n click.echo(f\"\\n{'─'*60}\")\n click.echo(f\" {result['title']} — {result['date']}\")\n click.echo(f\"{'─'*60}\\n\")\n click.echo(result[\"content\"])\n if \"stats\" in result:\n s = result[\"stats\"]\n click.echo(f\"\\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, \"\n f\"{s['audio_segments']} audio segments]\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--app\", \"-a\", default=None, help=\"Filter by app name\")\n@click.option(\"--windows\", \"-w\", is_flag=True, help=\"Show window breakdown instead of apps\")\ndef activity(d, app, windows):\n \"\"\"Show time spent per app or window.\"\"\"\n target = _resolve_date(d)\n label = str(target or date.today())\n\n if windows or app:\n rows = window_time(target, app)\n click.echo(f\"\\nWindow time — {label}\" + (f\" [{app}]\" if app else \"\"))\n click.echo(f\"{'App':<25} {'Window':<40} {'Min':>6}\")\n click.echo(\"─\" * 75)\n for r in rows:\n click.echo(f\"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}\")\n else:\n rows = app_time(target)\n stats = summary_stats(target)\n click.echo(f\"\\nApp time — {label}\")\n click.echo(f\"{'App':<30} {'Minutes':>8}\")\n click.echo(\"─\" * 40)\n for r in rows:\n click.echo(f\"{r['app_name']:<30} {r['minutes']:>8}\")\n click.echo(f\"\\nTotal frames: {stats['total_frames']} | \"\n f\"Active: {stats['first_seen']} → {stats['last_seen']}\")\n\n\n@cli.command()\n@click.argument(\"query\")\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\n@click.option(\"--audio\", is_flag=True, help=\"Search audio instead of screen text\")\n@click.option(\"--limit\", \"-n\", default=20, show_default=True)\ndef search(query, d, audio, limit):\n \"\"\"Search OCR screen text or audio transcriptions.\"\"\"\n from consumers.audio import search as audio_search_fn\n\n target = _resolve_date(d)\n if audio:\n rows = audio_search_fn(query, target, limit)\n click.echo(f\"\\nAudio results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['snippet']}\")\n else:\n rows = ocr_search(query, target, limit)\n click.echo(f\"\\nOCR results for '{query}':\")\n for r in rows:\n click.echo(f\" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}\")\n click.echo(f\" {r['snippet']}\")\n\n\n@cli.command()\n@click.option(\"--date\", \"-d\", \"d\", default=None, help=\"Date (YYYY-MM-DD, today, yesterday)\")\ndef audio(d):\n \"\"\"Show audio transcriptions for a date.\"\"\"\n target = _resolve_date(d)\n rows = transcriptions(target)\n if not rows:\n click.echo(\"No audio transcriptions found.\")\n return\n click.echo(f\"\\nAudio transcriptions — {target or date.today()}\")\n click.echo(\"─\" * 60)\n for r in rows:\n label = \"MIC\" if r[\"is_input_device\"] else \"SYS\"\n click.echo(f\"[{r['timestamp']}] [{label}] {r['transcription']}\")\n\n\n@cli.command()\ndef serve():\n \"\"\"Start the web UI (runs uvicorn on port 8000).\"\"\"\n import uvicorn\n click.echo(\"Starting Screenpipe dashboard at http://localhost:8000\")\n uvicorn.run(\"main:app\", host=\"0.0.0.0\", port=8000, reload=False)\n\n\nif __name__ == \"__main__\":\n cli()","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Now the frontend:","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"Write","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"}]...
|
9119589316943796240
|
-8191624847160147962
|
app_switch
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Build screenpipe data UI…, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Build screenpipe data UI and summary app
Session history
New session
Message actions
lets build app around screenpipe data and db. THe idea is to have ui and data consumers. So I can run regular commands at the end ot the day (or on demand) and create soem summary.
Thinking
Thinking
Let me explore the screenpipe directory to understand what we're working with.
Bash
List screenpipe directory contents
IN
ls /volume1/screenpipe/
Copy code to clipboard
OUT
data
db.sqlite
pipes
#recycle
[rerun: b1]
Bash
List data and pipes directories
IN
ls /volume1/screenpipe/data/ | head -20 && ls /volume1/screenpipe/pipes/
Copy code to clipboard
OUT
2026-04-09
2026-04-11
ai-habits
day-recap
meeting-summary
standup-update
time-breakdown
video-export
[rerun: b2]
Bash
List tables in screenpipe SQLite database
IN
sqlite3 /volume1/screenpipe/db.sqlite ".tables"
Copy code to clipboard
OUT
_sqlx_migrations meetings
audio_chunks memories
audio_tags memories_fts
audio_transcriptions memories_fts_config
audio_transcriptions_fts memories_fts_data
audio_transcriptions_fts_config memories_fts_docsize
audio_transcriptions_fts_data memories_fts_idx
audio_transcriptions_fts_docsize ocr_text
audio_transcriptions_fts_idx pipe_executions
elements pipe_scheduler_state
elements_fts speaker_embeddings
elements_fts_config speakers
elements_fts_data tags
elements_fts_docsize ui_events
elements_fts_idx ui_events_fts
frames ui_events_fts_config
frames_fts ui_events_fts_data
frames_fts_config ui_events_fts_docsize
frames_fts_content ui_events_fts_idx
frames_fts_data video_chunks
frames_fts_docsize vision_tags
frames_fts_idx
[rerun: b3]
Bash
Get ocr_text table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(ocr_text);"
Copy code to clipboard
OUT
0|frame_id|INTEGER|1||0
1|text|TEXT|1||0
2|text_json|TEXT|0||0
3|app_name|TEXT|1|''|0
4|ocr_engine|TEXT|1|'unknown'|0
5|window_name|TEXT|0||0
6|focused|BOOLEAN|0|FALSE|0
7|text_length|INTEGER|0||0
8|sync_id|TEXT|0||0
9|synced_at|DATETIME|0||0
[rerun: b4]
Bash
Get audio_transcriptions table schema
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(audio_transcriptions);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|audio_chunk_id|INTEGER|1||0
2|offset_index|INTEGER|1||0
3|timestamp|TIMESTAMP|1||0
4|transcription|TEXT|1||0
5|device|TEXT|1|''|0
6|is_input_device|BOOLEAN|1|TRUE|0
7|speaker_id|INTEGER|0||0
8|transcription_engine|TEXT|1|'Whisper'|0
9|start_time|REAL|0||0
10|end_time|REAL|0||0
11|text_length|INTEGER|0||0
12|sync_id|TEXT|0||0
13|synced_at|DATETIME|0||0
[rerun: b5]
Bash
Get frames, ui_events, video_chunks schemas
IN
sqlite3 /volume1/screenpipe/db.sqlite "PRAGMA table_info(frames); PRAGMA table_info(ui_events); PRAGMA table_info(video_chunks);"
Copy code to clipboard
OUT
0|id|INTEGER|0||1
1|video_chunk_id|INTEGER|0|NULL|0
2|offset_index|INTEGER|1|0|0
3|timestamp|TIMESTAMP|1||0
4|name|TEXT|0||0
5|app_name|TEXT|0|NULL|0
6|window_name|TEXT|0|NULL|0
7|focused|BOOLEAN|0|NULL|0
8|browser_url|TEXT|0|NULL|0
9|device_name|TEXT|1|''|0
10|sync_id|TEXT|0||0
11|machine_id|TEXT|0||0
12|synced_at|DATETIME|0||0
13|snapshot_path|TEXT|0|NULL|0
14|accessibility_text|TEXT|0|NULL|0
15|accessibility_tree_json|TEXT|0|NULL|0
16|content_hash|INTEGER|0|NULL|0
17|simhash|INTEGER|0|NULL|0
18|capture_trigger|TEXT|0|NULL|0
19|text_source|TEXT|0|NULL|0
20|cloud_blob_id|TEXT|0|NULL|0
21|full_text|TEXT|0|NULL|0
22|elements_ref_frame_id|INTEGER|0|NULL|0
0|id|INTEGER|0||1
1|timestamp|DATETIME|1|CURRENT_TIMESTAMP|0
2|session_id|TEXT|0||0
3|relative_ms|INTEGER|1|0|0
4|event_type|TEXT|1||0
5|x|INTEGER|0||0
6|y|INTEGER|0||0
7|delta_x|INTEGER|0||0
8|delta_y|INTEGER|0||0
9|button|INTEGER|0||0
10|click_count|INTEGER|0||0
11|key_code|INTEGER|0||0
12|modifiers|INTEGER|0||0
13|text_content|TEXT|0||0
14|text_length|INTEGER|0||0
15|app_name|TEXT|0||0
16|app_pid|INTEGER|0||0
17|window_title|TEXT|0||0
18|browser_url|TEXT|0||0
19|element_role|TEXT|0||0
20|element_name|TEXT|0||0
21|element_value|TEXT|0||0
22|element_description|TEXT|0||0
23|element_automation_id|TEXT|0||0
24|element_bounds|TEXT|0||0
25|frame_id|INTEGER|0||0
26|sync_id|TEXT|0||0
27|machine_id|TEXT|0||0
28|synced_at|DATETIME|0||0
0|id|INTEGER|0||1
1|file_path|TEXT|1||0
2|device_name|TEXT|1|''|0
3|sync_id|TEXT|0||0
4|machine_id|TEXT|0||0
5|synced_at|DATETIME|0||0
6|fps|REAL|1|0.5|0
7|cloud_blob_id|TEXT|0|NULL|0
[rerun: b6]
Bash
Check data counts and date ranges
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM frames;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*), MIN(timestamp), MAX(timestamp) FROM audio_transcriptions;" 2>/dev/null
sqlite3 /volume1/screenpipe/db.sqlite "SELECT COUNT(*) FROM ocr_text;" 2>/dev/null
Copy code to clipboard
OUT
1135|2026-04-09T16:53:09.043761+00:00|2026-04-11T12:54:42.597794+00:00
3|2026-04-09T16:53:20+00:00|2026-04-09T17:02:52+00:00
666
[rerun: b7]
Bash
Explore existing pipes for patterns
IN
ls /volume1/screenpipe/pipes/day-recap/ 2>/dev/null && cat /volume1/screenpipe/pipes/day-recap/*.js 2>/dev/null | head -50
Copy code to clipboard
OUT
pipe.md
[rerun: b8]
Bash
Read day-recap pipe documentation
IN
cat /volume1/screenpipe/pipes/day-recap/pipe.md
Copy code to clipboard
OUT
---
schedule: manual
enabled: true
template: true
title: Day Recap
description: "Today's accomplishments, key moments, and unfinished work"
icon: "📋"
featured: true
---
Analyze my screen and audio recordings from today (last 16 hours only).
Read screenpipe skill first.
Use this exact format:
## Summary
One sentence: what I mainly did today.
## Accomplishments
- Top 3 things I finished, with timestamps (e.g. "2:30 PM")
- Name specific apps, files, or projects
## Key Moments
- Important things I saw, said, or heard — with timestamps
## Unfinished Work
- What I should continue tomorrow — name the app/file/task
## Patterns
- Apps I used most, topics that came up repeatedly
Only report what you can verify from the data. End with: "**Next step:** [most important thing to continue]"
[rerun: b9]
Bash
Sample app names from frames
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT DISTINCT app_name FROM frames WHERE app_name != '' LIMIT 20;"
Copy code to clipboard
OUT
Safari
Activity Monitor
Dia
iTerm2
Control Centre
UserNotificationCenter
Finder
Claude
Slack
Firefox
Raycast
NetAuthAgent
QuickTime Player
[rerun: b10]
Bash
Sample joined frame+ocr data for today
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT f.timestamp, o.app_name, o.window_name, substr(o.text, 1, 100) as text FROM frames f JOIN ocr_text o ON f.id = o.frame_id WHERE DATE(f.timestamp) = '2026-04-11' LIMIT 5;"
Copy code to clipboard
OUT
2026-04-11T11:53:00.627698+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <47H5Sat 11 Apr 14:53:00-zshDOCKE
2026-04-11T11:53:02.005967+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA-zsh100% <7H5Sat 11 Apr 14:53:01-zshDOCKER
2026-04-11T11:53:03.627468+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:03-zshDOCKERuse p
2026-04-11T11:53:05.528794+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <47Sat 11 Apr 14:53:05-zshDOCKERuse p
2026-04-11T11:53:07.662098+00:00|||iTerm2ShellEditViewSessionScriptsProfilesWindowHelp$0(ahlA100% <7Sat 11 Apr 14:53:07-zshDOCKERuse pi
[rerun: b11]
Bash
Sample recent audio transcriptions
IN
sqlite3 /volume1/screenpipe/db.sqlite "SELECT timestamp, substr(transcription,1,100) as text, device FROM audio_transcriptions ORDER BY timestamp DESC LIMIT 5;"
Copy code to clipboard
OUT
2026-04-09T17:02:52+00:00|.|System Audio
2026-04-09T16:59:22+00:00|Class is technically...|System Audio
2026-04-09T16:53:20+00:00|I do for you. Run the credit check. Deal apartment. Okay.|System Audio
[rerun: b12]
Thinking
Thinking
Good data to work with. Here's what we have:
frames
+
ocr_text
: Screen captures with OCR text, app name, window name (1135 frames so far)
audio_transcriptions
: Voice/system audio transcripts
ui_events
: Mouse/keyboard events with app context
video_chunks
: Raw video files
Before I start building, a few quick questions:
Tech stack
— Python OK? (FastAPI for the web UI backend, Click for CLI commands, HTMX for the frontend to keep it simple)
AI summaries
— Do you have a Claude API key to use for generating the actual summaries? Or should I integrate with a local model / skip AI for now?
What summaries matter most?
Day recap (what apps/windows you used, key text seen)
App time breakdown (time per app)
Audio/conversation recap
Custom searchable timeline
Here's the architecture I'm thinking:
Copy code to clipboard
/volume1/screenpipe/app/
├── main.py # FastAPI web UI
├── cli.py # Click CLI: `python cli.py day-recap`
├── db.py # Read-only queries against db.sqlite
├── consumers/
│ ├── activity.py # App usage, time per window
│ ├── ocr.py # OCR text aggregation + search
│ ├── audio.py # Transcription queries
│ └── summary.py # AI summary builder
└── static/
└── index.html # Single-page UI
CLI commands would look like:
Copy code to clipboard
python cli.py day-recap # EOD summary
python cli.py activity --date today # App time breakdown
python cli.py search "docker" # Search OCR/audio text
python cli.py serve # Launch web UI
Does this direction work? Any preferences on the stack or which summaries to prioritize?
Message actions
seesm ok
Thinking
Thinking
Bash
Check Python and available packages
IN
python3 --version && pip3 list 2>/dev/null | grep -E "fastapi|uvicorn|click|anthropic|httpx|jinja"
Copy code to clipboard
OUT
Python 3.11.2
[rerun: b1]
Bash
Verify join works for today's data
IN
sqlite3 /volume1/screenpipe/db.sqlite "
SELECT f.timestamp, o.app_name, o.window_name, length(o.text) as txt_len
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE DATE(f.timestamp) = '2026-04-11'
ORDER BY f.timestamp DESC LIMIT 3;
"
Copy code to clipboard
OUT
2026-04-11T12:54:42.597794+00:00|||2111
2026-04-11T12:54:41.915916+00:00|||2110
2026-04-11T12:54:39.912712+00:00|||2111
[rerun: b2]
Bash
Install required packages
IN
pip3 install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -5
Copy code to clipboard
OUT
/bin/bash: line 1: pip3: command not found
[rerun: b3]
Bash
Find pip
IN
which pip || which pip3 || python3 -m pip --version 2>/dev/null
Copy code to clipboard
OUT
Exit code 1
[rerun: b4]
Bash
Bootstrap pip then install packages
IN
python3 -m ensurepip --upgrade 2>&1 && python3 -m pip install fastapi uvicorn click anthropic python-dotenv 2>&1 | tail -10
Copy code to clipboard
OUT
Exit code 1
/usr/bin/python3: No module named ensurepip
[rerun: b5]
Bash
Install pip via apt
IN
apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
OUT
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
[rerun: b6]
Bash
Install pip with sudo
Reason:
lets make it in docker, not to install it in nas directly
IN
sudo apt-get install -y python3-pip python3-venv 2>&1 | tail -5
Copy code to clipboard
Good call. Let me check what Docker setup you already have and build a proper containerized app.
Bash
Check existing docker-compose for reference
IN
ls /volume2/docker/paperlessngx/ && cat /volume2/docker/paperlessngx/docker-compose.yml 2>/dev/null | head -30
Copy code to clipboard
OUT
consume
data
db
docker-compose.yml
export
mcp-server
media
redis
trash
services:
redis:
image: redis:8
command:
- /bin/sh
- -c
- redis-server --requirepass redispass
container_name: PaperlessNGX-REDIS
hostname: paper-redis
security_opt:
- no-new-privileges:true
read_only: true
user: 1000:10
healthcheck:
test: ["CMD-SHELL", "redis-cli ping || exit 1"]
volumes:
- /volume2/docker/paperlessngx/redis:/data:rw
environment:
TZ: Europe/Sofia
restart: always
db:
image: postgres:17
container_name: PaperlessNGX-DB
hostname: paper-db
mem_limit: 1g
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperlessuser"]
[rerun: b7]
Bash
Check Docker availability
IN
docker --version && docker compose version 2>/dev/null
Copy code to clipboard
OUT
Docker version 26.1.0, build 9714adc
Docker Compose version v2.26.1
[rerun: b8]
Bash
Create app directory structure
IN
mkdir -p /volume1/screenpipe/app/consumers /volume1/screenpipe/app/static
Copy code to clipboard
OUT
[rerun: b9]
Now let me build everything. I'll create all the files:
Write
requirements.txt
requirements.txt
6 lines
fastapi==0.115.12
uvicorn[standard]==0.34.0
click==8.1.8
anthropic==0.49.0
python-dotenv==1.1.0
Write
Dockerfile
Dockerfile
13 lines
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "[IP_ADDRESS]", "--port", "8000"]
Write
db.py
db.py
28 lines
import sqlite3
from contextlib import contextmanager
from datetime import date, datetime, timedelta, timezone
from typing import Generator
DB_PATH = "/data/db.sqlite"
@contextmanager
def get_conn() -> Generator[sqlite3.Connection, None, None]:
conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def date_range(for_date: date) -> tuple[str, str]:
"""Return ISO timestamps for start/end of a given date (UTC)."""
start = datetime(for_date.year, for_date.month, for_date.day, tzinfo=timezone.utc)
end = start + timedelta(days=1)
return start.isoformat(), end.isoformat()
def today() -> date:
return datetime.now(timezone.utc).date()
Write
__init__.py
__init__.py
1 line
Write
activity.py
activity.py
123 lines
"""App usage and window focus time consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def app_time(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per app for a given date, sorted descending."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
o.app_name,
COUNT(*) as frame_count,
-- each frame ~ 2s at 0.5fps; convert to minutes
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY o.app_name
ORDER BY frame_count DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def window_time(for_date: date | None = None, app: str | None = None) -> list[dict[str, Any]]:
"""Return minutes spent per window/tab, optionally filtered by app."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
o.app_name,
o.window_name,
COUNT(*) as frame_count,
ROUND(COUNT(*) * 2.0 / 60, 1) as minutes
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
"""
params: list = [start, end]
if app:
query += " AND o.app_name = ?"
params.append(app)
query += " GROUP BY o.app_name, o.window_name ORDER BY frame_count DESC LIMIT 50"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def timeline(for_date: date | None = None, interval_minutes: int = 15) -> list[dict[str, Any]]:
"""Return activity bucketed into N-minute intervals."""
d = for_date or _today()
start, end = date_range(d)
interval_sec = interval_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
COUNT(*) as frames
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
GROUP BY bucket, o.app_name
ORDER BY bucket, frames DESC
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def summary_stats(for_date: date | None = None) -> dict[str, Any]:
"""Return high-level stats for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
frames_row = conn.execute(
"SELECT COUNT(*) as n, MIN(timestamp) as first, MAX(timestamp) as last "
"FROM frames WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
app_count = conn.execute(
"""
SELECT COUNT(DISTINCT o.app_name) as n
FROM frames f JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ? AND o.app_name != ''
""",
(start, end),
).fetchone()
audio_count = conn.execute(
"SELECT COUNT(*) as n FROM audio_transcriptions "
"WHERE timestamp >= ? AND timestamp < ?",
(start, end),
).fetchone()
return {
"date": str(d),
"total_frames": frames_row["n"],
"first_seen": frames_row["first"],
"last_seen": frames_row["last"],
"unique_apps": app_count["n"],
"audio_segments": audio_count["n"],
}
Write
ocr.py
ocr.py
92 lines
"""OCR text search and aggregation consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def search(query: str, for_date: date | None = None, limit: int = 50) -> list[dict[str, Any]]:
"""Full-text search OCR content."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
f.timestamp,
o.app_name,
o.window_name,
snippet(ocr_text_fts, 0, '[', ']', '...', 20) as snippet
FROM ocr_text_fts
JOIN ocr_text o ON ocr_text_fts.rowid = o.rowid
JOIN frames f ON o.frame_id = f.id
WHERE ocr_text_fts MATCH ?
AND f.timestamp >= ? AND f.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def text_for_date(for_date: date | None = None, focused_only: bool = True) -> list[dict[str, Any]]:
"""Return all OCR text for a date, optionally only focused windows."""
d = for_date or _today()
start, end = date_range(d)
query = """
SELECT
f.timestamp,
o.app_name,
o.window_name,
o.text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
"""
params: list = [start, end]
if focused_only:
query += " AND o.focused = TRUE"
query += " ORDER BY f.timestamp"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
return [dict(r) for r in rows]
def deduplicated_text(for_date: date | None = None, chunk_minutes: int = 5) -> list[dict[str, Any]]:
"""
Return OCR text deduplicated by time chunk + app to reduce LLM token usage.
Groups frames into N-minute buckets and returns one representative text per app per bucket.
"""
d = for_date or _today()
start, end = date_range(d)
interval_sec = chunk_minutes * 60
with get_conn() as conn:
rows = conn.execute(
f"""
SELECT
datetime(
(strftime('%s', f.timestamp) / {interval_sec}) * {interval_sec},
'unixepoch'
) as bucket,
o.app_name,
o.window_name,
substr(o.text, 1, 500) as text
FROM frames f
JOIN ocr_text o ON f.id = o.frame_id
WHERE f.timestamp >= ? AND f.timestamp < ?
AND o.app_name != ''
AND length(o.text) > 20
GROUP BY bucket, o.app_name
ORDER BY bucket
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
Write
audio.py
audio.py
61 lines
"""Audio transcription consumer."""
from datetime import date
from typing import Any
from db import get_conn, date_range, today as _today
def transcriptions(for_date: date | None = None) -> list[dict[str, Any]]:
"""Return all audio transcriptions for a date."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT timestamp, transcription, device, is_input_device, speaker_id
FROM audio_transcriptions
WHERE timestamp >= ? AND timestamp < ?
AND length(transcription) > 2
ORDER BY timestamp
""",
(start, end),
).fetchall()
return [dict(r) for r in rows]
def search(query: str, for_date: date | None = None, limit: int = 30) -> list[dict[str, Any]]:
"""Search audio transcriptions."""
d = for_date or _today()
start, end = date_range(d)
with get_conn() as conn:
rows = conn.execute(
"""
SELECT
a.timestamp,
a.device,
snippet(audio_transcriptions_fts, 0, '[', ']', '...', 20) as snippet
FROM audio_transcriptions_fts
JOIN audio_transcriptions a ON audio_transcriptions_fts.rowid = a.id
WHERE audio_transcriptions_fts MATCH ?
AND a.timestamp >= ? AND a.timestamp < ?
ORDER BY rank
LIMIT ?
""",
(query, start, end, limit),
).fetchall()
return [dict(r) for r in rows]
def formatted_transcript(for_date: date | None = None) -> str:
"""Return audio as a readable transcript string."""
rows = transcriptions(for_date)
if not rows:
return "(no audio transcriptions)"
lines = []
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
lines.append(f"[{r['timestamp']}] [{label}] {r['transcription']}")
return "\n".join(lines)
Write
summary.py
summary.py
162 lines
"""AI summary generator using Claude."""
import os
from datetime import date
from typing import Any
from consumers.activity import app_time, summary_stats
from consumers.ocr import deduplicated_text
from consumers.audio import formatted_transcript
SUMMARY_TYPES = {
"day_recap": {
"title": "Day Recap",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a concise day recap using EXACTLY this format:
## Summary
One sentence: what I mainly worked on today.
## Accomplishments
- Top 3 things I finished, with approximate times
- Name specific apps, files, or projects
## Key Moments
- Important things seen or heard, with timestamps
## Unfinished Work
- What to continue tomorrow
## Patterns
- Apps used most, topics that came up repeatedly
Only report what you can verify from the data. End with:
**Next step:** [most important thing to continue]""",
},
"standup": {
"title": "Standup Update",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
## Audio/speech:
{audio}
Write a brief standup update:
**Yesterday:** What I worked on (2-3 bullets)
**Today:** What I plan to continue (1-2 bullets)
**Blockers:** Any issues observed (or "None")
Keep it under 150 words. Only report what's verifiable from the data.""",
},
"focus_time": {
"title": "Focus & Distraction Analysis",
"prompt": """\
You are analyzing a person's computer activity for the day.
## App usage (minutes per app):
{app_time}
## Screen content (sampled every 5 minutes):
{ocr_text}
Analyze focus vs distraction patterns:
## Deep Work Periods
- When and how long were sustained focus sessions?
- What was being worked on?
## Distractions
- Which apps/sites were used recreationally?
- How much time was lost to context switching?
## Recommendations
- 2-3 concrete suggestions to improve focus tomorrow
Be specific with times and app names.""",
},
}
def _build_context(for_date: date) -> dict[str, str]:
apps = app_time(for_date)
app_lines = "\n".join(f"- {a['app_name']}: {a['minutes']} min" for a in apps[:20])
ocr_rows = deduplicated_text(for_date, chunk_minutes=5)
ocr_lines = "\n".join(
f"[{r['bucket']}] {r['app_name']} / {r['window_name'] or ''}: {r['text'][:200]}"
for r in ocr_rows[:100]
)
audio = formatted_transcript(for_date)
return {
"app_time": app_lines or "(no app data)",
"ocr_text": ocr_lines or "(no screen data)",
"audio": audio[:3000] if len(audio) > 3000 else audio,
}
def generate(
summary_type: str = "day_recap",
for_date: date | None = None,
api_key: str | None = None,
) -> dict[str, Any]:
"""Generate an AI summary. Returns dict with 'title', 'content', 'date'."""
import anthropic
d = for_date or date.today()
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
if not key:
return {
"title": "Error",
"content": "ANTHROPIC_API_KEY not set. Add it to your .env file.",
"date": str(d),
}
spec = SUMMARY_TYPES.get(summary_type)
if not spec:
return {"title": "Error", "content": f"Unknown summary type: {summary_type}", "date": str(d)}
stats = summary_stats(d)
if stats["total_frames"] == 0:
return {
"title": spec["title"],
"content": f"No screen data found for {d}.",
"date": str(d),
}
ctx = _build_context(d)
prompt = spec["prompt"].format(**ctx)
client = anthropic.Anthropic(api_key=key)
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return {
"title": spec["title"],
"content": message.content[0].text,
"date": str(d),
"stats": stats,
}
Now the FastAPI backend and CLI:
Write
main.py
main.py
89 lines
"""Screenpipe dashboard — FastAPI web UI."""
import os
from datetime import date, datetime
from typing import Any
from dotenv import load_dotenv
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
from consumers.activity import app_time, window_time, timeline, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions, search as audio_search
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
app = FastAPI(title="Screenpipe Dashboard")
app.mount("/static", StaticFiles(directory="static"), name="static")
def _parse_date(d: str | None) -> date | None:
if not d:
return None
try:
return date.fromisoformat(d)
except ValueError:
return None
@app.get("/", response_class=HTMLResponse)
async def index():
with open("static/index.html") as f:
return f.read()
# ── Activity ──────────────────────────────────────────────────────────────────
@app.get("/api/stats")
async def stats(d: str | None = Query(None)):
return summary_stats(_parse_date(d))
@app.get("/api/activity/apps")
async def activity_apps(d: str | None = Query(None)):
return app_time(_parse_date(d))
@app.get("/api/activity/windows")
async def activity_windows(d: str | None = Query(None), app_name: str | None = Query(None)):
return window_time(_parse_date(d), app_name)
@app.get("/api/activity/timeline")
async def activity_timeline(d: str | None = Query(None), interval: int = Query(15)):
return timeline(_parse_date(d), interval)
# ── Search ───────────────────────────────────────────────────────────────────
@app.get("/api/search/ocr")
async def search_ocr(q: str, d: str | None = Query(None), limit: int = 50):
return ocr_search(q, _parse_date(d), limit)
@app.get("/api/search/audio")
async def search_audio_endpoint(q: str, d: str | None = Query(None), limit: int = 30):
return audio_search(q, _parse_date(d), limit)
# ── Audio ─────────────────────────────────────────────────────────────────────
@app.get("/api/audio")
async def audio(d: str | None = Query(None)):
return transcriptions(_parse_date(d))
# ── Summaries ─────────────────────────────────────────────────────────────────
@app.get("/api/summary/types")
async def summary_types():
return [{"id": k, "title": v["title"]} for k, v in SUMMARY_TYPES.items()]
@app.post("/api/summary/generate")
async def generate_summary(type: str = "day_recap", d: str | None = None):
result = gen_summary(type, _parse_date(d))
return result
Write
cli.py
cli.py
131 lines
#!/usr/bin/env python3
"""Screenpipe CLI — run summaries and reports on demand."""
import os
import sys
from datetime import date, timedelta
import click
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from consumers.activity import app_time, window_time, summary_stats
from consumers.ocr import search as ocr_search
from consumers.audio import transcriptions
from consumers.summary import generate as gen_summary, SUMMARY_TYPES
def _resolve_date(d_str: str | None) -> date | None:
if not d_str:
return None
if d_str == "yesterday":
return date.today() - timedelta(days=1)
if d_str == "today":
return date.today()
return date.fromisoformat(d_str)
@click.group()
def cli():
"""Screenpipe data explorer and summariser."""
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--type", "-t", "summary_type", default="day_recap",
type=click.Choice(list(SUMMARY_TYPES.keys())), show_default=True)
def summary(d, summary_type):
"""Generate an AI summary for a date (default: today)."""
target = _resolve_date(d)
click.echo(f"Generating {SUMMARY_TYPES[summary_type]['title']} for {target or date.today()}...")
result = gen_summary(summary_type, target)
click.echo(f"\n{'─'*60}")
click.echo(f" {result['title']} — {result['date']}")
click.echo(f"{'─'*60}\n")
click.echo(result["content"])
if "stats" in result:
s = result["stats"]
click.echo(f"\n[data: {s['total_frames']} frames, {s['unique_apps']} apps, "
f"{s['audio_segments']} audio segments]")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--app", "-a", default=None, help="Filter by app name")
@click.option("--windows", "-w", is_flag=True, help="Show window breakdown instead of apps")
def activity(d, app, windows):
"""Show time spent per app or window."""
target = _resolve_date(d)
label = str(target or date.today())
if windows or app:
rows = window_time(target, app)
click.echo(f"\nWindow time — {label}" + (f" [{app}]" if app else ""))
click.echo(f"{'App':<25} {'Window':<40} {'Min':>6}")
click.echo("─" * 75)
for r in rows:
click.echo(f"{r['app_name']:<25} {(r['window_name'] or '')[:40]:<40} {r['minutes']:>6}")
else:
rows = app_time(target)
stats = summary_stats(target)
click.echo(f"\nApp time — {label}")
click.echo(f"{'App':<30} {'Minutes':>8}")
click.echo("─" * 40)
for r in rows:
click.echo(f"{r['app_name']:<30} {r['minutes']:>8}")
click.echo(f"\nTotal frames: {stats['total_frames']} | "
f"Active: {stats['first_seen']} → {stats['last_seen']}")
@cli.command()
@click.argument("query")
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
@click.option("--audio", is_flag=True, help="Search audio instead of screen text")
@click.option("--limit", "-n", default=20, show_default=True)
def search(query, d, audio, limit):
"""Search OCR screen text or audio transcriptions."""
from consumers.audio import search as audio_search_fn
target = _resolve_date(d)
if audio:
rows = audio_search_fn(query, target, limit)
click.echo(f"\nAudio results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['snippet']}")
else:
rows = ocr_search(query, target, limit)
click.echo(f"\nOCR results for '{query}':")
for r in rows:
click.echo(f" [{r['timestamp']}] {r['app_name']} / {r['window_name'] or ''}")
click.echo(f" {r['snippet']}")
@cli.command()
@click.option("--date", "-d", "d", default=None, help="Date (YYYY-MM-DD, today, yesterday)")
def audio(d):
"""Show audio transcriptions for a date."""
target = _resolve_date(d)
rows = transcriptions(target)
if not rows:
click.echo("No audio transcriptions found.")
return
click.echo(f"\nAudio transcriptions — {target or date.today()}")
click.echo("─" * 60)
for r in rows:
label = "MIC" if r["is_input_device"] else "SYS"
click.echo(f"[{r['timestamp']}] [{label}] {r['transcription']}")
@cli.command()
def serve():
"""Start the web UI (runs uvicorn on port 8000)."""
import uvicorn
click.echo("Starting Screenpipe dashboard at http://localhost:8000")
uvicorn.run("main:app", host="[IP_ADDRESS]", port=8000, reload=False)
if __name__ == "__main__":
cli()
Now the frontend:
Write...
|
NULL
|
|
1431
|
32
|
0
|
2026-04-11T17:27:18.249125+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928438249_m1.jpg...
|
Firefox
|
Screenpipe Dashboard — Personal
|
True
|
http://100.73.206.126:8766
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Close tab
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Search results for "bitwarden" – Add-ons for Firefox (en-US)
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Screenpipe
Screenpipe
Activity
Search
Audio
Work Report
AI Summary
Date
11
/
04
/
2026
Calendar
TOTAL SPAN
1.0h
14:52 → 15:54
ACTIVE
1.0h
BREAKS
0 breaks · 0.0h
SESSIONS
1
62m
FRAMES
612
APPS
6
UI EVENTS
1344
AUDIO
0
ACTIVE PERIOD
14:52 → 15:54
TIME PER APP
— CLICK TO FILTER RIGHT PANEL
iTerm2
5.2m
Firefox
1.5m
Finder
0.6m
QuickTime Player
0.5m
NetAuthAgent
0.1m
Raycast
0.1m
Websites
Windows
UI Events
nas.lakylak.xyz/desktop/#/
0.7m
play.hbomax.com/video/watch/6b742798-e5fa-4f90-8beb-74c328dc5028/65d19c63-e85e-4392-8594-f0f2e0a1dc2d
0.1m
nas.lakylak.xyz/desktop/#/login/account
0.1m
login.tailscale.com/login?next_url=%2Fadmin
0.1m
dennikn.sk/5263377/lod-orion-uspesne-pristala-na-zemi-desat-veci-ktore-si-pamatat-na-misii-artemis-ii/
0.1m
http://localhost:3030
0m
addons.mozilla.org/en-US/firefox/addon/vimium-ff/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search
0m
accounts.google.com/v3/signin/accountchooser?access_type=offline&client_id=674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com&prompt=select_account&redirect_uri=https%3A%2F%2Flogin.tailscale.com%2Fa%2Foauth_response&response_type=code&scope=openid+email+profile&state=tc-ftYwP8oB0urJfQZeh42KPfA&dsh=S1949714251%3A1775911302074699&o2v=2&service=lso&flowName=GeneralOAuthFlow&opparams=%253F&continue=https%3A%2F%2Faccounts.google.com%2Fsignin%2Foauth%2Fconsent%3Fauthuser%3Dunknown%26part%3DAJi8hAOOoeMJ9h1ZWE-Aau2rWQdjwJ-4Enpv6Ci-0hiDSq5jW9TsVTl1btOELm96_EZaQdgfRwBdj1zZGB_CYauRC0fkTqY_cZ35H_NcJOU2_4Ax0Gtto-fKf-nDgh4daUIBqdTh68bjSxOce25-KnK0XYdHV8mzb69bNhDZHVDj_gb42yLS6CaLOtRfvBqDUDqR3SzLMQPfBkXHhJz6IM_7wq52TLTL563ihzeGQBECsxCnD1Yk27REW5r_eZ0TNXkkAX7P1hB8gAkBKuZVXHltsgP7jvfc8R1fsuXURwjdV8kBOMOg3Reld6-0t5nB9qwgvuYCJsuo0Q7IMz9o-t41651a_Wsc0FnlTxFC_0DA-eKXtSlPZHRy9BW_O_UgliMh8Y9cnLpKZ4r9A_LwCm6flXHR1tZWe_NA_YGhQ9EYSdByTuW1hMcIXXh1Alssc0Sb9BtxZ5mPnlLjTXaWd8LEtCrsHv1HQQ%26flowName%3DGeneralOAuthFlow%26as%3DS1949714251%253A1775911302074699%26client_id%3D674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com%26requestPath%3D%252Fsignin%252Foauth%252Fconsent%23&app_domain=https%3A%2F%2Flogin.tailscale.com
0m...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Shameless • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Shameless • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Irán – Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Irán – Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium Options","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium Options","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe Dashboard","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Screenpipe Dashboard","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Search results for \"bitwarden\" – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search results for \"bitwarden\" – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Browser Extension Getting Started | Bitwarden","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Browser Extension Getting Started | Bitwarden","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Extensions – Add-ons for Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Extensions – Add-ons for Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Screenpipe","depth":7,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Screenpipe","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Activity","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Search","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Audio","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Work Report","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Summary","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Date","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"11","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"04","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2026","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Calendar","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"TOTAL SPAN","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.0h","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"14:52 → 15:54","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ACTIVE","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.0h","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"BREAKS","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0 breaks · 0.0h","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SESSIONS","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"62m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"FRAMES","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"612","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"APPS","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"6","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"UI EVENTS","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1344","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AUDIO","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ACTIVE PERIOD","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"14:52 → 15:54","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TIME PER APP","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"— CLICK TO FILTER RIGHT PANEL","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"iTerm2","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5.2m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Firefox","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.5m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Finder","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.6m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"QuickTime Player","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.5m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NetAuthAgent","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Raycast","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Websites","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Windows","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"UI Events","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"nas.lakylak.xyz/desktop/#/","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.7m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"play.hbomax.com/video/watch/6b742798-e5fa-4f90-8beb-74c328dc5028/65d19c63-e85e-4392-8594-f0f2e0a1dc2d","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"nas.lakylak.xyz/desktop/#/login/account","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"login.tailscale.com/login?next_url=%2Fadmin","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dennikn.sk/5263377/lod-orion-uspesne-pristala-na-zemi-desat-veci-ktore-si-pamatat-na-misii-artemis-ii/","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0.1m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"http://localhost:3030","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"addons.mozilla.org/en-US/firefox/addon/vimium-ff/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"accounts.google.com/v3/signin/accountchooser?access_type=offline&client_id=674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com&prompt=select_account&redirect_uri=https%3A%2F%2Flogin.tailscale.com%2Fa%2Foauth_response&response_type=code&scope=openid+email+profile&state=tc-ftYwP8oB0urJfQZeh42KPfA&dsh=S1949714251%3A1775911302074699&o2v=2&service=lso&flowName=GeneralOAuthFlow&opparams=%253F&continue=https%3A%2F%2Faccounts.google.com%2Fsignin%2Foauth%2Fconsent%3Fauthuser%3Dunknown%26part%3DAJi8hAOOoeMJ9h1ZWE-Aau2rWQdjwJ-4Enpv6Ci-0hiDSq5jW9TsVTl1btOELm96_EZaQdgfRwBdj1zZGB_CYauRC0fkTqY_cZ35H_NcJOU2_4Ax0Gtto-fKf-nDgh4daUIBqdTh68bjSxOce25-KnK0XYdHV8mzb69bNhDZHVDj_gb42yLS6CaLOtRfvBqDUDqR3SzLMQPfBkXHhJz6IM_7wq52TLTL563ihzeGQBECsxCnD1Yk27REW5r_eZ0TNXkkAX7P1hB8gAkBKuZVXHltsgP7jvfc8R1fsuXURwjdV8kBOMOg3Reld6-0t5nB9qwgvuYCJsuo0Q7IMz9o-t41651a_Wsc0FnlTxFC_0DA-eKXtSlPZHRy9BW_O_UgliMh8Y9cnLpKZ4r9A_LwCm6flXHR1tZWe_NA_YGhQ9EYSdByTuW1hMcIXXh1Alssc0Sb9BtxZ5mPnlLjTXaWd8LEtCrsHv1HQQ%26flowName%3DGeneralOAuthFlow%26as%3DS1949714251%253A1775911302074699%26client_id%3D674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com%26requestPath%3D%252Fsignin%252Foauth%252Fconsent%23&app_domain=https%3A%2F%2Flogin.tailscale.com","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0m","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-817909191078697115
|
-5356146286853993268
|
app_switch
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameles DXP4800PLUS-B5F8
Inbox - [EMAIL] - Gmail
Shameless • HBO Max
Shameless • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Irán – Denník N
Irán – Denník N
Vimium Options
Vimium Options
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Machines - Tailscale
Machines - Tailscale
Screenpipe Dashboard
Screenpipe Dashboard
Close tab
Search results for "bitwarden" – Add-ons for Firefox (en-US)
Search results for "bitwarden" – Add-ons for Firefox (en-US)
New Tab
New Tab
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Bitwarden Password Manager – Get this Extension for 🦊 Firefox (en-US)
Browser Extension Getting Started | Bitwarden
Browser Extension Getting Started | Bitwarden
Extensions – Add-ons for Firefox (en-US)
Extensions – Add-ons for Firefox (en-US)
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Screenpipe
Screenpipe
Activity
Search
Audio
Work Report
AI Summary
Date
11
/
04
/
2026
Calendar
TOTAL SPAN
1.0h
14:52 → 15:54
ACTIVE
1.0h
BREAKS
0 breaks · 0.0h
SESSIONS
1
62m
FRAMES
612
APPS
6
UI EVENTS
1344
AUDIO
0
ACTIVE PERIOD
14:52 → 15:54
TIME PER APP
— CLICK TO FILTER RIGHT PANEL
iTerm2
5.2m
Firefox
1.5m
Finder
0.6m
QuickTime Player
0.5m
NetAuthAgent
0.1m
Raycast
0.1m
Websites
Windows
UI Events
nas.lakylak.xyz/desktop/#/
0.7m
play.hbomax.com/video/watch/6b742798-e5fa-4f90-8beb-74c328dc5028/65d19c63-e85e-4392-8594-f0f2e0a1dc2d
0.1m
nas.lakylak.xyz/desktop/#/login/account
0.1m
login.tailscale.com/login?next_url=%2Fadmin
0.1m
dennikn.sk/5263377/lod-orion-uspesne-pristala-na-zemi-desat-veci-ktore-si-pamatat-na-misii-artemis-ii/
0.1m
http://localhost:3030
0m
addons.mozilla.org/en-US/firefox/addon/vimium-ff/?utm_source=addons.mozilla.org&utm_medium=referral&utm_content=search
0m
accounts.google.com/v3/signin/accountchooser?access_type=offline&client_id=674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com&prompt=select_account&redirect_uri=https%3A%2F%2Flogin.tailscale.com%2Fa%2Foauth_response&response_type=code&scope=openid+email+profile&state=tc-ftYwP8oB0urJfQZeh42KPfA&dsh=S1949714251%3A1775911302074699&o2v=2&service=lso&flowName=GeneralOAuthFlow&opparams=%253F&continue=https%3A%2F%2Faccounts.google.com%2Fsignin%2Foauth%2Fconsent%3Fauthuser%3Dunknown%26part%3DAJi8hAOOoeMJ9h1ZWE-Aau2rWQdjwJ-4Enpv6Ci-0hiDSq5jW9TsVTl1btOELm96_EZaQdgfRwBdj1zZGB_CYauRC0fkTqY_cZ35H_NcJOU2_4Ax0Gtto-fKf-nDgh4daUIBqdTh68bjSxOce25-KnK0XYdHV8mzb69bNhDZHVDj_gb42yLS6CaLOtRfvBqDUDqR3SzLMQPfBkXHhJz6IM_7wq52TLTL563ihzeGQBECsxCnD1Yk27REW5r_eZ0TNXkkAX7P1hB8gAkBKuZVXHltsgP7jvfc8R1fsuXURwjdV8kBOMOg3Reld6-0t5nB9qwgvuYCJsuo0Q7IMz9o-t41651a_Wsc0FnlTxFC_0DA-eKXtSlPZHRy9BW_O_UgliMh8Y9cnLpKZ4r9A_LwCm6flXHR1tZWe_NA_YGhQ9EYSdByTuW1hMcIXXh1Alssc0Sb9BtxZ5mPnlLjTXaWd8LEtCrsHv1HQQ%26flowName%3DGeneralOAuthFlow%26as%3DS1949714251%253A1775911302074699%26client_id%3D674241127656-lmq9su4p8ni1tcpuh6eqidoornqtvmvi.apps.googleusercontent.com%26requestPath%3D%252Fsignin%252Foauth%252Fconsent%23&app_domain=https%3A%2F%2Flogin.tailscale.com
0m...
|
NULL
|
|
1430
|
NULL
|
0
|
2026-04-11T17:27:08.016475+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928428016_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKER• 881auto-destruct piddeepgram keylanguagesDEV (-zsh)0not setO 82APP (-zsh)• *3-zsh• ×4-zsh• 285-zsh886-zshO 87Sat 11 Apr 20:27:07-zshT81• *8+all languagesmonitorsno monitors availableaudio devicesdisabledyouareusing local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.checklatest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFO screenpipe: starting UIevent capture2026-04-11T20:26:05.35365622026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shinstali faitedi t n onounar -2026-04-11T20:26:05.363864ZINFO screenpipe_engine::power::manager: initial power profile: Performance Con_ac=true, battery=Some(100))INFO screenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05.3783252INFOscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-1017:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883 frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.6831772INFOscreenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 1440x900)2026-04-11T20:26:05.683293ZINFO2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)Tcorting fot onterier, ene re: 202860-10 1-2350. 75- 1screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:2026-04-11T20:26:07.067446ZINFOpersistent SCK stream started for display 1 (1440x900,Zfps)screenpipe_engine::event_driven_capture: startup2026-04-11T20:26:08.752985Zcapture for monitor 1: frame_id=1407, dur=537msINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:57.331771Zscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)INFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05.408691ZINFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible frames...
|
NULL
|
-8482804634751830197
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100% <7-zshDOCKER• 881auto-destruct piddeepgram keylanguagesDEV (-zsh)0not setO 82APP (-zsh)• *3-zsh• ×4-zsh• 285-zsh886-zshO 87Sat 11 Apr 20:27:07-zshT81• *8+all languagesmonitorsno monitors availableaudio devicesdisabledyouareusing local processing. all your data stays on your computer.warning: telemetry isenabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.checklatest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.348754ZINFO screenpipe: starting UIevent capture2026-04-11T20:26:05.35365622026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shinstali faitedi t n onounar -2026-04-11T20:26:05.363864ZINFO screenpipe_engine::power::manager: initial power profile: Performance Con_ac=true, battery=Some(100))INFO screenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05.3783252INFOscreenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui_recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05.378564ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-1017:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect: :mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883 frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.6831772INFOscreenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 1440x900)2026-04-11T20:26:05.683293ZINFO2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)Tcorting fot onterier, ene re: 202860-10 1-2350. 75- 1screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager:2026-04-11T20:26:07.067446ZINFOpersistent SCK stream started for display 1 (1440x900,Zfps)screenpipe_engine::event_driven_capture: startup2026-04-11T20:26:08.752985Zcapture for monitor 1: frame_id=1407, dur=537msINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe:localretention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)2026-04-11T20:26:57.331771Zscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)INFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:27:05.408691ZINFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 61 eligible frames...
|
NULL
|
|
1429
|
31
|
22
|
2026-04-11T17:27:06.659773+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928426659_m1.jpg...
|
Code
|
Claude Code — screenpipe [SSH: 100.73.206.126]
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Claude Code, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Untitled
Session history
New session
Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.
Prefer the Terminal experience?
Switch back in Settings.
Switch back in Settings.
Close banner
⌘ Esc to focus or unfocus Claude
⌘ Esc to focus or unfocus Claude
Add
Show command menu (/)
Untitled-1
Untitled-1
Edit automatically
Edit automatically...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Untitled","depth":18,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Prefer the Terminal experience?","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"","depth":21,"role_description":"text"},{"role":"AXLink","text":"Switch back in Settings.","depth":21,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Switch back in Settings.","depth":22,"role_description":"text"},{"role":"AXButton","text":"Close banner","depth":20,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"⌘ Esc to focus or unfocus Claude","depth":23,"value":"⌘ Esc to focus or unfocus Claude","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌘ Esc to focus or unfocus Claude","depth":25,"role_description":"text"},{"role":"AXButton","text":"Add","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show command menu (/)","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Untitled-1","depth":22,"help_text":"Not showing Claude your current file selection (Untitled-1). Click to attach.","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Untitled-1","depth":23,"role_description":"text"},{"role":"AXButton","text":"Edit automatically","depth":23,"help_text":"Claude will edit your selected text or the whole file. Click to change, or press Shift+Tab to cycle.","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Edit automatically","depth":24,"role_description":"text"}]...
|
3808830041804600523
|
3204108459727491334
|
click
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Claude Code, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Untitled
Session history
New session
Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.
Prefer the Terminal experience?
Switch back in Settings.
Switch back in Settings.
Close banner
⌘ Esc to focus or unfocus Claude
⌘ Esc to focus or unfocus Claude
Add
Show command menu (/)
Untitled-1
Untitled-1
Edit automatically
Edit automatically...
|
1428
|
|
1428
|
31
|
21
|
2026-04-11T17:27:04.103218+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928424103_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp-zshDOCKER• 881cloud syncauto-destruct piddeepgram keylanguagesDEV (-zsh)disabled0not setO 82APP (-zsh)• *3-zsh• x4|-zsh• 85$0(ahl-zsh886-zsh100%O 87Sat 11 Apr 20:27:04-zsh181• *8all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.3487542INFOscreenpipe: starting UIevent capture2026-04-11T20:26:05.353656Z2026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shINFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05..378325ZINFOscreenpipe_engine::calendar_speaker_id: speaker identification: started(user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui.recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05i.378564ZINFOscreenpipe_engine::hotframe_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFO2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager: :manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900,2026-04-11T20:26:07.067446ZINFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1:2026-04-11T20:26:08..752985ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:26:57.331771ZINFOscreenpipe_engine:: event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)manter 1 Cons...
|
NULL
|
2070715516133017423
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp-zshDOCKER• 881cloud syncauto-destruct piddeepgram keylanguagesDEV (-zsh)disabled0not setO 82APP (-zsh)• *3-zsh• x4|-zsh• 85$0(ahl-zsh886-zsh100%O 87Sat 11 Apr 20:27:04-zsh181• *8all languagesmonitorsno monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes: pipe scheduler started (generation 2)2026-04-11T20:26:05.3487542INFOscreenpipe: starting UIevent capture2026-04-11T20:26:05.353656Z2026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shINFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05..378325ZINFOscreenpipe_engine::calendar_speaker_id: speaker identification: started(user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui.recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05i.378564ZINFOscreenpipe_engine::hotframe_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFO2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager: :manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900,2026-04-11T20:26:07.067446ZINFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1:2026-04-11T20:26:08..752985ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:26:57.331771ZINFOscreenpipe_engine:: event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)manter 1 Cons...
|
NULL
|
|
1427
|
31
|
20
|
2026-04-11T17:27:03.297626+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928423297_m1.jpg...
|
Code
|
Claude Code — screenpipe [SSH: 100.73.206.126]
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Claude Code, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Untitled
Session history
New session
Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.
Prefer the Terminal experience?
Switch back in Settings.
Switch back in Settings.
Close banner
⌘ Esc to focus or unfocus Claude
⌘ Esc to focus or unfocus Claude
Add
Show command menu (/)
Untitled-1
Untitled-1
Edit automatically
Edit automatically...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Untitled","depth":18,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Prefer the Terminal experience?","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"","depth":21,"role_description":"text"},{"role":"AXLink","text":"Switch back in Settings.","depth":21,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Switch back in Settings.","depth":22,"role_description":"text"},{"role":"AXButton","text":"Close banner","depth":20,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"⌘ Esc to focus or unfocus Claude","depth":23,"value":"⌘ Esc to focus or unfocus Claude","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌘ Esc to focus or unfocus Claude","depth":25,"role_description":"text"},{"role":"AXButton","text":"Add","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show command menu (/)","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Untitled-1","depth":22,"help_text":"Showing Claude your current file selection (Untitled-1)","role_description":"button","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Untitled-1","depth":23,"role_description":"text"},{"role":"AXButton","text":"Edit automatically","depth":23,"help_text":"Claude will edit your selected text or the whole file. Click to change, or press Shift+Tab to cycle.","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Edit automatically","depth":24,"role_description":"text"}]...
|
3808830041804600523
|
3204108459727491334
|
click
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Claude Code, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Untitled
Session history
New session
Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.
Prefer the Terminal experience?
Switch back in Settings.
Switch back in Settings.
Close banner
⌘ Esc to focus or unfocus Claude
⌘ Esc to focus or unfocus Claude
Add
Show command menu (/)
Untitled-1
Untitled-1
Edit automatically
Edit automatically...
|
1426
|
|
1426
|
31
|
19
|
2026-04-11T17:27:01.421062+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928421421_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100%-zshDOCKER• 881cloud syncauto-destruct piddeepgram keylanguagesmonitorsDEV (-zsh)disabled0O 82not setall languagesno monitors availableAPP (-zsh)• *3-zsh• ×4-zsh• 85-zsh886-zshO 87Sat 11 Apr 20:27:01-zshT81• *8audio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487542INFOscreenpipe: starting UIevent capture2026-04-11T20:26:05.353656Z2026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shINFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05..378325ZINFOscreenpipe_engine::calendar_speaker_id: speaker identification: started(user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui.recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05i.378564ZINFOscreenpipe_engine::hotframe_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache:warmed with 8832026-04-11T20:26:05.683177ZINFOframe entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager: :manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900,2026-04-11T20:26:07.067446ZINFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1:2026-04-11T20:26:08..752985ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)manter 1 Cons...
|
NULL
|
1571998857126649726
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahlA100%-zshDOCKER• 881cloud syncauto-destruct piddeepgram keylanguagesmonitorsDEV (-zsh)disabled0O 82not setall languagesno monitors availableAPP (-zsh)• *3-zsh• ×4-zsh• 85-zsh886-zshO 87Sat 11 Apr 20:27:01-zshT81• *8audio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487542INFOscreenpipe: starting UIevent capture2026-04-11T20:26:05.353656Z2026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shINFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05..378325ZINFOscreenpipe_engine::calendar_speaker_id: speaker identification: started(user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui.recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05i.378564ZINFOscreenpipe_engine::hotframe_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache:warmed with 8832026-04-11T20:26:05.683177ZINFOframe entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager: :manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900,2026-04-11T20:26:07.067446ZINFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1:2026-04-11T20:26:08..752985ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)manter 1 Cons...
|
NULL
|
|
1425
|
31
|
18
|
2026-04-11T17:27:01.025196+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928421025_m1.jpg...
|
Code
|
Claude Code — screenpipe [SSH: 100.73.206.126]
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Claude Code, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Untitled
Session history
New session
Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.
Prefer the Terminal experience?
Switch back in Settings.
Switch back in Settings.
Close banner
⌘ Esc to focus or unfocus Claude
⌘ Esc to focus or unfocus Claude
Add
Show command menu (/)
Untitled-1
Untitled-1
Edit automatically
Edit automatically...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Explorer (⇧⌘E) - 1 unsaved file","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Search (⇧⌘F)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Source Control (⌃⇧G)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Run and Debug (⇧⌘D)","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Remote Explorer","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Extensions (⇧⌘X) - 2 require update, 1 requires restart","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":22,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":22,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Containers","depth":19,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"EXPLORER","depth":17,"role_description":"heading"},{"role":"AXStaticText","text":"EXPLORER","depth":18,"role_description":"text"},{"role":"AXButton","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Explorer Section: screenpipe [SSH: 100.73.206.126]","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"SCREENPIPE [SSH: 100.73.206.126]","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"#recycle","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"app","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"consumers","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"static","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":".env","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"cli.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"docker-compose.yml","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"main.py","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"requirements.txt","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"data","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"pipes","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"ai-habits","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"pipe.md","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"day-recap","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"meeting-summary","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"standup-update","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"time-breakdown","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"video-export","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite","depth":27,"role_description":"text"},{"role":"AXButton","text":"Outline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"OUTLINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"OUTLINE","depth":23,"role_description":"text"},{"role":"AXButton","text":"Timeline Section","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXHeading","text":"TIMELINE","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"TIMELINE","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"pipe.md, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"#!/bin/bash • Untitled-1, Editor Group 1","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXTextArea","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":28,"value":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","role_description":"editor","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"#!/bin/bash\nDB=\"/volume1/Test/screenpipe/db.sqlite\"\nDATE=${1:-$(date -d \"yesterday\" '+%Y-%m-%d')}\n\necho \"═══════════════════════════════════════\"\necho \" SCREENPIPE REPORT — $DATE\"\necho \"═══════════════════════════════════════\"\n\necho -e \"\\n📱 APP USAGE (frames + estimated time)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n⌨️ ACTIVITY TYPE PER APP\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',\n app_name,\n SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),\n SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY app_name ORDER BY COUNT(*) DESC;\"\n\necho -e \"\\n🌐 BROWSER URLS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%4d frames %s', COUNT(*), browser_url)\nFROM frames\nWHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL\nGROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🪟 WINDOW TITLES (top 20)\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL\nGROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;\"\n\necho -e \"\\n🕐 HOURLY TIMELINE\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))\nFROM frames\nWHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL\nGROUP BY strftime('%H', timestamp), app_name\nORDER BY strftime('%H', timestamp), COUNT(*) DESC;\"\n\necho -e \"\\n📋 CLIPBOARD\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"\nSELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))\nFROM ui_events\nWHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL\nORDER BY timestamp LIMIT 20;\"\n\necho -e \"\\n🔢 TOTALS\"\necho \"───────────────────────────────────────\"\nsqlite3 \"$DB\" \"SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';\"\nsqlite3 \"$DB\" \"SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';\"","depth":29,"role_description":"text"},{"role":"AXRadioButton","text":"Claude Code, Editor Group 2","depth":28,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Problems (⇧⌘M)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PROBLEMS","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Output (⇧⌘U)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"OUTPUT","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Debug Console (⇧⌘Y)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DEBUG CONSOLE","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Terminal (⌃`)","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":true},{"role":"AXStaticText","text":"TERMINAL","depth":24,"role_description":"text"},{"role":"AXRadioButton","text":"Ports","depth":22,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PORTS","depth":24,"role_description":"text"},{"role":"AXButton","text":"remote SSH: 100.73.206.126","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"SSH: 100.73.206.126","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Problems","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"No Ports Forwarded","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":17,"role_description":"text"},{"role":"AXButton","text":"Update is ready, click to restart.","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Update is ready, click to restart.","depth":17,"role_description":"text"},{"role":"AXButton","text":"Notifications","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sign In","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Sign In","depth":17,"role_description":"text"},{"role":"AXButton","text":"Screen Reader Optimized","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Info: Setting up SSH Host 100.73.206.126: Setting up SSH tunnel","depth":12,"role_description":"text"},{"role":"AXButton","text":"Untitled","depth":18,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Session history","depth":18,"help_text":"Session history","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New session","depth":18,"help_text":"New session","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Prefer the Terminal experience?","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"","depth":21,"role_description":"text"},{"role":"AXLink","text":"Switch back in Settings.","depth":21,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Switch back in Settings.","depth":22,"role_description":"text"},{"role":"AXButton","text":"Close banner","depth":20,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"⌘ Esc to focus or unfocus Claude","depth":23,"value":"⌘ Esc to focus or unfocus Claude","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌘ Esc to focus or unfocus Claude","depth":25,"role_description":"text"},{"role":"AXButton","text":"Add","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show command menu (/)","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Untitled-1","depth":22,"help_text":"Showing Claude your current file selection (Untitled-1)","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Untitled-1","depth":23,"role_description":"text"},{"role":"AXButton","text":"Edit automatically","depth":23,"help_text":"Claude will edit your selected text or the whole file. Click to change, or press Shift+Tab to cycle.","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Edit automatically","depth":24,"role_description":"text"}]...
|
3808830041804600523
|
3204108459727491334
|
click
|
accessibility
|
NULL
|
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Explorer (⇧⌘E) - 1 unsaved file
1
Search (⇧⌘F)
Source Control (⌃⇧G)
Run and Debug (⇧⌘D)
Remote Explorer
Extensions (⇧⌘X) - 2 require update, 1 requires restart
3
Claude Code
Containers
EXPLORER
EXPLORER
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
Explorer Section: screenpipe [SSH: [IP_ADDRESS]]
SCREENPIPE [SSH: [IP_ADDRESS]]
#recycle
app
consumers
static
.env
cli.py
db.py
docker-compose.yml
Dockerfile
main.py
requirements.txt
data
pipes
ai-habits
pipe.md
day-recap
meeting-summary
standup-update
time-breakdown
video-export
db.sqlite
Outline Section
OUTLINE
OUTLINE
Timeline Section
TIMELINE
TIMELINE
pipe.md, Editor Group 1
#!/bin/bash • Untitled-1, Editor Group 1
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
#!/bin/bash
DB="/volume1/Test/screenpipe/db.sqlite"
DATE=${1:-$(date -d "yesterday" '+%Y-%m-%d')}
echo "═══════════════════════════════════════"
echo " SCREENPIPE REPORT — $DATE"
echo "═══════════════════════════════════════"
echo -e "\n📱 APP USAGE (frames + estimated time)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-30s %5d frames ~%5.1f min', app_name, COUNT(*), ROUND(COUNT(*) * 4.5 / 60.0, 1))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n⌨️ ACTIVITY TYPE PER APP"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-25s clicks:%-5d keys:%-5d switches:%-4d',
app_name,
SUM(CASE WHEN event_type='click' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='key' THEN 1 ELSE 0 END),
SUM(CASE WHEN event_type='app_switch' THEN 1 ELSE 0 END))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY app_name ORDER BY COUNT(*) DESC;"
echo -e "\n🌐 BROWSER URLS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%4d frames %s', COUNT(*), browser_url)
FROM frames
WHERE date(timestamp) = '$DATE' AND browser_url IS NOT NULL
GROUP BY browser_url ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🪟 WINDOW TITLES (top 20)"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %3d %s', app_name, COUNT(*), SUBSTR(window_name,1,60))
FROM frames
WHERE date(timestamp) = '$DATE' AND window_name IS NOT NULL AND app_name IS NOT NULL
GROUP BY app_name, window_name ORDER BY COUNT(*) DESC LIMIT 20;"
echo -e "\n🕐 HOURLY TIMELINE"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%s %-20s %3d frames', strftime('%H:00', timestamp), app_name, COUNT(*))
FROM frames
WHERE date(timestamp) = '$DATE' AND app_name IS NOT NULL
GROUP BY strftime('%H', timestamp), app_name
ORDER BY strftime('%H', timestamp), COUNT(*) DESC;"
echo -e "\n📋 CLIPBOARD"
echo "───────────────────────────────────────"
sqlite3 "$DB" "
SELECT printf('%-20s %s', app_name, SUBSTR(text_content, 1, 80))
FROM ui_events
WHERE date(timestamp) = '$DATE' AND event_type = 'clipboard' AND text_content IS NOT NULL
ORDER BY timestamp LIMIT 20;"
echo -e "\n🔢 TOTALS"
echo "───────────────────────────────────────"
sqlite3 "$DB" "SELECT 'Frames: ' || COUNT(*) FROM frames WHERE date(timestamp) = '$DATE';"
sqlite3 "$DB" "SELECT 'UI Events: ' || COUNT(*) FROM ui_events WHERE date(timestamp) = '$DATE';"
Claude Code, Editor Group 2
Problems (⇧⌘M)
PROBLEMS
Output (⇧⌘U)
OUTPUT
Debug Console (⇧⌘Y)
DEBUG CONSOLE
Terminal (⌃`)
TERMINAL
Ports
PORTS
remote SSH: [IP_ADDRESS]
SSH: [IP_ADDRESS]
No Problems
0
0
No Ports Forwarded
0
Update is ready, click to restart.
Update is ready, click to restart.
Notifications
Sign In
Sign In
Screen Reader Optimized
Info: Setting up SSH Host [IP_ADDRESS]: Setting up SSH tunnel
Untitled
Session history
New session
Tired of repeating yourself? Tell Claude to remember what you’ve told it using CLAUDE.md.
Prefer the Terminal experience?
Switch back in Settings.
Switch back in Settings.
Close banner
⌘ Esc to focus or unfocus Claude
⌘ Esc to focus or unfocus Claude
Add
Show command menu (/)
Untitled-1
Untitled-1
Edit automatically
Edit automatically...
|
1424
|
|
1424
|
31
|
17
|
2026-04-11T17:26:58.861859+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775928418861_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp-zshDOCKER• 881cloud syncauto-destruct piddeepgram keylanguagesmonitorsDEV (-zsh)disabled0not setall languagesO 82APP (-zsh)• *3-zsh• ×4-zsh• 85$0(ahl-zsh886-zsh100% C2O 87Sat 11 Apr 20:26:58-zsh181• *8no monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487542INFOscreenpipe: starting UIevent capture2026-04-11T20:26:05.353656Z2026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shINFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05..378325ZINFOscreenpipe_engine::calendar_speaker_id: speaker identification: started(user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui.recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05i.378564ZINFOscreenpipe_engine::hotframe_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFO2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager: :manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900,2026-04-11T20:26:07.067446ZINFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1:2026-04-11T20:26:08..752985ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)manter 1 Cons...
|
NULL
|
-115107245007011585
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp-zshDOCKER• 881cloud syncauto-destruct piddeepgram keylanguagesmonitorsDEV (-zsh)disabled0not setall languagesO 82APP (-zsh)• *3-zsh• ×4-zsh• 85$0(ahl-zsh886-zsh100% C2O 87Sat 11 Apr 20:26:58-zsh181• *8no monitors availableaudio devicesdisabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latestchanges here: https://github.com/screenpipe/screenpipe/releases2026-04-11T20:26:05.348501ZINFO screenpipe_core::pipes:pipe scheduler started (generation 2)2026-04-11T20:26:05.3487542INFOscreenpipe: starting UIevent capture2026-04-11T20:26:05.353656Z2026-04-11T20:26:05.358540ZWARN screenpipe: pi agent install failed: bun not found - install from https://bun.shINFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))2026-04-11T20:26:05.363864ZINFOscreenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T20:26:05..378325ZINFOscreenpipe_engine::calendar_speaker_id: speaker identification: started(user_name=<not set>)2026-04-11T20:26:05.378392ZINFOscreenpipe_engine::ui.recorder: UI recording session started: 5b17c7fe-3811-4788-a81b-6b04773808f12026-04-11T20:26:05i.378564ZINFOscreenpipe_engine::hotframe_cache: hot_frame_cache: warmingfrom DB (2026-04-10 17:26:05.378561 UTC to 2026-04-11 17:26:05.378561 UTC)2026-04-11T20:26:05.379467ZINFOscreenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)2026-04-11T20:26:05.384798ZINFOscreenpipe_engine::server: Server listening on [IP_ADDRESS]:30302026-04-11T20:26:05.389960ZINFOscreenpipe_connect::mdns: mdns: advertising screenpipe on port 30302026-04-11T20:26:05.528107ZINFO2026-04-11T20:26:05.683177ZINFOscreenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 883frame entries, coverage from 2026-04-10 17:26:05.378561 UTC2026-04-11T20:26:05.683293ZINFOscreenpipe_engine::vision_manager::manager: Starting vision recordingfor monitor 1 (1440x900)screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)2026-04-11T20:26:05.683303ZINFOscreenpipe_engine::vision_manager: :manager: Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed list2026-04-11T20:26:05.683311ZINFOscreenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)2026-04-11T20:26:05.683359ZINFOscreenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)2026-04-11T20:26:06.488545ZINFOsck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900,2026-04-11T20:26:07.067446ZINFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1:2026-04-11T20:26:08..752985ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-9215443531147982391, trigger=visual_change)2026-04-11T20:26:10.383131ZINFOscreenpipe_engine::retention: retention: initialized with 14d retention2026-04-11T20:26:10.383762ZINFOscreenpipe: local retention auto-enabled (14 days)2026-04-11T20:26:38.337011ZINFO2026-04-11T20:26:55.992688ZINFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=-9215443531147982391, trigger=click)screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)2026-04-11T20:26:57.331771ZINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=5606118560581161138, trigger=click)manter 1 Cons...
|
NULL
|