| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- using Spry;
- using Inversion;
- /**
- * ComponentsMkcomponentPage - Documentation for the spry-mkcomponent tool
- *
- * This page explains how to use spry-mkcomponent to inject HTML markup
- * into Vala component files.
- */
- public class ComponentsMkcomponentPage : PageComponent {
-
- public const string ROUTE = "/components/spry-mkcomponent";
-
- private ComponentFactory factory = inject<ComponentFactory>();
-
- public override string markup { get {
- return """
- <div sid="page" class="doc-content">
- <h1>spry-mkcomponent Tool</h1>
-
- <section class="doc-section">
- <h2>What is spry-mkcomponent?</h2>
- <p>
- <code>spry-mkcomponent</code> is a command-line tool that processes Vala component
- files by injecting HTML markup into the <code>markup</code> property getter. This
- allows you to maintain your HTML templates in separate files while keeping your
- Vala code clean and focused on logic.
- </p>
- <p>
- The tool enables a cleaner separation of concerns:
- </p>
- <ul>
- <li><strong>Component logic</strong> - Vala code handling actions, state, and behavior</li>
- <li><strong>HTML templates</strong> - Separate .html files with your markup</li>
- </ul>
- </section>
-
- <section class="doc-section">
- <h2>Command-Line Usage</h2>
-
- <spry-component name="CodeBlockComponent" sid="usage-code"/>
-
- <h3>Options</h3>
- <table class="doc-table">
- <thead>
- <tr>
- <th>Option</th>
- <th>Description</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><code>-o, --output FILE</code></td>
- <td>Output file path (default: input filename in build directory)</td>
- </tr>
- <tr>
- <td><code>-h, --html FILE</code></td>
- <td>HTML template file (default: <input_vala>.html)</td>
- </tr>
- <tr>
- <td><code>-v, --version</code></td>
- <td>Show version information</td>
- </tr>
- <tr>
- <td><code>--help</code></td>
- <td>Show help message</td>
- </tr>
- </tbody>
- </table>
- </section>
-
- <section class="doc-section">
- <h2>How It Works</h2>
- <p>
- The tool takes two input files and produces a single output file with the
- HTML template embedded in the markup property:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="workflow-diagram"/>
-
- <div class="info-box">
- <p>
- <strong>💡 Key Concept:</strong> The tool looks for an empty <code>markup</code>
- property getter and injects the HTML content. If the getter already has content,
- the file is passed through unchanged.
- </p>
- </div>
- </section>
-
- <section class="doc-section">
- <h2>Example Transformation</h2>
- <p>
- Here's a complete example showing how the tool transforms your component files:
- </p>
-
- <h3>Input Vala File (<code>UserContentComponent.vala</code>)</h3>
- <spry-component name="CodeBlockComponent" sid="input-vala"/>
-
- <h3>Input HTML File (<code>UserContentComponent.vala.html</code>)</h3>
- <spry-component name="CodeBlockComponent" sid="input-html"/>
-
- <h3>Output Vala File (<code>UserContentComponent.vala</code>)</h3>
- <spry-component name="CodeBlockComponent" sid="output-vala"/>
-
- <p>
- Notice how the empty <code>markup</code> getter now returns the HTML content
- as a verbatim string literal. The <code>sid</code> attributes in your HTML
- are preserved, allowing you to access elements via <code>this["sid"]</code>
- in your Vala code.
- </p>
- </section>
-
- <section class="doc-section">
- <h2>Integrating with Meson</h2>
- <p>
- The recommended way to use spry-mkcomponent is with meson's build system.
- You can use either <code>generator()</code> for processing multiple files
- or <code>custom_target()</code> for single files with custom options.
- </p>
-
- <h3>Using generator() for Multiple Files</h3>
- <spry-component name="CodeBlockComponent" sid="meson-generator"/>
-
- <p>
- The <code>generator()</code> function is ideal when you have multiple component
- files that all follow the same pattern (HTML file named
- <code><component>.vala.html</code>).
- </p>
-
- <h3>Using custom_target() for Single Files</h3>
- <spry-component name="CodeBlockComponent" sid="meson-custom-target"/>
-
- <p>
- Use <code>custom_target()</code> when you need more control over file paths
- or want to specify a custom HTML template location.
- </p>
-
- <h3>Complete meson.build Example</h3>
- <spry-component name="CodeBlockComponent" sid="meson-complete"/>
-
- <div class="info-box">
- <p>
- <strong>💡 Tip:</strong> The <code>@INPUT@</code> and <code>@OUTPUT@</code>
- placeholders are automatically replaced by meson with the actual file paths.
- The <code>@BASENAME@</code> placeholder is replaced with the input filename
- without extension.
- </p>
- </div>
- </section>
-
- <section class="doc-section">
- <h2>HTML Template Guidelines</h2>
- <p>
- When creating HTML templates for use with spry-mkcomponent, follow these guidelines:
- </p>
-
- <ul>
- <li>
- <strong>Use <code>sid</code> attributes</strong> - Add <code>sid="name"</code>
- to elements you need to access from Vala code via <code>this["name"]</code>
- </li>
- <li>
- <strong>Keep templates alongside Vala files</strong> - Name them
- <code>ComponentName.vala.html</code> for automatic discovery
- </li>
- <li>
- <strong>Use HTMX attributes</strong> - Add <code>hx-*</code> attributes for
- interactivity without writing JavaScript
- </li>
- <li>
- <strong>Avoid complex logic</strong> - Keep templates focused on structure;
- use Vala for conditional rendering
- </li>
- </ul>
-
- <spry-component name="CodeBlockComponent" sid="html-guidelines"/>
- </section>
-
- <section class="doc-section">
- <h2>Workflow Integration</h2>
- <p>
- A typical development workflow with spry-mkcomponent looks like this:
- </p>
-
- <ol>
- <li><strong>Create</strong> your Vala component with an empty <code>markup</code> getter</li>
- <li><strong>Create</strong> an HTML template file with the same base name</li>
- <li><strong>Build</strong> your project - meson runs spry-mkcomponent automatically</li>
- <li><strong>Iterate</strong> - Edit either file; changes are picked up on rebuild</li>
- </ol>
-
- <p>
- During development, you can run the tool manually to preview the generated output:
- </p>
-
- <spry-component name="CodeBlockComponent" sid="manual-run"/>
- </section>
-
- <section class="doc-section">
- <h2>When to Use spry-mkcomponent</h2>
-
- <h3>✅ Good Use Cases</h3>
- <ul>
- <li>Components with substantial HTML markup</li>
- <li>Teams where designers work on HTML separately from developers</li>
- <li>Reusing HTML templates across multiple components</li>
- <li>Keeping Vala files focused on logic</li>
- </ul>
-
- <h3>❌ When to Skip It</h3>
- <ul>
- <li>Simple components with minimal markup</li>
- <li>Quick prototypes where separation isn't beneficial</li>
- <li>Components with dynamically generated markup</li>
- </ul>
-
- <div class="info-box">
- <p>
- <strong>💡 Remember:</strong> You can always inline markup directly in your
- Vala files. spry-mkcomponent is opt-in - use it when the separation provides
- value for your project.
- </p>
- </div>
- </section>
-
- <section class="doc-section">
- <h2>Next Steps</h2>
- <div class="nav-cards">
- <a href="/components/template-syntax" class="nav-card">
- <h3>← Template Syntax</h3>
- <p>Learn about component template syntax</p>
- </a>
- <a href="/components/actions" class="nav-card">
- <h3>Actions →</h3>
- <p>Handle user interactions in components</p>
- </a>
- </div>
- </section>
- </div>
- """;
- }}
-
- public override async void prepare() throws Error {
- var usage_code = get_component_child<CodeBlockComponent>("usage-code");
- usage_code.language = "Bash";
- usage_code.code = "spry-mkcomponent [OPTIONS] <INPUT_VALA_FILE>\n\n" +
- "# Examples:\n" +
- "spry-mkcomponent Components/UserComponent.vala\n" +
- "spry-mkcomponent -o build/UserComponent.vala Components/UserComponent.vala\n" +
- "spry-mkcomponent --html Templates/User.html Components/UserComponent.vala";
-
- var workflow_diagram = get_component_child<CodeBlockComponent>("workflow-diagram");
- workflow_diagram.language = "Text";
- workflow_diagram.code = "┌─────────────────────────────┐ ┌─────────────────────────────┐\n" +
- "│ Component.vala │ │ Component.vala.html │\n" +
- "│ (empty markup getter) │ │ (HTML template) │\n" +
- "└─────────────┬───────────────┘ └─────────────┬───────────────┘\n" +
- " │ │\n" +
- " └───────────────┬───────────────────┘\n" +
- " │\n" +
- " ▼\n" +
- " ┌─────────────────┐\n" +
- " │ spry-mkcomponent │\n" +
- " └────────┬────────┘\n" +
- " │\n" +
- " ▼\n" +
- " ┌─────────────────────────────┐\n" +
- " │ Component.vala │\n" +
- " │ (with generated getter) │\n" +
- " └─────────────────────────────┘";
-
- var input_vala = get_component_child<CodeBlockComponent>("input-vala");
- input_vala.language = "Vala";
- input_vala.code = "class UserContentComponent : Component {\n" +
- " public override string markup { get; }\n\n" +
- " public async override void handle_action(string action) throws Error {\n" +
- " this[\"message\"].text_content = \"Hello, World!\";\n" +
- " }\n" +
- "}";
-
- var input_html = get_component_child<CodeBlockComponent>("input-html");
- input_html.language = "HTML";
- input_html.code = "<p>You said: <strong sid=\"message\"></strong></p>";
-
- var output_vala = get_component_child<CodeBlockComponent>("output-vala");
- output_vala.language = "Vala";
- output_vala.code = "class UserContentComponent : Component {\n" +
- " public override string markup { get {\n" +
- " return \"\"\"<p>You said: <strong sid=\"message\"></strong></p>\"\"\";\n" +
- " }}\n\n" +
- " public async override void handle_action(string action) throws Error {\n" +
- " this[\"message\"].text_content = \"Hello, World!\";\n" +
- " }\n" +
- "}";
-
- var meson_generator = get_component_child<CodeBlockComponent>("meson-generator");
- meson_generator.language = "Meson";
- meson_generator.code = "# Find the tool\n" +
- "spry_mkcomponent = find_program('spry-mkcomponent')\n\n" +
- "# Define the generator once\n" +
- "spry_mkcomponent_gen = generator(spry_mkcomponent,\n" +
- " output: '@BASENAME@.vala',\n" +
- " arguments: ['@INPUT@', '@OUTPUT@']\n" +
- ")\n\n" +
- "# Process multiple component files\n" +
- "generated_components = spry_mkcomponent_gen.process(files(\n" +
- " 'Components/UserContentComponent.vala',\n" +
- " 'Components/LoginFormComponent.vala',\n" +
- " 'Components/NavSidebarComponent.vala',\n" +
- "))\n\n" +
- "# Use in executable\n" +
- "executable('myapp',\n" +
- " sources: [other_sources, generated_components],\n" +
- " dependencies: [spry_dep]\n" +
- ")";
-
- var meson_custom_target = get_component_child<CodeBlockComponent>("meson-custom-target");
- meson_custom_target.language = "Meson";
- meson_custom_target.code = "# Simple case - HTML file is ComponentName.vala.html\n" +
- "user_content_component = custom_target('user-content-component',\n" +
- " input: 'Components/UserContentComponent.vala',\n" +
- " output: 'UserContentComponent.vala',\n" +
- " command: [spry_mkcomponent, '@INPUT@', '@OUTPUT@']\n" +
- ")\n\n" +
- "# With explicit HTML file path\n" +
- "login_form_component = custom_target('login-form-component',\n" +
- " input: 'Components/LoginFormComponent.vala',\n" +
- " output: 'LoginFormComponent.vala',\n" +
- " command: [spry_mkcomponent, '@INPUT@', '@OUTPUT@',\n" +
- " '--html', 'Templates/LoginForm.html']\n" +
- ")";
-
- var meson_complete = get_component_child<CodeBlockComponent>("meson-complete");
- meson_complete.language = "Meson";
- meson_complete.code = "# Find the spry-mkcomponent tool\n" +
- "spry_mkcomponent = find_program('spry-mkcomponent')\n\n" +
- "# Define generator for components\n" +
- "component_gen = generator(spry_mkcomponent,\n" +
- " output: '@BASENAME@.vala',\n" +
- " arguments: ['@INPUT@', '@OUTPUT@']\n" +
- ")\n\n" +
- "# Process all component files with HTML templates\n" +
- "generated_components = component_gen.process(files(\n" +
- " 'Components/HeaderComponent.vala',\n" +
- " 'Components/FooterComponent.vala',\n" +
- " 'Components/SidebarComponent.vala',\n" +
- "))\n\n" +
- "# Other source files (no HTML templates)\n" +
- "other_sources = files(\n" +
- " 'Main.vala',\n" +
- " 'Components/SimpleComponent.vala', # Inline markup\n" +
- ")\n\n" +
- "# Build executable with generated components\n" +
- "executable('myapp',\n" +
- " sources: [other_sources, generated_components],\n" +
- " dependencies: [spry_dep],\n" +
- " vala_args: ['--vapidir', spry_vapi_dir]\n" +
- ")";
-
- var html_guidelines = get_component_child<CodeBlockComponent>("html-guidelines");
- html_guidelines.language = "HTML";
- html_guidelines.code = "<!-- Good: Use sid attributes for elements you need to access -->\n" +
- "<div class=\"user-card\">\n" +
- " <h2 sid=\"username\"></h2>\n" +
- " <p sid=\"email\"></p>\n" +
- " <button sid=\"delete-btn\" hx-post=\"/delete\" hx-target=\"closest .user-card\">\n" +
- " Delete\n" +
- " </button>\n" +
- "</div>\n\n" +
- "<!-- Good: Keep it simple and focused on structure -->\n" +
- "<form sid=\"login-form\" hx-post=\"/login\">\n" +
- " <input type=\"text\" name=\"username\" sid=\"username-input\"/>\n" +
- " <input type=\"password\" name=\"password\" sid=\"password-input\"/>\n" +
- " <button type=\"submit\">Login</button>\n" +
- "</form>";
-
- var manual_run = get_component_child<CodeBlockComponent>("manual-run");
- manual_run.language = "Bash";
- manual_run.code = "# Run manually to see generated output\n" +
- "spry-mkcomponent Components/MyComponent.vala -o /tmp/output.vala\n\n" +
- "# View the result\n" +
- "cat /tmp/output.vala\n\n" +
- "# Check if the tool would modify a file (compare with diff)\n" +
- "spry-mkcomponent Components/MyComponent.vala -o /tmp/test.vala && \\\n" +
- " diff Components/MyComponent.vala /tmp/test.vala";
- }
- }
|