Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -582,10 +582,7 @@ export function overlayLivePreviews(
const out: Record<string, SessionInfo[]> = {}

for (const node of projects) {
if (!node.path) {
continue
}

const key = node.path ?? node.id
const liveRows = byProject.get(node.id) ?? []
const base = (node.previewSessions ?? []).filter(session => !removed.has(session.id))

Expand All @@ -602,7 +599,7 @@ export function overlayLivePreviews(
}
}

out[node.path] = [...map.values()].sort((a, b) => sessionRecency(b) - sessionRecency(a)).slice(0, limit)
out[key] = [...map.values()].sort((a, b) => sessionRecency(b) - sessionRecency(a)).slice(0, limit)
}

return out
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/store/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export async function refreshProjectTree(): Promise<void> {
$projectTreeLoading.set(true)

try {
const res = await gatewayRequest<ProjectTreePayload>('projects.tree', { preview_limit: 3 })
const res = await gatewayRequest<ProjectTreePayload>('projects.tree', { preview_limit: 20 })
// The flat Sessions list shows everything; scoped ids are only used here to
// reconcile the optimistic eviction layer against what the server still lists.
const scoped = new Set(res.scoped_session_ids ?? [])
Expand Down
49 changes: 49 additions & 0 deletions tui_gateway/project_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,55 @@ def _last_active(project_sessions: list[dict]) -> float:

result: list[dict] = []

# Add "No project" bucket for unowned sessions that can't be auto-grouped
no_project_sessions: list[dict] = []
for session in unowned:
root = _session_repo_root(session, resolve)
if root and _junk(root):
# Junk git repo (home, hermes_home), don't create auto project
no_project_sessions.append(session)
continue
if root:
# Valid repo, will be handled by auto-project tier
continue
cwd = (session.get("cwd") or "").strip()
if not cwd or _junk_cwd(cwd):
# Empty cwd or junk cwd (home/hermes_home root), no project
no_project_sessions.append(session)
continue
placement = _place(cwd, (session.get("git_branch") or "").strip(), resolve, (session.get("git_repo_root") or "").strip())
if not placement:
# No valid placement, add to no project
no_project_sessions.append(session)

if no_project_sessions:
scoped_ids.extend(s["id"] for s in no_project_sessions if s.get("id"))
result.append(
_project_node(
pid="__no_project__",
label="No project",
path=None,
repos=[{
"id": "__no_project__",
"label": "No project",
"path": None,
"groups": [{
"id": "__no_project__",
"label": "",
"path": None,
"sessions": [] if not hydrate else sorted(no_project_sessions, key=_session_time, reverse=True),
"sessionCount": len(no_project_sessions),
"isHome": False,
"isKanban": False,
}],
"sessionCount": len(no_project_sessions),
}],
session_count=len(no_project_sessions),
last_active=_last_active(no_project_sessions),
preview_sessions=_previews(no_project_sessions),
)
)

# Tier 1: explicit, user-created projects (always shown, even with 0 sessions).
for project in active_projects:
psessions = by_project.get(project["id"], [])
Expand Down