Selaa lähdekoodia

r3: template partials (arch 16) — Statum DOES support includes (the mkpstm fragment pass: pstm-fragment with pstm-input parameters and pstm-materialise-as tag carry), so the partial strategy is compile-time fragments, not JS extraction. Five new partials in src/pages (auto-discovered, wired into template_deps): goal-branches (the branch chips paragraph, included 3x from goal_detail — head passes its note via the optional input, the note span hides where undefined), goal-compare (the Compare changes link, 2x), progress-feed (the latest+history card body shared between task and goal pages, parameterised on entries — heading and empty-state note stay per page with their page-specific wording), block-answer (the option-card question form — radio options + Other, shared between both pages' block loops; both loops expose the block as 'block' so no inputs), and block-reply (the free-text RFC reply form, shared likewise). The goal page's select+textarea answer form unifies onto the task page's richer option-card shape (the U1-pinned one): AnswerGoalBlockAction gains the __other__ resolution the task action already had, so a picked radio posts option=<value> exactly like the old select and the Other row carries the free text — the goal questions stay answerable with and without options. The goal page also gains the RFC reply fallback the task page already had (its old single form covered rfc blocks implicitly). Compiled-page inspection pins the composition: chips wrappers carry stm-if + the note def, no pstm-* residue. 851 checks.

clanker 1 kuukausi sitten
vanhempi
sitoutus
bf9009c91b

+ 10 - 1
meson.build

@@ -32,7 +32,16 @@ statum_mkres = find_program('statum-mkres')
 # composes the shared layout (main.html), which the tool discovers next to
 # the pages.
 # =============================================================================
-template_deps = files('src/pages/main.html')
+template_deps = files(
+    'src/pages/main.html',
+    # Partials (plan 16 R3, arch 16): inlined into pages at compile
+    # time via <pstm-fragment>; discovered next to the pages.
+    'src/pages/goal_branches.html',
+    'src/pages/goal_compare.html',
+    'src/pages/progress_feed.html',
+    'src/pages/block_answer.html',
+    'src/pages/block_reply.html',
+)
 
 # spry:pages-begin
 home_page = custom_target('home-page',

+ 5 - 1
src/actions/GoalActions.vala

@@ -348,8 +348,12 @@ namespace ClankerSaddle {
             string option = Forms.field(request, "option");
             string verdict = Forms.field(request, "verdict");
             string feedback = Forms.field_raw(request, "feedback");
+            string other = Forms.field_raw(request, "other");
             if (answer.length == 0 && option.length > 0) {
-                answer = option;
+                // The shared option-card form: a picked radio posts its
+                // value; the Other row posts option=__other__ with the
+                // free text beside it.
+                answer = option == "__other__" ? other : option;
             }
             if (answer.length == 0
                     && (verdict.length > 0 || feedback.strip().length > 0)) {

+ 29 - 0
src/pages/block_answer.html

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <!-- A partial (plan 16 R3, arch 16): the option-card answer form
+         for a pending question block — shared between the task page's
+         blocks section and the goal page's attention blocks. Both
+         loops expose the block as `block`, so no inputs are needed:
+         the radios post `option` (or `option=__other__` + the `other`
+         textarea). A NULL-options question renders just the Other
+         row (the loop tolerates a missing list). -->
+    <pstm-name>block-answer</pstm-name>
+</head>
+<body>
+    <form stm-if="!block.answered && block.block_type == 'question'" stm-action="block.answer_action">
+        <div class="options">
+            <label stm-for-option-in="block.options" stm-key="option" class="option-card">
+                <input type="radio" name="option" stm-attribute.value="option">
+                <span stm-text="option">option</span>
+            </label>
+            <label>
+                <input type="radio" name="option" value="__other__">
+                <span>Other:</span>
+                <textarea name="other" rows="2"></textarea>
+            </label>
+        </div>
+        <button type="submit" class="primary">Answer</button>
+    </form>
+</body>
+</html>

+ 17 - 0
src/pages/block_reply.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <!-- A partial (plan 16 R3, arch 16): the free-text reply form for
+         a pending non-question block (an RFC) — shared between the
+         task page's blocks section and the goal page's attention
+         blocks. Posts `answer`. -->
+    <pstm-name>block-reply</pstm-name>
+</head>
+<body>
+    <form stm-if="!block.answered && block.block_type != 'question' && block.block_type != 'review'"
+          stm-action="block.answer_action" class="inline">
+        <textarea name="answer" rows="3" placeholder="Your reply (delivered to the agent)"></textarea>
+        <button type="submit" class="primary">Reply</button>
+    </form>
+</body>
+</html>

+ 22 - 0
src/pages/goal_branches.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <!-- A partial (plan 16 R3, arch 16): the goal's daemon-reported
+         branch chips — included 3x from goal_detail (status head,
+         pending completion review, terminal completion card). The
+         materialise tag carries the stm-if gate; the optional `note`
+         input renders the head's trailing muted note (absent
+         elsewhere: note resolves undefined and the span hides). -->
+    <pstm-name>goal-branches</pstm-name>
+</head>
+<body pstm-materialise-as="p" stm-if="goal.push_branches.length > 0">
+    <pstm-input name="note"/>
+    Goal branches:
+    <span stm-for-branch-in="goal.push_branches">
+        <code stm-text="branch.branch">b</code>
+        <span stm-attribute.class="branch.pushed ? 'badge badge-done' : 'badge badge-failed'"
+              stm-text="branch.pushed ? 'pushed' : 'unpushed'">state</span>
+    </span>
+    <span class="muted" stm-if="note != null" stm-text="note">note</span>
+</body>
+</html>

+ 14 - 0
src/pages/goal_compare.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <!-- A partial (plan 16 R3, arch 16): the goal's compare-changes
+         link — included 2x from goal_detail's completion review
+         cards (pending + terminal). The status head keeps its own
+         inline wording inside the branch line. -->
+    <pstm-name>goal-compare</pstm-name>
+</head>
+<body>
+    <p><a stm-if="goal.compare_url.length > 0" stm-attribute.href="goal.compare_url"
+          target="_blank" rel="noopener">Compare changes</a></p>
+</body>
+</html>

+ 9 - 48
src/pages/goal_detail.html

@@ -143,13 +143,8 @@
                     · priority <strong stm-text="goal.priority">3</strong>
                     <span stm-if="goal.agent_saddle.length > 0"> · agent saddle
                         <strong stm-text="goal.agent_saddle">saddle</strong></span></p>
-                <p stm-if="goal.push_branches.length > 0">Goal branches:
-                    <span stm-for-branch-in="goal.push_branches">
-                        <code stm-text="branch.branch">b</code>
-                        <span stm-attribute.class="branch.pushed ? 'badge badge-done' : 'badge badge-failed'"
-                              stm-text="branch.pushed ? 'pushed' : 'unpushed'">state</span>
-                    </span>
-                    <span class="muted">(the daemon's /status report)</span></p>
+                <pstm-fragment name="goal-branches"
+                               note="&quot;(the daemon's /status report)&quot;"/>
                 <p class="muted">Project:
                     <a stm-attribute.href="'/projects/' + goal.project_id" stm-text="goal.project_name">project</a>
                     · Created <span stm-text="goal.created_display">when</span>
@@ -193,19 +188,7 @@
                  card rhythm). -->
             <section class="card">
                 <h3>Agent progress</h3>
-                <div stm-if="goal.progress.length > 0">
-                    <p class="progress-latest" stm-text="goal.progress[0].summary">latest</p>
-                    <p class="muted" stm-text="'reported ' + goal.progress[0].when_display">when</p>
-                    <details class="history-expander" stm-if="goal.progress.length > 1">
-                        <summary>Show history (<span stm-text="goal.progress.length">0</span> updates)</summary>
-                        <ul class="progress-history">
-                            <li stm-for-item-in="goal.progress">
-                                <span class="muted" stm-text="item.when_display">when</span> —
-                                <span stm-text="item.summary">summary</span>
-                            </li>
-                        </ul>
-                    </details>
-                </div>
+                <pstm-fragment name="progress-feed" entries="goal.progress"/>
                 <p stm-if="goal.progress.length == 0" class="muted">No progress reports yet — the goal
                     agent reports via <code>saddle progress-update</code>.</p>
             </section>
@@ -231,14 +214,8 @@
                     <div stm-if="block.is_completion_review"
                          class="plan-review" data-plan-review
                          stm-attribute.data-block-id="block.id">
-                        <p stm-if="goal.push_branches.length > 0">Goal branches:
-                            <span stm-for-branch-in="goal.push_branches">
-                                <code stm-text="branch.branch">b</code>
-                                <span stm-attribute.class="branch.pushed ? 'badge badge-done' : 'badge badge-failed'"
-                                      stm-text="branch.pushed ? 'pushed' : 'unpushed'">state</span>
-                            </span></p>
-                        <p><a stm-if="goal.compare_url.length > 0" stm-attribute.href="goal.compare_url"
-                              target="_blank" rel="noopener">Compare changes</a></p>
+                        <pstm-fragment name="goal-branches"/>
+                        <pstm-fragment name="goal-compare"/>
                         <div class="md rfc-body goal-body" data-plan-body
                              stm-markdown="block.body">report</div>
                         <div class="actions">
@@ -260,18 +237,8 @@
                     <div stm-if="!block.is_completion_review">
                         <div stm-if="block.show_body && block.body.length > 0"
                              class="md rfc-body goal-body" stm-markdown="block.body">body</div>
-                        <form stm-action="block.answer_action">
-                            <div stm-if="block.options.length > 0" class="form-row">
-                                <label>Options</label>
-                                <select name="option">
-                                    <option stm-for-option-in="block.options" stm-attribute.value="option"
-                                             stm-text="option">opt</option>
-                                </select></div>
-                            <div class="form-row">
-                                <textarea name="answer" rows="2"
-                                          placeholder="Reply (or pick an option)"></textarea></div>
-                            <button type="submit" class="primary">Answer</button>
-                        </form>
+                        <pstm-fragment name="block-answer"/>
+                        <pstm-fragment name="block-reply"/>
                     </div>
                 </div>
                 <details class="history-expander" stm-if="goal.answered_blocks.length > 0">
@@ -296,14 +263,8 @@
                  the attention blocks above (the rich review form). -->
             <section class="card" stm-if="goal.terminal && goal.completion_report.length > 0">
                 <h3>Completion review</h3>
-                <p stm-if="goal.push_branches.length > 0">Goal branches:
-                    <span stm-for-branch-in="goal.push_branches">
-                        <code stm-text="branch.branch">b</code>
-                        <span stm-attribute.class="branch.pushed ? 'badge badge-done' : 'badge badge-failed'"
-                              stm-text="branch.pushed ? 'pushed' : 'unpushed'">state</span>
-                    </span></p>
-                <p><a stm-if="goal.compare_url.length > 0" stm-attribute.href="goal.compare_url"
-                      target="_blank" rel="noopener">Compare changes</a></p>
+                <pstm-fragment name="goal-branches"/>
+                <pstm-fragment name="goal-compare"/>
                 <div class="md rfc-body goal-body" stm-markdown="goal.completion_report">report</div>
             </section>
 

+ 28 - 0
src/pages/progress_feed.html

@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <!-- A partial (plan 16 R3, arch 16): the progress feed card shared
+         between the task and goal pages — newest line, "reported"
+         stamp, history behind the expander. The including page
+         supplies `entries` (task.progress / goal.progress); the
+         heading and the empty-state note stay per page (they carry
+         page-specific wording). -->
+    <pstm-name>progress-feed</pstm-name>
+</head>
+<body>
+    <pstm-input name="entries"/>
+    <div stm-if="entries.length > 0">
+        <p class="progress-latest" stm-text="entries[0].summary">latest</p>
+        <p class="muted" stm-text="'reported ' + entries[0].when_display">when</p>
+        <details class="history-expander" stm-if="entries.length > 1">
+            <summary>Show history (<span stm-text="entries.length">0</span> updates)</summary>
+            <ul class="progress-history">
+                <li stm-for-item-in="entries">
+                    <span class="muted" stm-text="item.when_display">when</span> —
+                    <span stm-text="item.summary">summary</span>
+                </li>
+            </ul>
+        </details>
+    </div>
+</body>
+</html>

+ 3 - 33
src/pages/task_detail.html

@@ -68,19 +68,7 @@
                  the agent's newest line leads the page, its history
                  behind the "Show history" expander. -->
             <h3>Progress</h3>
-            <div stm-if="task.progress.length > 0">
-                <p class="progress-latest" stm-text="task.progress[0].summary">latest</p>
-                <p class="muted" stm-text="'reported ' + task.progress[0].when_display">when</p>
-                <details class="history-expander" stm-if="task.progress.length > 1">
-                    <summary>Show history (<span stm-text="task.progress.length">0</span> updates)</summary>
-                    <ul class="progress-history">
-                        <li stm-for-item-in="task.progress">
-                            <span class="muted" stm-text="item.when_display">when</span> —
-                            <span stm-text="item.summary">summary</span>
-                        </li>
-                    </ul>
-                </details>
-            </div>
+            <pstm-fragment name="progress-feed" entries="task.progress"/>
             <p stm-if="task.progress.length == 0" class="muted">No progress reports yet — the agent reports via
                 <code>saddle progress-update</code>.</p>
 
@@ -267,26 +255,8 @@
                         <span class="badge" stm-if="block.answered_by == 'auto'">auto</span></p>
                 </div>
 
-                <form stm-if="!block.answered && block.block_type == 'question'" stm-action="block.answer_action">
-                    <div class="options">
-                        <label stm-for-option-in="block.options" stm-key="option" class="option-card">
-                            <input type="radio" name="option" stm-attribute.value="option">
-                            <span stm-text="option">option</span>
-                        </label>
-                        <label>
-                            <input type="radio" name="option" value="__other__">
-                            <span>Other:</span>
-                            <textarea name="other" rows="2"></textarea>
-                        </label>
-                    </div>
-                    <button type="submit" class="primary">Answer</button>
-                </form>
-
-                <form stm-if="!block.answered && block.block_type != 'question' && block.block_type != 'review'"
-                      stm-action="block.answer_action" class="inline">
-                    <textarea name="answer" rows="3" placeholder="Your reply (delivered to the agent)"></textarea>
-                    <button type="submit" class="primary">Reply</button>
-                </form>
+                <pstm-fragment name="block-answer"/>
+                <pstm-fragment name="block-reply"/>
             </div>
             <p stm-if="task.open_blocks.length == 0 && task.answered_blocks.length == 0" class="muted">No blocks — the agent has no open questions.</p>