From ebc22baf37ed8c2138ffe61e78883662de5aaaf7 Mon Sep 17 00:00:00 2001 From: Nick Bradley Date: Fri, 27 Mar 2026 13:11:47 +0000 Subject: [PATCH] Fix webview compliance issues --- .gitignore | 2 + src/commands/uploadWidget.ts | 2 +- src/webview/client/upload-widget.ts | 126 ++++++++++++++++++---------- src/webview/index.ts | 62 -------------- 4 files changed, 85 insertions(+), 107 deletions(-) diff --git a/.gitignore b/.gitignore index 2058881..e29aa63 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ Thumbs.db *.swo *~ .cursor/ +.claude/ +CLAUDE.md # Temporary files *.tmp diff --git a/src/commands/uploadWidget.ts b/src/commands/uploadWidget.ts index ba1bd83..d283a76 100644 --- a/src/commands/uploadWidget.ts +++ b/src/commands/uploadWidget.ts @@ -246,7 +246,7 @@ function createUploadPanel( const presetsJson = JSON.stringify(provider.uploadPresets); const initScript = ` initUploadWidget({ - cloudName: "${escapeHtml(cloudName)}", + cloudName: ${JSON.stringify(cloudName)}, presets: ${presetsJson} }); `; diff --git a/src/webview/client/upload-widget.ts b/src/webview/client/upload-widget.ts index b047c71..afd73ec 100644 --- a/src/webview/client/upload-widget.ts +++ b/src/webview/client/upload-widget.ts @@ -297,15 +297,28 @@ function addToQueue(fileId: string, fileName: string): void { const displayName = truncateString(fileName, 30, "middle"); - item.innerHTML = ` - ${displayName} -
-
-
-
-
- Pending... - `; + const nameEl = document.createElement("span"); + nameEl.className = "queue-item__name"; + nameEl.title = fileName; + nameEl.textContent = displayName; + + const progressBar = document.createElement("div"); + progressBar.className = "progress__bar"; + progressBar.style.width = "0%"; + const progress = document.createElement("div"); + progress.className = "progress"; + progress.appendChild(progressBar); + const progressWrapper = document.createElement("div"); + progressWrapper.className = "queue-item__progress"; + progressWrapper.appendChild(progress); + + const statusEl = document.createElement("span"); + statusEl.className = "queue-item__status"; + statusEl.textContent = "Pending..."; + + item.appendChild(nameEl); + item.appendChild(progressWrapper); + item.appendChild(statusEl); uploadQueue.appendChild(item); } @@ -378,55 +391,80 @@ function renderUploadedAsset(asset: AssetData): void { const card = document.createElement("div"); card.className = "asset-card"; - const fileIcon = - ""; + // Static SVG icon — no user data + const fileIconSvg = ""; + + // Thumbnail wrapper + const thumbnailWrapper = document.createElement("div"); + thumbnailWrapper.className = "asset-card__thumbnail"; + thumbnailWrapper.setAttribute("data-asset-id", asset.public_id); - let mediaHtml: string; if (thumbnailUrl) { - mediaHtml = ` -
- Thumbnail - -
- `; + const img = document.createElement("img"); + img.className = "asset-card__image"; + img.src = thumbnailUrl; + img.alt = "Thumbnail"; + const fallbackEl = document.createElement("div"); + fallbackEl.className = "asset-card__icon fallback"; + fallbackEl.style.display = "none"; + fallbackEl.innerHTML = fileIconSvg; + img.addEventListener("error", function () { + this.style.display = "none"; + fallbackEl.style.display = "flex"; + }); + thumbnailWrapper.appendChild(img); + thumbnailWrapper.appendChild(fallbackEl); } else { - mediaHtml = ` -
-
${fileIcon}
-
- `; + const iconEl = document.createElement("div"); + iconEl.className = "asset-card__icon"; + iconEl.innerHTML = fileIconSvg; + thumbnailWrapper.appendChild(iconEl); } + // Folder + const folderEl = document.createElement("div"); + folderEl.className = "asset-card__folder"; + const folderCode = document.createElement("code"); + folderCode.textContent = folderDisplay; + folderEl.appendChild(document.createTextNode("📂 ")); + folderEl.appendChild(folderCode); + + // Asset ID const displayId = truncateString(asset.public_id, 25, "start"); + const idEl = document.createElement("div"); + idEl.className = "asset-card__id"; + idEl.title = asset.public_id; + idEl.textContent = displayId; + + // Action buttons + const actionsEl = document.createElement("div"); + actionsEl.className = "asset-card__actions"; + + const copyUrlBtn = document.createElement("button"); + copyUrlBtn.className = "btn btn--secondary btn--sm btn--copy"; + copyUrlBtn.setAttribute("data-copy", asset.secure_url); + copyUrlBtn.textContent = "Copy URL"; - card.innerHTML = ` - ${mediaHtml} -
📂 ${folderDisplay}
-
${displayId}
-
- - -
- `; + const copyIdBtn = document.createElement("button"); + copyIdBtn.className = "btn btn--secondary btn--sm btn--copy"; + copyIdBtn.setAttribute("data-copy", asset.public_id); + copyIdBtn.textContent = "Copy ID"; + + actionsEl.appendChild(copyUrlBtn); + actionsEl.appendChild(copyIdBtn); + + card.appendChild(thumbnailWrapper); + card.appendChild(folderEl); + card.appendChild(idEl); + card.appendChild(actionsEl); // Click thumbnail to preview - const thumbnailWrapper = card.querySelector(".asset-card__thumbnail"); - if (thumbnailWrapper && vscode) { + if (vscode) { thumbnailWrapper.addEventListener("click", () => { vscode.postMessage({ command: "openAsset", asset: asset }); }); } - // Handle image load errors - const img = card.querySelector(".asset-card__image"); - if (img) { - img.addEventListener("error", function () { - this.style.display = "none"; - const fallback = this.parentElement?.querySelector(".fallback"); - if (fallback) {fallback.style.display = "flex";} - }); - } - // Initialize copy buttons card.querySelectorAll(".btn--copy[data-copy]").forEach((btn) => { btn.addEventListener("click", async (e) => { diff --git a/src/webview/index.ts b/src/webview/index.ts index 2dc888f..c5d3331 100644 --- a/src/webview/index.ts +++ b/src/webview/index.ts @@ -52,7 +52,6 @@ export * from "./components"; // Utility scripts export * from "./utils"; -import { escapeHtml } from "./utils/helpers"; // Icons export { @@ -115,65 +114,4 @@ export function getCompleteScripts(): string { ].join("\n"); } -/** - * Creates a complete HTML document for a webview panel. - * - * @param options - Document configuration - * @returns Complete HTML string - * - * @example - * ```typescript - * panel.webview.html = createWebviewDocument({ - * title: 'Upload to Cloudinary', - * body: createPanel({ content: '...' }), - * additionalStyles: '.custom { color: red; }', - * additionalScripts: 'console.log("Hello");' - * }); - * ``` - */ -export function createWebviewDocument(options: { - /** Document title */ - title: string; - /** Body content (HTML string) */ - body: string; - /** Additional CSS to include */ - additionalStyles?: string; - /** Additional JavaScript to include */ - additionalScripts?: string; - /** Whether to include all component scripts (default: true) */ - includeScripts?: boolean; -}): string { - const { - title, - body, - additionalStyles = "", - additionalScripts = "", - includeScripts = true, - } = options; - - const styles = getCompleteStyles(); - const scripts = includeScripts ? getCompleteScripts() : ""; - - return ` - - - - - - ${escapeHtml(title)} - - - - ${body} - - - - `; -}