-
Notifications
You must be signed in to change notification settings - Fork 61
fix(exporters): added save output button on rendered output #1664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9e382a1
1aba51a
5486821
c16d75d
1f694d0
d9efb50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,8 @@ | |
| // SPDX-License-Identifier: Apache-2.0 | ||
| import type { ActivationFunction } from "vscode-notebook-renderer"; | ||
|
|
||
| let outputIndex = 0; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like this one is global. So if I switch between multiple notebooks the output index will be wrong? |
||
|
|
||
| /** | ||
| * Replace the last occurrence of a substring | ||
| */ | ||
|
|
@@ -19,18 +21,94 @@ function replaceLast( | |
| ); | ||
| } | ||
|
|
||
| export const activate: ActivationFunction = () => ({ | ||
| export const activate: ActivationFunction = (context) => ({ | ||
| renderOutputItem(data, element) { | ||
| const html = data.text(); | ||
| const currentIndex = outputIndex++; | ||
|
|
||
| let shadow = element.shadowRoot; | ||
| if (!shadow) { | ||
| shadow = element.attachShadow({ mode: "open" }); | ||
| } | ||
| shadow.innerHTML = replaceLast( | ||
|
|
||
| const container = document.createElement("div"); | ||
| container.style.position = "relative"; | ||
|
|
||
| if (context.postMessage) { | ||
| const toolbar = document.createElement("div"); | ||
| toolbar.style.cssText = ` | ||
| position: absolute; | ||
| top: -22px; | ||
| right: 8px; | ||
| display: flex; | ||
| gap: 4px; | ||
| opacity: 0; | ||
| transition: opacity 0.1s ease; | ||
| background: var(--vscode-editor-background); | ||
| border: 1px solid var(--vscode-widget-border); | ||
| border-radius: 4px; | ||
| padding: 2px; | ||
| z-index: 1000; | ||
| `; | ||
|
|
||
| const saveButton = document.createElement("button"); | ||
| saveButton.title = "Save Output"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i18n? |
||
| saveButton.setAttribute("aria-label", "Save Output"); | ||
| saveButton.innerHTML = ` | ||
| <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"> | ||
| <path fill-rule="evenodd" clip-rule="evenodd" d="M10.012 2H2.5l-.5.5v11l.5.5h11l.5-.5V5l-4-3h-.488zM3 13V3h6v2.5l.5.5h3v7H3zm7-9v2h2l-2-2z"/> | ||
| <path d="M5 7h6v1H5V7zm0 2h6v1H5V9z"/> | ||
| </svg> | ||
| `; | ||
| saveButton.style.cssText = ` | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| width: 24px; | ||
| height: 24px; | ||
| padding: 0; | ||
| background: transparent; | ||
| border: none; | ||
| color: var(--vscode-icon-foreground); | ||
| cursor: pointer; | ||
| border-radius: 3px; | ||
| `; | ||
|
|
||
| saveButton.onmouseover = () => { | ||
| saveButton.style.background = "var(--vscode-toolbar-hoverBackground)"; | ||
| }; | ||
| saveButton.onmouseout = () => { | ||
| saveButton.style.background = "transparent"; | ||
| }; | ||
|
|
||
| saveButton.onclick = () => { | ||
| context.postMessage({ | ||
| command: "saveOutput", | ||
| outputType: "html", | ||
| content: html, | ||
| mime: data.mime, | ||
| cellIndex: currentIndex, | ||
| }); | ||
| }; | ||
|
|
||
| toolbar.appendChild(saveButton); | ||
| container.onmouseenter = () => { | ||
| toolbar.style.opacity = "1"; | ||
| }; | ||
| container.onmouseleave = () => { | ||
| toolbar.style.opacity = "0"; | ||
| }; | ||
|
|
||
| container.appendChild(toolbar); | ||
| } // Add the HTML content | ||
| const contentDiv = document.createElement("div"); | ||
| contentDiv.innerHTML = replaceLast( | ||
| // it's not a whole webview, body not allowed | ||
| html.replace("<body ", "<div "), | ||
| "</body>", | ||
| "</div>", | ||
| ); | ||
| container.appendChild(contentDiv); | ||
| shadow.replaceChildren(container); | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,19 +4,93 @@ import type { ActivationFunction } from "vscode-notebook-renderer"; | |
|
|
||
| import type { LogLine } from "../../../connection"; | ||
|
|
||
| let outputIndex = 0; | ||
|
|
||
| const colorMap = { | ||
| error: "var(--vscode-editorError-foreground)", | ||
| warning: "var(--vscode-editorWarning-foreground)", | ||
| note: "var(--vscode-editorInfo-foreground)", | ||
| }; | ||
|
|
||
| export const activate: ActivationFunction = () => ({ | ||
| export const activate: ActivationFunction = (context) => ({ | ||
| renderOutputItem(data, element) { | ||
| const logs: LogLine[] = data.json(); | ||
| const currentIndex = outputIndex++; | ||
|
|
||
| const container = document.createElement("div"); | ||
| container.style.position = "relative"; | ||
|
|
||
| if (context.postMessage) { | ||
| const toolbar = document.createElement("div"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are you able to re-use some code with HTMLRenderer? |
||
| toolbar.style.cssText = ` | ||
| position: absolute; | ||
| top: -10px; | ||
| right: 8px; | ||
| display: flex; | ||
| gap: 4px; | ||
| opacity: 0; | ||
| transition: opacity 0.1s ease; | ||
| background: var(--vscode-editor-background); | ||
| border: 1px solid var(--vscode-widget-border); | ||
| border-radius: 4px; | ||
| padding: 2px; | ||
| z-index: 1000; | ||
| `; | ||
|
|
||
| const saveButton = document.createElement("button"); | ||
| saveButton.title = "Save Output"; | ||
| saveButton.setAttribute("aria-label", "Save Output"); | ||
| saveButton.innerHTML = ` | ||
| <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"> | ||
| <path fill-rule="evenodd" clip-rule="evenodd" d="M10.012 2H2.5l-.5.5v11l.5.5h11l.5-.5V5l-4-3h-.488zM3 13V3h6v2.5l.5.5h3v7H3zm7-9v2h2l-2-2z"/> | ||
| <path d="M5 7h6v1H5V7zm0 2h6v1H5V9z"/> | ||
| </svg> | ||
| `; | ||
| saveButton.style.cssText = ` | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| width: 24px; | ||
| height: 24px; | ||
| padding: 0; | ||
| background: transparent; | ||
| border: none; | ||
| color: var(--vscode-icon-foreground); | ||
| cursor: pointer; | ||
| border-radius: 3px; | ||
| `; | ||
|
|
||
| saveButton.onmouseover = () => { | ||
| saveButton.style.background = "var(--vscode-toolbar-hoverBackground)"; | ||
| }; | ||
| saveButton.onmouseout = () => { | ||
| saveButton.style.background = "transparent"; | ||
| }; | ||
|
|
||
| saveButton.onclick = () => { | ||
| context.postMessage({ | ||
| command: "saveOutput", | ||
| outputType: "log", | ||
| content: logs, | ||
| mime: data.mime, | ||
| cellIndex: currentIndex, | ||
| }); | ||
| }; | ||
|
|
||
| toolbar.appendChild(saveButton); | ||
| container.onmouseenter = () => { | ||
| toolbar.style.opacity = "1"; | ||
| }; | ||
| container.onmouseleave = () => { | ||
| toolbar.style.opacity = "0"; | ||
| }; | ||
|
|
||
| container.appendChild(toolbar); | ||
| } | ||
| const root = document.createElement("div"); | ||
| root.style.whiteSpace = "pre"; | ||
| root.style.fontFamily = "var(--vscode-editor-font-family)"; | ||
|
|
||
| const logs: LogLine[] = data.json(); | ||
| for (const line of logs) { | ||
| const color = colorMap[line.type]; | ||
| const div = document.createElement("div"); | ||
|
|
@@ -26,6 +100,7 @@ export const activate: ActivationFunction = () => ({ | |
| } | ||
| root.append(div); | ||
| } | ||
| element.replaceChildren(root); | ||
| container.appendChild(root); | ||
| element.replaceChildren(container); | ||
| }, | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like this one is global. So if I just save another notebook, the notebook name is different but the count simply added which may look weird.
In my opinion it's not necessary to manage a count. If a name duplicated, system will prompt the user to change a name, right?