Refactored to use the unified handler output format from setMessages():
Removed always_enabled: true — plugin is opt-in, not mandatory for A0 operation Eliminated manual DOM stamping (data-log-no) — log number now available via context.results[].args.no Eliminated full-document querySelectorAll scan — action bar accessed directly via context.results[].result.element No changes to backend API handler (branch_chat.py)
keyboardstaff committed
Feb 25, 2026 at 07:22 UTC
f3c517ee45998f0da9299d2e592373da7af7dc28
2 files changed
+10
-30
plugins/chat_branching/extensions/webui/set_messages_after_loop/inject-branch-buttons.js
+9
-28
@@ -1,41 +1,22 @@
1
// Chat Branching Plugin — injects a "branch" button into every message's action bar.
2
// Runs as a set_messages_after_loop JS extension.
3
+//
4
+// Uses the unified handler output: context.results is an array of
5
+// { args: { no, id, type, … }, result: { element, … } } from each setMessage() call.
6
7
import { createActionButton } from "/components/messages/action-buttons/simple-action-buttons.js";
8
import { callJsonApi } from "/js/api.js";
9
7
-const LOG_NO_ATTR = "data-log-no";
8
-
9
-/**
10
- * default export called by callJsExtensions("set_messages_after_loop", context)
11
- * context.messages is the raw log items array with { no, id, type, ... }
12
- */
10
export default async function injectBranchButtons(context) {
14
- if (!context?.messages?.length) return;
15
-
16
- // 1. Stamp every rendered element with its log "no" so the button can read it.
17
- for (const msg of context.messages) {
18
- const domId = msg.id || msg.no;
19
- // message containers use id="message-{id}", process steps use id="process-step-{id}"
20
- const el =
21
- document.getElementById(`message-${domId}`) ||
22
- document.getElementById(`process-step-${domId}`);
23
- if (el) el.setAttribute(LOG_NO_ATTR, String(msg.no));
24
- }
11
+ if (!context?.results?.length) return;
12
26
- // 2. Inject branch button into every action bar.
27
- // Core renderers clear and rebuild action-button children on each update,
28
- // so we check for the actual button element instead of a marker attribute.
29
- const bars = document.querySelectorAll(".step-action-buttons");
30
- for (const bar of bars) {
31
- if (bar.querySelector(".action-fork_right")) continue;
13
+ for (const { args, result } of context.results) {
14
+ if (!result?.element || args.no == null) continue;
15
33
- // Resolve the log no from the nearest stamped ancestor
34
- const stamped = bar.closest(`[${LOG_NO_ATTR}]`);
35
- if (!stamped) continue;
36
- const logNo = Number(stamped.getAttribute(LOG_NO_ATTR));
37
- if (Number.isNaN(logNo)) continue;
16
+ const bar = result.element.querySelector(".step-action-buttons");
17
+ if (!bar || bar.querySelector(".action-fork_right")) continue;
18
19
+ const logNo = args.no;
20
const btn = createActionButton("fork_right", "Branch chat", async () => {
21
const ctxid = globalThis.getContext?.();
22
if (!ctxid) throw new Error("No active chat");
plugins/chat_branching/plugin.yaml
+1
-2
@@ -1,4 +1,3 @@
1
name: Chat Branching
2
description: Branch a chat from any message, creating a new chat with history up to that point.
3
-version: 1.0.0
4
-always_enabled: true
\ No newline at end of file
3
+version: 1.0.0
\ No newline at end of file