diff --git a/.changeset/edit-diff-contained-scroll.md b/.changeset/edit-diff-contained-scroll.md new file mode 100644 index 0000000..00368b4 --- /dev/null +++ b/.changeset/edit-diff-contained-scroll.md @@ -0,0 +1,5 @@ +--- +"@jmfederico/pi-web": patch +--- + +Contain long edit diff lines inside the diff viewer so they scroll horizontally within the tool card instead of widening the chat transcript. diff --git a/.changeset/sticky-chat-headers.md b/.changeset/sticky-chat-headers.md new file mode 100644 index 0000000..7a42be6 --- /dev/null +++ b/.changeset/sticky-chat-headers.md @@ -0,0 +1,5 @@ +--- +"@jmfederico/pi-web": patch +--- + +Keep chat bubble and event group headers sticky while scrolling so long messages remain easier to orient within the transcript. diff --git a/src/client/src/components/ToolExecutionView.ts b/src/client/src/components/ToolExecutionView.ts index 8488c9c..0d5da48 100644 --- a/src/client/src/components/ToolExecutionView.ts +++ b/src/client/src/components/ToolExecutionView.ts @@ -98,8 +98,8 @@ export class ToolExecutionView extends LitElement { } static override styles = css` - :host { display: block; max-width: 100%; min-width: 0; color: var(--pi-text); } - .tool-card { display: grid; gap: 8px; border: 1px solid var(--pi-border); border-radius: 8px; background: var(--pi-bg); padding: 9px; color: var(--pi-text); } + :host { display: block; width: 100%; max-width: 100%; min-width: 0; color: var(--pi-text); } + .tool-card { display: grid; gap: 8px; width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; overflow: hidden; border: 1px solid var(--pi-border); border-radius: 8px; background: var(--pi-bg); padding: 9px; color: var(--pi-text); } .tool-card.running, .tool-card.pending { border-color: var(--pi-warning-border); background: var(--pi-warning-surface); } .tool-card.success { border-color: var(--pi-success-border); background: var(--pi-success-bg); } .tool-card.error { border-color: var(--pi-danger); background: color-mix(in srgb, var(--pi-danger) 10%, var(--pi-bg)); } @@ -119,13 +119,15 @@ export class ToolExecutionView extends LitElement { .error-text { margin: 0; border: 1px solid var(--pi-danger); border-radius: 7px; background: color-mix(in srgb, var(--pi-danger) 10%, var(--pi-bg)); color: var(--pi-danger); padding: 8px; white-space: pre-wrap; overflow-wrap: anywhere; font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } .text-body { border-top: 1px solid var(--pi-border-muted); padding-top: 6px; } .text-body pre { margin: 6px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--pi-text); } - .diff-details { border-top: 1px solid var(--pi-border-muted); padding-top: 6px; } - .diff-details > summary { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; color: var(--pi-muted); cursor: pointer; } - .diff-details > summary small { color: var(--pi-dim); } - .diff-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 8px; color: var(--pi-muted); font-size: 12px; } + .diff-details { min-width: 0; max-width: 100%; border-top: 1px solid var(--pi-border-muted); padding-top: 6px; } + .diff-details > summary { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; min-width: 0; color: var(--pi-muted); cursor: pointer; } + .diff-details > summary span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .diff-details > summary small { flex: 0 0 auto; color: var(--pi-dim); } + .diff-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; margin-top: 8px; color: var(--pi-muted); font-size: 12px; } + .diff-toolbar span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } button { border: 1px solid var(--pi-border); border-radius: 6px; background: var(--pi-surface); color: var(--pi-text); padding: 3px 7px; font: 12px system-ui, sans-serif; cursor: pointer; } button:hover, button:focus { border-color: var(--pi-accent); } - .diff { margin: 0; max-width: 100%; overflow-x: auto; overflow-y: hidden; border: 1px solid var(--pi-border-muted); border-radius: 7px; background: var(--pi-bg); padding: 8px 0; color: var(--pi-muted); font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; line-height: 1.45; } + .diff { box-sizing: border-box; width: 100%; max-width: 100%; min-width: 0; margin: 0; overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; border: 1px solid var(--pi-border-muted); border-radius: 7px; background: var(--pi-bg); padding: 8px 0; color: var(--pi-muted); font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; line-height: 1.45; } .diff span { display: block; min-height: 1.45em; padding: 0 8px; white-space: pre; } .diff .context { color: var(--pi-muted); } .diff .hunk { color: var(--pi-accent); } diff --git a/src/client/src/components/shared.ts b/src/client/src/components/shared.ts index 7e7b1ef..7d41821 100644 --- a/src/client/src/components/shared.ts +++ b/src/client/src/components/shared.ts @@ -181,7 +181,7 @@ export const chatStyles = css` .activity-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; opacity: .45; flex: 0 0 auto; } .activity-dock.active .dot { animation: pulse 1s ease-in-out infinite; opacity: 1; } - .msg { max-width: 100%; min-width: 0; box-sizing: border-box; margin: 0 0 14px; padding: 12px; border: 1px solid var(--pi-border); border-radius: 10px; background: var(--pi-surface); overflow: hidden; } + .msg { max-width: 100%; min-width: 0; box-sizing: border-box; margin: 0 0 14px; padding: 12px; border: 1px solid var(--pi-border); border-radius: 10px; background: var(--pi-surface); overflow: visible; } .msg.user { border-color: var(--pi-accent-border); background: var(--pi-selection-bg); } .msg.tool { border-color: var(--pi-warning-border); background: var(--pi-warning-surface); color: var(--pi-warning); } .msg.tool-execution-shell { padding: 0; border: 0; background: transparent; color: var(--pi-text); } @@ -190,11 +190,11 @@ export const chatStyles = css` .msg.skill { border-color: var(--pi-purple-border); background: var(--pi-purple-surface); } .msg.event-group { padding: 0; border-color: var(--pi-border); background: var(--pi-bg); color: var(--pi-muted); } .msg.event-group.live { border-color: var(--pi-success-border); background: var(--pi-success-bg); } - .msg.event-group > summary { display: flex; align-items: center; gap: 8px; padding: 8px 12px; color: var(--pi-muted); } - .msg.event-group.live > summary { color: var(--pi-success); } + .msg.event-group > summary { position: sticky; top: -16px; z-index: 3; display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 9px 9px 0 0; border-bottom: 1px solid var(--pi-border-muted); background: var(--pi-bg); color: var(--pi-muted); } + .msg.event-group.live > summary { border-bottom-color: var(--pi-success-border); background: var(--pi-success-bg); color: var(--pi-success); } .msg.event-group > summary .label { margin: 0; } .group-body { padding: 0 12px 12px; } - .group-msg { max-width: 100%; min-width: 0; box-sizing: border-box; padding: 10px 0; border-top: 1px solid var(--pi-border-muted); color: var(--pi-text); overflow: hidden; } + .group-msg { max-width: 100%; min-width: 0; box-sizing: border-box; padding: 10px 0; border-top: 1px solid var(--pi-border-muted); color: var(--pi-text); overflow: visible; } .group-msg.tool { color: var(--pi-warning); } .group-msg.tool-execution-shell { color: var(--pi-text); } .group-msg.system { color: var(--pi-danger); } @@ -217,7 +217,13 @@ export const chatStyles = css` .session-activity.receiving strong { color: var(--pi-success); } .session-activity span, .session-activity small { color: var(--pi-muted); } .history-boundary small { color: var(--pi-dim); } - .msg-header { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; } + .msg-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 22px; margin-bottom: 8px; } + .msg > .msg-header { position: sticky; top: -16px; z-index: 2; margin: -12px -12px 8px; padding: 7px 10px 6px; border-radius: 9px 9px 0 0; border-bottom: 1px solid color-mix(in srgb, var(--pi-border-muted) 35%, transparent); background: var(--pi-surface); box-shadow: 0 8px 18px var(--pi-shadow-soft); } + .msg.user > .msg-header { border-bottom-color: color-mix(in srgb, var(--pi-accent-border) 35%, transparent); background: var(--pi-selection-bg); } + .msg.tool > .msg-header { border-bottom-color: color-mix(in srgb, var(--pi-warning-border) 35%, transparent); background: var(--pi-warning-surface); } + .msg.bash > .msg-header { border-bottom-color: color-mix(in srgb, var(--pi-success) 35%, transparent); background: var(--pi-success-bg); } + .msg.skill > .msg-header { border-bottom-color: color-mix(in srgb, var(--pi-purple-border) 35%, transparent); background: var(--pi-purple-surface); } + .group-msg > .msg-header { position: sticky; top: -16px; z-index: 2; margin: -10px 0 8px; padding: 7px 0 6px; border-bottom: 1px solid color-mix(in srgb, var(--pi-border-muted) 35%, transparent); background: var(--pi-bg); } .msg-header-trailing { min-width: 0; display: inline-flex; align-items: baseline; justify-content: flex-end; gap: 8px; } .msg-actions { display: inline-flex; gap: 6px; opacity: 0; transition: opacity .12s ease; } .msg-action { display: inline-grid; place-items: center; width: 24px; height: 24px; border: 1px solid var(--pi-border); border-radius: 6px; background: var(--pi-surface); color: var(--pi-muted); padding: 0; font: 14px system-ui, sans-serif; line-height: 1; cursor: pointer; }