From 4a72008b2e54f495411edb8869a64751e4331946 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 21 Oct 2026 17:21:32 +0900 Subject: [PATCH] feat(conversation): follow active reasoning while preserving manual reading --- scripts/completed-fold-nested-regression.tsx | 9 +- scripts/conversation-renderer-regression.tsx | 4 +- scripts/reasoning-follow-dom-regression.tsx | 237 ++++++++++++++++++ scripts/run-alignment-regression.mjs | 6 +- scripts/run-reasoning-follow-regression.mjs | 13 + src/renderer/src/components/ChatPane.tsx | 21 +- src/renderer/src/components/MessageBubble.tsx | 9 +- .../workbuddy/conversation/CompletedFold.tsx | 36 ++- .../conversation/SpecializedBlocks.tsx | 118 +++++++-- .../conversation/disclosure-state.tsx | 48 +++- .../conversation/reasoning-reading-anchor.ts | 61 +++++ .../workbuddy/conversation/source-styles.css | 8 + 12 files changed, 530 insertions(+), 40 deletions(-) create mode 100644 scripts/reasoning-follow-dom-regression.tsx create mode 100644 scripts/run-reasoning-follow-regression.mjs create mode 100644 src/renderer/src/workbuddy/conversation/reasoning-reading-anchor.ts diff --git a/scripts/completed-fold-nested-regression.tsx b/scripts/completed-fold-nested-regression.tsx index ae085cd..7ad7bb2 100644 --- a/scripts/completed-fold-nested-regression.tsx +++ b/scripts/completed-fold-nested-regression.tsx @@ -3,9 +3,9 @@ import assert from 'node:assert/strict' import { createRequire } from 'node:module' const { JSDOM } = createRequire(process.cwd() + '/package.json')(process.env.YCODE_TEST_DOM_MODULE || 'jsdom') const dom = new JSDOM('
', { pretendToBeVisual: true, url: 'http://localhost/' }) -for (const key of ['window', 'document', 'Node', 'Element', 'HTMLElement', 'MouseEvent', 'KeyboardEvent', 'Event']) Object.defineProperty(globalThis, key, { value: dom.window[key], configurable: true }) +for (const key of ['window', 'document', 'Node', 'Element', 'HTMLElement', 'MouseEvent', 'KeyboardEvent', 'Event', 'CustomEvent']) Object.defineProperty(globalThis, key, { value: dom.window[key], configurable: true }) Object.defineProperty(globalThis, 'navigator', { value: dom.window.navigator, configurable: true }) -Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true }) +Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true, requestAnimationFrame: dom.window.requestAnimationFrame.bind(dom.window), cancelAnimationFrame: dom.window.cancelAnimationFrame.bind(dom.window) }) const { act } = require('react') as typeof import('react') const { createRoot } = require('react-dom/client') as typeof import('react-dom/client') const { CompletedFold } = require('../src/renderer/src/workbuddy/conversation/CompletedFold') as typeof import('../src/renderer/src/workbuddy/conversation/CompletedFold') @@ -34,7 +34,7 @@ async function main() { await act(async () => root.render()) assert.equal(expanded(outer), 'false') await keyboard(outer, 'Enter') - for (const selector of [group, thinking, ordinary, question]) { assert.equal(expanded(selector), 'false'); await click(selector); assert.equal(expanded(selector), 'true') } + for (const selector of [group, thinking, ordinary, question]) { assert.equal(expanded(selector), 'false'); await click(selector); assert.equal(expanded(selector), 'true', `${selector} opens on click`) } assert.equal(expanded(second), 'false', 'local summaries are independent') for (const selector of ['.cb-task-result-item-title', '.cb-plan-header-left']) { assert.equal(expanded(selector), 'true'); await click(selector); assert.equal(expanded(selector), 'false') } await keyboard(outer, ' ') @@ -59,8 +59,7 @@ async function main() { await act(async () => root.render()) for (const selector of [group, thinking, ordinary, question]) await click(selector) await act(async () => root.render()) - assert.equal(expanded(outer), 'false', 'completion starts folded after live events') - await click(outer) + assert.equal(expanded(outer), 'true', 'completion preserves manually opened reasoning reader') for (const selector of [group, thinking, ordinary, question]) assert.equal(expanded(selector), 'true', `${selector} survives live-to-completed reparenting`) await act(async () => root.unmount()); dom.window.close() console.log('completed-fold-nested: real question/tool/thinking/local-summary remount, event, keyboard and identity contracts passed') diff --git a/scripts/conversation-renderer-regression.tsx b/scripts/conversation-renderer-regression.tsx index 4d6f423..a668bba 100644 --- a/scripts/conversation-renderer-regression.tsx +++ b/scripts/conversation-renderer-regression.tsx @@ -355,10 +355,12 @@ const collapsedThinking = renderToStaticMarkup(