Enhance commit display in UI

Completely redesigned the commit display UI to make it more intuitive and useful:

1. Format & Appearance:
   - Format commit display as 'hash (branch) subject' matching git log --decorate
   - Improve spacing and readability of all elements
   - Add 'View Diff' button directly after the commit subject
   - Remove extraneous whitespace for a cleaner look

2. Interactive Features:
   - Make commit hash and branch name directly clickable to copy to clipboard
   - Show a floating 'Copied!' message when text is copied
   - Add hover effects to indicate clickable elements
   - Add tooltips showing full hash and branch name on hover

3. UX Improvements:
   - Show floating feedback messages instead of modifying content
   - Animate feedback messages for better visibility
   - Only underline branch name on hover, not the surrounding parentheses
   - Use consistent styling for all interactive elements

Co-Authored-By: sketch
diff --git a/loop/webui/src/web-components/sketch-timeline-message.ts b/loop/webui/src/web-components/sketch-timeline-message.ts
index ef707bd..478f6d9 100644
--- a/loop/webui/src/web-components/sketch-timeline-message.ts
+++ b/loop/webui/src/web-components/sketch-timeline-message.ts
@@ -232,6 +232,9 @@
       font-weight: bold;
       margin-bottom: 5px;
       color: #24292e;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
     }
 
     .commit-boxes-row {
@@ -254,10 +257,13 @@
 
     .commit-preview {
       padding: 8px 12px;
-      cursor: pointer;
       font-family: monospace;
       background-color: #f6f8fa;
       border-bottom: 1px dashed #d1d5da;
+      display: flex;
+      align-items: center;
+      flex-wrap: wrap;
+      gap: 4px;
     }
 
     .commit-preview:hover {
@@ -267,6 +273,56 @@
     .commit-hash {
       color: #0366d6;
       font-weight: bold;
+      cursor: pointer;
+      margin-right: 8px;
+      text-decoration: none;
+      position: relative;
+    }
+
+    .commit-hash:hover {
+      text-decoration: underline;
+    }
+
+    .commit-hash:hover::after {
+      content: "📋";
+      font-size: 10px;
+      position: absolute;
+      top: -8px;
+      right: -12px;
+      opacity: 0.7;
+    }
+
+    .branch-wrapper {
+      margin-right: 8px;
+      color: #555;
+    }
+
+    .commit-branch {
+      color: #28a745;
+      font-weight: 500;
+      cursor: pointer;
+      text-decoration: none;
+      position: relative;
+    }
+
+    .commit-branch:hover {
+      text-decoration: underline;
+    }
+
+    .commit-branch:hover::after {
+      content: "📋";
+      font-size: 10px;
+      position: absolute;
+      top: -8px;
+      right: -12px;
+      opacity: 0.7;
+    }
+
+    .commit-preview {
+      display: flex;
+      align-items: center;
+      flex-wrap: wrap;
+      gap: 4px;
     }
 
     .commit-details {
@@ -292,16 +348,15 @@
     }
 
     .commit-diff-button {
-      padding: 6px 12px;
+      padding: 3px 6px;
       border: 1px solid #ccc;
       border-radius: 3px;
       background-color: #f7f7f7;
       color: #24292e;
-      font-size: 12px;
+      font-size: 11px;
       cursor: pointer;
       transition: all 0.2s ease;
-      margin: 8px 12px;
-      display: block;
+      margin-left: auto;
     }
 
     .commit-diff-button:hover {
@@ -458,6 +513,64 @@
     );
   }
 
+  copyToClipboard(text: string, event: Event) {
+    const element = event.currentTarget as HTMLElement;
+    const rect = element.getBoundingClientRect();
+
+    navigator.clipboard
+      .writeText(text)
+      .then(() => {
+        this.showFloatingMessage("Copied!", rect, "success");
+      })
+      .catch((err) => {
+        console.error("Failed to copy text: ", err);
+        this.showFloatingMessage("Failed to copy!", rect, "error");
+      });
+  }
+
+  showFloatingMessage(
+    message: string,
+    targetRect: DOMRect,
+    type: "success" | "error",
+  ) {
+    // Create floating message element
+    const floatingMsg = document.createElement("div");
+    floatingMsg.textContent = message;
+    floatingMsg.className = `floating-message ${type}`;
+
+    // Position it near the clicked element
+    // Position just above the element
+    const top = targetRect.top - 30;
+    const left = targetRect.left + targetRect.width / 2 - 40;
+
+    floatingMsg.style.position = "fixed";
+    floatingMsg.style.top = `${top}px`;
+    floatingMsg.style.left = `${left}px`;
+    floatingMsg.style.zIndex = "9999";
+
+    // Add to document body
+    document.body.appendChild(floatingMsg);
+
+    // Animate in
+    floatingMsg.style.opacity = "0";
+    floatingMsg.style.transform = "translateY(10px)";
+
+    setTimeout(() => {
+      floatingMsg.style.opacity = "1";
+      floatingMsg.style.transform = "translateY(0)";
+    }, 10);
+
+    // Remove after animation
+    setTimeout(() => {
+      floatingMsg.style.opacity = "0";
+      floatingMsg.style.transform = "translateY(-10px)";
+
+      setTimeout(() => {
+        document.body.removeChild(floatingMsg);
+      }, 300);
+    }, 1500);
+  }
+
   render() {
     return html`
       <div
@@ -531,23 +644,46 @@
                       <div class="commit-boxes-row">
                         <div class="commit-box">
                           <div class="commit-preview">
-                            <span class="commit-hash"
-                              >${commit.hash.substring(0, 8)}</span
+                            <span
+                              class="commit-hash"
+                              title="Click to copy: ${commit.hash}"
+                              @click=${(e) =>
+                                this.copyToClipboard(
+                                  commit.hash.substring(0, 7),
+                                  e,
+                                )}
                             >
-                            ${commit.subject}
-                            <span class="pushed-branch"
-                              >→ pushed to ${commit.pushed_branch}</span
+                              ${commit.hash.substring(0, 7)}
+                            </span>
+                            ${commit.pushed_branch
+                              ? html`
+                                  <span class="branch-wrapper"
+                                    >(<span
+                                      class="commit-branch"
+                                      title="Click to copy: ${commit.pushed_branch}"
+                                      @click=${(e) =>
+                                        this.copyToClipboard(
+                                          commit.pushed_branch,
+                                          e,
+                                        )}
+                                      >${commit.pushed_branch}</span
+                                    >)</span
+                                  >
+                                `
+                              : ``}
+                            <span class="commit-subject"
+                              >${commit.subject}</span
                             >
+                            <button
+                              class="commit-diff-button"
+                              @click=${() => this.showCommit(commit.hash)}
+                            >
+                              View Diff
+                            </button>
                           </div>
                           <div class="commit-details is-hidden">
                             <pre>${commit.body}</pre>
                           </div>
-                          <button
-                            class="commit-diff-button"
-                            @click=${() => this.showCommit(commit.hash)}
-                          >
-                            View Changes
-                          </button>
                         </div>
                       </div>
                     `;
@@ -563,35 +699,65 @@
 
 function copyButton(textToCopy: string) {
   // Add click event listener to handle copying
+  const buttonClass = "copy-button";
+  const buttonContent = "Copy";
+  const successContent = "Copied!";
+  const failureContent = "Failed";
+
   const ret = html`<button
-    class="copy-button"
-    title="Copy text to clipboard"
+    class="${buttonClass}"
+    title="Copy to clipboard"
     @click=${(e: Event) => {
       e.stopPropagation();
       const copyButton = e.currentTarget as HTMLButtonElement;
       navigator.clipboard
         .writeText(textToCopy)
         .then(() => {
-          copyButton.textContent = "Copied!";
+          copyButton.textContent = successContent;
           setTimeout(() => {
-            copyButton.textContent = "Copy";
+            copyButton.textContent = buttonContent;
           }, 2000);
         })
         .catch((err) => {
           console.error("Failed to copy text: ", err);
-          copyButton.textContent = "Failed";
+          copyButton.textContent = failureContent;
           setTimeout(() => {
-            copyButton.textContent = "Copy";
+            copyButton.textContent = buttonContent;
           }, 2000);
         });
     }}
   >
-    Copy
+    ${buttonContent}
   </button>`;
 
   return ret;
 }
 
+// Create global styles for floating messages
+const floatingMessageStyles = document.createElement("style");
+floatingMessageStyles.textContent = `
+  .floating-message {
+    background-color: rgba(0, 0, 0, 0.8);
+    color: white;
+    padding: 5px 10px;
+    border-radius: 4px;
+    font-size: 12px;
+    font-family: system-ui, sans-serif;
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+    pointer-events: none;
+    transition: opacity 0.3s ease, transform 0.3s ease;
+  }
+  
+  .floating-message.success {
+    background-color: rgba(40, 167, 69, 0.9);
+  }
+  
+  .floating-message.error {
+    background-color: rgba(220, 53, 69, 0.9);
+  }
+`;
+document.head.appendChild(floatingMessageStyles);
+
 declare global {
   interface HTMLElementTagNameMap {
     "sketch-timeline-message": SketchTimelineMessage;