blob: 8122db743bde44bc369651530254c11a4ad53b7d [file] [log] [blame]
import {css, html, LitElement} from 'lit';
import {repeat} from 'lit/directives/repeat.js';
import {customElement, property} from 'lit/decorators.js';
import {State, TimelineMessage} from '../types';
import './sketch-timeline-message'
@customElement('sketch-timeline')
export class SketchTimeline extends LitElement {
@property()
messages: TimelineMessage[] = [];
// See https://lit.dev/docs/components/styles/ for how lit-element handles CSS.
// Note that these styles only apply to the scope of this web component's
// shadow DOM node, so they won't leak out or collide with CSS declared in
// other components or the containing web page (...unless you want it to do that).
static styles = css`
/* Hide views initially to prevent flash of content */
.timeline-container .timeline,
.timeline-container .diff-view,
.timeline-container .chart-view,
.timeline-container .terminal-view {
visibility: hidden;
}
/* Will be set by JavaScript once we know which view to display */
.timeline-container.view-initialized .timeline,
.timeline-container.view-initialized .diff-view,
.timeline-container.view-initialized .chart-view,
.timeline-container.view-initialized .terminal-view {
visibility: visible;
}
.timeline-container {
width: 100%;
position: relative;
}
/* Timeline styles that should remain unchanged */
.timeline {
position: relative;
margin: 10px 0;
scroll-behavior: smooth;
}
.timeline::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 15px;
width: 2px;
background: #e0e0e0;
border-radius: 1px;
}
/* Hide the timeline vertical line when there are no messages */
.timeline.empty::before {
display: none;
}
`;
constructor() {
super();
// Binding methods
this._handleShowCommitDiff = this._handleShowCommitDiff.bind(this);
}
/**
* Handle showCommitDiff event
*/
private _handleShowCommitDiff(event: CustomEvent) {
const { commitHash } = event.detail;
if (commitHash) {
// Bubble up the event to the app shell
const newEvent = new CustomEvent('show-commit-diff', {
detail: { commitHash },
bubbles: true,
composed: true
});
this.dispatchEvent(newEvent);
}
}
// See https://lit.dev/docs/components/lifecycle/
connectedCallback() {
super.connectedCallback();
// Listen for showCommitDiff events from the renderer
document.addEventListener('showCommitDiff', this._handleShowCommitDiff as EventListener);
}
// See https://lit.dev/docs/components/lifecycle/
disconnectedCallback() {
super.disconnectedCallback();
// Remove event listeners
document.removeEventListener('showCommitDiff', this._handleShowCommitDiff as EventListener);
}
messageKey(message: TimelineMessage): string {
// If the message has tool calls, and any of the tool_calls get a response, we need to
// re-render that message.
const toolCallResponses = message.tool_calls?.filter((tc)=>tc.result_message).map((tc)=>tc.tool_call_id).join('-');
return `message-${message.idx}-${toolCallResponses}`;
}
render() {
return html`
<div class="timeline-container">
${repeat(this.messages, this.messageKey, (message, index) => {
let previousMessage: TimelineMessage;
if (index > 0) {
previousMessage = this.messages[index-1];
}
return html`<sketch-timeline-message .message=${message} .previousMessage=${previousMessage}></sketch-timeline-message>`;
})}
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"sketch-timeline": SketchTimeline;
}
}