Skip to content

fix: guard suggestion rendering and preserve input text injection#94

Merged
fu050409 merged 3 commits into
mainfrom
copilot/fix-suggestion-injection-bug
May 12, 2026
Merged

fix: guard suggestion rendering and preserve input text injection#94
fu050409 merged 3 commits into
mainfrom
copilot/fix-suggestion-injection-bug

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 12, 2026

Two related bugs in Suggestion: selecting an item could silently corrupt user input, and the panel's container div rendered unconditionally regardless of trigger state.

Root causes

  • Input corruption: onInject(newText, context?.triggerIndex ?? -1) — the -1 fallback reached prevMessage.slice(0, -1) downstream, stripping the last character on every suggestion selection when no context was active.
  • Ghost panel: The absolute-positioned, styled container div lived in sender.tsx and always rendered, leaving a white floating box above the textarea even with no active trigger.

Changes

  • src/suggestion.tsx

    • Return null early when useSuggestionContext returns null — cuts off both the empty panel and the corrupt-injection path in one guard.
    • Use context.triggerIndex directly (no ?? -1); context is guaranteed non-null past the guard.
    • Move the panel container div into Suggestion so the component fully owns its DOM presence.
  • src/sender.tsx

    • Remove the now-redundant wrapper <div>Suggestion renders nothing when inactive, so the container no longer needs to live in the parent.
  • .changes/fix-suggestion-injection.mdpatch:fix changeset entry.

// Before — always rendered a container; injected with invalid index when context was null
<div className="absolute bottom-full ... bg-white ...">
  <Suggestion ... />          // Suggestion rendered List even with no context
</div>

// onChange inside Suggestion:
onInject(newText, context?.triggerIndex ?? -1);  // -1 → slice(0,-1) → last char deleted

// After — Suggestion owns its container and returns null when inactive
if (!context) return null;
// …
onInject(newText, context.triggerIndex);  // always a valid index
Original prompt

Create the next pull request in DevCloudFE/matechat-react to fix the suggestion injection bug and the unwanted empty suggestion panel rendering.

Scope:

  • Modify src/suggestion.tsx.
  • Modify src/sender.tsx only if needed to correctly conditionally render the suggestion container or integrate the fix cleanly.
  • Add a changeset-style description file under .changes/ for this PR using patch:fix.

Required fixes:

  1. Fix the text injection bug caused by fallbacking triggerIndex to -1.
    • The current code path can call onInject(newText, context?.triggerIndex ?? -1).
    • When context is null, downstream slicing with slice(0, -1) can silently delete the last character of the user's input.
    • Ensure injection only happens with a valid suggestion context and valid trigger index.
  2. Prevent the suggestion list/panel from rendering when there is no active trigger context.
    • Today the list/container may render even when context is null, causing an empty floating panel in the UI.
    • Prefer guarding at the source (Suggestion returns null when no context exists), and update parent conditional rendering if necessary.
  3. Preserve the intended suggestion behavior when a trigger is active.
  4. Keep the patch focused; do not include unrelated refactors.

Changes file requirements:

  • Create a new file in .changes/ for this PR.
  • The file must declare a patch fix entry for the package.
  • Suggested summary: fix suggestion injection to avoid input corruption and hide the suggestion panel when inactive.

Suggested PR title:
fix: guard suggestion rendering and preserve input text injection

Acceptance criteria:

  • Selecting a suggestion never deletes the last character due to an invalid -1 trigger index.
  • The suggestion UI is not rendered when there is no active trigger context.
  • Existing active-trigger suggestion behavior still works.
  • A .changes/* file is added with patch:fix.
  • Only the targeted suggestion fix is included in this PR.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: 深度阅读src/在新版的项目代码,审查可能的脆弱性、漏洞和使用体验问题

Assistant: 请提供明确的仓库信息(owner/repo),我就可以开始深度审查 src/ 目录在“新版”代码中的脆弱性、安全漏洞和使用体验问题。

如果你方便,也请一并给我以下任一信息,以便我直接定位“新版”:

  • 分支名,例如 maindeveloprelease/v2
  • 或一个 GitHub 链接(仓库 / tree / blob / PR 都可以)
  • 或说明“新版”指的是默认分支上的当前代码

你可以直接回复例如:

  • owner/repo: octo-org/my-app,分支: main
  • 请审查 https://github.com/octo-org/my-app/tree/main/src

拿到仓库后,我会:

  1. 深度阅读 src/
  2. 排查潜在安全与稳定性风险
  3. 评估明显的 UX / DX 使用体验问题
  4. 给出按严重级别分类的审查结论和修复建议

User: 在matechat-react里

Assistant: 我正在等待你对深度审查请求的确认,一旦你接受,我就继续读取并审查 DevCloudFE/matechat-reactsrc/ 目录。

User: @copilot Accepted Confirmation: Are you sure?

Assistant: 已开始对 DevCloudFE/matechat-reactsrc/ 进行深度审查。

你可以通过下面的任务链接跟踪进度:

data:
  - title: 请对仓库 DevCloudFE/matechat-react 的新版项目代码进行深度审查,重点阅读 src/ 目录,并系统性识别以下问题: 1. 脆弱性与稳定性...
    repository: DevCloudFE/matechat-react
    url: https://github.com/DevCloudFE/matechat-react/tasks/44e1c609-e020-4159-aa45-51fbf0370e80
    state: queued

任务完成后,我会基于实际代码证据给出按严重级别分类的问题清单,以及修复建议和 Top 5 优先修复项。

User: 你能够看到任务执行结果吗,可以的话生成多个任务来针对每一个修复项进行修复和pr。执行审查的Agent报告无法推送多个分支来创建pr。

Assistant: 可以,但我现在还看不到那个深度审查任务的详细执行结果内容,至少在当前对话里还没有自动返回报告正文。

不过你这个目标我可以支持,方式是:

  1. 先拿到审查结果
    你把审查 Agent 的结论贴给我,或者把任务结果链接里可见的报告内容发我。

  2. 我再把问题拆成多个独立修复任务
    我会按“一个问题/一类问题一个 PR”的方式拆分,避免耦合,方便审阅和回滚。

  3. 然后我可以逐个为每个修复项创建 PR
    前提是你对每个 PR 明确指定:

    • 仓库:DevCloudFE/matechat-react
    • 基础分支:例如 main
    • 是否要我直接开 PR,而不是只给方案

需要注意一件事:
我不能直接“基于那个深度研究任务自动批量派生多个 PR”,因为深度研究工具本身只负责调研,不直接暴露成可让我批量消费的结构化修复队列;而创建 PR 需要我针对每个修复项单独发起一次 PR 创建任务。

如果你想现在就开始,最省事的方式是直接给我以下任一项:

方案 A:贴审查结果

把审查报告贴过来,我会直接输出:

  • 修复项拆分清单
  • 每个修复项的 PR 标题
  • 每个修复项的 problem statement
  • 然后按你的确认逐个创建 PR

方案 B:让我先做“修复任务拆分”

如果你不方便贴报告,我也可以基于你的原始目标,先给出一个建议拆分,例如:

  • PR 1:修复潜在 XSS / 富文本渲染风险
  • PR 2:修复消息流渲染中的资源清理与竞态问题
  • PR 3:修复输入组件可访问性与快捷键冲突
  • PR 4:修复错误态/空态/重试机制缺失
  • PR 5:修复类型逃逸与危险断言
  • PR 6:优化长列表与消息滚动体验

但这只是“预拆分”,不如基于真实审查结果准确。

如果你要我直接开始创建多个 PR

请按这个格式回复我:

  • 仓库:DevCloudFE/matechat-react
  • 基础分支:main(或你的实际分支)
  • 审查结果:粘贴内容
  • 执行方式:为每个修复项分别创建 PR

如果你愿意,我下一条就可以先帮你把“常见审查结论”整理成一个适合批量开 PR 的修复任务清单模板。

User: ```
深度代码审查报告:DevCloudFE/matechat-react (src/)
一、脆弱性与稳定性风险
🔴 Critical
C-1:src/suggestion.tsx — triggerIndex 默认值 -1 导致文本注入逻辑错误
位置:src/suggestion.tsx:73-75,src/sender.tsx:132-134

现象:

TSX
// suggestion.tsx:73-75
onInject(newText, context?.triggerIndex ?? -1);
当 context 为 null(没有触发词时),triggerIndex 回退到 -1。随后在 sender.tsx 的 handleTextInject 中:

TSX
const textBefore = prevMessage.slice(0, suggestionStartPosition); // slice(0, -1) → 截掉最后一个字符
const textAfter = prevMessage.slice(currentCaretPosition);
slice(0, -1) 会截去消息末尾最后一个字符,使得用户每次从建议列表选择时都会静默删除最后一字。

风险:静默数据损坏,用户无感知地丢失输入内容。

触发条件:用户点击/选择建议列表中的项目,且 context 为 null(这只有在 List 未被条件渲染的情况下才可触发,与下一个问题关联)。

修复建议:在 Suggestion 组件中,当 context 为 null 时不渲染 List(if (!context) return null),彻底切断该路径。

🔴 Critical
C-2:src/bubble/markdown.tsx — Link 组件未过滤 javascript: href,...

This pull request was created from Copilot chat.

Copilot AI changed the title [WIP] Fix suggestion injection bug and prevent empty suggestion panel rendering fix: guard suggestion rendering and preserve input text injection May 12, 2026
Copilot AI requested a review from fu050409 May 12, 2026 05:19
@fu050409 fu050409 marked this pull request as ready for review May 12, 2026 05:39
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

Workspace change through: 361506f

5 changesets found

Planned changes to release
Package Bump Level Current Version Next Version
@matechat/react major 0.1.1 1.0.0
@matechat/react-playground major 0.0.0 1.0.0

@fu050409 fu050409 merged commit b707094 into main May 12, 2026
2 checks passed
@fu050409 fu050409 deleted the copilot/fix-suggestion-injection-bug branch May 12, 2026 05:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants