Skip to content

Add stylesheet hotloading for DEBUG builds - #79

Merged
Xenthio merged 5 commits into
mainfrom
copilot/add-hotloading-for-razor-stylesheets
Jan 11, 2026
Merged

Xenthio merged 5 commits into
mainfrom
copilot/add-hotloading-for-razor-stylesheets

Conversation

Copilot AI commented Jan 11, 2026 •

Copy link
Copy Markdown
Contributor

Implements hot-reloading for stylesheets, similar to S&box's implementation. When running DEBUG builds, modifying .scss files automatically refreshes the UI without restarting the application.

Changes

  • FileWatcher (src/Sandbox.UI/Utility/FileWatcher.cs)

    • Wraps System.IO.FileSystemWatcher with 200ms debouncing
    • Thread-safe, exact path matching, no-op stubs in Release
  • StyleSheet hotloading (src/Sandbox.UI/Styles/StyleSheet.cs)

    • Added OnStyleSheetsHotloaded event and file watcher integration
    • Auto-watches main file and @import includes
  • UI refresh (src/Sandbox.UI/Panel/RootPanel.cs, src/Fazor.UI/FazorApplication.cs)

    • DirtyAllStyles() invalidates all panel styles on file change
    • Event subscription wired up in FazorApplication.RunPanel()
  • Render loop (src/Fazor.UI/NativeWindow.cs)

    • FileWatcher.Tick() called each frame to process queued changes

Usage

// No code changes needed - just run in DEBUG
// Edit any .scss file and save - UI updates automatically

All hotloading code is #if DEBUG guarded for zero release overhead.

Original prompt

This section details on the original issue you should resolve

<issue_title>Razor and Stylesheet hotloading</issue_title>
<issue_description>It would be great if we could have hotloading for razor and stylesheets. S&box has this</issue_description>

<agent_instructions>Implement and get this working in at least debug builds, perhaps see how sbox does it. You have no time limits, this session has unlimited.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 4 commits January 11, 2026 12:14
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
Copilot AI changed the title [WIP] Add hotloading for razor and stylesheets Add stylesheet hotloading for DEBUG builds Jan 11, 2026
Copilot AI requested a review from Xenthio January 11, 2026 12:27
@Xenthio

Xenthio commented Jan 11, 2026

Copy link
Copy Markdown
Owner

@copilot how do i use this in a project?

Copilot AI commented Jan 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot how do i use this in a project?

The stylesheet hotloading works automatically in DEBUG builds - no code changes needed in your project!

How it works:

  1. Build and run your Fazor app in DEBUG configuration
  2. Load stylesheets using StyleSheet.FromFile("path/to/style.scss") (this is likely already happening via [StyleSheet] attributes or in your component constructors)
  3. While the app is running, edit any .scss file and save
  4. The UI automatically refreshes with the new styles

Requirements:

  • Must be a DEBUG build (dotnet run or dotnet build without -c Release)
  • Stylesheets must be loaded from actual files on disk (not embedded resources)
  • The file path must be resolvable at runtime

Example project setup:

<!-- In your .csproj, ensure stylesheets are copied to output -->
<ItemGroup>
  <Content Include="Styles\**\*.scss">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>

The feature is completely disabled in Release builds for zero overhead.

@Xenthio
Xenthio marked this pull request as ready for review January 11, 2026 21:51
Copilot AI review requested due to automatic review settings January 11, 2026 21:51
@Xenthio
Xenthio merged commit cad30fd into main Jan 11, 2026
1 check passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements stylesheet hot-reloading for DEBUG builds, allowing developers to modify .scss files and see changes immediately without restarting the application. The implementation is based on S&box's hot-reloading system.

Changes:

  • Implemented a FileWatcher utility with debouncing and thread-safe file change detection
  • Added hot-reload support to StyleSheet with automatic watching of main files and @import includes
  • Integrated hot-reload events into the UI refresh pipeline via RootPanel and FazorApplication

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Sandbox.UI/Utility/FileWatcher.cs New file watcher utility wrapping FileSystemWatcher with 200ms debouncing, exact path matching, and DEBUG/Release conditional compilation
src/Sandbox.UI/Styles/StyleSheet.cs Added file watching setup, hotload event, and automatic reloading of stylesheets and their includes
src/Sandbox.UI/Panel/RootPanel.cs Added DirtyAllStyles() to invalidate all panel styles when stylesheets change, plus unused TriggerHotload() for future Razor support
src/Fazor.UI/FazorApplication.cs Subscribed to stylesheet hotload events to trigger UI refresh
src/Fazor.UI/NativeWindow.cs Added FileWatcher.Tick() call in render loop to process file change notifications
tests/Sandbox.UI.Tests/FileWatcherTests.cs Basic smoke tests verifying FileWatcher API works in DEBUG and is no-op in Release
tests/Sandbox.UI.Tests/StyleSheetTests.cs Added unused import (likely accidental)

@@ -1,4 +1,5 @@
using Sandbox.UI;
using Fazor.Controls;

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This using directive for Fazor.Controls is unused. The file doesn't reference any types from this namespace.

Suggested change
using Fazor.Controls;

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +254
StyleSheet.OnStyleSheetsHotloaded += () =>
{
Sandbox.UI.Log.Info("[Hotload] Stylesheets changed - refreshing all panel styles");
rootPanel.DirtyAllStyles();
};

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This static event subscription creates a potential memory leak. Each time RunPanel is called, a new lambda is subscribed to the static event, but there's no corresponding unsubscription. The lambda captures rootPanel, which prevents the RootPanel from being garbage collected even after the window closes.

Consider either:

  1. Unsubscribing when the window is disposed/closed
  2. Using weak event patterns
  3. Making this subscription at a different lifecycle point where it can be properly cleaned up
Suggested change
StyleSheet.OnStyleSheetsHotloaded += () =>
{
Sandbox.UI.Log.Info("[Hotload] Stylesheets changed - refreshing all panel styles");
rootPanel.DirtyAllStyles();
};
// Use a weak reference so the RootPanel can be collected, and unsubscribe when it's gone
var rootPanelRef = new WeakReference<RootPanel>(rootPanel);
void OnHotload()
{
if (rootPanelRef.TryGetTarget(out var rp))
{
Sandbox.UI.Log.Info("[Hotload] Stylesheets changed - refreshing all panel styles");
rp.DirtyAllStyles();
}
else
{
// Root panel has been collected; remove this handler to avoid leaking it
StyleSheet.OnStyleSheetsHotloaded -= OnHotload;
}
}
StyleSheet.OnStyleSheetsHotloaded += OnHotload;

Copilot uses AI. Check for mistakes.
Comment on lines +358 to +361
internal void TriggerHotload()
{
OnHotloaded();
}

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TriggerHotload method is defined but never called anywhere in the codebase. If this is intended for future Razor hotloading support, consider adding a comment explaining this. If it should be used for stylesheet hotloading, it should be called alongside or instead of DirtyAllStyles().

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Razor and Stylesheet hotloading

3 participants