@@ -1622,7 +1622,10 @@ fn renderSwitchList(
16221622 try out .writeAll (if (is_selected ) "> " else " " );
16231623 try writeIndexPadded (out , selectable_counter + 1 , idx_width );
16241624 try out .writeAll (" " );
1625- try writeTruncatedPadded (out , row .account , widths .email );
1625+ const indent : usize = @as (usize , row .depth ) * 2 ;
1626+ const indent_to_print : usize = @min (indent , widths .email );
1627+ try writeRepeat (out , ' ' , indent_to_print );
1628+ try writeTruncatedPadded (out , row .account , widths .email - indent_to_print );
16261629 try out .writeAll (" " );
16271630 try writeTruncatedPadded (out , row .plan , widths .plan );
16281631 try out .writeAll (" " );
@@ -1700,7 +1703,10 @@ fn renderRemoveList(
17001703 try out .writeAll (" " );
17011704 try writeIndexPadded (out , selectable_counter + 1 , idx_width );
17021705 try out .writeAll (" " );
1703- try writeTruncatedPadded (out , row .account , widths .email );
1706+ const indent : usize = @as (usize , row .depth ) * 2 ;
1707+ const indent_to_print : usize = @min (indent , widths .email );
1708+ try writeRepeat (out , ' ' , indent_to_print );
1709+ try writeTruncatedPadded (out , row .account , widths .email - indent_to_print );
17041710 try out .writeAll (" " );
17051711 try writeTruncatedPadded (out , row .plan , widths .plan );
17061712 try out .writeAll (" " );
@@ -1754,6 +1760,13 @@ fn writeTruncatedPadded(out: *std.Io.Writer, value: []const u8, width: usize) !v
17541760 try out .writeAll ("." );
17551761}
17561762
1763+ fn writeRepeat (out : * std.Io.Writer , ch : u8 , count : usize ) ! void {
1764+ var i : usize = 0 ;
1765+ while (i < count ) : (i += 1 ) {
1766+ try out .writeByte (ch );
1767+ }
1768+ }
1769+
17571770const SwitchWidths = struct {
17581771 email : usize ,
17591772 plan : usize ,
@@ -1769,6 +1782,7 @@ const SwitchRow = struct {
17691782 rate_5h : []u8 ,
17701783 rate_week : []u8 ,
17711784 last : []u8 ,
1785+ depth : u8 ,
17721786 is_active : bool ,
17731787 is_header : bool ,
17741788
@@ -1820,10 +1834,11 @@ fn buildSwitchRows(allocator: std.mem.Allocator, reg: *registry.Registry) !Switc
18201834 .rate_5h = rate_5h_str ,
18211835 .rate_week = rate_week_str ,
18221836 .last = last ,
1837+ .depth = display_row .depth ,
18231838 .is_active = display_row .is_active ,
18241839 .is_header = false ,
18251840 };
1826- widths .email = @max (widths .email , display_row .account_cell .len );
1841+ widths .email = @max (widths .email , display_row .account_cell .len + ( @as ( usize , display_row . depth ) * 2 ) );
18271842 widths .plan = @max (widths .plan , plan .len );
18281843 widths .rate_5h = @max (widths .rate_5h , rate_5h_str .len );
18291844 widths .rate_week = @max (widths .rate_week , rate_week_str .len );
@@ -1836,10 +1851,11 @@ fn buildSwitchRows(allocator: std.mem.Allocator, reg: *registry.Registry) !Switc
18361851 .rate_5h = try allocator .dupe (u8 , "" ),
18371852 .rate_week = try allocator .dupe (u8 , "" ),
18381853 .last = try allocator .dupe (u8 , "" ),
1854+ .depth = display_row .depth ,
18391855 .is_active = false ,
18401856 .is_header = true ,
18411857 };
1842- widths .email = @max (widths .email , display_row .account_cell .len );
1858+ widths .email = @max (widths .email , display_row .account_cell .len + ( @as ( usize , display_row . depth ) * 2 ) );
18431859 }
18441860 }
18451861 if (widths .email > 32 ) widths .email = 32 ;
@@ -1882,10 +1898,11 @@ fn buildSwitchRowsFromIndices(
18821898 .rate_5h = rate_5h_str ,
18831899 .rate_week = rate_week_str ,
18841900 .last = last ,
1901+ .depth = display_row .depth ,
18851902 .is_active = display_row .is_active ,
18861903 .is_header = false ,
18871904 };
1888- widths .email = @max (widths .email , display_row .account_cell .len );
1905+ widths .email = @max (widths .email , display_row .account_cell .len + ( @as ( usize , display_row . depth ) * 2 ) );
18891906 widths .plan = @max (widths .plan , plan .len );
18901907 widths .rate_5h = @max (widths .rate_5h , rate_5h_str .len );
18911908 widths .rate_week = @max (widths .rate_week , rate_week_str .len );
@@ -1898,10 +1915,11 @@ fn buildSwitchRowsFromIndices(
18981915 .rate_5h = try allocator .dupe (u8 , "" ),
18991916 .rate_week = try allocator .dupe (u8 , "" ),
19001917 .last = try allocator .dupe (u8 , "" ),
1918+ .depth = display_row .depth ,
19011919 .is_active = false ,
19021920 .is_header = true ,
19031921 };
1904- widths .email = @max (widths .email , display_row .account_cell .len );
1922+ widths .email = @max (widths .email , display_row .account_cell .len + ( @as ( usize , display_row . depth ) * 2 ) );
19051923 }
19061924 }
19071925 if (widths .email > 32 ) widths .email = 32 ;
@@ -2046,3 +2064,64 @@ test "Scenario: Given q quit input when checking switch picker helpers then both
20462064 try std .testing .expect (isQuitKey ('Q' ));
20472065 try std .testing .expect (! isQuitKey ('j' ));
20482066}
2067+
2068+ fn makeTestRegistry () registry.Registry {
2069+ return .{
2070+ .schema_version = registry .current_schema_version ,
2071+ .active_account_key = null ,
2072+ .active_account_activated_at_ms = null ,
2073+ .auto_switch = registry .defaultAutoSwitchConfig (),
2074+ .api = registry .defaultApiConfig (),
2075+ .accounts = std .ArrayList (registry .AccountRecord ).empty ,
2076+ };
2077+ }
2078+
2079+ fn appendTestAccount (
2080+ allocator : std.mem.Allocator ,
2081+ reg : * registry.Registry ,
2082+ record_key : []const u8 ,
2083+ email : []const u8 ,
2084+ alias : []const u8 ,
2085+ plan : registry.PlanType ,
2086+ ) ! void {
2087+ const sep = std .mem .lastIndexOf (u8 , record_key , "::" ) orelse return error .InvalidRecordKey ;
2088+ const chatgpt_user_id = record_key [0.. sep ];
2089+ const chatgpt_account_id = record_key [sep + 2 .. ];
2090+ try reg .accounts .append (allocator , .{
2091+ .account_key = try allocator .dupe (u8 , record_key ),
2092+ .chatgpt_account_id = try allocator .dupe (u8 , chatgpt_account_id ),
2093+ .chatgpt_user_id = try allocator .dupe (u8 , chatgpt_user_id ),
2094+ .email = try allocator .dupe (u8 , email ),
2095+ .alias = try allocator .dupe (u8 , alias ),
2096+ .account_name = null ,
2097+ .plan = plan ,
2098+ .auth_mode = .chatgpt ,
2099+ .created_at = 1 ,
2100+ .last_used_at = null ,
2101+ .last_usage = null ,
2102+ .last_usage_at = null ,
2103+ .last_local_rollout = null ,
2104+ });
2105+ }
2106+
2107+ test "Scenario: Given grouped accounts when rendering switch list then child rows keep indentation" {
2108+ const gpa = std .testing .allocator ;
2109+ var reg = makeTestRegistry ();
2110+ defer reg .deinit (gpa );
2111+
2112+ try appendTestAccount (gpa , & reg , "user-1::acc-1" , "user@example.com" , "" , .team );
2113+ reg .accounts .items [0 ].account_name = try gpa .dupe (u8 , "Als's Workspace" );
2114+ try appendTestAccount (gpa , & reg , "user-1::acc-2" , "user@example.com" , "" , .free );
2115+
2116+ var rows = try buildSwitchRows (gpa , & reg );
2117+ defer rows .deinit (gpa );
2118+
2119+ var buffer : [2048 ]u8 = undefined ;
2120+ var writer : std.Io.Writer = .fixed (& buffer );
2121+ const idx_width = @max (@as (usize , 2 ), indexWidth (rows .selectable_row_indices .len ));
2122+ try renderSwitchList (& writer , & reg , rows .items , idx_width , rows .widths , null , false );
2123+
2124+ const output = writer .buffered ();
2125+ try std .testing .expect (std .mem .indexOf (u8 , output , "01 Als's Workspace" ) != null );
2126+ try std .testing .expect (std .mem .indexOf (u8 , output , "02 free" ) != null );
2127+ }
0 commit comments