using Spry; using Inversion; using Invercargill.DataStructures; /** * DemoHostComponent - A component that hosts demo implementations with source/demo toggle * * This component provides a frame for demo content with the ability to toggle * between viewing the demo and viewing the source code. * * Usage: * var host = factory.create(); * host.demo_component_name = "SimpleCounterDemo"; * host.source_file = "demo/DemoComponents/SimpleCounterDemo.vala"; */ public class DemoHostComponent : Component { private ComponentFactory factory = inject(); /// Name of the demo component to create and display public string demo_component_name { get; set; } /// Path to the source file to display (relative to project root) public string source_file { get; set; default = ""; } /// Toggle state - true when showing source code public bool showing_source { get; set; default = false; } /// The loaded source code content (cached after first load) private string? _source_content = null; public override string markup { get { return """
Demo
"""; }} public override async void prepare() throws Error { // Update the title with the demo component name if (demo_component_name != null) { this["title"].text_content = demo_component_name; } // Create the demo component and add it to the outlet when showing demo if (!showing_source && source_file != null) { var demo = factory.create_by_name(demo_component_name); var series = new Series(); series.add(demo); set_outlet_children("demo-outlet", series); } // Update the source code display if we're showing source if (showing_source && _source_content != null) { this["source-code"].text_content = _source_content; } } public async override void handle_action(string action) throws Error { switch (action) { case "ShowDemo": showing_source = false; break; case "ShowSource": if (_source_content == null) { _source_content = yield load_source_file(); } showing_source = true; break; } } /** * Load the source file from disk and escape HTML entities */ private async string load_source_file() { try { var path = source_file; // Try to read from the project root (relative path) var file = File.new_for_path(path); if (!file.query_exists()) { return @"Error: Source file not found: $path"; } uint8[] contents; string etag_out; yield file.load_contents_async(null, out contents, out etag_out); var source = (string)contents; return escape_html(source); } catch (Error e) { return @"Error loading source file: $(e.message)"; } } /** * Escape HTML entities for safe display in
 blocks
     */
    private string escape_html(string input) {
        var result = input
            .replace("&", "&")
            .replace("<", "<")
            .replace(">", ">")
            .replace("\"", """)
            .replace("'", "'");
        return result;
    }
}