@@ -16,6 +16,7 @@ import {
1616 resolveGeometry ,
1717 type GeometryInput ,
1818} from "./geometry/index.js" ;
19+ import type { Rect } from "./geometry/resolve.js" ;
1920
2021function idle80x24 ( overrides : Partial < GeometryInput > = { } ) {
2122 return resolveGeometry ( {
@@ -48,6 +49,9 @@ describe("worker wait zone", () => {
4849
4950 test ( "is the last optional row cut before prompt growth is reclaimed" , ( ) => {
5051 expect ( COLLAPSE_ORDER . indexOf ( "worker_wait" ) ) . toBe (
52+ COLLAPSE_ORDER . indexOf ( "input_required" ) - 1 ,
53+ ) ;
54+ expect ( COLLAPSE_ORDER . indexOf ( "input_required" ) ) . toBe (
5155 COLLAPSE_ORDER . indexOf ( "prompt" ) - 1 ,
5256 ) ;
5357 const layout = resolveGeometry ( {
@@ -60,6 +64,82 @@ describe("worker wait zone", () => {
6064 } ) ;
6165} ) ;
6266
67+ describe ( "input required zone" , ( ) => {
68+ test ( "requests one row directly on the prompt box without shrinking it" , ( ) => {
69+ const idle = idle80x24 ( ) ;
70+ const layout = idle80x24 ( { visibility : { inputRequired : true } } ) ;
71+ const strip = defined ( layout . regions . input_required , "input_required" ) ;
72+ const prompt = defined ( layout . regions . prompt , "prompt" ) ;
73+ expect ( strip . height ) . toBe ( 1 ) ;
74+ expect ( strip . y + strip . height ) . toBe ( prompt . y ) ;
75+ expect ( layout . heights . prompt ) . toBe ( idle . heights . prompt ) ;
76+ expect ( layout . transcriptHeight ) . toBe ( idle . transcriptHeight - 1 ) ;
77+ expect (
78+ layout . chromeHeight + layout . overlayHeight + layout . transcriptHeight ,
79+ ) . toBe ( 24 ) ;
80+ } ) ;
81+
82+ test ( "collapses before prompt growth and restores to zero after settlement" , ( ) => {
83+ const layout = resolveGeometry ( {
84+ terminal : { columns : 80 , rows : 18 } ,
85+ visibility : {
86+ workerWait : true ,
87+ inputRequired : true ,
88+ notice : true ,
89+ pending : 2 ,
90+ } ,
91+ } ) ;
92+ // worker_wait is cut first: input_required is the newest optional strip
93+ // and survives whenever one optional row can be retained.
94+ expect ( layout . heights . notice ) . toBe ( 0 ) ;
95+ expect ( layout . heights . pending ) . toBe ( 0 ) ;
96+ expect ( layout . heights . worker_wait ) . toBe ( 0 ) ;
97+ expect ( layout . heights . input_required ) . toBe ( 1 ) ;
98+ expect (
99+ layout . chromeHeight + layout . overlayHeight + layout . transcriptHeight ,
100+ ) . toBe ( 18 ) ;
101+ // A settled strip asks for nothing and adds no region.
102+ const restored = idle80x24 ( ) ;
103+ expect ( restored . heights . input_required ) . toBe ( 0 ) ;
104+ expect ( restored . regions . input_required ) . toBeUndefined ( ) ;
105+ } ) ;
106+
107+ test ( "a 24-row narrow terminal keeps both strips in bounds with no overlap" , ( ) => {
108+ const layout = resolveGeometry ( {
109+ terminal : { columns : 40 , rows : 24 } ,
110+ visibility : { workerWait : true , inputRequired : true } ,
111+ } ) ;
112+ const regions = Object . values ( layout . regions ) . filter (
113+ ( rect ) : rect is Rect => rect !== undefined ,
114+ ) ;
115+ expect ( regions . length ) . toBeGreaterThan ( 0 ) ;
116+ for ( const rect of regions ) {
117+ expect ( rect . y ) . toBeGreaterThanOrEqual ( 0 ) ;
118+ expect ( rect . y + rect . height ) . toBeLessThanOrEqual ( 24 ) ;
119+ expect ( rect . x ) . toBeGreaterThanOrEqual ( 0 ) ;
120+ expect ( rect . x + rect . width ) . toBeLessThanOrEqual ( 40 ) ;
121+ }
122+ for ( let i = 0 ; i < regions . length ; i ++ ) {
123+ const a = regions [ i ] ;
124+ if ( a === undefined ) continue ;
125+ for ( let j = i + 1 ; j < regions . length ; j ++ ) {
126+ const b = regions [ j ] ;
127+ if ( b === undefined ) continue ;
128+ const overlapY = a . y < b . y + b . height && b . y < a . y + a . height ;
129+ expect ( overlapY ) . toBe ( false ) ;
130+ }
131+ }
132+ // Vertical order: worker_wait above input_required above prompt.
133+ const worker = defined ( layout . regions . worker_wait , "worker_wait" ) ;
134+ const strip = defined ( layout . regions . input_required , "input_required" ) ;
135+ const prompt = defined ( layout . regions . prompt , "prompt" ) ;
136+ expect ( worker . y ) . toBeLessThan ( strip . y ) ;
137+ expect ( strip . y + strip . height ) . toBe ( prompt . y ) ;
138+ expect ( strip . x ) . toBe ( layout . sideMargin ) ;
139+ expect ( strip . width ) . toBe ( layout . contentWidth ) ;
140+ } ) ;
141+ } ) ;
142+
63143describe ( "resolveGeometry — 80×24 idle floor" , ( ) => {
64144 test ( "idle default chrome yields transcript ≥ 12" , ( ) => {
65145 const layout = idle80x24 ( ) ;
0 commit comments