snapshot_capture.go RawHistoryFolder

1package daemon
2
3import snapcodec "github.com/bnema/vev/internal/snapshotcodec"
4
5// captureSnapshotState copies history tails and recovery transcripts while holding
6// each pane lock. The returned capture contains only immutable state; encoding
7// and persistence are deliberately deferred to snapshotEncodeWorker.
8func (d *Daemon) captureSnapshotState(sess *session, generation uint64) (*snapshotCapture, bool) {
9	sess.mu.Lock()
10	capture := &snapshotCapture{
11		session:     sess,
12		generation:  generation,
13		name:        sess.name,
14		incarnation: sess.incarnation,
15		createdAt:   uint64(sess.createdAt),
16		// Snapshot format retains a deterministic first-tab default; it is not
17		// an interactive attachment selection.
18		defaultTab: 0,
19	}
20	ephemeral := sess.ephemeral
21	fallbackCwd := sess.cwd
22	tabs := append([]*tab(nil), sess.tabs...)
23	sess.mu.Unlock()
24	if ephemeral || capture.name == "" {
25		return capture, false
26	}
27
28	capture.tabs = make([]snapshotCaptureTab, 0, len(tabs))
29	for _, tb := range tabs {
30		tb.mu.Lock()
31		tabCapture := snapshotCaptureTab{
32			stableID:   tb.stableID,
33			cols:       uint16(max(tb.size.Cols, 0)),
34			rows:       uint16(max(tb.size.Rows, 0)),
35			nextPaneID: uint64(max(tb.nextPaneID, 0)),
36			tree:       tb.tree.Clone(),
37		}
38		if tb.tree != nil {
39			tabCapture.focus = tb.tree.Focus
40		}
41		panes := make([]*pane, 0, len(tb.panes))
42		for _, p := range tb.panes {
43			panes = append(panes, p)
44		}
45		tb.mu.Unlock()
46
47		tabCapture.panes = make([]snapshotCapturePane, 0, len(panes))
48		for _, p := range panes {
49			p.mu.Lock()
50			pty := p.pty
51			pid := 0
52			if pty != nil {
53				pid = pty.Pid()
54			}
55			paneCapture := snapshotCapturePane{
56				id:         p.id,
57				stableID:   p.stableID,
58				transcript: p.screen.RecoveryTranscriptSnapshot(),
59			}
60			paneCapture.sealed = p.history.SnapshotView()
61			paneCapture.tail = paneCapture.sealed.Tail()
62			p.mu.Unlock()
63			paneCapture.cwd = fallbackCwd
64			if d.proc != nil && pid > 0 {
65				if cwd, err := d.proc.Cwd(pid); err == nil && cwd != "" {
66					paneCapture.cwd = cwd
67				}
68			}
69			paneCapture.process = d.capturePaneProcess(pty, pid)
70			tabCapture.panes = append(tabCapture.panes, paneCapture)
71		}
72		capture.tabs = append(capture.tabs, tabCapture)
73	}
74	return capture, true
75}
76
77// captureSession remains the synchronous producer-facing trigger for callers
78// such as teardown and benchmarks; the actual encoding and Write stay async.
79func (d *Daemon) captureSession(sess *session) bool {
80	markSnapshotDirty(sess)
81	return d.scheduleSnapshot(sess)
82}
83
84func (d *Daemon) capturePaneProcess(pty interface{ ForegroundPgid() (int, error) }, shellPid int) *snapcodec.Process {
85	if d == nil || pty == nil || shellPid <= 0 || !d.hasProcessDetails() {
86		return nil
87	}
88	pgid, err := pty.ForegroundPgid()
89	if err != nil || pgid <= 0 || pgid == shellPid {
90		return nil
91	}
92	argv, err := d.proc.GroupArgv(pgid, shellPid)
93	if err != nil || len(argv) == 0 || argv[0] == "" {
94		return nil
95	}
96	strategy := detectProcessStrategy(argv)
97	return &snapcodec.Process{
98		Argv:     append([]string(nil), argv...),
99		Strategy: strategy,
100		Opts: snapcodec.ProcessOpts{
101			AgentSessionID: extractAgentSessionID(strategy, argv),
102		},
103	}
104}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close