Implement shared compact split and unified tool-call diff layout (#270)

# PR Title

Implement shared compact split and unified tool-call diff layout

---
Fixes #268 
# PR Description

## Summary

This PR makes tool-call diffs more compact in both `Unified` and `Split`
views by reducing wasted horizontal space in line-number gutters and
content indentation.

## What changed

- introduced a shared compact-diff framework for tool-call diffs
- kept mobile-specific policy limited to:
  - forcing unified mode below the breakpoint
  - enabling wrap only in mobile unified mode
- added mode-specific compact applicators in the diff viewer:
  - unified applicator
  - split applicator
- reduced gutter width waste by measuring rendered line-number text and
tightening column width around it
- removed unnecessary right-side content padding
- aligned `+` / `-` markers closer to the left edge across both views
- simplified cleanup after gatekeeper review by removing extra plumbing
and residue

## Screenshots

### Before

<img width="581" height="341" alt="image"
src="https://github.com/user-attachments/assets/ec47b256-749a-4afc-8879-aaf33f0b46b6"
/>

### After

<img width="470" height="586" alt="image"
src="https://github.com/user-attachments/assets/7258a5a2-47c4-408d-84bc-1b497761c7ad"
/>

## Architectural approach

This change intentionally uses:

- shared policy in
`packages/ui/src/components/tool-call/diff-render.tsx`
- shared helper/measurement logic in
`packages/ui/src/components/diff-viewer.tsx`
- mode-specific applicators where unified and split DOM differ
- CSS for shared visual spacing and alignment cleanup

The goal was to keep the implementation architecturally clean and avoid
building separate duplicated compact-diff features for:

- mobile vs desktop
- unified vs split

Instead, the feature shares one compact-diff concept and only diverges
where the upstream diff DOM requires separate handling.

## Files changed

- `packages/ui/src/components/tool-call/diff-render.tsx`
- `packages/ui/src/components/diff-viewer.tsx`
- `packages/ui/src/styles/messaging/tool-call.css`
- `packages/ui/src/types/message.ts`

## Validation

Manual validation was performed in the running UI.

Verified manually:

- compact unified gutters on mobile
- compact unified gutters on desktop
- compact split gutters on desktop
- tighter operator alignment in both modes

Also verified:

- `npm run typecheck` passes

## Notes

- This PR is intended to address the compact diff layout problem
described in the related issue.
- Diff-specific CSS still lives in `tool-call.css`; future extraction
into a smaller dedicated stylesheet is possible but not required for
this change.

---------

Co-authored-by: Shantur Rathore <i@shantur.com>
This commit is contained in:
VooDisss
2026-04-02 01:13:32 +03:00
committed by GitHub
parent df16b64a95
commit 4f236ce36f
11 changed files with 384 additions and 35 deletions

View File

@@ -321,6 +321,7 @@
.tool-call-diff-shell {
padding: 0;
scrollbar-gutter: auto;
}
.tool-call-diff-viewer {
@@ -343,6 +344,8 @@
.tool-call-diff-shell .tool-call-diff-viewer {
max-height: none;
overflow: visible;
width: 100%;
min-width: 100%;
}
.tool-call-diff-toolbar-label {
@@ -513,6 +516,84 @@
font-size: var(--font-size-xs);
}
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-content,
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-hunk-content,
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-old-content,
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-new-content {
padding-right: 0 !important;
}
.tool-call-diff-shell[data-diff-mode="unified"] .tool-call-diff-viewer .diff-line-num {
padding-left: 1px !important;
padding-right: 1px !important;
text-align: left !important;
}
.tool-call-diff-shell[data-diff-mode="unified"] .tool-call-diff-viewer .unified-diff-table {
table-layout: fixed;
}
.tool-call-diff-shell[data-diff-mode="unified"] .tool-call-diff-viewer .unified-diff-table-num-col {
width: auto !important;
}
.tool-call-diff-shell[data-diff-mode="unified"] .tool-call-diff-viewer .tool-call-diff-compact-line-number {
display: block;
width: 100%;
overflow: hidden;
text-align: left;
white-space: nowrap;
}
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-old-num,
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-new-num,
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-hunk-action {
padding-left: 2px !important;
padding-right: 2px !important;
text-align: left !important;
}
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-old-num,
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-new-num,
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-old-num [data-line-num],
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-new-num [data-line-num] {
white-space: nowrap !important;
word-break: normal !important;
overflow-wrap: normal !important;
}
.tool-call-diff-shell[data-diff-mode="split"] .tool-call-diff-viewer .diff-line-hunk-action {
padding-top: 1px !important;
padding-bottom: 1px !important;
}
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-content-item {
padding-left: 1.1em !important;
}
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-content-operator {
margin-left: -1.1em !important;
width: 0.9em !important;
min-width: 0.9em !important;
text-indent: 0 !important;
}
@media (max-width: 640px) {
.tool-call-diff-shell[data-diff-mode="unified"] .tool-call-diff-viewer .unified-diff-table-wrapper {
--diff-aside-width: 18px;
}
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-content-item {
padding-left: 1.5em !important;
}
.tool-call-diff-shell .tool-call-diff-viewer .diff-line-content-operator {
margin-left: -1.5em !important;
width: 1.1em !important;
min-width: 1.1em !important;
}
}
.tool-call-markdown .markdown-code-block {
margin: 0;
border-radius: 0;