@@ -352,6 +352,64 @@ export class SheetManager {
352352 return this . insertRule ( registry , flattenedRules , className , root ) ;
353353 }
354354
355+ /**
356+ * Adjust rule indices after deletion to account for shifting
357+ */
358+ private adjustIndicesAfterDeletion (
359+ registry : RootRegistry ,
360+ sheetIndex : number ,
361+ startIdx : number ,
362+ endIdx : number ,
363+ deleteCount : number ,
364+ deletedRuleInfo : RuleInfo ,
365+ ) : void {
366+ try {
367+ // Helper function to adjust a single RuleInfo
368+ const adjustRuleInfo = ( info : RuleInfo ) : void => {
369+ if ( info === deletedRuleInfo ) return ; // Skip the deleted rule
370+ if ( info . sheetIndex !== sheetIndex ) return ; // Different sheet
371+
372+ const infoEnd = ( info . endRuleIndex as number ) ?? info . ruleIndex ;
373+
374+ if ( info . ruleIndex > endIdx ) {
375+ // Rule is after deleted range - shift left
376+ info . ruleIndex = Math . max ( 0 , info . ruleIndex - deleteCount ) ;
377+ if ( info . endRuleIndex != null ) {
378+ info . endRuleIndex = Math . max ( info . ruleIndex , infoEnd - deleteCount ) ;
379+ }
380+ } else if ( info . ruleIndex <= endIdx && infoEnd >= startIdx ) {
381+ // Rule overlaps with deleted range (should not normally happen)
382+ // Clamp endRuleIndex to avoid pointing into deleted region
383+ if ( info . endRuleIndex != null ) {
384+ const newEnd = Math . max ( info . ruleIndex , startIdx - 1 ) ;
385+ info . endRuleIndex = Math . max ( info . ruleIndex , newEnd ) ;
386+ }
387+ }
388+ } ;
389+
390+ // Adjust active rules
391+ for ( const info of registry . rules . values ( ) ) {
392+ adjustRuleInfo ( info ) ;
393+ }
394+
395+ // Adjust unused rules (their ruleInfo references the same objects as in rules map)
396+ for ( const unused of registry . unusedRules . values ( ) ) {
397+ adjustRuleInfo ( unused . ruleInfo ) ;
398+ }
399+
400+ // Adjust keyframes indices stored in cache
401+ for ( const entry of registry . keyframesCache . values ( ) ) {
402+ const ki = entry . info as KeyframesInfo ;
403+ if ( ki . sheetIndex !== sheetIndex ) continue ;
404+ if ( ki . ruleIndex > endIdx ) {
405+ ki . ruleIndex = Math . max ( 0 , ki . ruleIndex - deleteCount ) ;
406+ }
407+ }
408+ } catch ( _ ) {
409+ // Defensive: do not let index adjustments crash cleanup
410+ }
411+ }
412+
355413 /**
356414 * Delete a CSS rule from the sheet
357415 */
@@ -384,13 +442,22 @@ export class SheetManager {
384442 ) ;
385443
386444 if ( Number . isFinite ( startIdx ) && endIdx >= startIdx ) {
445+ const deleteCount = endIdx - startIdx + 1 ;
387446 for ( let idx = endIdx ; idx >= startIdx ; idx -- ) {
388447 if ( idx < 0 || idx >= styleSheet . cssRules . length ) continue ;
389448 styleSheet . deleteRule ( idx ) ;
390449 }
391- sheet . ruleCount = Math . max (
392- 0 ,
393- sheet . ruleCount - ( endIdx - startIdx + 1 ) ,
450+ sheet . ruleCount = Math . max ( 0 , sheet . ruleCount - deleteCount ) ;
451+
452+ // After deletion, all subsequent rule indices shift left by deleteCount.
453+ // We must adjust stored indices for all other RuleInfo within the same sheet.
454+ this . adjustIndicesAfterDeletion (
455+ registry ,
456+ ruleInfo . sheetIndex ,
457+ startIdx ,
458+ endIdx ,
459+ deleteCount ,
460+ ruleInfo ,
394461 ) ;
395462 }
396463 }
@@ -612,23 +679,6 @@ export class SheetManager {
612679 continue ;
613680 }
614681
615- // EXTRA SAFETY: If this class is present in the DOM, skip deletion.
616- // This protects against refCount drift or races where the style is actually in use.
617- try {
618- const canQuery = rootNode && ( rootNode as any ) . querySelector ;
619- if ( canQuery ) {
620- const el = ( rootNode as any ) . querySelector ?.( `.${ className } ` ) ;
621- if ( el ) {
622- // Class is currently used in DOM; do not delete its rules
623- // Also remove it from unused list to avoid repeated checks
624- registry . unusedRules . delete ( className ) ;
625- continue ;
626- }
627- }
628- } catch ( _ ) {
629- // If querying fails for any reason, proceed with other safeguards only
630- }
631-
632682 // Optional last-resort safety: ensure the sheet element still exists
633683 const sheetInfo = registry . sheets [ ruleInfo . sheetIndex ] ;
634684 if ( ! sheetInfo || ! sheetInfo . sheet ) {
0 commit comments