blob: 7471dedcdd440db035004af1a8ea3e7d3ecff2fc [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;
}
}