Component Architecture: sketch-tool-card and Related Components

This document explains the relationship between LitElement subclasses in webui/src/web-components/ and the sketch-tool-card custom element, focusing on their containment relationship and CSS styling effects.

Containment Relationship

The component hierarchy and containment relationship is structured as follows:

  1. sketch-app-shell (the main application container)
    • Contains sketch-timeline (for displaying conversation history)
      • Contains sketch-timeline-message (for individual messages)
        • Contains sketch-tool-calls (collection of tool calls)
          • Contains specific tool card components like:
            • sketch-tool-card-bash
            • sketch-tool-card-think
            • sketch-tool-card-codereview
            • sketch-tool-card-done
            • sketch-tool-card-patch
            • sketch-tool-card-take-screenshot
            • sketch-tool-card-set-slug
            • sketch-tool-card-commit-message-style
            • sketch-tool-card-multiple-choice
            • sketch-tool-card-generic (fallback for unknown tools)
              • All of these specialized components contain or compose with the base sketch-tool-card

The key aspect is that the specialized tool card components do not inherit from SketchToolCard in a class hierarchy sense. Instead, they use composition by embedding a <sketch-tool-card> element within their render method and passing data to it.

For example, from sketch-tool-card-bash.ts:

render() {
  return html` <sketch-tool-card
    .open=${this.open}
    .toolCall=${this.toolCall}
  >
    <span slot="summary" class="summary-text">...</span>
    <div slot="input" class="input">...</div>
    <div slot="result" class="result">...</div>
  </sketch-tool-card>`;
}

CSS Styling and Effects

Regarding how CSS rules defined in sketch-tool-card affect elements that contain it:

  1. Shadow DOM Encapsulation:

    • Each Web Component has its own Shadow DOM, which encapsulates its styles
    • Styles defined in sketch-tool-card apply only within its shadow DOM, not to parent components
  2. Slot Content Styling:

    • The base sketch-tool-card defines three slots: "summary", "input", and "result"
    • Specialized tool cards provide content for these slots
    • The base component can style the slot containers, but cannot directly style the slotted content
  3. Style Inheritance and Sharing:

    • The code uses a commonStyles constant that is shared across some components
    • These common styles ensure consistent styling for elements like pre, code blocks
    • Each specialized component adds its own unique styles as needed
  4. Parent CSS Targeting:

    • In sketch-timeline-message.ts, there are styles that target the tool components using the ::slotted() pseudo-element:
    ::slotted(sketch-tool-calls) {
      max-width: 100%;
      width: 100%;
      overflow-wrap: break-word;
      word-break: break-word;
    }
    
    • This allows parent components to influence the layout of slotted components while preserving Shadow DOM encapsulation
  5. Host Element Styling:

    • The :host selector is used in sketch-tool-card for styling the component itself:
    :host {
      display: block;
      max-width: 100%;
      width: 100%;
      box-sizing: border-box;
      overflow: hidden;
    }
    
    • This affects how the component is displayed in its parent context

In summary, the architecture uses composition rather than inheritance, with specialized tool cards wrapping the base sketch-tool-card component and filling its slots with custom content. The CSS styling is carefully managed through Shadow DOM, with some targeted styling using ::slotted selectors to ensure proper layout and appearance throughout the component hierarchy.

API URLs Must Be Relative

When making fetch requests to backend APIs in Sketch, all URLs must be relative without leading slashes. The base URL for Sketch varies depending on how it is deployed and run:

// CORRECT - use relative paths without leading slash
const response = await fetch(`git/rawdiff?commit=${commit}`);
const data = await fetch(`git/show?hash=${hash}`);

// INCORRECT - do not use absolute paths with leading slash
const response = await fetch(`/git/rawdiff?commit=${commit}`); // WRONG!
const data = await fetch(`/git/show?hash=${hash}`); // WRONG!

The reason for this requirement is that Sketch may be deployed:

  1. At the root path of a domain
  2. In a subdirectory
  3. Behind a proxy with a path prefix
  4. In different environments (dev, production)

Using relative paths ensures that requests are correctly routed regardless of the deployment configuration. The browser will resolve the URLs relative to the current page location.