@@ -74,6 +74,7 @@ function getSnapshot(el, preStyle = null, options = {}) {
7474 if ( rec && rec . epoch === __epoch ) return rec . snapshot ;
7575 const style = preStyle || getComputedStyle ( el ) ;
7676 const snap = snapshotComputedStyleFull ( style , options ) ;
77+ stripHeightForWrappers ( el , style , snap ) ;
7778 snapshotCache . set ( el , { epoch : __epoch , snapshot : snap } ) ;
7879 return snap ;
7980}
@@ -148,3 +149,97 @@ export async function inlineAllStyles(source, clone, sessionOrCtx, opts) {
148149 }
149150 session . styleMap . set ( clone , key ) ;
150151}
152+ /**
153+ * @param {Element } el
154+ * @returns {boolean }
155+ */
156+ function isReplaced ( el ) {
157+ return el instanceof HTMLImageElement ||
158+ el instanceof HTMLCanvasElement ||
159+ el instanceof HTMLVideoElement ||
160+ el instanceof HTMLIFrameElement ||
161+ el instanceof SVGElement ||
162+ el instanceof HTMLObjectElement ||
163+ el instanceof HTMLEmbedElement ;
164+ }
165+
166+ /**
167+ * Caja “visual”: bg/border/padding u overflow ≠ visible.
168+ * @param {CSSStyleDeclaration } cs
169+ */
170+ function hasBox ( cs ) {
171+ if ( cs . backgroundImage && cs . backgroundImage !== 'none' ) return true ;
172+ if ( cs . backgroundColor && cs . backgroundColor !== 'rgba(0, 0, 0, 0)' && cs . backgroundColor !== 'transparent' ) return true ;
173+ if ( ( parseFloat ( cs . borderTopWidth ) || 0 ) > 0 ) return true ;
174+ if ( ( parseFloat ( cs . borderBottomWidth ) || 0 ) > 0 ) return true ;
175+ if ( ( parseFloat ( cs . paddingTop ) || 0 ) > 0 ) return true ;
176+ if ( ( parseFloat ( cs . paddingBottom ) || 0 ) > 0 ) return true ;
177+ const ob = cs . overflowBlock || cs . overflowY || 'visible' ;
178+ return ob !== 'visible' ;
179+ }
180+
181+ /**
182+ * Item de flex/grid (mirando display del padre, 1 getComputedStyle).
183+ * @param {Element } el
184+ */
185+ function isFlexOrGridItem ( el ) {
186+ const p = el . parentElement ;
187+ if ( ! p ) return false ;
188+ const pd = getComputedStyle ( p ) . display || '' ;
189+ return pd . includes ( 'flex' ) || pd . includes ( 'grid' ) ;
190+ }
191+
192+ /**
193+ * ¿Hay contenido en flujo? Versión rápida:
194+ * - Texto no vacío → true (no dispara layout).
195+ * - <br> inmediato → true.
196+ * - Geometry probe: scrollHeight > padding (abspos NO suma) → true.
197+ * @param {Element } el
198+ * @param {CSSStyleDeclaration } cs // ya lo tenemos en mano
199+ */
200+ function hasFlowFast ( el , cs ) {
201+ if ( el . textContent && / \S / . test ( el . textContent ) ) return true ;
202+ const f = el . firstElementChild , l = el . lastElementChild ;
203+ if ( ( f && f . tagName === 'BR' ) || ( l && l . tagName === 'BR' ) ) return true ;
204+
205+ // Probe geométrico (1 lectura de layout): evita recorrer hijos
206+ // Nota: scrollHeight no incluye hijos absolute; si sólo hay absolute → ≈ padding
207+ const sh = el . scrollHeight ;
208+ if ( sh === 0 ) return false ;
209+ const pt = parseFloat ( cs . paddingTop ) || 0 ;
210+ const pb = parseFloat ( cs . paddingBottom ) || 0 ;
211+ return sh > pt + pb ;
212+ }
213+
214+ /**
215+ * Quita height/block-size SOLO en wrappers transparentes de flujo normal
216+ * que SÍ tienen contenido en flujo. Mantiene height si no hay flujo (sólo abspos),
217+ * si el autor lo puso inline, si es replaced, posicionado/transform, tiene caja visual,
218+ * o es item flex/grid.
219+ * @param {Element } el
220+ * @param {CSSStyleDeclaration } cs
221+ * @param {Record<string,string> } snap
222+ */
223+ function stripHeightForWrappers ( el , cs , snap ) {
224+ // autor inline → respetar
225+ if ( el instanceof HTMLElement && el . style && el . style . height ) return ;
226+
227+ // ⛳️ clave para Orbit: si EL ELEMENTO es contenedor flex/grid, no tocar su height
228+ const disp = cs . display || '' ;
229+ if ( disp . includes ( 'flex' ) || disp . includes ( 'grid' ) ) return ;
230+
231+ // guardas existentes
232+ if ( isReplaced ( el ) ) return ;
233+ const pos = cs . position ;
234+ if ( pos === 'absolute' || pos === 'fixed' || pos === 'sticky' ) return ;
235+ if ( cs . transform !== 'none' ) return ;
236+ if ( hasBox ( cs ) ) return ;
237+ if ( isFlexOrGridItem ( el ) ) return ;
238+
239+ // wrapper transparente con flujo → permitir margin-collapsing
240+ if ( ! hasFlowFast ( el , cs ) ) return ;
241+
242+ delete snap . height ;
243+ delete snap [ 'block-size' ] ;
244+ }
245+
0 commit comments