Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f4f963b
fix(tasty): cleanup method
tenphi Sep 1, 2025
d6119f5
fix(tasty): cleanup method * 2
tenphi Sep 2, 2025
2b2754f
feat(tasty): improved caching and consistent dev mode
tenphi Sep 2, 2025
218ca67
fix(ComboBox): trigger button styles
tenphi Sep 2, 2025
71f8ed5
fix(FilterPicker): correctly pass all props to FilterListBox
tenphi Sep 2, 2025
a7d3035
feat(tasty): new debug tools and improved injector for global styles
tenphi Sep 2, 2025
2f43788
feat(tasty): new debug tools and improved injector for global styles * 2
tenphi Sep 2, 2025
b6a1af1
chore: increase size limit
tenphi Sep 2, 2025
21bf21b
chore: add changelog
tenphi Sep 2, 2025
df35834
fix(tasty): cleanup
tenphi Sep 2, 2025
167f58f
fix(tasty): cleanup
tenphi Sep 2, 2025
2283ed1
fix(FilterListBox): props passing
tenphi Sep 2, 2025
d9b6643
fix(tasty): improve caching for responsive styles
tenphi Sep 2, 2025
adfb7b3
fix(tasty): optimizations
tenphi Sep 3, 2025
128002c
fix(tasty): optimizations * 2
tenphi Sep 3, 2025
450e617
fix(tasty): deleteRule bug
tenphi Sep 3, 2025
2b20463
fix(tasty): cache optimizations
tenphi Sep 3, 2025
4694299
fix(tasty): injector optimizations
tenphi Sep 3, 2025
781e245
fix(tasty): injector optimizations
tenphi Sep 3, 2025
e3c6fae
fix(tasty): injector optimizations * 2
tenphi Sep 3, 2025
230036c
fix(tasty): injection optimizations
tenphi Sep 3, 2025
5129e72
fix(tasty): optimize caching
tenphi Sep 3, 2025
cfa41d5
fix(tasty): optimizations
tenphi Sep 3, 2025
b5e9e10
fix(tasty): optimize disposal
tenphi Sep 3, 2025
73bc85a
fix(tasty): optimize config definition
tenphi Sep 3, 2025
375c2f8
chore(tasty): add global tests
tenphi Sep 3, 2025
138eff7
fix(tasty): finalize injector
tenphi Sep 3, 2025
6169751
chore: increase size limit
tenphi Sep 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/breezy-planes-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cube-dev/ui-kit": patch
---

Fix cleanup of style in the new style injector.
4 changes: 4 additions & 0 deletions src/tasty/injector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export function configure(config: Partial<StyleInjectorConfig> = {}): void {
collectMetrics: false, // default to no performance tracking
forceTextInjection: false, // auto-enable for test environments
debugMode: false, // reduce memory usage by avoiding full cssText storage
bulkCleanupBatchRatio: 0.5,
unusedStylesMinAgeMs: 10000,
...config,
};

Expand Down Expand Up @@ -189,6 +191,8 @@ export function createInjector(
collectMetrics: false, // default to no performance tracking
forceTextInjection: isTest, // auto-enable for test environments
debugMode: false, // reduce memory usage by avoiding full cssText storage
bulkCleanupBatchRatio: 0.5,
unusedStylesMinAgeMs: 10000,
...config,
};

Expand Down
35 changes: 30 additions & 5 deletions src/tasty/injector/sheet-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,29 @@ export class SheetManager {
if (registry.unusedRules.size === 0) return;

const cleanupStartTime = Date.now();
const classNamesToCleanup = Array.from(registry.unusedRules.keys());
// Build candidates list with age and sort by oldest first
const now = Date.now();
const minAge = Math.max(0, this.config.unusedStylesMinAgeMs || 0);
const candidates = Array.from(registry.unusedRules.entries())
.map(([className, info]) => ({
className,
info,
age: now - (info.markedUnusedAt || 0),
}))
// Filter out too-fresh entries to avoid racing unmount/mount cycles
.filter((entry) => entry.age >= minAge)
// Sort from oldest to newest
.sort((a, b) => b.age - a.age);

if (candidates.length === 0) return;

// Limit deletion scope per run (batch ratio)
const ratio = this.config.bulkCleanupBatchRatio ?? 0.5;
const limit = Math.max(
1,
Math.floor(candidates.length * Math.min(1, Math.max(0, ratio))),
);
const selected = candidates.slice(0, limit);
let cleanedUpCount = 0;
let totalCssSize = 0;
let totalRulesDeleted = 0;
Expand All @@ -546,10 +568,7 @@ export class SheetManager {
>();

// Calculate CSS size before deletion and group rules
for (const className of classNamesToCleanup) {
const unusedInfo = registry.unusedRules.get(className);
if (!unusedInfo) continue;

for (const { className, info: unusedInfo } of selected) {
const ruleInfo = unusedInfo.ruleInfo;
const sheetIndex = ruleInfo.sheetIndex;

Expand Down Expand Up @@ -594,6 +613,12 @@ export class SheetManager {
continue;
}

// Optional last-resort safety: ensure the sheet element still exists
const sheetInfo = registry.sheets[ruleInfo.sheetIndex];
if (!sheetInfo || !sheetInfo.sheet) {
continue;
}

this.deleteRule(registry, ruleInfo);
registry.rules.delete(className);
registry.unusedRules.delete(className);
Expand Down
11 changes: 11 additions & 0 deletions src/tasty/injector/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ export interface StyleInjectorConfig {
forceTextInjection?: boolean; // default: auto-detected (true in test environments, false otherwise)
/** When false, avoid storing full cssText for each rule block to reduce memory. */
debugMode?: boolean; // default: false (store less data)
/**
* Ratio of unused styles to delete per bulk cleanup run (0..1).
* Defaults to 0.5 (oldest half) to reduce risk of removing styles
* that may be restored shortly after being marked unused.
*/
bulkCleanupBatchRatio?: number;
/**
* Minimum age (in ms) a style must remain unused before eligible for deletion.
* Helps avoid races during rapid mount/unmount cycles. Default: 2000ms.
*/
unusedStylesMinAgeMs?: number;
}

export interface RuleInfo {
Expand Down
Loading