Skip to content

Latest commit

 

History

History
163 lines (125 loc) · 6.72 KB

ui-rendering.md

File metadata and controls

163 lines (125 loc) · 6.72 KB

UI Rendering with Language Server

This document explains the architecture and interaction between the Language Server and IDEs for rendering the UI components. It aims to guide contributors in adding or modifying UI components.

Architecture Overview

Our approach unifies the user interface for Snyk Code Ignores across multiple IDEs by leveraging the Language Server Protocol. This ensures consistency, reduces redundancy, and simplifies future enhancements.

sequenceDiagram
    participant LSP
    participant Template
    participant IDE
    LSP->>Template: Generate HTML Template
    Template->>IDE: Send HTML Template
    IDE->>Template: Inject IDE-specific CSS
    IDE->>User: Rendered Content
Loading

Workflow Description

  1. Generate HTML Template: The Language Server generates an HTML template using the Go html/template library. The template includes placeholders for dynamic content.
  2. Send HTML Template: The generated HTML template is sent to the IDEs. This template includes the structure of the UI components but lacks specific styling.
  3. Injected IDE-specific CSS: Each IDE injects its own CSS to style the HTML template according to its theming and design guidelines. This allows the same HTML structure to be visually consistent with the rest of the IDE.

Using //go:embed for CSS and HTML Files

This directive loads static assets like CSS and HTML files directly into our application. This is particularly useful for embedding resources such as templates and stylesheets that are needed for rendering the UI components.

//go:embed template/index.html
var detailsHtmlTemplate string

//go:embed template/styles.css
var stylesCSS string

The Go build system will recognize the directives and arrange for the declared variable to be populated with the matching files from the file system.

Adding or Modifying UI Components

Snyk Code Suggestion Panel

To add or modify UI components in the IDEs that support the Language Server, follow these steps:

  1. Process the dynamic data to be rendered in the HTML template in infrastructure/code/code_html.go. For example, to display the issue ecosystem in the UI:
func getCodeDetailsHtml(issue snyk.Issue) string {
	c := config.CurrentConfig()

	data := map[string]interface{}{
		"Ecosystem":          issue.Ecosystem,
		"IssueTitle":         additionalData.Title,
    // more data
	}

	var html bytes.Buffer
	if err := globalTemplate.Execute(&html, data); err != nil {
		c.Logger().Error().Msgf("Failed to execute main details template: %v", err)
		return ""
	}

	return html.String()
}
  1. Update the HTML template in infrastructure/code/template/details.html to include the placeholders for the dynamic data. Include CSS in the template itself if the styles are the same across all IDEs, otherwise use IDE-specific files.
<head>
  <style>
    .ecosystem-badge {
      padding: 0.35em 0.35em;
      border-radius: 0.25em;
      margin-left: 1em;
      background: #FFF4ED;
      color: #B6540B;
      border: 1px solid #E27122;
    }
  </style>
</head>
<body>
  <h2 class="severity-title">{{.IssueTitle}}</h2>
  <span class="delimiter"></span>
  <div>Priority score: {{.PriorityScore}}</div>
  <span class="delimiter"></span>
  <div class="ecosystem-badge">{{.Ecosystem}}</div>
  <!-- more HTML -->
</body>
  1. If necessary, add IDE-specific CSS:

VSCode

IntelliJ

  1. Handle Nonce and IDE-Specific Styles

When dealing with Content Security Policies (CSP) in the HTML generated by the Language Server, it’s important to correctly handle nonce attributes for both styles and scripts to ensure they are applied securely.

  • Language Server: Ensure the HTML template includes nonce placeholders that will be replaced by dynamically generated nonces. For example:
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="Content-Security-Policy"
    content="default-src 'none'; style-src 'self' 'nonce-{{.Nonce}}' 'nonce-ideNonce'; script-src 'nonce-{{.Nonce}}';">
  <!--noformat-->
  <style nonce="{{.Nonce}}">
    {{.Styles}}
  </style>
  <!--noformat-->
  <style nonce="ideNonce" data-ide-style></style>
</head>
  • {{.Nonce}}: This is a dynamically generated nonce passed from, for example, infrastructure/iac/iac_html.go.

  • {{.Styles}}: This is where the styles defined in the Language Server are injected, also passed via infrastructure/iac/iac_html.go.

  • ideNonce: This placeholder will be replaced by the IDE with its own dynamically generated nonce for IDE-specific styles.

  • IDE (e.g., VSCode): Replace the nonce placeholder in the HTML with the actual nonce generated by the IDE, and inject IDE-specific styles:

private getHtmlFromLanguageServer(html: string): string {
  const nonce = getNonce();
  const ideStylePath = vscode.Uri.joinPath(
    vscode.Uri.file(this.context.extensionPath),
    'media',
    'views',
    'snykCode',
    'suggestion',
    'suggestionLS.css',
  );

  const ideStyle = readFileSync(ideStylePath.fsPath, 'utf8');
  html = html.replace(/nonce-ideNonce/g, `nonce-${nonce}`); // Replace the placeholder with IDE nonce
  html = html.replace(
  '<style nonce="ideNonce" data-ide-style></style>',
  `<style nonce="${nonce}">${ideStyle}</style>`,
);

return html;
}

Final Workflow

  1. Generate HTML Template with Nonce: The Language Server generates an HTML template, including placeholders for nonce attributes.
  2. Send HTML Template to IDE: The IDE receives the template and prepares to render it.
  3. Replace Nonce and Inject Styles: The IDE replaces the nonce placeholders with actual nonces and injects any IDE-specific styles.