@@ -138,6 +138,66 @@ def _terminal_styles(curses_module=curses) -> dict[str, int]:
138138 return styles
139139
140140
141+ def _layout_mode (width : int ) -> str :
142+ return "stacked" if width < 100 else "columns"
143+
144+
145+ def _fit_text (text : str , max_width : int ) -> str :
146+ if max_width <= 0 :
147+ return ""
148+ if len (text ) <= max_width :
149+ return text
150+ if max_width <= 3 :
151+ return "." * max_width
152+ return text [: max_width - 3 ].rstrip () + "..."
153+
154+
155+ def _short_path (path : str , max_width : int ) -> str :
156+ if len (path ) <= max_width :
157+ return path
158+ name = Path (path ).name
159+ parent = Path (path ).parent .name
160+ compact = f".../{ parent } /{ name } " if parent else f".../{ name } "
161+ return _fit_text (compact , max_width )
162+
163+
164+ def _dashboard_rows (payload : dict , width : int , selected : int , message : str ) -> list [tuple [str , str ]]:
165+ max_width = max (20 , width - 1 )
166+ inv = payload ["inventory" ]
167+ health = payload ["health" ]
168+ rows : list [tuple [str , str ]] = []
169+
170+ def row (text : str = "" , style : str = "normal" ) -> None :
171+ rows .append ((_fit_text (text , max_width ), style ))
172+
173+ row (f"GateFlow Terminal { payload ['plugin' ]['name' ]} { payload ['plugin' ]['version' ]} " , "accent" )
174+ row (payload ["mode" ], "muted" )
175+ row ("─" * max_width , "muted" )
176+ row ()
177+ row ("Actions" , "heading" )
178+ for index , action in enumerate (payload ["actions" ], start = 1 ):
179+ marker = ">" if index - 1 == selected else " "
180+ style = "selected" if index - 1 == selected else "normal"
181+ row (f"{ marker } { index } . { action ['command' ]:<11} { action ['description' ]} " , style )
182+ row ()
183+ row ("Workspace" , "heading" )
184+ row (f" path { _short_path (payload ['workspace' ], max_width - 10 )} " )
185+ row (f" plugin { payload ['plugin' ]['name' ]} { payload ['plugin' ]['version' ]} " , "accent" )
186+ row ()
187+ row ("Inventory" , "heading" )
188+ row (f" { inv ['agents' ]} agents { inv ['skills' ]} skills { inv ['commands' ]} commands" )
189+ row (f" { inv ['ip_blocks' ]} IP blocks { inv ['boards' ]} boards" )
190+ row ()
191+ row ("Health" , "heading" )
192+ row (f" doctor { health ['doctor' ]:<10} release { health ['release' ]} " )
193+ row (f" map { health ['map' ]['status' ]:<10} verilator { health ['verilator' ]} " )
194+ row (f" yosys { health ['yosys' ]:<10} sby { health ['sby' ]} " )
195+ row ()
196+ row ("─" * max_width , "muted" )
197+ row (message or "↑/↓ select Enter show command r refresh q quit" , "footer" )
198+ return rows
199+
200+
141201def render_snapshot (root : Path , plain : bool = False ) -> str :
142202 payload = build_payload (root )
143203 inv = payload ["inventory" ]
@@ -182,11 +242,12 @@ def _draw(stdscr, payload: dict, selected: int, message: str) -> None:
182242 actions = payload ["actions" ]
183243 inv = payload ["inventory" ]
184244 health = payload ["health" ]
185- left_width = min ( 34 , max ( 24 , width // 3 ) )
245+ mode = _layout_mode ( width )
186246
187247 def add (y : int , x : int , text : str , attr = 0 ) -> None :
188248 if 0 <= y < height :
189- stdscr .addnstr (y , x , text , max (0 , width - x - 1 ), attr )
249+ max_width = max (0 , width - x - 1 )
250+ stdscr .addnstr (y , x , _fit_text (text , max_width ), max_width , attr )
190251
191252 styles = _terminal_styles ()
192253 accent = styles ["accent" ]
@@ -198,22 +259,47 @@ def add(y: int, x: int, text: str, attr=0) -> None:
198259 add (0 , 20 , payload ["mode" ], muted )
199260 add (1 , 0 , "─" * (width - 1 ), muted )
200261
262+ if mode == "stacked" :
263+ style_attrs = {
264+ "accent" : accent ,
265+ "footer" : styles ["footer" ],
266+ "heading" : curses .A_BOLD ,
267+ "muted" : muted ,
268+ "normal" : 0 ,
269+ "selected" : curses .A_REVERSE ,
270+ }
271+ for y , (text , style ) in enumerate (_dashboard_rows (payload , width , selected , message )):
272+ add (y , 0 , text , style_attrs .get (style , 0 ))
273+ stdscr .refresh ()
274+ return
275+
201276 add (3 , 2 , "Actions" , curses .A_BOLD )
277+ right_x = 52 if mode == "columns" else 2
278+ action_desc_width = (right_x - 18 ) if mode == "columns" else (width - 18 )
202279 for idx , action in enumerate (actions ):
203280 attr = curses .A_REVERSE if idx == selected else 0
204- add (5 + idx , 2 , f"{ idx + 1 } . { action ['command' ]} " , attr )
205- add (5 + idx , 16 , action ["description" ], attr )
206-
207- x = left_width + 2
208- add (3 , x , "Workspace" , curses .A_BOLD )
209- add (5 , x , payload ["workspace" ])
210- add (6 , x , f"{ payload ['plugin' ]['name' ]} { payload ['plugin' ]['version' ]} " , accent )
211-
212- add (8 , x , "Inventory" , curses .A_BOLD )
213- add (10 , x , f"{ inv ['agents' ]} agents { inv ['skills' ]} skills { inv ['commands' ]} commands" )
214- add (11 , x , f"{ inv ['ip_blocks' ]} IP blocks { inv ['boards' ]} boards" )
215-
216- add (13 , x , "Health" , curses .A_BOLD )
281+ y = 5 + idx
282+ add (y , 2 , f"{ idx + 1 } . { action ['command' ]} " , attr )
283+ add (y , 16 , _fit_text (action ["description" ], action_desc_width ), attr )
284+
285+ if mode == "columns" :
286+ workspace_y = 3
287+ inventory_y = 8
288+ health_y = 13
289+ else :
290+ workspace_y = 6 + len (actions )
291+ inventory_y = workspace_y + 5
292+ health_y = inventory_y + 5
293+
294+ add (workspace_y , right_x , "Workspace" , curses .A_BOLD )
295+ add (workspace_y + 2 , right_x , payload ["workspace" ])
296+ add (workspace_y + 3 , right_x , f"{ payload ['plugin' ]['name' ]} { payload ['plugin' ]['version' ]} " , accent )
297+
298+ add (inventory_y , right_x , "Inventory" , curses .A_BOLD )
299+ add (inventory_y + 2 , right_x , f"{ inv ['agents' ]} agents { inv ['skills' ]} skills { inv ['commands' ]} commands" )
300+ add (inventory_y + 3 , right_x , f"{ inv ['ip_blocks' ]} IP blocks { inv ['boards' ]} boards" )
301+
302+ add (health_y , right_x , "Health" , curses .A_BOLD )
217303 rows = [
218304 ("doctor" , health ["doctor" ]),
219305 ("release" , health ["release" ]),
@@ -224,7 +310,7 @@ def add(y: int, x: int, text: str, attr=0) -> None:
224310 ]
225311 for offset , (name , value ) in enumerate (rows ):
226312 attr = ok if value in {"ready" , "installed" } else warn
227- add (15 + offset , x , f"{ name :<10} { value } " , attr )
313+ add (health_y + 2 + offset , right_x , f"{ name :<10} { value } " , attr )
228314
229315 footer = message or "↑/↓ select Enter show command r refresh q quit"
230316 add (height - 2 , 0 , "─" * (width - 1 ), muted )
0 commit comments