ComponentsMkconstPage.vala 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. using Spry;
  2. using Inversion;
  3. /**
  4. * ComponentsMkconstPage - Documentation for the spry-mkconst tool
  5. *
  6. * This page explains how to use spry-mkconst to convert HTML files
  7. * to Vala string constants that can be referenced in component markup properties.
  8. */
  9. public class ComponentsMkconstPage : PageComponent {
  10. public override string title { get { return "spry-mkconst Tool - Spry"; } }
  11. public const string ROUTE = "/components/spry-mkconst";
  12. private ComponentFactory factory = inject<ComponentFactory>();
  13. public override string markup { get {
  14. return """
  15. <div sid="page" class="doc-content">
  16. <h1>spry-mkconst Tool</h1>
  17. <section class="doc-section">
  18. <h2>What is spry-mkconst?</h2>
  19. <p>
  20. <code>spry-mkconst</code> is a command-line tool that converts file contents
  21. (typically HTML templates) into Vala string constants. This allows you to:
  22. </p>
  23. <ul>
  24. <li>Keep HTML templates in separate files for easier editing</li>
  25. <li>Maintain valid Vala syntax that the Vala language server can recognize</li>
  26. <li>Reference generated constants from your component's <code>markup</code> property</li>
  27. </ul>
  28. <p>
  29. Unlike the previous <code>spry-mkcomponent</code> approach which modified Vala files,
  30. <code>spry-mkconst</code> generates separate constant declarations, keeping your
  31. original Vala files intact and recognizable by IDEs and language servers.
  32. </p>
  33. </section>
  34. <section class="doc-section">
  35. <h2>Command-Line Usage</h2>
  36. <spry-component name="CodeBlockComponent" sid="usage-code"/>
  37. <h3>Options</h3>
  38. <table class="doc-table">
  39. <thead>
  40. <tr>
  41. <th>Option</th>
  42. <th>Description</th>
  43. </tr>
  44. </thead>
  45. <tbody>
  46. <tr>
  47. <td><code>-o, --output FILE</code></td>
  48. <td>Output file path (default: stdout)</td>
  49. </tr>
  50. <tr>
  51. <td><code>--ns NAMESPACE</code></td>
  52. <td>Wrap const in a namespace</td>
  53. </tr>
  54. <tr>
  55. <td><code>-n, --name NAME</code></td>
  56. <td>Override const name (default: derived from filename)</td>
  57. </tr>
  58. <tr>
  59. <td><code>-v, --version</code></td>
  60. <td>Show version information</td>
  61. </tr>
  62. <tr>
  63. <td><code>--help</code></td>
  64. <td>Show help message</td>
  65. </tr>
  66. </tbody>
  67. </table>
  68. </section>
  69. <section class="doc-section">
  70. <h2>How It Works</h2>
  71. <p>
  72. The tool takes a single input file and generates a Vala const declaration:
  73. </p>
  74. <spry-component name="CodeBlockComponent" sid="workflow-diagram"/>
  75. <div class="info-box">
  76. <p>
  77. <strong>💡 Key Concept:</strong> The const name is automatically derived from the
  78. filename using CAPITAL_SNAKE_CASE. For example, <code>UserContentComponent.vala.html</code>
  79. becomes <code>USER_CONTENT_COMPONENT_VALA_HTML</code>.
  80. </p>
  81. </div>
  82. </section>
  83. <section class="doc-section">
  84. <h2>Example Transformation</h2>
  85. <p>
  86. Here's a complete example showing how the tool works:
  87. </p>
  88. <h3>Input HTML File (<code>UserContentComponent.vala.html</code>)</h3>
  89. <spry-component name="CodeBlockComponent" sid="input-html"/>
  90. <h3>Generated Output (stdout or file)</h3>
  91. <spry-component name="CodeBlockComponent" sid="output-const"/>
  92. <h3>Your Vala Component (unchanged, valid Vala)</h3>
  93. <spry-component name="CodeBlockComponent" sid="vala-component"/>
  94. <p>
  95. The key advantage is that your Vala file remains valid, parseable Vala code.
  96. The Vala language server can still recognize it, provide autocomplete, and
  97. show errors. The generated constant is referenced via <code>return USER_CONTENT_COMPONENT_VALA_HTML;</code>
  98. </p>
  99. </section>
  100. <section class="doc-section">
  101. <h2>Integrating with Meson</h2>
  102. <p>
  103. The recommended way to use spry-mkconst is with meson's build system.
  104. You generate a separate Vala file containing the const declarations,
  105. then include it in your build.
  106. </p>
  107. <h3>Basic meson.build Setup</h3>
  108. <spry-component name="CodeBlockComponent" sid="meson-basic"/>
  109. <h3>With Namespace</h3>
  110. <spry-component name="CodeBlockComponent" sid="meson-namespace"/>
  111. <h3>Complete Example</h3>
  112. <spry-component name="CodeBlockComponent" sid="meson-complete"/>
  113. <div class="info-box">
  114. <p>
  115. <strong>💡 Tip:</strong> The generated file should be included in your
  116. executable's sources. Meson will run spry-mkconst automatically before
  117. compilation.
  118. </p>
  119. </div>
  120. </section>
  121. <section class="doc-section">
  122. <h2>HTML Template Guidelines</h2>
  123. <p>
  124. When creating HTML templates for use with spry-mkconst, follow these guidelines:
  125. </p>
  126. <ul>
  127. <li>
  128. <strong>Use <code>sid</code> attributes</strong> - Add <code>sid="name"</code>
  129. to elements you need to access from Vala code via <code>this["name"]</code>
  130. </li>
  131. <li>
  132. <strong>Name templates consistently</strong> - Use <code>ComponentName.vala.html</code>
  133. for clear association with the component
  134. </li>
  135. <li>
  136. <strong>Use HTMX attributes</strong> - Add <code>hx-*</code> attributes for
  137. interactivity without writing JavaScript
  138. </li>
  139. <li>
  140. <strong>Avoid complex logic</strong> - Keep templates focused on structure;
  141. use Vala for conditional rendering
  142. </li>
  143. </ul>
  144. <spry-component name="CodeBlockComponent" sid="html-guidelines"/>
  145. </section>
  146. <section class="doc-section">
  147. <h2>Why spry-mkconst vs spry-mkcomponent?</h2>
  148. <p>
  149. The previous <code>spry-mkcomponent</code> tool modified Vala source files by
  150. injecting HTML into the markup getter. This had a significant drawback:
  151. the Vala language server couldn't recognize the modified files as valid Vala
  152. during development.
  153. </p>
  154. <p>
  155. <code>spry-mkconst</code> solves this by:
  156. </p>
  157. <ul>
  158. <li>Keeping your Vala files as valid, parseable Vala code</li>
  159. <li>Generating constants in a separate file that's included at build time</li>
  160. <li>Allowing the language server to provide full IDE support</li>
  161. <li>Using <code>public override string markup { get { return CONSTANT_NAME; } }</code>
  162. which is valid Vala syntax</li>
  163. </ul>
  164. </section>
  165. <section class="doc-section">
  166. <h2>Workflow Integration</h2>
  167. <p>
  168. A typical development workflow with spry-mkconst looks like this:
  169. </p>
  170. <ol>
  171. <li><strong>Create</strong> your HTML template file (e.g., <code>MyComponent.vala.html</code>)</li>
  172. <li><strong>Write</strong> your Vala component referencing the generated constant name</li>
  173. <li><strong>Configure</strong> meson to generate the const file</li>
  174. <li><strong>Build</strong> your project - meson runs spry-mkconst automatically</li>
  175. <li><strong>Iterate</strong> - Edit the HTML file; changes are picked up on rebuild</li>
  176. </ol>
  177. <p>
  178. During development, you can run the tool manually to see the generated output:
  179. </p>
  180. <spry-component name="CodeBlockComponent" sid="manual-run"/>
  181. </section>
  182. <section class="doc-section">
  183. <h2>When to Use spry-mkconst</h2>
  184. <h3>✅ Good Use Cases</h3>
  185. <ul>
  186. <li>Components with substantial HTML markup</li>
  187. <li>When you need Vala language server support (autocomplete, errors)</li>
  188. <li>Teams where designers work on HTML separately from developers</li>
  189. <li>Keeping Vala files focused on logic</li>
  190. </ul>
  191. <h3>❌ When to Skip It</h3>
  192. <ul>
  193. <li>Simple components with minimal markup (inline is fine)</li>
  194. <li>Quick prototypes where separation isn't beneficial</li>
  195. <li>Components with dynamically generated markup</li>
  196. </ul>
  197. <div class="info-box">
  198. <p>
  199. <strong>💡 Remember:</strong> You can always inline markup directly in your
  200. Vala files using triple-quoted strings. spry-mkconst is opt-in - use it
  201. when the separation provides value for your project.
  202. </p>
  203. </div>
  204. </section>
  205. <section class="doc-section">
  206. <h2>Next Steps</h2>
  207. <div class="nav-cards">
  208. <a href="/components/template-syntax" class="nav-card">
  209. <h3>← Template Syntax</h3>
  210. <p>Learn about component template syntax</p>
  211. </a>
  212. <a href="/components/actions" class="nav-card">
  213. <h3>Actions →</h3>
  214. <p>Handle user interactions in components</p>
  215. </a>
  216. </div>
  217. </section>
  218. </div>
  219. """;
  220. }}
  221. public override async void prepare() throws Error {
  222. var usage_code = get_component_child<CodeBlockComponent>("usage-code");
  223. usage_code.language = "Bash";
  224. usage_code.code = "spry-mkconst [OPTIONS] <INPUT_FILE>\n\n" +
  225. "# Examples:\n" +
  226. "spry-mkconst Templates/UserContent.html\n" +
  227. "spry-mkconst -o generated/constants.vala Templates/UserContent.html\n" +
  228. "spry-mkconst --ns MyApp.Templates Templates/UserContent.html\n" +
  229. "spry-mkconst -n CUSTOM_NAME Templates/UserContent.html";
  230. var workflow_diagram = get_component_child<CodeBlockComponent>("workflow-diagram");
  231. workflow_diagram.language = "Text";
  232. workflow_diagram.code = "┌─────────────────────────────────────┐\n" +
  233. "│ UserContentComponent.vala.html │\n" +
  234. "│ (HTML template) │\n" +
  235. "└─────────────┬───────────────────────┘\n" +
  236. " │\n" +
  237. " ▼\n" +
  238. " ┌───────────────┐\n" +
  239. " │ spry-mkconst │\n" +
  240. " └───────┬───────┘\n" +
  241. " │\n" +
  242. " ▼\n" +
  243. "┌─────────────────────────────────────────────────┐\n" +
  244. "│ const string USER_CONTENT_COMPONENT_VALA_HTML │\n" +
  245. "│ = \"\"\"<html>...</html>\"\"\"; │\n" +
  246. "└─────────────────────────────────────────────────┘";
  247. var input_html = get_component_child<CodeBlockComponent>("input-html");
  248. input_html.language = "HTML";
  249. input_html.code = "<p>You said: <strong sid=\"message\"></strong> <em>(via <span sid=\"action\"></span>)</em></p>";
  250. var output_const = get_component_child<CodeBlockComponent>("output-const");
  251. output_const.language = "Vala";
  252. output_const.code = "const string USER_CONTENT_COMPONENT_VALA_HTML = \"\"\"\n" +
  253. "<p>You said: <strong sid=\"message\"></strong> <em>(via <span sid=\"action\"></span>)</em></p>\n" +
  254. "\"\"\";";
  255. var vala_component = get_component_child<CodeBlockComponent>("vala-component");
  256. vala_component.language = "Vala";
  257. vala_component.code = "// This file is valid Vala - the language server recognizes it!\n" +
  258. "class UserContentComponent : Component {\n" +
  259. " public override string markup { get { return USER_CONTENT_COMPONENT_VALA_HTML; } }\n\n" +
  260. " public async override void handle_action(string action) throws Error {\n" +
  261. " this[\"message\"].text_content = \"Hello, World!\";\n" +
  262. " }\n" +
  263. "}";
  264. var meson_basic = get_component_child<CodeBlockComponent>("meson-basic");
  265. meson_basic.language = "Meson";
  266. meson_basic.code = "# Find the tool\n" +
  267. "spry_mkconst = find_program('spry-mkconst')\n\n" +
  268. "# Generate constants from HTML templates\n" +
  269. "markup_constants = custom_target('markup-constants',\n" +
  270. " input: 'Templates/UserContentComponent.vala.html',\n" +
  271. " output: 'UserContentConstants.vala',\n" +
  272. " command: [spry_mkconst, '@INPUT@', '-o', '@OUTPUT@']\n" +
  273. ")\n\n" +
  274. "# Include in your executable\n" +
  275. "executable('myapp',\n" +
  276. " sources: ['Main.vala', 'UserContentComponent.vala', markup_constants],\n" +
  277. " dependencies: [spry_dep]\n" +
  278. ")";
  279. var meson_namespace = get_component_child<CodeBlockComponent>("meson-namespace");
  280. meson_namespace.language = "Meson";
  281. meson_namespace.code = "# Generate constants with namespace\n" +
  282. "spry_mkconst = find_program('spry-mkconst')\n\n" +
  283. "markup_constants = custom_target('markup-constants',\n" +
  284. " input: 'Templates/UserContentComponent.vala.html',\n" +
  285. " output: 'UserContentConstants.vala',\n" +
  286. " command: [spry_mkconst, '@INPUT@', '-o', '@OUTPUT@',\n" +
  287. " '--ns', 'MyApp.Markup']\n" +
  288. ")\n\n" +
  289. "# In your Vala file, use:\n" +
  290. "# public override string markup { get { return MyApp.Markup.USER_CONTENT_COMPONENT_VALA_HTML; } }";
  291. var meson_complete = get_component_child<CodeBlockComponent>("meson-complete");
  292. meson_complete.language = "Meson";
  293. meson_complete.code = "# Find the spry-mkconst tool\n" +
  294. "spry_mkconst = find_program('spry-mkconst')\n\n" +
  295. "# Collect all HTML template files\n" +
  296. "template_files = files(\n" +
  297. " 'Templates/HeaderComponent.vala.html',\n" +
  298. " 'Templates/FooterComponent.vala.html',\n" +
  299. " 'Templates/UserContentComponent.vala.html',\n" +
  300. ")\n\n" +
  301. "# Generate a single constants file from all templates\n" +
  302. "# Note: You can also generate separate files per template\n" +
  303. "markup_constants = custom_target('markup-constants',\n" +
  304. " input: template_files,\n" +
  305. " output: 'MarkupConstants.vala',\n" +
  306. " command: [spry_mkconst, '@INPUT@', '-o', '@OUTPUT@', '--ns', 'Markup']\n" +
  307. ")\n\n" +
  308. "# Component source files (reference the constants)\n" +
  309. "component_sources = files(\n" +
  310. " 'Components/HeaderComponent.vala',\n" +
  311. " 'Components/FooterComponent.vala',\n" +
  312. " 'Components/UserContentComponent.vala',\n" +
  313. ")\n\n" +
  314. "# Build executable\n" +
  315. "executable('myapp',\n" +
  316. " sources: ['Main.vala', component_sources, markup_constants],\n" +
  317. " dependencies: [spry_dep],\n" +
  318. ")";
  319. var html_guidelines = get_component_child<CodeBlockComponent>("html-guidelines");
  320. html_guidelines.language = "HTML";
  321. html_guidelines.code = "<!-- Good: Use sid attributes for elements you need to access -->\n" +
  322. "<div class=\"user-card\">\n" +
  323. " <h2 sid=\"username\"></h2>\n" +
  324. " <p sid=\"email\"></p>\n" +
  325. " <button sid=\"delete-btn\" hx-post=\"/delete\" hx-target=\"closest .user-card\">\n" +
  326. " Delete\n" +
  327. " </button>\n" +
  328. "</div>\n\n" +
  329. "<!-- Good: Keep it simple and focused on structure -->\n" +
  330. "<form sid=\"login-form\" hx-post=\"/login\">\n" +
  331. " <input type=\"text\" name=\"username\" sid=\"username-input\"/>\n" +
  332. " <input type=\"password\" name=\"password\" sid=\"password-input\"/>\n" +
  333. " <button type=\"submit\">Login</button>\n" +
  334. "</form>";
  335. var manual_run = get_component_child<CodeBlockComponent>("manual-run");
  336. manual_run.language = "Bash";
  337. manual_run.code = "# Run manually to see generated output\n" +
  338. "spry-mkconst Templates/MyComponent.vala.html\n\n" +
  339. "# Save to a file\n" +
  340. "spry-mkconst Templates/MyComponent.vala.html -o generated/MyComponentMarkup.vala\n\n" +
  341. "# View the result\n" +
  342. "cat generated/MyComponentMarkup.vala\n\n" +
  343. "# Generate with namespace\n" +
  344. "spry-mkconst Templates/MyComponent.vala.html --ns MyApp.Templates";
  345. }
  346. }