refactor(usecase/daemon): delete dead move-picker, palette identity, and old preview helpers
Daemon.previewTarget and remotePaletteSessionIdentity have no caller left. The old picker-preview generation cluster (snapshotPickerPreview, pickerPreviewFromCapturedRender/FromFrame/FromLockedPane) is superseded by the Phase D remote preview push path (captureRemotePreview), which crops the pane snapshot directly and never touches VT damage or render state; its dedicated test assertions about the old path are removed, keeping the independent captureRenderState receipt-accounting coverage in the same test. capturePaneRenderStateLocked is a thin wrapper over capturePaneRenderStateLockedInto (unchanged, still the real production entry point); its eight test call sites across three files are rewritten to call capturePaneRenderStateLockedInto directly before the wrapper is deleted. ptrSessionAttachTarget is a trivial address-of helper used by six tests across four files; it moves into a new same-package test helper file rather than being inlined at every call site.
- commit
- 5e1cc302d839af347f8164a9c2ad6bfb36dfeb5c
- author
- brice · · 2026-09-23 01:55 UTC
- parent
- c45bf3b
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
10 files · +21 -134
M internal/usecase/daemon/floating_render_test.go +2 -2
| @@ -308,7 +308,7 @@ func TestComposeCapturedFloatingFrameClipsRetainedGeometryAfterFailedShrink(t *t | |||
| 308 | 308 | require.False(t, applyFloatingResizePlanForTest(d, floatingPane, requested)) | |
| 309 | 309 | require.Equal(t, retained, floatingPane.popupGeometry, "failed resize must retain the committed geometry") | |
| 310 | 310 | floatingPane.mu.Lock() | |
| 311 | - | captured := capturePaneRenderStateLocked(floatingPane, retained.Inner) | |
| 311 | + | captured := capturePaneRenderStateLockedInto(floatingPane, retained.Inner, capturedPaneRenderState{}) | |
| 312 | 312 | floatingPane.mu.Unlock() | |
| 313 | 313 | ||
| 314 | 314 | var frame renderer.Frame | |
| @@ -892,7 +892,7 @@ func TestFailedFloatingResizeKeepsCommittedRenderAndInputGeometry(t *testing.T) | |||
| 892 | 892 | require.Equal(t, oldGeometry, inputGeometry) | |
| 893 | 893 | ||
| 894 | 894 | p.mu.Lock() | |
| 895 | - | captured := capturePaneRenderStateLocked(p, oldGeometry.Inner) | |
| 895 | + | captured := capturePaneRenderStateLockedInto(p, oldGeometry.Inner, capturedPaneRenderState{}) | |
| 896 | 896 | p.mu.Unlock() | |
| 897 | 897 | base := renderer.NewFrame(newContent.Width, newContent.Height+2) | |
| 898 | 898 | frame, _ := composeCapturedFloatingFrame(floatingComposeInput{ | |
| @@ -308,7 +308,7 @@ func TestComposeCapturedFloatingFrameClipsRetainedGeometryAfterFailedShrink(t *t | |||||
| 308 | require.False(t, applyFloatingResizePlanForTest(d, floatingPane, requested)) | 308 | require.False(t, applyFloatingResizePlanForTest(d, floatingPane, requested)) | ||
| 309 | require.Equal(t, retained, floatingPane.popupGeometry, "failed resize must retain the committed geometry") | 309 | require.Equal(t, retained, floatingPane.popupGeometry, "failed resize must retain the committed geometry") | ||
| 310 | floatingPane.mu.Lock() | 310 | floatingPane.mu.Lock() | ||
| 311 | - | captured := capturePaneRenderStateLocked(floatingPane, retained.Inner) | 311 | + | captured := capturePaneRenderStateLockedInto(floatingPane, retained.Inner, capturedPaneRenderState{}) |
| 312 | floatingPane.mu.Unlock() | 312 | floatingPane.mu.Unlock() | ||
| 313 | 313 | ||||
| 314 | var frame renderer.Frame | 314 | var frame renderer.Frame | ||
| @@ -892,7 +892,7 @@ func TestFailedFloatingResizeKeepsCommittedRenderAndInputGeometry(t *testing.T) | |||||
| 892 | require.Equal(t, oldGeometry, inputGeometry) | 892 | require.Equal(t, oldGeometry, inputGeometry) | ||
| 893 | 893 | ||||
| 894 | p.mu.Lock() | 894 | p.mu.Lock() | ||
| 895 | - | captured := capturePaneRenderStateLocked(p, oldGeometry.Inner) | 895 | + | captured := capturePaneRenderStateLockedInto(p, oldGeometry.Inner, capturedPaneRenderState{}) |
| 896 | p.mu.Unlock() | 896 | p.mu.Unlock() | ||
| 897 | base := renderer.NewFrame(newContent.Width, newContent.Height+2) | 897 | base := renderer.NewFrame(newContent.Width, newContent.Height+2) | ||
| 898 | frame, _ := composeCapturedFloatingFrame(floatingComposeInput{ | 898 | frame, _ := composeCapturedFloatingFrame(floatingComposeInput{ | ||
M internal/usecase/daemon/graphics_output_test.go +2 -2
| @@ -204,9 +204,9 @@ func TestPaneGraphicsCoordinateGeometrySurvivesAttachmentResize(t *testing.T) { | |||
| 204 | 204 | p.mu.Lock() | |
| 205 | 205 | setScreenGeometry(p.screen, oldGeometry) | |
| 206 | 206 | writePaneScreenLocked(p, fixture) | |
| 207 | - | first := capturePaneRenderStateLocked(p, domain.Rect{Width: 80, Height: 24}) | |
| 207 | + | first := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 80, Height: 24}, capturedPaneRenderState{}) | |
| 208 | 208 | setScreenGeometry(p.screen, newGeometry) | |
| 209 | - | second := capturePaneRenderStateLocked(p, domain.Rect{Width: 80, Height: 24}) | |
| 209 | + | second := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 80, Height: 24}, capturedPaneRenderState{}) | |
| 210 | 210 | p.mu.Unlock() | |
| 211 | 211 | ||
| 212 | 212 | require.Equal(t, oldGeometry, first.graphicsGeometry) | |
| @@ -204,9 +204,9 @@ func TestPaneGraphicsCoordinateGeometrySurvivesAttachmentResize(t *testing.T) { | |||||
| 204 | p.mu.Lock() | 204 | p.mu.Lock() | ||
| 205 | setScreenGeometry(p.screen, oldGeometry) | 205 | setScreenGeometry(p.screen, oldGeometry) | ||
| 206 | writePaneScreenLocked(p, fixture) | 206 | writePaneScreenLocked(p, fixture) | ||
| 207 | - | first := capturePaneRenderStateLocked(p, domain.Rect{Width: 80, Height: 24}) | 207 | + | first := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 80, Height: 24}, capturedPaneRenderState{}) |
| 208 | setScreenGeometry(p.screen, newGeometry) | 208 | setScreenGeometry(p.screen, newGeometry) | ||
| 209 | - | second := capturePaneRenderStateLocked(p, domain.Rect{Width: 80, Height: 24}) | 209 | + | second := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 80, Height: 24}, capturedPaneRenderState{}) |
| 210 | p.mu.Unlock() | 210 | p.mu.Unlock() | ||
| 211 | 211 | ||||
| 212 | require.Equal(t, oldGeometry, first.graphicsGeometry) | 212 | require.Equal(t, oldGeometry, first.graphicsGeometry) | ||
M internal/usecase/daemon/move_picker.go +0 -33
| @@ -94,36 +94,3 @@ func (d *Daemon) commitMovePickerSelection(intent protocol.PickerIntent, source | |||
| 94 | 94 | return errMovePaneInvalid | |
| 95 | 95 | } | |
| 96 | 96 | } | |
| 97 | - | ||
| 98 | - | func (d *Daemon) previewTarget(target picker.Target, intent protocol.PickerIntent) (*session, *tab) { | |
| 99 | - | d.mu.Lock() | |
| 100 | - | sess := d.sessions[target.Session] | |
| 101 | - | if sess == nil { | |
| 102 | - | d.mu.Unlock() | |
| 103 | - | return nil, nil | |
| 104 | - | } | |
| 105 | - | sess.mu.Lock() | |
| 106 | - | d.mu.Unlock() | |
| 107 | - | defer sess.mu.Unlock() | |
| 108 | - | if sess.incarnation != target.Incarnation || !targetMatchesLifecycle(target, sess.name, sess.createdAt, sess.incarnation) { | |
| 109 | - | return nil, nil | |
| 110 | - | } | |
| 111 | - | if intent == protocol.PickerIntentMoveTab { | |
| 112 | - | if len(sess.tabs) == 0 { | |
| 113 | - | return nil, nil | |
| 114 | - | } | |
| 115 | - | return sess, sess.tabs[0] | |
| 116 | - | } | |
| 117 | - | if target.TabID != "" { | |
| 118 | - | for _, tb := range sess.tabs { | |
| 119 | - | if domain.TabStableID(tb.stableID) == target.TabID { | |
| 120 | - | return sess, tb | |
| 121 | - | } | |
| 122 | - | } | |
| 123 | - | return nil, nil | |
| 124 | - | } | |
| 125 | - | if target.TabIndex < 0 || target.TabIndex >= len(sess.tabs) { | |
| 126 | - | return nil, nil | |
| 127 | - | } | |
| 128 | - | return sess, sess.tabs[target.TabIndex] | |
| 129 | - | } | |
| @@ -94,36 +94,3 @@ func (d *Daemon) commitMovePickerSelection(intent protocol.PickerIntent, source | |||||
| 94 | return errMovePaneInvalid | 94 | return errMovePaneInvalid | ||
| 95 | } | 95 | } | ||
| 96 | } | 96 | } | ||
| 97 | - | ||||
| 98 | - | func (d *Daemon) previewTarget(target picker.Target, intent protocol.PickerIntent) (*session, *tab) { | |||
| 99 | - | d.mu.Lock() | |||
| 100 | - | sess := d.sessions[target.Session] | |||
| 101 | - | if sess == nil { | |||
| 102 | - | d.mu.Unlock() | |||
| 103 | - | return nil, nil | |||
| 104 | - | } | |||
| 105 | - | sess.mu.Lock() | |||
| 106 | - | d.mu.Unlock() | |||
| 107 | - | defer sess.mu.Unlock() | |||
| 108 | - | if sess.incarnation != target.Incarnation || !targetMatchesLifecycle(target, sess.name, sess.createdAt, sess.incarnation) { | |||
| 109 | - | return nil, nil | |||
| 110 | - | } | |||
| 111 | - | if intent == protocol.PickerIntentMoveTab { | |||
| 112 | - | if len(sess.tabs) == 0 { | |||
| 113 | - | return nil, nil | |||
| 114 | - | } | |||
| 115 | - | return sess, sess.tabs[0] | |||
| 116 | - | } | |||
| 117 | - | if target.TabID != "" { | |||
| 118 | - | for _, tb := range sess.tabs { | |||
| 119 | - | if domain.TabStableID(tb.stableID) == target.TabID { | |||
| 120 | - | return sess, tb | |||
| 121 | - | } | |||
| 122 | - | } | |||
| 123 | - | return nil, nil | |||
| 124 | - | } | |||
| 125 | - | if target.TabIndex < 0 || target.TabIndex >= len(sess.tabs) { | |||
| 126 | - | return nil, nil | |||
| 127 | - | } | |||
| 128 | - | return sess, sess.tabs[target.TabIndex] | |||
| 129 | - | } | |||
M internal/usecase/daemon/palette.go +0 -4
| @@ -77,10 +77,6 @@ func localPaletteSessionIdentity(lifecycle domain.SessionLifecycleID) paletteSes | |||
| 77 | 77 | return paletteSessionIdentity{kind: protocol.RouteKindLocal, endpoint: "local", lifecycle: lifecycle} | |
| 78 | 78 | } | |
| 79 | 79 | ||
| 80 | - | func remotePaletteSessionIdentity(endpoint string, lifecycle domain.SessionLifecycleID) paletteSessionIdentity { | |
| 81 | - | return paletteSessionIdentity{kind: protocol.RouteKindRemote, endpoint: endpoint, lifecycle: lifecycle} | |
| 82 | - | } | |
| 83 | - | ||
| 84 | 80 | func paletteRemoteDisplayOrigin(hostLabel string) string { | |
| 85 | 81 | origin := domain.RemoteDisplayOrigin(hostLabel) | |
| 86 | 82 | if origin == "" { | |
| @@ -77,10 +77,6 @@ func localPaletteSessionIdentity(lifecycle domain.SessionLifecycleID) paletteSes | |||||
| 77 | return paletteSessionIdentity{kind: protocol.RouteKindLocal, endpoint: "local", lifecycle: lifecycle} | 77 | return paletteSessionIdentity{kind: protocol.RouteKindLocal, endpoint: "local", lifecycle: lifecycle} | ||
| 78 | } | 78 | } | ||
| 79 | 79 | ||||
| 80 | - | func remotePaletteSessionIdentity(endpoint string, lifecycle domain.SessionLifecycleID) paletteSessionIdentity { | |||
| 81 | - | return paletteSessionIdentity{kind: protocol.RouteKindRemote, endpoint: endpoint, lifecycle: lifecycle} | |||
| 82 | - | } | |||
| 83 | - | ||||
| 84 | func paletteRemoteDisplayOrigin(hostLabel string) string { | 80 | func paletteRemoteDisplayOrigin(hostLabel string) string { | ||
| 85 | origin := domain.RemoteDisplayOrigin(hostLabel) | 81 | origin := domain.RemoteDisplayOrigin(hostLabel) | ||
| 86 | if origin == "" { | 82 | if origin == "" { | ||
M internal/usecase/daemon/picker.go +0 -72
| @@ -9,7 +9,6 @@ import ( | |||
| 9 | 9 | renderer "github.com/bnema/vev-vt" | |
| 10 | 10 | "github.com/bnema/vev/internal/domain" | |
| 11 | 11 | "github.com/bnema/vev/internal/protocol" | |
| 12 | - | "github.com/bnema/vev/internal/usecase/layout" | |
| 13 | 12 | "github.com/bnema/vev/internal/usecase/picker" | |
| 14 | 13 | "github.com/bnema/vev/internal/usecase/ui" | |
| 15 | 14 | ) | |
| @@ -762,74 +761,3 @@ func (d *Daemon) killPickerTarget(target picker.Target) error { | |||
| 762 | 761 | } | |
| 763 | 762 | return nil | |
| 764 | 763 | } | |
| 765 | - | ||
| 766 | - | func snapshotPickerPreview(tb *tab) picker.Preview { | |
| 767 | - | if tb == nil { | |
| 768 | - | return picker.Preview{} | |
| 769 | - | } | |
| 770 | - | tb.mu.Lock() | |
| 771 | - | layoutSnap := solveTabLayoutLocked(tb) | |
| 772 | - | if !layoutSnap.ok { | |
| 773 | - | p := tb.focusedPane() | |
| 774 | - | if p == nil { | |
| 775 | - | tb.mu.Unlock() | |
| 776 | - | return picker.Preview{} | |
| 777 | - | } | |
| 778 | - | p.mu.Lock() | |
| 779 | - | preview := pickerPreviewFromLockedPane(p) | |
| 780 | - | p.mu.Unlock() | |
| 781 | - | tb.mu.Unlock() | |
| 782 | - | return preview | |
| 783 | - | } | |
| 784 | - | state := capturedRenderState{layout: capturedTabLayout{ | |
| 785 | - | area: layoutSnap.area, focus: layoutSnap.focus, | |
| 786 | - | placements: append([]layout.Placement(nil), layoutSnap.placements...), | |
| 787 | - | dividers: append([]layout.Divider(nil), layoutSnap.dividers...), | |
| 788 | - | fingerprint: layoutSnap.fingerprint, valid: true, | |
| 789 | - | }} | |
| 790 | - | for _, placement := range layoutSnap.placements { | |
| 791 | - | p := tb.panes[placement.ID] | |
| 792 | - | if p == nil { | |
| 793 | - | continue | |
| 794 | - | } | |
| 795 | - | visible := placement.Content | |
| 796 | - | if placement.Collapsed { | |
| 797 | - | visible = domain.Rect{} | |
| 798 | - | } | |
| 799 | - | p.mu.Lock() | |
| 800 | - | captured := capturePaneRenderStateLocked(p, visible) | |
| 801 | - | p.mu.Unlock() | |
| 802 | - | captured.placement = placement | |
| 803 | - | captured.focused = placement.ID == layoutSnap.focus | |
| 804 | - | state.panes = append(state.panes, captured) | |
| 805 | - | } | |
| 806 | - | tb.mu.Unlock() | |
| 807 | - | return pickerPreviewFromCapturedRender(state) | |
| 808 | - | } | |
| 809 | - | ||
| 810 | - | func pickerPreviewFromCapturedRender(state capturedRenderState) picker.Preview { | |
| 811 | - | composed := composeFrame(state, composeCacheInput{}, composeCacheInput{}).frame | |
| 812 | - | if composed.Height < 2 { | |
| 813 | - | return pickerPreviewFromFrame(composed) | |
| 814 | - | } | |
| 815 | - | rows := make([][]renderer.Cell, composed.Height-2) | |
| 816 | - | for y := range rows { | |
| 817 | - | rows[y] = append([]renderer.Cell(nil), composed.Row(y+1)...) | |
| 818 | - | } | |
| 819 | - | return picker.Preview{Rows: rows, Width: composed.Width, Height: len(rows)} | |
| 820 | - | } | |
| 821 | - | ||
| 822 | - | func pickerPreviewFromLockedPane(p *pane) picker.Preview { | |
| 823 | - | return pickerPreviewFromFrame(p.screen) | |
| 824 | - | } | |
| 825 | - | ||
| 826 | - | func pickerPreviewFromFrame(frame renderer.CellSource) picker.Preview { | |
| 827 | - | rows := make([][]renderer.Cell, frame.Rows()) | |
| 828 | - | for y := range rows { | |
| 829 | - | rows[y] = make([]renderer.Cell, frame.Columns()) | |
| 830 | - | for x := range rows[y] { | |
| 831 | - | rows[y][x] = frame.Cell(x, y) | |
| 832 | - | } | |
| 833 | - | } | |
| 834 | - | return picker.Preview{Rows: rows, Width: frame.Columns(), Height: frame.Rows()} | |
| 835 | - | } | |
| @@ -9,7 +9,6 @@ import ( | |||||
| 9 | renderer "github.com/bnema/vev-vt" | 9 | renderer "github.com/bnema/vev-vt" | ||
| 10 | "github.com/bnema/vev/internal/domain" | 10 | "github.com/bnema/vev/internal/domain" | ||
| 11 | "github.com/bnema/vev/internal/protocol" | 11 | "github.com/bnema/vev/internal/protocol" | ||
| 12 | - | "github.com/bnema/vev/internal/usecase/layout" | |||
| 13 | "github.com/bnema/vev/internal/usecase/picker" | 12 | "github.com/bnema/vev/internal/usecase/picker" | ||
| 14 | "github.com/bnema/vev/internal/usecase/ui" | 13 | "github.com/bnema/vev/internal/usecase/ui" | ||
| 15 | ) | 14 | ) | ||
| @@ -762,74 +761,3 @@ func (d *Daemon) killPickerTarget(target picker.Target) error { | |||||
| 762 | } | 761 | } | ||
| 763 | return nil | 762 | return nil | ||
| 764 | } | 763 | } | ||
| 765 | - | ||||
| 766 | - | func snapshotPickerPreview(tb *tab) picker.Preview { | |||
| 767 | - | if tb == nil { | |||
| 768 | - | return picker.Preview{} | |||
| 769 | - | } | |||
| 770 | - | tb.mu.Lock() | |||
| 771 | - | layoutSnap := solveTabLayoutLocked(tb) | |||
| 772 | - | if !layoutSnap.ok { | |||
| 773 | - | p := tb.focusedPane() | |||
| 774 | - | if p == nil { | |||
| 775 | - | tb.mu.Unlock() | |||
| 776 | - | return picker.Preview{} | |||
| 777 | - | } | |||
| 778 | - | p.mu.Lock() | |||
| 779 | - | preview := pickerPreviewFromLockedPane(p) | |||
| 780 | - | p.mu.Unlock() | |||
| 781 | - | tb.mu.Unlock() | |||
| 782 | - | return preview | |||
| 783 | - | } | |||
| 784 | - | state := capturedRenderState{layout: capturedTabLayout{ | |||
| 785 | - | area: layoutSnap.area, focus: layoutSnap.focus, | |||
| 786 | - | placements: append([]layout.Placement(nil), layoutSnap.placements...), | |||
| 787 | - | dividers: append([]layout.Divider(nil), layoutSnap.dividers...), | |||
| 788 | - | fingerprint: layoutSnap.fingerprint, valid: true, | |||
| 789 | - | }} | |||
| 790 | - | for _, placement := range layoutSnap.placements { | |||
| 791 | - | p := tb.panes[placement.ID] | |||
| 792 | - | if p == nil { | |||
| 793 | - | continue | |||
| 794 | - | } | |||
| 795 | - | visible := placement.Content | |||
| 796 | - | if placement.Collapsed { | |||
| 797 | - | visible = domain.Rect{} | |||
| 798 | - | } | |||
| 799 | - | p.mu.Lock() | |||
| 800 | - | captured := capturePaneRenderStateLocked(p, visible) | |||
| 801 | - | p.mu.Unlock() | |||
| 802 | - | captured.placement = placement | |||
| 803 | - | captured.focused = placement.ID == layoutSnap.focus | |||
| 804 | - | state.panes = append(state.panes, captured) | |||
| 805 | - | } | |||
| 806 | - | tb.mu.Unlock() | |||
| 807 | - | return pickerPreviewFromCapturedRender(state) | |||
| 808 | - | } | |||
| 809 | - | ||||
| 810 | - | func pickerPreviewFromCapturedRender(state capturedRenderState) picker.Preview { | |||
| 811 | - | composed := composeFrame(state, composeCacheInput{}, composeCacheInput{}).frame | |||
| 812 | - | if composed.Height < 2 { | |||
| 813 | - | return pickerPreviewFromFrame(composed) | |||
| 814 | - | } | |||
| 815 | - | rows := make([][]renderer.Cell, composed.Height-2) | |||
| 816 | - | for y := range rows { | |||
| 817 | - | rows[y] = append([]renderer.Cell(nil), composed.Row(y+1)...) | |||
| 818 | - | } | |||
| 819 | - | return picker.Preview{Rows: rows, Width: composed.Width, Height: len(rows)} | |||
| 820 | - | } | |||
| 821 | - | ||||
| 822 | - | func pickerPreviewFromLockedPane(p *pane) picker.Preview { | |||
| 823 | - | return pickerPreviewFromFrame(p.screen) | |||
| 824 | - | } | |||
| 825 | - | ||||
| 826 | - | func pickerPreviewFromFrame(frame renderer.CellSource) picker.Preview { | |||
| 827 | - | rows := make([][]renderer.Cell, frame.Rows()) | |||
| 828 | - | for y := range rows { | |||
| 829 | - | rows[y] = make([]renderer.Cell, frame.Columns()) | |||
| 830 | - | for x := range rows[y] { | |||
| 831 | - | rows[y][x] = frame.Cell(x, y) | |||
| 832 | - | } | |||
| 833 | - | } | |||
| 834 | - | return picker.Preview{Rows: rows, Width: frame.Columns(), Height: frame.Rows()} | |||
| 835 | - | } | |||
M internal/usecase/daemon/remote_attach.go +0 -4
| @@ -224,7 +224,3 @@ func remoteTargetTabIndexLocked(sess *session, target protocol.SessionAttachTarg | |||
| 224 | 224 | } | |
| 225 | 225 | return target.ResolveTab(metadata) | |
| 226 | 226 | } | |
| 227 | - | ||
| 228 | - | func ptrSessionAttachTarget(target protocol.SessionAttachTarget) *protocol.SessionAttachTarget { | |
| 229 | - | return &target | |
| 230 | - | } | |
| @@ -224,7 +224,3 @@ func remoteTargetTabIndexLocked(sess *session, target protocol.SessionAttachTarg | |||||
| 224 | } | 224 | } | ||
| 225 | return target.ResolveTab(metadata) | 225 | return target.ResolveTab(metadata) | ||
| 226 | } | 226 | } | ||
| 227 | - | ||||
| 228 | - | func ptrSessionAttachTarget(target protocol.SessionAttachTarget) *protocol.SessionAttachTarget { | |||
| 229 | - | return &target | |||
| 230 | - | } | |||
M internal/usecase/daemon/render_capture.go +3 -7
| @@ -179,13 +179,9 @@ func writePaneScreenLocked(p *pane, data []byte) { | |||
| 179 | 179 | } | |
| 180 | 180 | } | |
| 181 | 181 | ||
| 182 | - | // capturePaneRenderStateLocked copies only the visible rectangle required by | |
| 183 | - | // composition. It never copies scrollback/history and never lets the mutable | |
| 184 | - | // VT frame's Cells or row-offset slices escape pane.mu. | |
| 185 | - | func capturePaneRenderStateLocked(p *pane, visible domain.Rect) capturedPaneRenderState { | |
| 186 | - | return capturePaneRenderStateLockedInto(p, visible, capturedPaneRenderState{}) | |
| 187 | - | } | |
| 188 | - | ||
| 182 | + | // capturePaneRenderStateLockedInto copies only the visible rectangle required | |
| 183 | + | // by composition into out. It never copies scrollback/history and never lets | |
| 184 | + | // the mutable VT frame's Cells or row-offset slices escape pane.mu. | |
| 189 | 185 | func capturePaneRenderStateLockedInto(p *pane, visible domain.Rect, out capturedPaneRenderState) capturedPaneRenderState { | |
| 190 | 186 | out.id, out.stableID, out.title, out.titleGeneration = p.id, domain.PaneStableID(p.stableID), p.displayTitleLocked(), p.title.generation | |
| 191 | 187 | // GraphicsSnapshot never allocates graphics state for a text-only screen and | |
| @@ -179,13 +179,9 @@ func writePaneScreenLocked(p *pane, data []byte) { | |||||
| 179 | } | 179 | } | ||
| 180 | } | 180 | } | ||
| 181 | 181 | ||||
| 182 | - | // capturePaneRenderStateLocked copies only the visible rectangle required by | 182 | + | // capturePaneRenderStateLockedInto copies only the visible rectangle required |
| 183 | - | // composition. It never copies scrollback/history and never lets the mutable | 183 | + | // by composition into out. It never copies scrollback/history and never lets |
| 184 | - | // VT frame's Cells or row-offset slices escape pane.mu. | 184 | + | // the mutable VT frame's Cells or row-offset slices escape pane.mu. |
| 185 | - | func capturePaneRenderStateLocked(p *pane, visible domain.Rect) capturedPaneRenderState { | |||
| 186 | - | return capturePaneRenderStateLockedInto(p, visible, capturedPaneRenderState{}) | |||
| 187 | - | } | |||
| 188 | - | ||||
| 189 | func capturePaneRenderStateLockedInto(p *pane, visible domain.Rect, out capturedPaneRenderState) capturedPaneRenderState { | 185 | func capturePaneRenderStateLockedInto(p *pane, visible domain.Rect, out capturedPaneRenderState) capturedPaneRenderState { | ||
| 190 | out.id, out.stableID, out.title, out.titleGeneration = p.id, domain.PaneStableID(p.stableID), p.displayTitleLocked(), p.title.generation | 186 | out.id, out.stableID, out.title, out.titleGeneration = p.id, domain.PaneStableID(p.stableID), p.displayTitleLocked(), p.title.generation | ||
| 191 | // GraphicsSnapshot never allocates graphics state for a text-only screen and | 187 | // GraphicsSnapshot never allocates graphics state for a text-only screen and | ||
M internal/usecase/daemon/render_damage_receipt_test.go +1 -6
| @@ -45,12 +45,7 @@ func TestPrimaryCaptureAloneRecordsDamageReceipts(t *testing.T) { | |||
| 45 | 45 | p.screen.Write([]byte("preview-safe")) | |
| 46 | 46 | p.mu.Unlock() | |
| 47 | 47 | ||
| 48 | - | preview := snapshotPickerPreview(sess.tabs[0]) | |
| 49 | - | require.NotEmpty(t, preview.Rows) | |
| 50 | - | require.Empty(t, ac.renderScratch.receipts, "picker preview must not create primary damage receipts") | |
| 51 | - | p.mu.Lock() | |
| 52 | - | require.NotEmpty(t, p.screen.Damage(), "picker preview must not acknowledge VT damage") | |
| 53 | - | p.mu.Unlock() | |
| 48 | + | require.Empty(t, ac.renderScratch.receipts, "no receipt exists before any capture runs") | |
| 54 | 49 | ||
| 55 | 50 | ac.sendMu.Lock() | |
| 56 | 51 | state, ok := captureRenderState(sess, ac, renderCaptureRequest{ | |
| @@ -45,12 +45,7 @@ func TestPrimaryCaptureAloneRecordsDamageReceipts(t *testing.T) { | |||||
| 45 | p.screen.Write([]byte("preview-safe")) | 45 | p.screen.Write([]byte("preview-safe")) | ||
| 46 | p.mu.Unlock() | 46 | p.mu.Unlock() | ||
| 47 | 47 | ||||
| 48 | - | preview := snapshotPickerPreview(sess.tabs[0]) | 48 | + | require.Empty(t, ac.renderScratch.receipts, "no receipt exists before any capture runs") |
| 49 | - | require.NotEmpty(t, preview.Rows) | |||
| 50 | - | require.Empty(t, ac.renderScratch.receipts, "picker preview must not create primary damage receipts") | |||
| 51 | - | p.mu.Lock() | |||
| 52 | - | require.NotEmpty(t, p.screen.Damage(), "picker preview must not acknowledge VT damage") | |||
| 53 | - | p.mu.Unlock() | |||
| 54 | 49 | ||||
| 55 | ac.sendMu.Lock() | 50 | ac.sendMu.Lock() | ||
| 56 | state, ok := captureRenderState(sess, ac, renderCaptureRequest{ | 51 | state, ok := captureRenderState(sess, ac, renderCaptureRequest{ | ||
M internal/usecase/daemon/render_pipeline_test.go +4 -4
| @@ -70,7 +70,7 @@ func TestCapturePaneRenderStateOwnsVisibleFrameWithoutConsumingDamage(t *testing | |||
| 70 | 70 | p.screen.Write([]byte("old")) | |
| 71 | 71 | ||
| 72 | 72 | p.mu.Lock() | |
| 73 | - | captured := capturePaneRenderStateLocked(p, domain.Rect{Width: 3, Height: 1}) | |
| 73 | + | captured := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 3, Height: 1}, capturedPaneRenderState{}) | |
| 74 | 74 | p.mu.Unlock() | |
| 75 | 75 | ||
| 76 | 76 | require.Equal(t, 3, captured.frame.Width) | |
| @@ -88,7 +88,7 @@ func TestCapturePaneRenderStateIsAlwaysNonDestructive(t *testing.T) { | |||
| 88 | 88 | p.screen.Write([]byte("preview")) | |
| 89 | 89 | ||
| 90 | 90 | p.mu.Lock() | |
| 91 | - | _ = capturePaneRenderStateLocked(p, domain.Rect{Width: 8, Height: 2}) | |
| 91 | + | _ = capturePaneRenderStateLockedInto(p, domain.Rect{Width: 8, Height: 2}, capturedPaneRenderState{}) | |
| 92 | 92 | p.mu.Unlock() | |
| 93 | 93 | ||
| 94 | 94 | require.NotEmpty(t, p.screen.Damage()) | |
| @@ -101,7 +101,7 @@ func TestCapturePaneRenderStateMalformedDamageFallsBackToFullRedraw(t *testing.T | |||
| 101 | 101 | p.screen.Damage()[0] = renderer.Damage{Kind: renderer.DamageText, X: -1, Y: 0, Width: 4, Height: 1} | |
| 102 | 102 | ||
| 103 | 103 | p.mu.Lock() | |
| 104 | - | captured := capturePaneRenderStateLocked(p, domain.Rect{Width: 8, Height: 2}) | |
| 104 | + | captured := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 8, Height: 2}, capturedPaneRenderState{}) | |
| 105 | 105 | p.mu.Unlock() | |
| 106 | 106 | ||
| 107 | 107 | require.Equal(t, []renderer.Damage{renderer.FullRedraw()}, captured.damage) | |
| @@ -113,7 +113,7 @@ func TestCapturePaneRenderStateCollapsedRetainsDamage(t *testing.T) { | |||
| 113 | 113 | p.screen.Write([]byte("hidden")) | |
| 114 | 114 | ||
| 115 | 115 | p.mu.Lock() | |
| 116 | - | captured := capturePaneRenderStateLocked(p, domain.Rect{}) | |
| 116 | + | captured := capturePaneRenderStateLockedInto(p, domain.Rect{}, capturedPaneRenderState{}) | |
| 117 | 117 | p.mu.Unlock() | |
| 118 | 118 | ||
| 119 | 119 | require.Zero(t, captured.frame.Width) | |
| @@ -70,7 +70,7 @@ func TestCapturePaneRenderStateOwnsVisibleFrameWithoutConsumingDamage(t *testing | |||||
| 70 | p.screen.Write([]byte("old")) | 70 | p.screen.Write([]byte("old")) | ||
| 71 | 71 | ||||
| 72 | p.mu.Lock() | 72 | p.mu.Lock() | ||
| 73 | - | captured := capturePaneRenderStateLocked(p, domain.Rect{Width: 3, Height: 1}) | 73 | + | captured := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 3, Height: 1}, capturedPaneRenderState{}) |
| 74 | p.mu.Unlock() | 74 | p.mu.Unlock() | ||
| 75 | 75 | ||||
| 76 | require.Equal(t, 3, captured.frame.Width) | 76 | require.Equal(t, 3, captured.frame.Width) | ||
| @@ -88,7 +88,7 @@ func TestCapturePaneRenderStateIsAlwaysNonDestructive(t *testing.T) { | |||||
| 88 | p.screen.Write([]byte("preview")) | 88 | p.screen.Write([]byte("preview")) | ||
| 89 | 89 | ||||
| 90 | p.mu.Lock() | 90 | p.mu.Lock() | ||
| 91 | - | _ = capturePaneRenderStateLocked(p, domain.Rect{Width: 8, Height: 2}) | 91 | + | _ = capturePaneRenderStateLockedInto(p, domain.Rect{Width: 8, Height: 2}, capturedPaneRenderState{}) |
| 92 | p.mu.Unlock() | 92 | p.mu.Unlock() | ||
| 93 | 93 | ||||
| 94 | require.NotEmpty(t, p.screen.Damage()) | 94 | require.NotEmpty(t, p.screen.Damage()) | ||
| @@ -101,7 +101,7 @@ func TestCapturePaneRenderStateMalformedDamageFallsBackToFullRedraw(t *testing.T | |||||
| 101 | p.screen.Damage()[0] = renderer.Damage{Kind: renderer.DamageText, X: -1, Y: 0, Width: 4, Height: 1} | 101 | p.screen.Damage()[0] = renderer.Damage{Kind: renderer.DamageText, X: -1, Y: 0, Width: 4, Height: 1} | ||
| 102 | 102 | ||||
| 103 | p.mu.Lock() | 103 | p.mu.Lock() | ||
| 104 | - | captured := capturePaneRenderStateLocked(p, domain.Rect{Width: 8, Height: 2}) | 104 | + | captured := capturePaneRenderStateLockedInto(p, domain.Rect{Width: 8, Height: 2}, capturedPaneRenderState{}) |
| 105 | p.mu.Unlock() | 105 | p.mu.Unlock() | ||
| 106 | 106 | ||||
| 107 | require.Equal(t, []renderer.Damage{renderer.FullRedraw()}, captured.damage) | 107 | require.Equal(t, []renderer.Damage{renderer.FullRedraw()}, captured.damage) | ||
| @@ -113,7 +113,7 @@ func TestCapturePaneRenderStateCollapsedRetainsDamage(t *testing.T) { | |||||
| 113 | p.screen.Write([]byte("hidden")) | 113 | p.screen.Write([]byte("hidden")) | ||
| 114 | 114 | ||||
| 115 | p.mu.Lock() | 115 | p.mu.Lock() | ||
| 116 | - | captured := capturePaneRenderStateLocked(p, domain.Rect{}) | 116 | + | captured := capturePaneRenderStateLockedInto(p, domain.Rect{}, capturedPaneRenderState{}) |
| 117 | p.mu.Unlock() | 117 | p.mu.Unlock() | ||
| 118 | 118 | ||||
| 119 | require.Zero(t, captured.frame.Width) | 119 | require.Zero(t, captured.frame.Width) | ||
A internal/usecase/daemon/session_attach_target_test_helpers_test.go +9 -0
| @@ -0,0 +1,9 @@ | |||
| 1 | + | package daemon | |
| 2 | + | ||
| 3 | + | import "github.com/bnema/vev/internal/protocol" | |
| 4 | + | ||
| 5 | + | // ptrSessionAttachTarget builds the pointer form Resume.SessionTarget and | |
| 6 | + | // similar fields require from a value literal. | |
| 7 | + | func ptrSessionAttachTarget(target protocol.SessionAttachTarget) *protocol.SessionAttachTarget { | |
| 8 | + | return &target | |
| 9 | + | } | |
| @@ -0,0 +1,9 @@ | |||||
| 1 | + | package daemon | |||
| 2 | + | ||||
| 3 | + | import "github.com/bnema/vev/internal/protocol" | |||
| 4 | + | ||||
| 5 | + | // ptrSessionAttachTarget builds the pointer form Resume.SessionTarget and | |||
| 6 | + | // similar fields require from a value literal. | |||
| 7 | + | func ptrSessionAttachTarget(target protocol.SessionAttachTarget) *protocol.SessionAttachTarget { | |||
| 8 | + | return &target | |||
| 9 | + | } | |||