Repository navigation
Expand file tree
/
Copy pathos.go
More file actions
2535 lines (2433 loc) · 128 KB
/
Copy pathos.go
File metadata and controls
2535 lines (2433 loc) · 128 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Package app provides the core TUIOS application logic and window management.
package app
import (
"errors"
"fmt"
"image"
"image/color"
"os"
"regexp"
"sync"
"sync/atomic"
"time"
tea "charm.land/bubbletea/v2"
"github.com/Gaurav-Gosain/tuios/internal/config"
"github.com/Gaurav-Gosain/tuios/internal/federation"
"github.com/Gaurav-Gosain/tuios/internal/hooks"
"github.com/Gaurav-Gosain/tuios/internal/layout"
"github.com/Gaurav-Gosain/tuios/internal/listnav"
"github.com/Gaurav-Gosain/tuios/internal/overlay"
"github.com/Gaurav-Gosain/tuios/internal/pastebuf"
"github.com/Gaurav-Gosain/tuios/internal/session"
"github.com/Gaurav-Gosain/tuios/internal/sessiontree"
"github.com/Gaurav-Gosain/tuios/internal/tape"
"github.com/Gaurav-Gosain/tuios/internal/terminal"
"github.com/Gaurav-Gosain/tuios/internal/ui"
"github.com/Gaurav-Gosain/tuios/pkg/applist"
"github.com/google/uuid"
"tuios.dev/swatch"
)
// Mode represents the current interaction mode of the application.
type Mode int
const (
// WindowManagementMode allows window manipulation and navigation.
WindowManagementMode Mode = iota
// TerminalMode passes input directly to the focused terminal.
TerminalMode
)
// ResizeCorner identifies which corner is being used for window resizing.
type ResizeCorner int
const (
// TopLeft represents the top-left corner for resizing.
TopLeft ResizeCorner = iota
// TopRight represents the top-right corner for resizing.
TopRight
// BottomLeft represents the bottom-left corner for resizing.
BottomLeft
// BottomRight represents the bottom-right corner for resizing.
BottomRight
)
// BorderResizeEdge names which single edge a pane-border drag moves. Unlike a
// corner resize, a border drag moves exactly one edge: a tiled pane's shared
// divider, or one side of a floating pane.
type BorderResizeEdge int
const (
// BorderEdgeNone means no border-resize gesture is active.
BorderEdgeNone BorderResizeEdge = iota
// BorderEdgeLeft moves the window's left edge.
BorderEdgeLeft
// BorderEdgeRight moves the window's right edge.
BorderEdgeRight
// BorderEdgeTop moves the window's top edge.
BorderEdgeTop
// BorderEdgeBottom moves the window's bottom edge.
BorderEdgeBottom
)
// SnapQuarter represents window snapping positions.
type SnapQuarter int
const (
// NoSnap indicates the window is not snapped.
NoSnap SnapQuarter = iota
// SnapLeft snaps window to left half of screen.
SnapLeft
// SnapRight snaps window to right half of screen.
SnapRight
// SnapTopLeft snaps window to top-left quarter.
SnapTopLeft
// SnapTopRight snaps window to top-right quarter.
SnapTopRight
// SnapBottomLeft snaps window to bottom-left quarter.
SnapBottomLeft
// SnapBottomRight snaps window to bottom-right quarter.
SnapBottomRight
// SnapFullScreen maximizes window to full screen.
SnapFullScreen
// Unsnap restores window to its previous position.
Unsnap
)
// WindowLayout stores a window's position and size for workspace persistence
type WindowLayout struct {
WindowID string
X int
Y int
Width int
Height int
}
// OS represents the main application state and window manager.
// It manages all windows, workspaces, and user interactions.
type OS struct {
// tml holds the TML surfaces this client draws. See tml.go.
tml tmlClient
// switchSeenPending is set by a session switch and consumed once the
// message that caused it has been handled. See markSwitchSeen.
switchSeenPending bool
// ClientTitle is the title of the terminal this client runs in, set
// through herdr's client.window_title.set. Empty sets none.
ClientTitle string
// FullZoom makes every zoom take the whole pane region, as zoom_size = 100
// does, whatever the config says. A zoom of part of the screen is a camera
// over the whole layout, which resizes the hidden panes' PTYs for every
// client of the session. A renderer that draws the zoom itself (the GUI
// bridge) sets it, so a zoom there resizes the zoomed pane alone.
FullZoom bool
// NoRail keeps this model from taking any columns for a rail, and from
// reading or writing whether the session shows one. A renderer that draws
// its own sidebar (the GUI bridge) sets it. A rail its terminal clients
// show would take columns from panes the renderer draws whole, and a rail
// this model hid would stay hidden for them.
NoRail bool
// OwnPaneBorders lays the panes out with shared borders on this client
// only. Pane geometry is session state, and a terminal client with
// shared_borders off gives each guest two columns and two rows less than
// its rectangle. A renderer that draws its own frames (the GUI bridge)
// sets it to give the guest the whole rectangle, and sends
// SessionSharedBorders in its state, so the session keeps the setting it had.
OwnPaneBorders bool
// SessionSharedBorders is the session's shared borders setting while
// OwnPaneBorders is on. It starts as this client's config says.
SessionSharedBorders bool
// frameRate drives the program's frame ticker from NormalFPS and finds the
// display's rate for max_fps = "auto". See frame_rate.go.
frameRate frameRate
Dragging bool
Resizing bool
BorderResizing bool // a pane-border drag is moving one edge
BorderResizeEdge BorderResizeEdge
// BorderResizeGrab is how far the dragged edge lies from the cell the
// pointer pressed, along the axis it moves: a pane-border drag keeps the
// edge that far from the pointer, so the pane does not jump on the press.
BorderResizeGrab int
ResizeCorner ResizeCorner
PreResizeState terminal.Window
ResizeStartX int
ResizeStartY int
DragOffsetX int
DragOffsetY int
DragStartX int // Track where drag started
DragStartY int // Track where drag started
TiledX int // Original tiled position X
TiledY int // Original tiled position Y
TiledWidth int // Original tiled width
TiledHeight int // Original tiled height
DraggedWindowIndex int // Index of window being dragged
AutoScrollDir int // -1 = up, 0 = none, 1 = down (for drag auto-scroll)
AutoScrollActive bool
SelectionDragged bool // the pointer moved during the current selection gesture
ScrollbarDragging bool
ScrollbarDragWindowIndex int // -1 when not dragging
// ScrollbarGrabOffset is the rows between the pointer and the thumb's first
// row, fixed when the bar is grabbed so the thumb rides under the pointer
// instead of jumping to it on every motion event.
ScrollbarGrabOffset int
Windows []*terminal.Window
FocusedWindow int
// PrevFocusedID is the window focus came from most recently, for the
// last_pane toggle. A window ID, not an index: indices shift on close and
// reorder.
PrevFocusedID string
// lastFocusedID is the window reconcilePrevFocus last saw focused. Focus
// moves through FocusWindow, new panes, workspace switches, closes and
// the daemon, so the previous pane is read off the landed focus once a
// message, not written by every caller.
lastFocusedID string
Width int
Height int
Mode Mode
// terminalMu guards the m.Windows slice and the per-window dirty flags and
// render caches against the UI goroutine's render pass. It does NOT guard
// emulator cell data; that is Window.ioMu.
//
// LOCK ORDER (global, whole process):
// app.OS.terminalMu -> Window.ioMu -> KittyPassthrough.mu / SixelPassthrough.mu
//
// terminalMu is the outermost of the three. renderTerminal is the only
// place that holds terminalMu and a window's ioMu at once, and it takes
// them in that order. Nothing may take terminalMu while holding any
// window's ioMu.
//
// NOT REENTRANT. The holders here (MarkAllDirty,
// MarkTerminalsWithNewContent, FlushPTYBuffersAfterResize,
// renderTerminal) must not call each other. In particular do not call
// MarkAllDirty from inside a renderTerminal locked region.
//
// NEVER BLOCK WHILE HOLDING IT: it is taken on the UI goroutine every
// frame, so any block here is a visible stall.
terminalMu sync.RWMutex
LastMouseX int
LastMouseY int
// sessionView is where the last frame put the view of a session larger
// than this client, and pointerInLayout says the mouse event being
// handled was mapped through it. See pane_view.go.
sessionView sessionView
pointerInLayout bool
// pressInLayout says the button press now held was mapped to the layout
// frame, so the drag and the release that follow it are too.
pressInLayout bool
// pressInLeftover says the button press now held landed on the leftover,
// and pointerSwallowed that the mouse event being handled is one the
// leftover takes. See MapPointer.
pressInLeftover bool
pointerSwallowed bool
// lastActivity is when the person last gave input at this client, and
// wasLatest whether the daemon named the session's window_size policy
// latest on the last message. activitySession is the session that was
// for, and activitySince when this client moved into it. See
// reportActivity.
lastActivity time.Time
wasLatest bool
activitySession string
activitySince time.Time
// viewMark is the last mark renderViewMark built. See viewMarkCache.
viewMark viewMarkCache
// leftoverRight and leftoverBottom are the last strips renderLeftover
// built. See pane_leftover.go.
leftoverRight, leftoverBottom leftoverCache
// sessionClients is how many clients the daemon last said the session
// has: from the attach reply, then from each join, leave and resize. It
// is 0 from a daemon that does not say. The leftover is drawn only while
// it is not 1: a client alone in its session sees a smaller session only
// for the moment between its own resize and the daemon's answer.
sessionClients int
// pointerSeenX/Y is where the host last reported the pointer, whether or
// not that motion reached Update. LastMouseX/Y is the position of the
// last motion that did reach it, which is what the filter's "moved a
// cell" tests are written against, so the two cannot be one field. See
// NotePointerSeen.
pointerSeenX int
pointerSeenY int
ShowHelp bool
InteractionMode bool // True when actively dragging/resizing
WindowExitChan chan string // Channel to signal window closure
windowExits windowExitQueue // Overflow for exits WindowExitChan could not take
PTYDataChan chan struct{} // Signaled by PTY readers when new output arrives (buffered 1, coalescing)
StateSyncChan chan StateSyncMsg // Channel for thread-safe state sync from callbacks
ClientEventChan chan ClientEvent // Channel for thread-safe client join/leave notifications
// pluginCellsIn holds the newest push of the plugins' cells. A push
// carries every cell, so only the newest matters, and a plugin that
// changes its cells often must not crowd resizes, approvals and mail
// out of ClientEventChan. See plugin_ui.go.
pluginCellsIn chan session.PluginUIPayload
// DaemonExitChan carries the two daemon events that end this client: the
// session was destroyed, or the connection dropped. See daemon_exit.go.
DaemonExitChan chan tea.Msg
// daemonReconnect is the wait for a new daemon after the connection to
// this machine's daemon was lost. See daemon_reconnect.go.
daemonReconnect *daemonReconnect
daemonReconnectGen int
// daemonWaiting is daemonReconnect != nil, for the panes' write funcs,
// which run off the Update goroutine too: input typed while the client
// waits is not sent to a connection that is gone.
daemonWaiting atomic.Bool
// restart is the restart dialog. See daemon_restart.go.
restart daemonRestart
// RestartExec is set with ExitRestarted: what the entry point runs to
// bring the client back. RestartFailure is set with ExitRestartFailed.
RestartExec *RestartExec
RestartFailure *RestartFailure
// calm is calm mode's runtime state. See calm.go.
calm calmState
// restartLater says the person put the version notice off for this run.
restartLater bool
// afterRestart is OSOptions.AfterRestart.
afterRestart bool
Animations []*ui.Animation // Active animations
CPUHistory []float64 // CPU usage history for graph
LastCPUUpdate time.Time // Last time CPU was updated
RAMUsage float64 // Cached RAM usage percentage
LastRAMUpdate time.Time // Last time RAM was updated
cpuLast cpuTicks // previous CPU reading, the baseline for the next sample
cpuHasLast bool // cpuLast holds a reading
cpuUnavailable bool // the last CPU reading failed; the meter shows n/a
ramUnavailable bool // the last RAM reading failed; the meter shows n/a
AutoTiling bool // Automatic tiling mode enabled
MasterRatio float64 // Master window width ratio for tiling (0.1-0.9)
// TouchClient marks a session whose pointer is a finger. It is per session
// rather than a config global because one server holds several at once and
// a phone attaching must not change what the desktop beside it can hit.
TouchClient bool
// BrowserPaste says which keys make this session's browser paste. See
// OSOptions.BrowserPaste.
BrowserPaste BrowserPasteKeys
// RemoteClient marks a client process that is not on the user's machine.
// See OSOptions.RemoteClient.
RemoteClient bool
// Settings is this session's appearance and behaviour, copied from the
// process seed when the session was built and thereafter its own.
//
// A value, not a pointer, so a read is a field offset and an OS built as a
// struct literal is never nil. It is written only on the Bubble Tea
// goroutine: the settings page edits it live through setConfigFromRegistry,
// and a config reload replaces it wholesale. Background goroutines that
// need a setting are handed the value they need when they are started, not
// a pointer to this.
//
// This is the thing that stops one client's settings page reaching another
// client's frame. See config.Settings.
Settings config.Settings
// Caps is the terminal this session draws to, as it described itself when
// the connection was made. It is per session rather than a package global
// because one server process holds several sessions at once: an SSH client
// in kitty and one in xterm are two different terminals, and the graphics
// each gets must be decided from its own.
//
// Immutable after NewOS. Never nil: NewOS falls back to the process host
// when the caller has nothing better, and a hand-built OS literal in a test
// gets the same fallback through Caps() below.
Caps *HostCapabilities
// Capture is capture mode: the window pick, the region drag and the
// full-screen grab. Zero when the mode is off.
Capture captureState
// ShotPreview is the panel that opens after a capture. Zero when closed,
// and never read by tickNeedsWork: it is a static panel.
ShotPreview screenshotPreview
// captureHits are the window rectangles capture mode drew a highlight
// around this frame, so the click handler reads what was drawn instead of
// recomputing a layout. Reused between frames, never reallocated.
captureHits []captureHit
// shotImagePlaced records that the preview's kitty placement is on the
// host, so closing the panel takes it down again. shotImageSent records
// that the picture itself is resident, so a panel that only moved costs a
// placement and not another upload. shotPlacement is what was last drawn,
// so an unchanged frame emits nothing at all.
shotImagePlaced bool
shotImageSent bool
shotPlacement screenshotPlacementState
// shotCaptures counts the captures this client has taken. It is what
// names the picture the host holds, because the host holds one picture
// under the preview's image id and the only question that matters is
// whether that picture is this capture's. The file name cannot answer it:
// two captures in one second share a name.
shotCaptures int
// shotDiscarded holds the serials of captures the user dismissed before
// their file was written. The result that arrives afterwards removes its
// own file and says nothing. Nil when nothing is pending, which is almost
// always, so it costs an idle frame nothing.
shotDiscarded []int
// BSP tiling state
WorkspaceTrees map[int]*layout.BSPTree // BSP tree per workspace
PreselectionDir layout.PreselectionDir // Pending preselection direction (0 = none)
TilingScheme layout.AutoScheme // Default auto-insertion scheme
SplitTargetWindowID string // Window ID to split (set before AddWindow for splits)
// pendingSplitDir/pendingSplitTarget carry a forced-direction split (ctrl+b |
// / -) across the daemon round trip. The daemon owns window creation, so the
// new pane is not built locally; it arrives later through a state sync. These
// remember which side of which pane the split was meant for so the sync path
// can honor the direction instead of falling back to the spiral scheme.
pendingSplitDir layout.PreselectionDir
pendingSplitTarget string
WindowToBSPID map[string]int // Maps window UUID to stable BSP integer ID
BSPIDToWindowID map[int]string // Reverse of WindowToBSPID: BSP integer ID to window UUID (speed-up for GetWindowByIntID)
NextBSPWindowID int // Next BSP window ID to assign (starts at 1)
RenameKind RenameKind // What the open rename editor targets (RenameNone when closed)
RenameBuffer string // Buffer for new window name
RenameTargetID string // Window the rename in flight applies to
renameHit overlay.Rect // Where the dialog was drawn, in screen cells
renameClearHit overlay.Rect // Where its clear hint was drawn, in screen cells
PrefixActive bool // True when prefix key was pressed (tmux-style)
WorkspacePrefixActive bool // True when Ctrl+B, w was pressed (workspace sub-prefix)
MinimizePrefixActive bool // True when Ctrl+B, m was pressed (minimize sub-prefix)
TilingPrefixActive bool // True when Ctrl+B, t was pressed (tiling/window sub-prefix)
DebugPrefixActive bool // True when Ctrl+B, D was pressed (debug sub-prefix)
LastPrefixTime time.Time // Time when prefix was activated
// LockHeld is true while lock mode keeps a menu open after a repeatable
// action, until esc. See lock_mode.go.
LockHeld bool
// nest is true while nest mode gives the focused pane every key, the
// leader too. See nest_mode.go.
nest bool
HelpScrollOffset int // Scroll offset for help menu
HelpCategory int // Current help category index (for left/right navigation)
HelpSearchMode bool // True when help search is active
HelpSearchQuery string // Current search query in help menu
// helpBackToAgents says the help was opened from the agents view, which
// opens again when the help closes. See HelpClosed.
helpBackToAgents bool
CurrentWorkspace int // Current active workspace (1-9)
NumWorkspaces int // Total number of workspaces
WorkspaceFocus map[int]int // Remembers focused window per workspace
FocusHistory map[int][]string // Newest-first focus history per workspace
WorkspaceLayouts map[int][]WindowLayout // Stores custom layouts per workspace
WorkspaceHasCustom map[int]bool // Tracks if workspace has custom layout
WorkspaceMasterRatio map[int]float64 // Stores master ratio per workspace
WorkspaceStackRatio map[int]float64 // Stack ratio per workspace, the only copy (see setWorkspaceStackRatio)
WorkspaceMasterSplits map[int]layout.MasterSplits // Every other master-stack split per workspace, the only copy (see SyncMasterStackFromGeometry)
WorkspaceLayoutMode map[int]string // Layout mode chosen per workspace; no entry follows the session's
sessionLayoutMode string // The mode the session was created with, the fallback for a workspace the state did not count
ShowLogs bool // True when showing log overlay
LogMessages []LogMessage // Store log messages
LogScrollOffset int // first log row the viewer draws
LogSelected int // the log entry the viewer's cursor is on
msgView messageViewState // the message view, see message_view.go
recentMessages []messageEntry // the newest messages the dock showed, for prefix+N
notifHoverAt time.Time // when the pointer came onto the message block; zero when it is not on it
notifHoldSpent bool // the hold ended with the pointer still on the block; it starts again only after the pointer leaves
wrapCache [wrapCacheSize]wrapCacheEntry // wrapped message texts, see wrapCached
wrapCacheNext int // the slot wrapCached fills next
Notifications []Notification // Active notifications
notifHit notifHitZones // where the message block was drawn last frame
dockWorkspaceHits []dockWorkspaceHit // where the dock's workspace pills were drawn last frame
dockWorkspaceArrowHits []dockWorkspaceArrowHit // where the strip's overflow arrows were drawn last frame
dockWorkspaceScroll int // index of the first workspace pill the strip draws
dockWorkspaceScrollFor int // the workspace that offset was last pulled into view for
dockWorkspaceScrollAt int // the viewport width it was pulled into view at
dockWorkspaceDrag dockWorkspaceDragState // the click-or-drag gesture on a workspace pill
dockItemHits []dockItemHit // where the dock's minimized entries were drawn last frame
dockOverflowHit dockOverflowHit // where the entries' overflow marker was drawn last frame
dockSessionHits []dockSessionHit // where the dock's session controls were drawn last frame
notifDrawn notifDrawn // what the last frame showed of the live message
dockSessionHover DockSessionAction // which session control the pointer is on, DockSessionNone for neither
dockCustomHits []dockCustomHit // where the custom components were drawn last frame
dockPlan dockPlan // which components are on which side, in draw order
diagramsTold map[string]bool // the diagram types the person heard need the sketch plugin, this session
pluginCells map[string]session.PluginCell // what plugins show on the dock, by name, nil until one does
dockEngine *dockEngine // refresh scheduler for the components that move on their own
// RemoteCommandChan carries a verb the daemon routed to this client, for
// the hosts that cannot Send into the program directly. See dock_remote.go.
RemoteCommandChan chan RemoteCommandMsg
ClipboardContent string // Store clipboard content from tea.ClipboardMsg
ShowCacheStats bool // True when showing style cache statistics overlay
// Quit menu state. The menu replaces the old yes/no quit dialog: a small
// list overlay on the shared list-overlay grammar, registered in OverlayHits
// as kind "quit" so hover, click and click-away routing come from the same
// machinery as every other overlay. Items are built once per open (see
// OpenQuitMenu) so the rows reflect the session state at that moment.
ShowQuitMenu bool
QuitMenuSelected int
QuitMenuScroll int
QuitMenuItems []QuitMenuItem
QuitMenuOtherSessions []string // non-current sessions at open time; [0] is the kill-and-go-next target
// Close-session confirmation, the micro-dialog the dock's recessed control
// and ctrl+b X both raise. Its rows are fixed, so only the selection is
// state; what it says about the session is counted as it draws.
// SessionCloseTarget is the session it was raised on, by identity, or "" for
// the attached one: closing that quits this client, closing any other leaves
// this client where it is.
ShowSessionClose bool
SessionCloseTarget string
// SessionCloseWorkspace turns the dialog into close_workspace's: it asks
// about the panes of this workspace, not the session. 0 is the session.
SessionCloseWorkspace int
// SessionCloseWindow turns the dialog into a one-pane confirmation. It is
// how a tuios://window/<id>/close link closes: the pane a program in
// another pane named gets named back to the person holding the mouse
// before anything dies. It holds the pane's ID, not its index, because
// m.Windows can change while the dialog is open, and an index would then
// name another pane. "" is no pane.
SessionCloseWindow string
SessionCloseSelected int
// SessionCloseWorktreeToll is what the delete-worktree row would take, read
// by a round trip after the dialog opened. Nil, or Loaded false, is a toll
// that has not landed yet, and the dialog says nothing about the worktree
// until it has: a count it does not have is a count it must not invent.
SessionCloseWorktreeToll *worktreeToll
// pendingWorktreeKill makes the next SessionKilledMsg a silent one: the
// worktree-deletion note is on screen and is the one record of where the
// stashed changes went, so the kill's own "Killed session" note would
// replace it with less news. Set only when KillOtherSession will actually
// post, and read once, by the kill handler.
pendingWorktreeKill bool
// Pending resize tracking for debouncing PTY resize during mouse drag
PendingResizes map[string][2]int // windowID -> [width, height] of pending PTY resize
// pendingCopy is text a settled multi-click selection will put on the
// clipboard, and selectionSeq names the gesture it belongs to. See
// clipboard_copy.go for why the write waits.
pendingCopy string
selectionSeq uint64
// pastePending says a clipboard query is still waiting for the terminal's
// answer, and pasteSeq names the query it belongs to. See clipboard_paste.go
// for why an unanswered query has to time out.
pastePending bool
pasteSeq uint64
// pasteAskedAt is when the last clipboard query went out, zero when no
// query waits for its answer. It outlives pastePending: the timeout only
// says nothing came back yet, and a terminal that asks the user first
// (Ghostty does) answers later. See ClaimPasteReply.
pasteAskedAt time.Time
// imagePaste holds the image paste's test seams. See image_paste.go.
imagePaste imagePasteState
// clipboardAsk is the pane clipboard write waiting for the user, and
// clipboardApproved the one the user allowed and not yet sent. See
// clipboard_osc52.go.
clipboardAsks map[string]*clipboardAsk
// clipboardCopyNotes is the dock message id each pane's "copied" line
// has, so the line is updated in place.
clipboardCopyNotes map[string]string
clipboardAskSeq uint64
clipboardApproved *string
// Performance optimization caches
cachedSeparator string // Cached dock separator, styled
cachedSeparatorWidth int // Width of cached separator
cachedSeparatorChar string // Glyph the cached separator was built from
cachedSeparatorColor color.Color // Rule colour the cached separator was styled in
cachedViewContent string // Cached full View() output to skip rendering on idle ticks
renderSkipped bool // True when frame-skip fired; View() returns cached content
// keepHostPictures says this host has kitty graphics and is not the browser
// client, so a frame carries no ED 2. The render sets it and the frame writer reads it. See keepPicturesFrame.
keepHostPictures atomic.Bool
quietFrame bool // The tick composes for the message block alone; see TickerMsg
// tickStats records how the maintenance tick spent itself so the idle
// benchmark and idle e2e can prove ticks stay cheap when nothing moves.
tickStats tickStats
// emulatorReleasedAt is when the maintenance tick last dropped the panes'
// spare emulator buffers. See emulator_release.go.
emulatorReleasedAt time.Time
// emulatorReleasePending says a frame changed since the last release, so
// the panes may hold buffers again. The release is armed only while it is
// set, so an idle client releases once and then sleeps.
emulatorReleasePending bool
// tick is the maintenance tick armed for the next job that is due. See
// tick_deadline.go.
tick tickTimer
// screensaver is the idle animation. It deliberately adds nothing to the
// maintenance tick: arming is one deferred timer and the running animation
// drives its own frames. See screensaver.go.
screensaver screensaverState
// spotlight is the beam that lights one part of the screen and dims the
// rest. Client-local appearance, like ShowKeys: nothing about it crosses
// the wire and a peer sees its own screen unchanged. See spotlight.go.
spotlight spotlightState
// motion is the overlay fade-in, the scrim's shade and the working-row
// shimmer, with the clock that drives them. See motion.go.
motion motionState
// shake is the pointer gesture that toggles the beam, when the person
// turned it on. Fixed size, no timer, no tick: see shake.go.
shake shakeState
// celebration is a short confetti burst drawn over the composed frame. It
// schedules frames only while particles are alive. See celebrate.go.
celebration celebrationState
// themeFx is the running theme transition, if any. See
// theme_transition.go.
themeFx themeTransition
// lastInteractionRender is when a motion event last produced a frame.
// Motion events arrive faster than a frame can be composed, so this
// bounds how often they are allowed to redraw.
lastInteractionRender time.Time
// frameWritten is when the renderer last finished writing a frame. See
// motionBudgetFrom.
frameWritten frameWrittenClock
// pointerMotionPending is set when a motion event dropped its frame to the
// frame budget. It is the term that flushes that position: hover and the
// mouse-anchored beam have no tick of their own. False whenever the pointer
// is at rest, which is what keeps the idle tick idle. See update.go.
pointerMotionPending bool
// viewportResizing is set while terminal sizes are still arriving. A retile
// it drives places panes directly instead of easing them into position, and
// resizes them visually only, exactly as a mouse resize drag does: a resize
// is not a transition to be animated, and telling every PTY and backend
// about a size the user is still choosing is the single most expensive thing
// in the path.
viewportResizing bool
// viewportResizeGen counts terminal resizes so a settle armed by an earlier
// one can be recognised as stale and ignored.
viewportResizeGen uint64
// viewportResizeAt is when the last terminal size arrived, and
// lastPointerAt when the last mouse event did. They are what make the two
// deferrals above expire on their own: see resizeDeferralActive. A flag that
// is only ever cleared by a message arriving is a flag that stays set
// forever the one time that message does not arrive, and there is no way to
// guarantee it does: a panic recovered in Update drops the command that
// would have armed the settle, and a mouse release is lost whenever the
// pointer leaves the surface the events come from.
viewportResizeAt time.Time
lastPointerAt time.Time
// wheelAxis is the axis the scroll gesture in progress is on, so a
// trackpad's sideways drift can be told from a scroll the user meant. See
// wheel_axis.go.
wheelAxis wheelAxis
// zenHidden records the zen-mode border visibility of the last composed
// frame, so the idle tick can detect the mouse-mode timeout crossing and
// force a repaint (borders must reappear or melt away exactly once).
zenHidden bool
// pointerDown tracks whether a mouse button is held, so a gesture cannot
// outlive the button that started it even when no further event arrives.
pointerDown bool
// announceGestureHeld is whether the pointer gesture's announcement hold is
// open. See announce_batch.go: while it is, no pane is told a size, so the
// sizes a drag passes through never reach a guest as a SIGWINCH.
announceGestureHeld bool
// pendingBSPSync is set when a resize motion changed window geometry and the
// BSP tree's ratios have not been re-derived from it yet. The sync exists so
// the shared-borders separator overlay follows the drag, so it only has to
// run on frames that are actually composed; it is whole-tree work and running
// it per motion event makes the drag cost scale with window count.
pendingBSPSync bool
// bspResizeScratch holds the layout rebuilt on each resize step. It is
// reused so a mouse drag does not allocate a map per motion event.
bspResizeScratch map[int]layout.Rect
renderCanvas *frameCanvas // Reused across frames; resized on change, cleared per frame
canvasIsFrame bool // renderCanvas holds the last frame drawn, which the fast path and the saver do not leave
// separatorMemo is the last divider overlay and the inputs it was drawn
// from. See renderSeparatorOverlay.
separatorMemo separatorMemo
// layerCells is each identified layer's string parsed to cells, kept across
// frames so an unchanged layer is copied rather than parsed. See compose.go.
layerCells map[string]*cellLayer
composeGen uint64
composeScratch []composedLayer
// railFit is the rail's row budget scratch, reused by every row the rail
// draws. See railScratch.
railFit railScratch
// groundCache is each surface's resolved background and the setting and
// theme it was resolved from. paneContentRects is each pane layer's
// content rectangle on this frame, in screen cells, filled only while the
// pane or chrome background is on. See background.go.
groundCache [surfaceCount]groundMemo
paneContentRects map[string]image.Rectangle
// host is what this client's terminal said about its own colours, and
// paneReport the answer panes are given from it. See host_colors.go.
host hostColors
paneReport paneReportMemo
// paneReportSent is the answer panes were last given, held while a theme
// is only previewed. See reportedToPanes in background.go.
paneReportSent paneReport
paneReportSentSet bool
// hostPixel is the state of SGR-pixel mouse reports from this client's
// terminal. See host_pixel_mouse.go.
hostPixel hostPixelMouse
// splitMouse holds a pixel report the input decoder split at a minus
// sign while the rest of it arrives. See host_mouse_split.go.
splitMouse splitMouseReport
// conptyNUL holds a Ctrl+Space that may be the phantom ConPTY puts before
// a shifted character on Windows. See conpty_shift_nul.go.
conptyNUL heldNUL
// hostPS is whether this client's terminal takes OSC 7501 reports and
// what was last sent to it. See host_program_status.go.
hostPS hostProgramStatus
// fastPaint is the buffer the fullscreen fast path paints its frame into
// while a background it draws is on. See background_fast.go.
fastPaint fastPainter
// scrollbarRects is where each pane's scrollbar was drawn on the last frame,
// keyed by window ID. Recorded by the renderer, read by input.
scrollbarRects map[string]ScrollbarRect
// windowButtonRects is where each window's title-bar controls were drawn,
// keyed by window ID. Recorded by the renderer, read by input. Unlike the
// scrollbar's it is not cleared per frame: a window composed from its cached
// layer still has its controls on screen. See pruneWindowButtonRects.
windowButtonRects map[string][]WindowButtonRect
// windowButtonHover is the window whose controls the pointer is on, empty
// for none. The dots style reveals its symbols on it.
windowButtonHover string
// linkHover is the run of pane cells the pointer is on, and linkHoverOn
// whether there is one. Gesture-scoped runtime state, resolved from arriving
// motion and never from a frame. The render loop reads it to underline the
// run, the label reads it to name the target, and the click handler reads it
// to decide what a press on pane content means. See link_pointer.go.
linkHover PaneLink
linkHoverOn bool
// Reused per-frame scratch for graphics placement refresh (avoids per-frame allocs)
kittyPosMap map[string]*WindowPositionInfo // Reused map for kitty placement refresh
kittyPosBacking []WindowPositionInfo // Backing storage for kittyPosMap values
// kittyChrome is the rectangle of every chrome layer the last composed
// frame drew above all panes: popovers, dialogs, the rail, the dock,
// tooltips. A kitty image is painted by the host over the finished frame,
// so it is cut around these. Reused across frames.
kittyChrome []cellRect
// Scrollback lengths snapshotted before a placement refresh takes the
// passthrough lock. The refresh callbacks run under kp.mu/sp.mu and must
// not take a window's ioMu there: the PTY reader holds ioMu while
// Terminal.Write drives the kitty and sixel callbacks, which take
// kp.mu/sp.mu, so reading ioMu under kp.mu/sp.mu closes a lock cycle.
placementScrollbackLen map[string]int
// SSH mode fields
SSHSession SSHConn // SSH session reference (nil in local mode)
// configReloads is this session's subscription to the config file, and
// stopConfigWatch ends it. See config_watch.go.
configReloads <-chan tea.Msg
themeReloads <-chan ThemeFilesReloadedMsg
stopConfigWatch func()
// omarchyChanges is this session's subscription to the Omarchy theme,
// and stopOmarchyFollow ends it. See omarchy_follow.go.
omarchyChanges <-chan swatch.Palette
stopOmarchyFollow func()
// Client is where the person looking at this screen is sitting. The
// booleans around it are what it implies, kept because they are what the
// code reads and what the tests set. See ClientKind.
Client ClientKind
IsSSHMode bool // True when running over SSH
// SSHIsLoopback is true when an SSH session arrived over loopback (the
// same machine). The human is at this box, so the native clipboard is
// theirs and the local fallback is safe; a remote SSH peer is not.
SSHIsLoopback bool
// Daemon mode fields
IsDaemonSession bool // True when running as part of a persistent daemon session
DaemonClient *session.TUIClient // Client for daemon communication (nil in local mode)
SessionName string // Name of the daemon session (if attached)
// lastWorkspace and lastSession are where this client was before the
// current workspace and session, for last_workspace and last_session.
lastWorkspace int
lastSession lastSession
// shownOn is the session this client last showed on each machine, by
// machine name, for next_host and prev_host. See host_cycle.go.
shownOn map[string]string
// AttachedHost is the host DaemonClient reaches its daemon through, or ""
// for the daemon on this machine. See SwitchToHostSession.
AttachedHost string
// hostReturn is the session on this machine to come back to if the link
// to AttachedHost drops and cannot be got back, or "" when the client
// started on the host.
hostReturn string
// sessionSwitchHook, when set, replaces the connection work of
// switchSession. Tests set it to see which machine and name a surface
// asked for without a daemon on either end. Nil in production.
sessionSwitchHook func(host, name string) error
// hostReconnect is the attempt to get a dropped link back, nil when there
// is nothing to get back. See host_reconnect.go.
hostReconnect *hostReconnect
// hostReconnectGen numbers those attempts, so a dial that finishes after
// the user has moved on is discarded rather than applied.
hostReconnectGen int
// hostReconnectReason is the sentence the exit notice shows when the
// client gave up on a link and had nowhere on this machine to return to.
hostReconnectReason string
// foreignTickGen is the generation of the listing poll timer now armed, and
// foreignSessionReplan asks Update to arm a new one. See
// foreignSessionReplanCmd.
foreignTickGen uint64
foreignSessionReplan bool
// SessionDisplayName and SessionAccent are the attached session's
// daemon-owned label and accent slot, both empty when unset. They are
// labels only: SessionName stays the identity every keyed map, every
// switch and the daemon's own addressing use.
SessionDisplayName string
SessionAccent string
// SessionNote is the attached session's return note, daemon-owned like
// the label. noteAnnouncedFor is the session whose note the dock last
// showed on arrival, so the note is shown once per attach or switch and
// not again when it changes while you stay. paneNotes are the attached
// session's pane notes by window id, only the panes that have one.
SessionNote string
noteAnnouncedFor string
paneNotes map[string]string
// sessionColors is the automatic colour each session was arbitrated onto for
// the surface currently being drawn, settled once per render by
// refreshSessionColors. Derived state, never persisted and never synced: it
// is a pure function of the session names on screen, so every client
// computes the same map without saying anything to anyone.
sessionColors map[string]Accent
// sessionPoolCache remembers the theme's tell-apart hues and the key they
// were built from, so a render asking per row does not re-lift the
// palette each time.
sessionPoolCache []sessionHue
sessionPoolKey sessionPoolKey
// SessionRestored is the attached session's daemon-owned restored mark. The
// daemon clears it on attach, so it is normally false here; it is carried
// anyway so the attached row reads from the same field every other row does.
// Distinct from RestoredFromState below, which is this client's own
// bookkeeping about having applied a state snapshot.
SessionRestored bool
// SessionGlobal is the attached session's daemon-owned global mark, which
// says it is meant to hold panes from more than one machine. Every other
// session's arrives with the cached listing; this one comes down with the
// session state, the same way SessionRestored does.
SessionGlobal bool
// SessionSpace is the space the attached session is filed under, empty for
// none. It is daemon-owned and comes down with the session state, like
// SessionGlobal; every other session's arrives with the cached listing.
SessionSpace string
// The machines on this user's tailnet, offered as addresses on the Hosts
// settings page. Filled once by a goroutine, because the call behind it is
// a round trip and the row that shows them is drawn in the render path.
// See tailnetAddrCandidates.
// markStyleCache holds the selection, search and copy mode cursor styles
// so they are built once rather than per matching cell per frame. See
// marks() in render_terminal.go.
markStyleCache *markStyles
// copyFlash is the band of light crossing text that was just copied, or
// nil. It is dropped the moment it has run its course, so an idle client
// holds nothing. See copy_flash.go.
copyFlash *copyFlash
// copyFlashMore is the sweeps over the other panes of a multi copy mode
// yank. They share copyFlash's clock and mean nothing while it is nil.
copyFlashMore []copyFlash
// MultiCopy is multi copy mode while it is on, or nil. See multicopy.go.
MultiCopy *MultiCopy
// hints is hints mode while it is open, or nil. See hints.go.
hints *hintsState
// paneLabels is the display_panes labels while they are up, or nil. See
// pane_labels.go.
paneLabels *paneLabelsState
// dropZone is where the pane a drag holds would land if dropped where the
// pointer last was, or nil outside a drag: the press, the release and a
// lost release each clear it. See tiling_drop.go.
dropZone *Drop
// dropZoneShown is whether the highlight is drawn: the zone's delay has
// run out, or there is none. dropZoneGen moves on whenever the zone
// changes or is cleared, which cancels the delay armed for the last one.
dropZoneShown bool
dropZoneGen uint64
// dropZoneX/Y is the point dropZone was worked out at, in the layout
// frame, so a retile can work it out again without a motion.
dropZoneX, dropZoneY int
// dropLook is the preview's colours and its memo of washed text colours,
// kept across a drag's frames. See drop_zone_render.go.
dropLook dropLook
// fileDrag is a row of the rail's files section held by the pointer, on
// its way to a pane. See file_drop.go.
fileDrag fileDragState
// dropLayout is the map the landing rectangle of an edge drop is laid out
// into, kept so that working one out does not allocate a map each time.
dropLayout map[int]layout.Rect
// prefixRepeatUntil is when the prefix stops being armed after a
// repeatable prefix command. Zero when nothing is armed. See
// ArmPrefixRepeat.
prefixRepeatUntil time.Time
tailnetMu sync.Mutex
tailnetAskedAt time.Time
tailnetCandidates []string
// The [tailscale] table as last read, and the version of the config file
// it was read from. See tailnetOptionsCached.
tailnetOpt federation.TailnetOptions
tailnetOptEnabled bool
tailnetOptKey tailnetFileKey
tailnetOptValid bool
// SessionWorktree is the attached session's daemon-owned worktree record,
// nil when its directory is not a linked git worktree. Every other
// session's arrives with the cached listing; this one comes down with the
// session state, so the rail reads the attached row from live state exactly
// as it reads the rest of that row.
SessionWorktree *session.WorktreeInfo
// WorkspaceNames maps a workspace number to its daemon-owned label. An
// unnamed workspace shows its number, so an absent entry is not a missing
// label but the normal case.
WorkspaceNames map[int]string
// pendingWorkspaceOrder is the order a drag of the dock's pills dropped
// them in, shown until the daemon's renumber arrives, so the strip does
// not snap back for the length of the round trip. See workspace_order.go.
pendingWorkspaceOrder []int
// workspaceTabKeys is the session's herdr tab keys as this client last
// adopted them, which is what a renumber is read against. See
// adoptWorkspaceRenumbering. workspaceTabKeysKnown is false until a state
// has been adopted.
workspaceTabKeys map[int]int
workspaceTabKeysKnown bool
// workspaceMoveInFlight is set while a renumber this client asked for has
// not come back in a state. See RenumberWorkspaces.
workspaceMoveInFlight bool
RestoredFromState bool // True after RestoreFromState, cleared after first resize
// DaemonStateVersion is the daemon state version this client last saw. It is
// echoed back on every state sync so the daemon can tell a snapshot built
// from its current state apart from one built before a mutation of its own.
DaemonStateVersion int
streamTap StreamTap // See SetStreamTap; nil for every client but tuios gui-bridge
// ReloadFilter, when set, sees a config read from disk before it is put
// in force. tuios gui-bridge keeps its own chrome off with it: its
// renderer draws the dock and the rail itself, and a reload would bring
// them back above the panes for every client of the session.
ReloadFilter func(*config.UserConfig) *config.UserConfig
// SnapshotScrollback is how many history rows a pane's snapshot asks
// the daemon for; 0 is the daemon's default, 1000. tuios gui-bridge sets
// it to its renderer's own history, so a reopen shows as much as the
// renderer keeps.
SnapshotScrollback int
SubscribedPTYs map[string]bool // Tracks which PTY IDs are currently subscribed (for visibility optimization)
// RestoredStreamSeq is the stream position each pane's snapshot was taken
// at, from the restore that precedes the subscribe on the attach path.
RestoredStreamSeq map[string]int64
// ExitReason records why the program stopped, for the caller to report and
// to pick an exit status. Empty means the user quit or detached normally.
// It is written only on the Bubble Tea goroutine, in Update.
ExitReason ExitReason
// detachFired makes the after-detach hook fire once per client, however
// that client left. There are two arrivals now, a deliberate detach and a
// connection that ended, and over SSH one client can reach both: leader-d
// fires it and the middleware's Cleanup runs afterwards anyway. Atomic
// because Cleanup runs after the program has exited, off the UI goroutine.
detachFired atomic.Bool
// focusReportedID is the pane this client last treated as focused for
// DECSET 1004 focus reports. See reportFocusChange.
focusReportedID string
// QuitRequested records that the user deliberately quit this client, which
// in a daemon session also kills the session. The daemon then announces the
// session ending and the connection dropping, and both announcements can
// arrive before the program finishes quitting. Without this flag those
// announcements are indistinguishable from a session killed from elsewhere,
// and a deliberate quit reports an error. Written only on the Bubble Tea
// goroutine, like ExitReason.
QuitRequested bool
// stripSnapGen counts the wheel steps that moved the strip, so a snap
// timer fires only for the last one, and stripSnapDir is the way the last
// one went. See strip_snap.go.
stripSnapGen uint64
stripSnapDir int
// clickReveal is a press that focused a pane, held until the button comes
// up so the release can tell a click from the start of a drag. See
// ArmClickReveal.
clickReveal struct {
armed bool
x, y int
}
// SidebarWidthPref is the expanded rail width this session asks for, or 0
// to take the configured default. It is synced with the session's other
// clients: the rail is chrome, and the panes' box is settled across every
// client, so two clients disagreeing about the rail costs one of them a
// blank band rather than moving anybody's panes.
SidebarWidthPref int
// SharedBorders and PaneGap are the layout arithmetic inside the panes' box:
// whether tiled panes merge their border boxes into single dividers, and how
// much empty ground the tiler keeps between neighbours. They are model state
// rather than reads of the config globals because they are inputs to pane
// geometry, and every input to pane geometry has to be identical across a
// session's attached clients, because a PTY has exactly one size. Two clients whose
// config files (or live settings) disagreed here computed different
// rectangles for the same panes and dragged the shared PTYs back and forth
// between the two answers on every push.
//
// Seeded from this client's config, then settled across the session by state
// sync (session.PaneGeometryState). Purely visual appearance (theme,
// colours, border style, title position, dimming) deliberately stays
// per-client and configurable; these two are synced only because they move
// rectangles.
SharedBorders bool
PaneGap int
// ScrollColumnWidth is a column's width in the scrolling layout, as a
// percent of the screen, before anything resizes it. Session state for the
// same reason the two above are: it is what every column's cell width is
// computed from, so two clients holding different values would hand the same
// pane two different widths.
ScrollColumnWidth int
// lastConfigSharedBorders and lastConfigPaneGap are the config globals as
// this OS last saw them, so adoptConfigPaneGeometry can tell a config-side
// change (adopt it) from a session-side one (leave the config alone). See
// adoptConfigPaneGeometry.
lastConfigSharedBorders bool
lastConfigPaneGap int
lastConfigScrollWidth int
// WorkspaceMasterLayout is each workspace's master-stack shape, for the
// workspaces somebody changed at run time. See master_layout.go.
WorkspaceMasterLayout map[int]session.MasterLayoutState
// masterSeeded marks the workspaces this client has offered its configured
// shape to the session for. See seedMasterLayout.
masterSeeded map[int]bool
// sidebarSeeded records that this client has offered its configured
// sidebar visibility to the session it is attached to. See seedSidebar.
sidebarSeeded bool
// sidebarSession is the session's sidebar visibility as this client last
// adopted it, or empty when the session has none or the daemon does not
// keep it. See adoptSidebarVisibility.
sidebarSession string
// chromeSeen is the chrome this client drew around the panes, and the
// size it drew it on, as of the end of the last Update. See settleChrome.
chromeSeen struct {
reserve session.LayoutReserve
width, height int
}
// layoutGenApplied is the layout generation of the last session resize
// this client laid its panes out for, and layoutGenPushed the one its