webui: remove unused diff2html-based diff view
Remove the old diff view implementation that used diff2html library
in favor of the Monaco-based diff2 view. This cleanup removes:
- sketch-diff-view.ts component that used diff2html library
- diff2html static CSS files (diff2html.min.css, diff2.css)
- diff2html npm package dependency from package.json
- Old diff view mode references throughout the codebase
- Demo files for the old diff view component
Changes include:
1. Removed Files:
- webui/src/web-components/sketch-diff-view.ts
- webui/src/diff2html.min.css
- webui/src/diff2.css
- webui/src/web-components/demo/sketch-diff-view.demo.html
2. Updated Components:
- sketch-app-shell.ts: Remove old diff view import, ViewMode type,
CSS selectors, and view switching logic
- sketch-view-mode-select.ts: Update type definitions to remove 'diff' mode
- sketch-terminal.ts: Fix view mode type and correct CSS loading comments
3. Package Management:
- Removed diff2html 3.4.51 dependency from package.json
- Updated package-lock.json to reflect removed dependency
4. Demo Cleanup:
- Removed reference to old diff view demo from index.html
- Updated timeline demo to remove diff2html from dependencies list
The Monaco-based diff2 view (sketch-diff2-view.ts) remains fully
functional and is now the only diff view implementation. All file
picker, range picker, and empty view components continue to work
with the new diff view.
Testing confirms the diff functionality works correctly after cleanup.
Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s22dd1dc722d02125k
diff --git a/webui/src/web-components/demo/index.html b/webui/src/web-components/demo/index.html
index 2dd8f18..cc5697e 100644
--- a/webui/src/web-components/demo/index.html
+++ b/webui/src/web-components/demo/index.html
@@ -7,7 +7,7 @@
<ul>
<li><a href="sketch-app-shell.demo.html">sketch-app-shell</a></li>
<li><a href="sketch-chat-input.demo.html">sketch-chat-input</a></li>
- <li><a href="sketch-diff-view.demo.html">sketch-diff-view</a></li>
+
<li>
<a href="sketch-container-status.demo.html">sketch-container-status</a>
</li>
diff --git a/webui/src/web-components/demo/sketch-diff-view.demo.html b/webui/src/web-components/demo/sketch-diff-view.demo.html
deleted file mode 100644
index 6ab6e62..0000000
--- a/webui/src/web-components/demo/sketch-diff-view.demo.html
+++ /dev/null
@@ -1,109 +0,0 @@
-<!doctype html>
-<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Sketch Diff Viewer Demo</title>
- <link
- rel="stylesheet"
- href="../../../node_modules/diff2html/bundles/css/diff2html.min.css"
- />
- <script type="module" src="../sketch-diff-view.ts"></script>
- <style>
- body {
- font-family:
- -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
- Arial, sans-serif;
- max-width: 1200px;
- margin: 0 auto;
- padding: 2rem;
- }
-
- h1 {
- color: #333;
- margin-bottom: 2rem;
- }
-
- .control-panel {
- margin-bottom: 2rem;
- padding: 1rem;
- background-color: #f0f0f0;
- border-radius: 4px;
- }
-
- input {
- padding: 0.5rem;
- border-radius: 4px;
- border: 1px solid #ccc;
- width: 300px;
- }
-
- button {
- padding: 0.5rem 1rem;
- background-color: #2196f3;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- margin-left: 1rem;
- }
-
- button:hover {
- background-color: #0d8bf2;
- }
- </style>
-
- <script>
- document.addEventListener("DOMContentLoaded", () => {
- const diffViewer = document.getElementById("diffViewer");
- const commitHashInput = document.getElementById("commitHash");
- const viewDiffButton = document.getElementById("viewDiff");
- let commit = false;
- viewDiffButton.addEventListener("click", () => {
- let diffContent = `diff --git a/sample.txt b/sample.txt
-index 1111111..2222222 100644
---- a/sample.txt
-+++ b/sample.txt
-@@ -1,5 +1,5 @@
- This is a sample file
--This line will be removed
-+This line is added as a replacement
- This line stays the same
--Another line to remove
-+A completely new line
- The last line is unchanged`;
- if (commit) {
- // For demo purposes, generate fake diff based on commit hash
- diffContent = `diff --git a/file-${commit.substring(0, 5)}.txt b/file-${commit.substring(0, 5)}.txt
-index 3333333..4444444 100644
---- a/file-${commit.substring(0, 5)}.txt
-+++ b/file-${commit.substring(0, 5)}.txt
-@@ -1,4 +1,6 @@
- File with commit: ${commit}
-+This line was added in commit ${commit}
- This line exists in both versions
--This line was removed in commit ${commit}
-+This line replaced the removed line
-+Another new line added in this commit
- Last line of the file`;
- }
- diffViewer.diffText = diffContent;
- diffViewer.commitHash = commitHashInput.value.trim();
- });
- });
- </script>
- </head>
- <body>
- <h1>Sketch Diff Viewer Demo</h1>
-
- <div class="control-panel">
- <label for="commitHash"
- >Commit Hash (leave empty for unstaged changes):</label
- >
- <input type="text" id="commitHash" placeholder="Enter commit hash" />
- <button id="viewDiff">View Diff</button>
- </div>
-
- <sketch-diff-view id="diffViewer"></sketch-diff-view>
- </body>
-</html>
diff --git a/webui/src/web-components/demo/sketch-timeline.demo.html b/webui/src/web-components/demo/sketch-timeline.demo.html
index b535fa1..3314ed2 100644
--- a/webui/src/web-components/demo/sketch-timeline.demo.html
+++ b/webui/src/web-components/demo/sketch-timeline.demo.html
@@ -105,7 +105,7 @@
tool_call_id: "call_longoutput",
args: '{"command": "cat /app/webui/package.json | grep -A 5 dependencies"}',
result:
- ' "dependencies": {\n "@xterm/addon-fit": "^0.10.0",\n "@xterm/xterm": "^5.5.0",\n "diff2html": "3.4.51",\n "lit": "^3.2.1",\n "marked": "^15.0.7",\n "mermaid": "^11.6.0",\n "sanitize-html": "^2.15.0",\n "vega": "^5.33.0",\n "vega-embed": "^6.29.0",\n "vega-lite": "^5.23.0",\n "react": "^18.2.0",\n "react-dom": "^18.2.0",\n "styled-components": "^6.1.8",\n "tailwindcss": "^3.4.1",\n "typescript": "^5.3.3",\n "zod": "^3.22.4",\n "@types/react": "^18.2.55",\n "@types/react-dom": "^18.2.19",\n "eslint": "^8.56.0",\n "prettier": "^3.2.5"\n },',
+ ' "dependencies": {\n "@xterm/addon-fit": "^0.10.0",\n "@xterm/xterm": "^5.5.0",\n "lit": "^3.2.1",\n "marked": "^15.0.7",\n "mermaid": "^11.6.0",\n "sanitize-html": "^2.15.0",\n "vega": "^5.33.0",\n "vega-embed": "^6.29.0",\n "vega-lite": "^5.23.0",\n "react": "^18.2.0",\n "react-dom": "^18.2.0",\n "styled-components": "^6.1.8",\n "tailwindcss": "^3.4.1",\n "typescript": "^5.3.3",\n "zod": "^3.22.4",\n "@types/react": "^18.2.55",\n "@types/react-dom": "^18.2.19",\n "eslint": "^8.56.0",\n "prettier": "^3.2.5"\n },',
},
],
},
diff --git a/webui/src/web-components/sketch-app-shell.ts b/webui/src/web-components/sketch-app-shell.ts
index 81d2fad..bdab955 100644
--- a/webui/src/web-components/sketch-app-shell.ts
+++ b/webui/src/web-components/sketch-app-shell.ts
@@ -6,8 +6,7 @@
import "./sketch-chat-input";
import "./sketch-container-status";
-import "./sketch-diff-view";
-import { SketchDiffView } from "./sketch-diff-view";
+
import "./sketch-diff2-view";
import { SketchDiff2View } from "./sketch-diff2-view";
import { DefaultGitDataService } from "./git-data-service";
@@ -22,7 +21,7 @@
import { createRef, ref } from "lit/directives/ref.js";
import { SketchChatInput } from "./sketch-chat-input";
-type ViewMode = "chat" | "diff" | "diff2" | "terminal";
+type ViewMode = "chat" | "diff2" | "terminal";
@customElement("sketch-app-shell")
export class SketchAppShell extends LitElement {
@@ -178,7 +177,6 @@
}
/* Allow the container to expand to full width and height in diff mode */
- #view-container-inner.diff-active,
#view-container-inner.diff2-active {
max-width: 100%;
width: 100%;
@@ -192,7 +190,6 @@
/* Individual view styles */
.chat-view,
- .diff-view,
.diff2-view,
.terminal-view {
display: none; /* Hidden by default */
@@ -630,13 +627,13 @@
// Only add view parameter if not in default chat view
if (mode !== "chat") {
url.searchParams.set("view", mode);
- const diffView = this.shadowRoot?.querySelector(
- ".diff-view",
- ) as SketchDiffView;
+ const diff2View = this.shadowRoot?.querySelector(
+ "sketch-diff2-view",
+ ) as SketchDiff2View;
- // If in diff view and there's a commit hash, include that too
- if (mode === "diff" && diffView.commitHash) {
- url.searchParams.set("commit", diffView.commitHash);
+ // If in diff2 view and there's a commit hash, include that too
+ if (mode === "diff2" && diff2View?.commit) {
+ url.searchParams.set("commit", diff2View.commit);
}
}
@@ -656,7 +653,7 @@
* Handle view mode selection event
*/
private _handleViewModeSelect(event: CustomEvent) {
- const mode = event.detail.mode as "chat" | "diff" | "diff2" | "terminal";
+ const mode = event.detail.mode as "chat" | "diff2" | "terminal";
this.toggleViewMode(mode, true);
}
@@ -698,7 +695,7 @@
}
/**
- * Toggle between different view modes: chat, diff, terminal
+ * Toggle between different view modes: chat, diff2, terminal
*/
private toggleViewMode(mode: ViewMode, updateHistory: boolean): void {
// Don't do anything if the mode is already active
@@ -719,25 +716,18 @@
"#view-container-inner",
);
const chatView = this.shadowRoot?.querySelector(".chat-view");
- const diffView = this.shadowRoot?.querySelector(".diff-view");
const diff2View = this.shadowRoot?.querySelector(".diff2-view");
const terminalView = this.shadowRoot?.querySelector(".terminal-view");
// Remove active class from all views
chatView?.classList.remove("view-active");
- diffView?.classList.remove("view-active");
diff2View?.classList.remove("view-active");
terminalView?.classList.remove("view-active");
- // Add/remove diff-active class on view container
- if (mode === "diff") {
- viewContainerInner?.classList.add("diff-active");
- viewContainerInner?.classList.remove("diff2-active");
- } else if (mode === "diff2") {
+ // Add/remove diff2-active class on view container
+ if (mode === "diff2") {
viewContainerInner?.classList.add("diff2-active");
- viewContainerInner?.classList.remove("diff-active");
} else {
- viewContainerInner?.classList.remove("diff-active");
viewContainerInner?.classList.remove("diff2-active");
}
@@ -746,17 +736,6 @@
case "chat":
chatView?.classList.add("view-active");
break;
- case "diff":
- diffView?.classList.add("view-active");
- // Load diff content if we have a diff view
- const diffViewComp =
- this.shadowRoot?.querySelector("sketch-diff-view");
- if (diffViewComp && this.currentCommitHash) {
- (diffViewComp as any).showCommitDiff(this.currentCommitHash);
- } else if (diffViewComp) {
- (diffViewComp as any).loadDiffContent();
- }
- break;
case "diff2":
diff2View?.classList.add("view-active");
@@ -1312,14 +1291,6 @@
></sketch-todo-panel>
</div>
<div
- class="diff-view ${this.viewMode === "diff" ? "view-active" : ""}"
- >
- <sketch-diff-view
- .commitHash=${this.currentCommitHash}
- ></sketch-diff-view>
- </div>
-
- <div
class="diff2-view ${this.viewMode === "diff2" ? "view-active" : ""}"
>
<sketch-diff2-view
diff --git a/webui/src/web-components/sketch-diff-view.ts b/webui/src/web-components/sketch-diff-view.ts
deleted file mode 100644
index d08b520..0000000
--- a/webui/src/web-components/sketch-diff-view.ts
+++ /dev/null
@@ -1,659 +0,0 @@
-import { css, html, LitElement, unsafeCSS } from "lit";
-import { customElement, property, state } from "lit/decorators.js";
-import * as Diff2Html from "diff2html";
-
-@customElement("sketch-diff-view")
-export class SketchDiffView extends LitElement {
- // Current commit hash being viewed
- @property({ type: String })
- commitHash: string = "";
-
- // Selected line in the diff for commenting
- @state()
- private selectedDiffLine: string | null = null;
-
- // The clicked button element used for positioning the comment box
- @state()
- private clickedElement: HTMLElement | null = null;
-
- // View format (side-by-side or line-by-line)
- @state()
- private viewFormat: "side-by-side" | "line-by-line" = "side-by-side";
-
- static styles = css`
- .diff-view {
- flex: 1;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- height: 100%;
- }
-
- .diff-container {
- height: 100%;
- overflow: auto;
- flex: 1;
- padding: 0 1rem;
- }
-
- #diff-view-controls {
- display: flex;
- justify-content: flex-end;
- padding: 10px;
- background: #f8f8f8;
- border-bottom: 1px solid #eee;
- }
-
- .diff-view-format {
- display: flex;
- gap: 10px;
- }
-
- .diff-view-format label {
- cursor: pointer;
- }
-
- .diff2html-content {
- font-family: var(--monospace-font);
- position: relative;
- }
-
- /* Comment box styles */
- .diff-comment-box {
- position: absolute;
- width: 400px;
- background-color: white;
- border: 1px solid #ddd;
- border-radius: 4px;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
- padding: 16px;
- z-index: 1000;
- margin-top: 10px;
- }
-
- .diff-comment-box h3 {
- margin-top: 0;
- margin-bottom: 10px;
- font-size: 16px;
- }
-
- .selected-line {
- margin-bottom: 10px;
- font-size: 14px;
- }
-
- .selected-line pre {
- padding: 6px;
- background: #f5f5f5;
- border: 1px solid #eee;
- border-radius: 3px;
- margin: 5px 0;
- max-height: 100px;
- overflow: auto;
- font-family: var(--monospace-font);
- font-size: 13px;
- white-space: pre-wrap;
- }
-
- #diffCommentInput {
- width: 100%;
- height: 100px;
- padding: 8px;
- border: 1px solid #ddd;
- border-radius: 4px;
- resize: vertical;
- font-family: inherit;
- margin-bottom: 10px;
- }
-
- .diff-comment-buttons {
- display: flex;
- justify-content: flex-end;
- gap: 8px;
- }
-
- .diff-comment-buttons button {
- padding: 6px 12px;
- border-radius: 4px;
- border: 1px solid #ddd;
- background: white;
- cursor: pointer;
- }
-
- .diff-comment-buttons button:hover {
- background: #f5f5f5;
- }
-
- .diff-comment-buttons button#submitDiffComment {
- background: #1a73e8;
- color: white;
- border-color: #1a73e8;
- }
-
- .diff-comment-buttons button#submitDiffComment:hover {
- background: #1967d2;
- }
-
- /* Styles for the comment button on diff lines */
- .d2h-gutter-comment-button {
- position: absolute;
- right: 0;
- top: 50%;
- transform: translateY(-50%);
- visibility: hidden;
- background: rgba(255, 255, 255, 0.8);
- border-radius: 50%;
- width: 16px;
- height: 16px;
- line-height: 13px;
- text-align: center;
- font-size: 14px;
- cursor: pointer;
- color: #666;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
- }
-
- tr:hover .d2h-gutter-comment-button {
- visibility: visible;
- }
-
- .d2h-gutter-comment-button:hover {
- background: white;
- color: #333;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
- }
- `;
-
- constructor() {
- super();
- }
-
- // See https://lit.dev/docs/components/lifecycle/
- connectedCallback() {
- super.connectedCallback();
-
- // Load the diff2html CSS if needed
- this.loadDiff2HtmlCSS();
- }
-
- // Load diff2html CSS into the shadow DOM
- private async loadDiff2HtmlCSS() {
- try {
- // Check if diff2html styles are already loaded
- const styleId = "diff2html-styles";
- if (this.shadowRoot?.getElementById(styleId)) {
- return; // Already loaded
- }
-
- // Fetch the diff2html CSS
- const response = await fetch("static/diff2html.min.css");
-
- if (!response.ok) {
- console.error(
- `Failed to load diff2html CSS: ${response.status} ${response.statusText}`,
- );
- return;
- }
-
- const cssText = await response.text();
-
- // Create a style element and append to shadow DOM
- const style = document.createElement("style");
- style.id = styleId;
- style.textContent = cssText;
- this.shadowRoot?.appendChild(style);
-
- console.log("diff2html CSS loaded into shadow DOM");
- } catch (error) {
- console.error("Error loading diff2html CSS:", error);
- }
- }
-
- // See https://lit.dev/docs/components/lifecycle/
- disconnectedCallback() {
- super.disconnectedCallback();
- }
-
- // Method called to load diff content
- async loadDiffContent() {
- // Wait for the component to be rendered
- await this.updateComplete;
-
- const diff2htmlContent =
- this.shadowRoot?.getElementById("diff2htmlContent");
- if (!diff2htmlContent) return;
-
- try {
- // Build the diff URL - include commit hash if specified
- const diffUrl = this.commitHash
- ? `diff?commit=${this.commitHash}`
- : "diff";
-
- if (this.commitHash) {
- diff2htmlContent.innerHTML = `Loading diff for commit <strong>${this.commitHash}</strong>...`;
- } else {
- diff2htmlContent.innerHTML = "Loading diff...";
- }
-
- // Fetch the diff from the server
- const response = await fetch(diffUrl);
-
- if (!response.ok) {
- throw new Error(
- `Server returned ${response.status}: ${response.statusText}`,
- );
- }
-
- const diffText = await response.text();
-
- if (!diffText || diffText.trim() === "") {
- diff2htmlContent.innerHTML =
- "<span style='color: #666; font-style: italic;'>No changes detected since conversation started.</span>";
- return;
- }
-
- // Render the diff using diff2html
- const diffHtml = Diff2Html.html(diffText, {
- outputFormat: this.viewFormat,
- drawFileList: true,
- matching: "lines",
- renderNothingWhenEmpty: false,
- colorScheme: "light" as any, // Force light mode to match the rest of the UI
- });
-
- // Insert the generated HTML
- diff2htmlContent.innerHTML = diffHtml;
-
- // Add CSS styles to ensure we don't have double scrollbars
- const d2hFiles = diff2htmlContent.querySelectorAll(".d2h-file-wrapper");
- d2hFiles.forEach((file) => {
- const contentElem = file.querySelector(".d2h-files-diff");
- if (contentElem) {
- // Remove internal scrollbar - the outer container will handle scrolling
- (contentElem as HTMLElement).style.overflow = "visible";
- (contentElem as HTMLElement).style.maxHeight = "none";
- }
- });
-
- // Intercept clicks on anchor links within the diff to prevent browser navigation
- this.interceptAnchorClicks(diff2htmlContent);
-
- // Add click event handlers to each code line for commenting
- this.setupDiffLineComments();
- } catch (error) {
- console.error("Error loading diff2html content:", error);
- const errorMessage =
- error instanceof Error ? error.message : "Unknown error";
- diff2htmlContent.innerHTML = `<span style='color: #dc3545;'>Error loading enhanced diff: ${errorMessage}</span>`;
- }
- }
-
- // Handle view format changes
- private handleViewFormatChange(event: Event) {
- const input = event.target as HTMLInputElement;
- if (input.checked) {
- this.viewFormat = input.value as "side-by-side" | "line-by-line";
- this.loadDiffContent();
- }
- }
-
- /**
- * Setup handlers for diff code lines to enable commenting
- */
- private setupDiffLineComments(): void {
- const diff2htmlContent =
- this.shadowRoot?.getElementById("diff2htmlContent");
- if (!diff2htmlContent) return;
-
- // Add plus buttons to each code line
- this.addCommentButtonsToCodeLines();
-
- // Use event delegation for handling clicks on plus buttons
- diff2htmlContent.addEventListener("click", (event) => {
- const target = event.target as HTMLElement;
-
- // Only respond to clicks on the plus button
- if (target.classList.contains("d2h-gutter-comment-button")) {
- // Find the parent row first
- const row = target.closest("tr");
- if (!row) return;
-
- // Then find the code line in that row
- const codeLine =
- row.querySelector(".d2h-code-side-line") ||
- row.querySelector(".d2h-code-line");
- if (!codeLine) return;
-
- // Get the line text content
- const lineContent = codeLine.querySelector(".d2h-code-line-ctn");
- if (!lineContent) return;
-
- const lineText = lineContent.textContent?.trim() || "";
-
- // Get file name to add context
- const fileHeader = codeLine
- .closest(".d2h-file-wrapper")
- ?.querySelector(".d2h-file-name");
- const fileName = fileHeader
- ? fileHeader.textContent?.trim()
- : "Unknown file";
-
- // Get line number if available
- const lineNumElem = codeLine
- .closest("tr")
- ?.querySelector(".d2h-code-side-linenumber");
- const lineNum = lineNumElem ? lineNumElem.textContent?.trim() : "";
- const lineInfo = lineNum ? `Line ${lineNum}: ` : "";
-
- // Format the line for the comment box with file context and line number
- const formattedLine = `${fileName} ${lineInfo}${lineText}`;
-
- console.log("Comment button clicked for line: ", formattedLine);
-
- // Open the comment box with this line and store the clicked element for positioning
- this.clickedElement = target;
- this.openDiffCommentBox(formattedLine);
-
- // Prevent event from bubbling up
- event.stopPropagation();
- }
- });
- }
-
- /**
- * Add plus buttons to each table row in the diff for commenting
- */
- private addCommentButtonsToCodeLines(): void {
- const diff2htmlContent =
- this.shadowRoot?.getElementById("diff2htmlContent");
- if (!diff2htmlContent) return;
-
- // Target code lines first, then find their parent rows
- const codeLines = diff2htmlContent.querySelectorAll(
- ".d2h-code-side-line, .d2h-code-line",
- );
-
- // Create a Set to store unique rows to avoid duplicates
- const rowsSet = new Set<HTMLElement>();
-
- // Get all rows that contain code lines
- codeLines.forEach((line) => {
- const row = line.closest("tr");
- if (row) rowsSet.add(row as HTMLElement);
- });
-
- // Convert Set back to array for processing
- const codeRows = Array.from(rowsSet);
-
- codeRows.forEach((row) => {
- const rowElem = row as HTMLElement;
-
- // Skip info lines without actual code (e.g., "file added")
- if (rowElem.querySelector(".d2h-info")) {
- return;
- }
-
- // Find the code line number element (first TD in the row)
- const lineNumberCell = rowElem.querySelector(
- ".d2h-code-side-linenumber, .d2h-code-linenumber",
- );
-
- if (!lineNumberCell) return;
-
- // Create the plus button
- const plusButton = document.createElement("span");
- plusButton.className = "d2h-gutter-comment-button";
- plusButton.innerHTML = "+";
- plusButton.title = "Add a comment on this line";
-
- // Add button to the line number cell for proper positioning
- (lineNumberCell as HTMLElement).style.position = "relative"; // Ensure positioning context
- lineNumberCell.appendChild(plusButton);
- });
- }
-
- /**
- * Open the comment box for a selected diff line
- */
- private openDiffCommentBox(lineText: string): void {
- // Make sure the comment box div exists
- const commentBoxId = "diffCommentBox";
- let commentBox = this.shadowRoot?.getElementById(commentBoxId);
-
- // If it doesn't exist, create it
- if (!commentBox) {
- commentBox = document.createElement("div");
- commentBox.id = commentBoxId;
- commentBox.className = "diff-comment-box";
-
- // Create the comment box contents
- commentBox.innerHTML = `
- <h3>Add a comment</h3>
- <div class="selected-line">
- Line:
- <pre id="selectedLine"></pre>
- </div>
- <textarea
- id="diffCommentInput"
- placeholder="Enter your comment about this line..."
- ></textarea>
- <div class="diff-comment-buttons">
- <button id="cancelDiffComment">Cancel</button>
- <button id="submitDiffComment">Add Comment</button>
- </div>
- `;
-
- // Append the comment box to the diff container to ensure proper positioning
- const diffContainer = this.shadowRoot?.querySelector(".diff-container");
- if (diffContainer) {
- diffContainer.appendChild(commentBox);
- } else {
- this.shadowRoot?.appendChild(commentBox);
- }
- }
-
- // Store the selected line
- this.selectedDiffLine = lineText;
-
- // Display the line in the comment box
- const selectedLine = this.shadowRoot?.getElementById("selectedLine");
- if (selectedLine) {
- selectedLine.textContent = lineText;
- }
-
- // Reset the comment input
- const commentInput = this.shadowRoot?.getElementById(
- "diffCommentInput",
- ) as HTMLTextAreaElement;
- if (commentInput) {
- commentInput.value = "";
- }
-
- // Show the comment box and position it below the clicked line
- if (commentBox && this.clickedElement) {
- // Get the row that contains the clicked button
- const row = this.clickedElement.closest("tr");
- if (row) {
- // Get the position of the row
- const rowRect = row.getBoundingClientRect();
- const diffContainerRect = this.shadowRoot
- ?.querySelector(".diff-container")
- ?.getBoundingClientRect();
-
- if (diffContainerRect) {
- // Position the comment box below the row
- const topPosition =
- rowRect.bottom -
- diffContainerRect.top +
- this.shadowRoot!.querySelector(".diff-container")!.scrollTop;
- const leftPosition = rowRect.left - diffContainerRect.left;
-
- commentBox.style.top = `${topPosition}px`;
- commentBox.style.left = `${leftPosition}px`;
- commentBox.style.display = "block";
- }
- } else {
- // Fallback if we can't find the row
- commentBox.style.display = "block";
- }
- } else if (commentBox) {
- // Fallback if we don't have clickedElement
- commentBox.style.display = "block";
- }
-
- // Add event listeners for submit and cancel buttons
- const submitButton = this.shadowRoot?.getElementById("submitDiffComment");
- if (submitButton) {
- submitButton.onclick = () => this.submitDiffComment();
- }
-
- const cancelButton = this.shadowRoot?.getElementById("cancelDiffComment");
- if (cancelButton) {
- cancelButton.onclick = () => this.closeDiffCommentBox();
- }
-
- // Add keydown event listener to handle Escape key
- if (commentInput) {
- commentInput.addEventListener("keydown", (event) => {
- // If Escape key is pressed
- if (event.key === "Escape") {
- // If the comment input is empty, dismiss the comment box
- if (commentInput.value.trim() === "") {
- this.closeDiffCommentBox();
- }
- }
- });
- }
-
- // Focus on the comment input
- if (commentInput) {
- commentInput.focus();
- }
- }
-
- /**
- * Close the diff comment box without submitting
- */
- private closeDiffCommentBox(): void {
- const commentBox = this.shadowRoot?.getElementById("diffCommentBox");
- if (commentBox) {
- commentBox.style.display = "none";
- }
- this.selectedDiffLine = null;
- this.clickedElement = null;
- }
-
- /**
- * Submit a comment on a diff line
- */
- private submitDiffComment(): void {
- const commentInput = this.shadowRoot?.getElementById(
- "diffCommentInput",
- ) as HTMLTextAreaElement;
-
- if (!commentInput) return;
-
- const comment = commentInput.value.trim();
-
- // Validate inputs
- if (!this.selectedDiffLine || !comment) {
- alert("Please select a line and enter a comment.");
- return;
- }
-
- // Format the comment in a readable way
- const formattedComment = `\`\`\`\n${this.selectedDiffLine}\n\`\`\`\n\n${comment}`;
-
- // Dispatch a custom event with the formatted comment
- const event = new CustomEvent("diff-comment", {
- detail: { comment: formattedComment },
- bubbles: true,
- composed: true,
- });
- this.dispatchEvent(event);
-
- // Close only the comment box but keep the diff view open
- this.closeDiffCommentBox();
- }
-
- // Clear the current state
- public clearState(): void {
- this.commitHash = "";
- }
-
- // Show diff for a specific commit
- public showCommitDiff(commitHash: string): void {
- // Store the commit hash
- this.commitHash = commitHash;
- // Load the diff content
- this.loadDiffContent();
- }
-
- /**
- * Intercept clicks on anchor links within the diff to prevent default browser navigation
- * and instead scroll to the target element without changing URL
- *
- * @param container The container element containing diff content
- */
- private interceptAnchorClicks(container: HTMLElement): void {
- const anchors = container.querySelectorAll('a[href^="#"]');
-
- anchors.forEach((anchor) => {
- anchor.addEventListener("click", (event) => {
- event.preventDefault();
-
- // Extract the target ID from the href
- const href = (anchor as HTMLAnchorElement).getAttribute("href");
- if (!href || !href.startsWith("#")) return;
-
- const targetId = href.substring(1);
- const targetElement = container.querySelector(`[id="${targetId}"]`);
-
- if (targetElement) {
- // Scroll the target element into view
- targetElement.scrollIntoView({ behavior: "smooth" });
- }
- });
- });
- }
-
- render() {
- return html`
- <div class="diff-view">
- <div class="diff-container">
- <div id="diff-view-controls">
- <div class="diff-view-format">
- <label>
- <input
- type="radio"
- name="diffViewFormat"
- value="side-by-side"
- ?checked=${this.viewFormat === "side-by-side"}
- @change=${this.handleViewFormatChange}
- />
- Side-by-side
- </label>
- <label>
- <input
- type="radio"
- name="diffViewFormat"
- value="line-by-line"
- ?checked=${this.viewFormat === "line-by-line"}
- @change=${this.handleViewFormatChange}
- />
- Line-by-line
- </label>
- </div>
- </div>
- <div id="diff2htmlContent" class="diff2html-content"></div>
- </div>
- </div>
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "sketch-diff-view": SketchDiffView;
- }
-}
diff --git a/webui/src/web-components/sketch-terminal.ts b/webui/src/web-components/sketch-terminal.ts
index b405657..0c3b9a7 100644
--- a/webui/src/web-components/sketch-terminal.ts
+++ b/webui/src/web-components/sketch-terminal.ts
@@ -91,7 +91,7 @@
* Handle view mode selection event to detect when terminal becomes visible
*/
private _handleViewModeSelect(event: CustomEvent) {
- const mode = event.detail.mode as "chat" | "diff" | "terminal";
+ const mode = event.detail.mode as "chat" | "diff2" | "terminal";
if (mode === "terminal") {
// Terminal tab is now visible
if (!this.isInitialized) {
@@ -112,13 +112,13 @@
// Load xterm CSS into the shadow DOM
private async loadXtermlCSS() {
try {
- // Check if diff2html styles are already loaded
+ // Check if xterm styles are already loaded
const styleId = "xterm-styles";
if (this.shadowRoot?.getElementById(styleId)) {
return; // Already loaded
}
- // Fetch the diff2html CSS
+ // Fetch the xterm CSS
const response = await fetch("static/xterm.css");
if (!response.ok) {
diff --git a/webui/src/web-components/sketch-view-mode-select.ts b/webui/src/web-components/sketch-view-mode-select.ts
index 80eb0e6..c83b964 100644
--- a/webui/src/web-components/sketch-view-mode-select.ts
+++ b/webui/src/web-components/sketch-view-mode-select.ts
@@ -6,7 +6,7 @@
export class SketchViewModeSelect extends LitElement {
// Current active mode
@property()
- activeMode: "chat" | "diff" | "diff2" | "terminal" = "chat";
+ activeMode: "chat" | "diff2" | "terminal" = "chat";
// Header bar: view mode buttons
static styles = css`
@@ -87,7 +87,7 @@
/**
* Handle view mode button clicks
*/
- private _handleViewModeClick(mode: "chat" | "diff" | "diff2" | "terminal") {
+ private _handleViewModeClick(mode: "chat" | "diff2" | "terminal") {
// Dispatch a custom event to notify the app shell to change the view
const event = new CustomEvent("view-mode-select", {
detail: { mode },