diff --git a/README.md b/README.md index 940eb7d..e20eaff 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ Your intelligent coding companion for comprehensive repository management, right * **File changes display:** View detailed file modifications and diffs * **Smart branch selection:** Choose source and destination branches with intelligent defaults * **Workspace integration:** Auto-detect repository information from your current workspace +* **Enhanced viewing experience:** Modern, responsive webview interface for viewing issues and PRs +* **Interactive comments section:** Beautiful comments display with hover effects and markdown support ### 🔐 **Authentication & Security** @@ -45,6 +47,8 @@ Your intelligent coding companion for comprehensive repository management, right * **Branch enumeration:** List and select from available local and remote branches * **Progress tracking:** Visual progress indication during operations * **Error handling:** Comprehensive error reporting and troubleshooting +* **Status bar integration:** Display current account name and configuration status in VS Code status bar +* **Modern UI components:** Enhanced webviews with card-based layouts, smooth animations, and VS Code theme integration ## Requirements @@ -123,7 +127,22 @@ You can access these settings through VS Code's Settings UI or by adding them to ## Release Notes -### 0.0.4 (Current) +### 0.0.5 (Current) + +* Bump version to 0.0.5 + +**UI/UX Enhancements:** +* **Enhanced comments section:** Complete redesign of comments display in issue and PR webviews with modern card-based layout +* **Improved visual design:** Added hover effects, smooth transitions, and better typography for comments +* **Account name display:** Status bar now shows the authenticated user's account name after "WizGIT: " +* **Better empty states:** Friendly messages when no comments exist with call-to-action prompts +* **Markdown support hints:** Added helpful text about markdown formatting in comment forms +* **Enhanced comment forms:** Improved styling with better focus states, padding, and visual feedback + +**Bug Fixes & Improvements:** +* Fixed status bar initialization to properly display account information on extension startup + +### 0.0.4 * Bump version to 0.0.4 * Fix issue with branch selection dropdown not populating correctly in some cases diff --git a/package-lock.json b/package-lock.json index ea1b0fa..c8f0d10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,13 @@ { "name": "wizgit-in-vscode", - "version": "0.0.1", + "version": "0.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wizgit-in-vscode", - "version": "0.0.1", + "version": "0.0.5", + "license": "MIT", "dependencies": { "node-fetch": "^3.3.2" }, diff --git a/package.json b/package.json index b6a3d1e..c5f85b2 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "wizgit-in-vscode", "displayName": "WizGIT in VS Code", "description": "A VS Code extension to interact with WizGIT API for repository management.", - "version": "0.0.4", + "version": "0.0.5", "publisher": "TerenceCarrera", "icon": "resources/wizgit-marketplace-icon.png", "license": "MIT", diff --git a/src/extension.ts b/src/extension.ts index 0fb76bc..ad0ebf3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,12 +8,15 @@ import { promisify } from 'util'; // Import new feature modules import { searchRepositories, searchIssues, searchPullRequests, showFilterQuickPick } from './features/search'; import { showRepositoryQuickPick, addToRecent } from './features/favorites'; -import { createStatusBarItem, registerStatusBarClickHandler } from './features/statusBar'; +import { createStatusBarItem, registerStatusBarClickHandler, updateStatusBar } from './features/statusBar'; import { getUnreadNotifications, showNotificationModal } from './features/notifications'; import { exportIssues, exportPullRequests, showExportDialog, saveToFile } from './features/export'; const execAsync = promisify(child_process.exec); +// Global status bar item reference +let globalStatusBarItem: vscode.StatusBarItem | null = null; + // Interfaces for API responses interface WizGitRepoData { id: number; @@ -655,7 +658,7 @@ class WizGitStatusBar { } } -export function activate(context: vscode.ExtensionContext) { +export async function activate(context: vscode.ExtensionContext) { console.log('WizGIT in VS Code Extension is Now Active! 🚀'); // Initialize tree data providers @@ -713,10 +716,15 @@ export function activate(context: vscode.ExtensionContext) { configureWizGitCredentials(context); }); - let refreshDisposable = vscode.commands.registerCommand('wizgit-in-vscode.refreshRepositories', () => { + let refreshDisposable = vscode.commands.registerCommand('wizgit-in-vscode.refreshRepositories', async () => { repositoryProvider.refresh(); issueProvider.refresh(); prProvider.refresh(); + + // Update status bar state + // For now, status bar will be updated on next initialization + // TODO: Store statusBarItem reference globally for updates + vscode.window.showInformationMessage('WizGIT data refreshed!'); }); @@ -842,6 +850,17 @@ export function activate(context: vscode.ExtensionContext) { // Register Clear WizGIT Configuration command let clearConfigDisposable = vscode.commands.registerCommand('wizgit-in-vscode.clearConfig', async () => { await clearWizGitCredentials(context); + + // Update status bar to show not configured state + if (globalStatusBarItem) { + updateStatusBar(globalStatusBarItem, { + configured: false, + hasRepositories: false, + notificationCount: 0, + accountName: undefined + }); + } + vscode.window.showInformationMessage('WizGIT credentials cleared successfully.'); }); @@ -1140,7 +1159,11 @@ export function activate(context: vscode.ExtensionContext) { }); // Create and register status bar (subscriptions handled by registerStatusBarClickHandler) - createStatusBarItem(context); + const statusBarItem = createStatusBarItem(context); + globalStatusBarItem = statusBarItem; // Store globally for updates + + // Initialize status bar state based on current configuration + await initializeStatusBarState(context, statusBarItem); registerStatusBarClickHandler(context, async (action) => { switch (action) { case 'search-repos': @@ -1181,6 +1204,18 @@ export function activate(context: vscode.ExtensionContext) { ); } +// Status bar management +async function initializeStatusBarState(context: vscode.ExtensionContext, statusBarItem: vscode.StatusBarItem): Promise { + const credentials = await getStoredCredentials(context); + const username = credentials ? await context.secrets.get('wizgit.username') : undefined; + updateStatusBar(statusBarItem, { + configured: !!credentials, + hasRepositories: false, // Could be enhanced to check for repos + notificationCount: 0, // Could be enhanced to check notifications + accountName: username + }); +} + // Credential management functions async function getStoredCredentials(context: vscode.ExtensionContext): Promise<{ apiEndpoint: string, token: string } | null> { const apiEndpoint = await context.secrets.get('wizgit.apiEndpoint'); @@ -1200,6 +1235,7 @@ async function storeCredentials(context: vscode.ExtensionContext, apiEndpoint: s async function clearWizGitCredentials(context: vscode.ExtensionContext): Promise { await context.secrets.delete('wizgit.apiEndpoint'); await context.secrets.delete('wizgit.token'); + await context.secrets.delete('wizgit.username'); } async function configureWizGitCredentials(context: vscode.ExtensionContext): Promise { @@ -1262,14 +1298,28 @@ async function configureWizGitCredentials(context: vscode.ExtensionContext): Pro progress.report({ increment: 100, message: "Credentials verified!" }); - // Store the credentials + // Store the credentials and username await storeCredentials(context, apiEndpoint, token); + await context.secrets.store('wizgit.username', userData.login); vscode.window.showInformationMessage( `WizGIT credentials configured successfully for user: ${userData.login}`, 'OK' ); + // Update status bar immediately to show configured state + if (globalStatusBarItem) { + updateStatusBar(globalStatusBarItem, { + configured: true, + hasRepositories: false, + notificationCount: 0, + accountName: userData.login + }); + } + + // Also refresh tree views + vscode.commands.executeCommand('wizgit-in-vscode.refreshRepositories'); + } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; vscode.window.showErrorMessage(`Failed to verify credentials: ${errorMessage}`); @@ -1420,8 +1470,9 @@ function getIssueWebviewContent(credentials?: { apiEndpoint: string, token: stri line-height: 1.5; } .gitea-header { - background: linear-gradient(135deg, #e53a37 0%, #458be6 100%); - color: white; + background: var(--vscode-sideBar-background); + color: var(--vscode-sideBar-foreground); + border-bottom: 1px solid var(--vscode-panel-border); } .gitea-container { max-width: 1200px; @@ -1493,7 +1544,7 @@ function getIssueWebviewContent(credentials?: { apiEndpoint: string, token: stri
- +
` : ''} @@ -1510,12 +1561,12 @@ function getIssueWebviewContent(credentials?: { apiEndpoint: string, token: stri
- +
- +
Supports Markdown formatting
@@ -1537,17 +1588,17 @@ function getIssueWebviewContent(credentials?: { apiEndpoint: string, token: stri
- +
- +
- ${branches ? ` @@ -1570,7 +1621,7 @@ function getIssueWebviewContent(credentials?: { apiEndpoint: string, token: stri
-
- +
` : ''} @@ -1830,16 +1882,22 @@ function getPullRequestWebviewContent(credentials?: { apiEndpoint: string, token
+ +
+ Select Branches to Compare +
-
-
-
- - - - - ${branches ? ` @@ -1858,18 +1916,20 @@ function getPullRequestWebviewContent(credentials?: { apiEndpoint: string, token
-
+
-
-
- - - - - ${branches ? ` ${branches.local.filter(branch => branch !== 'main').map(branch => ` @@ -1888,12 +1948,12 @@ function getPullRequestWebviewContent(credentials?: { apiEndpoint: string, token
- +
- +
Supports Markdown formatting
@@ -1915,19 +1975,19 @@ function getPullRequestWebviewContent(credentials?: { apiEndpoint: string, token
- +
- +
- +

✍️ Add a comment

+ +
+ + Supports markdown formatting
@@ -3224,6 +3506,22 @@ function getIssueViewerContent(issue: WizGitIssueData, comments: WizGitCommentDa } function getPullRequestViewerContent(pr: WizGitPRData, comments: WizGitCommentData[], files: any[], rawPrData?: any, filesLoadMethod?: string): string { + // Merge raw PR data for more accurate status information + const enhancedPr = { + ...pr, + mergeable: rawPrData?.mergeable !== undefined ? rawPrData.mergeable : pr.mergeable, + mergeable_state: rawPrData?.mergeable_state || 'unknown', + state: rawPrData?.state || pr.state + }; + + console.log('Enhanced PR data:', { + original_mergeable: pr.mergeable, + raw_mergeable: rawPrData?.mergeable, + enhanced_mergeable: enhancedPr.mergeable, + mergeable_state: enhancedPr.mergeable_state, + state: enhancedPr.state + }); + const commentsHtml = comments.map(comment => `
@@ -3276,7 +3574,7 @@ function getPullRequestViewerContent(pr: WizGitPRData, comments: WizGitCommentDa - PR #${pr.number} + PR #${enhancedPr.number}
-

#${pr.number} ${escapeHtml(pr.title)}

+

#${enhancedPr.number} ${escapeHtml(enhancedPr.title)}

- ${pr.state} - ${pr.user.login} - ${pr.user.login} wants to merge into ${pr.base.ref} from ${pr.head.ref} + ${enhancedPr.state} + ${enhancedPr.user.login} + ${enhancedPr.user.login} wants to merge into ${enhancedPr.base.ref} from ${enhancedPr.head.ref}
- ${escapeHtml(pr.head.ref)} → ${escapeHtml(pr.base.ref)} + ${escapeHtml(enhancedPr.head.ref)} → ${escapeHtml(enhancedPr.base.ref)}
+ + ${enhancedPr.state === 'open' && enhancedPr.mergeable === false ? ` +
+ + + + This branch is already included in the target branch. There is nothing to merge. +
+ ` : ''} + + ${enhancedPr.state === 'merged' ? ` +
+ + + + Pull request successfully merged +
+ ` : ''} +
- ${pr.state === 'open' && pr.mergeable ? '' : ''} + ${enhancedPr.state === 'open' && enhancedPr.mergeable !== false ? '' : ''} + ${enhancedPr.state === 'open' && enhancedPr.mergeable === false ? '' : ''}
-
${escapeHtml(pr.body || 'No description provided.')}
+ +
+ PR Debug Info:
+ • State: ${enhancedPr.state}
+ • Mergeable: ${enhancedPr.mergeable}
+ • Mergeable State: ${enhancedPr.mergeable_state}
+ • Number: ${enhancedPr.number}
+ • Base ref: ${enhancedPr.base.ref}
+ • Head ref: ${enhancedPr.head.ref} +
+ +
${escapeHtml(enhancedPr.body || 'No description provided.')}

Files changed (${files.length})

@@ -3470,21 +3842,24 @@ function getPullRequestViewerContent(pr: WizGitPRData, comments: WizGitCommentDa Debug Info:
• Files loaded: ${files.length}
• Files load method: ${filesLoadMethod || 'unknown'}
- • PR Number: ${pr.number}
+ • PR Number: ${enhancedPr.number}
• Raw PR Data:
Click to expand
${rawPrData}
-

Comments (${comments.length})

- ${commentsHtml} +

💬 Comments (${comments.length})

+ ${comments.length === 0 ? + '
No comments yet. Be the first to comment!
' : + commentsHtml}
-

Add a comment

- -
- +

✍️ Add a comment

+ +
+ + Supports markdown formatting
@@ -3496,9 +3871,236 @@ function getPullRequestViewerContent(pr: WizGitPRData, comments: WizGitCommentDa } function mergePR() { - if (confirm('Are you sure you want to merge this pull request?')) { - vscode.postMessage({ command: 'mergePR' }); + // Check if merge is possible + const mergeWarning = document.querySelector('.merge-warning'); + if (mergeWarning) { + // Show a simple alert since merge is not possible + showSimpleAlert( + 'Cannot Merge', + 'This branch is already included in the target branch. There is nothing to merge.', + 'warning' + ); + return; } + + // Show detailed merge dialog + showMergeDialog(); + } + + function showMergeDialog() { + // Create modal overlay + const overlay = document.createElement('div'); + overlay.style.position = 'fixed'; + overlay.style.top = '0'; + overlay.style.left = '0'; + overlay.style.width = '100%'; + overlay.style.height = '100%'; + overlay.style.background = 'rgba(0, 0, 0, 0.5)'; + overlay.style.display = 'flex'; + overlay.style.justifyContent = 'center'; + overlay.style.alignItems = 'center'; + overlay.style.zIndex = '1000'; + + const dialog = document.createElement('div'); + dialog.style.background = 'var(--vscode-editor-background)'; + dialog.style.border = '1px solid var(--vscode-panel-border)'; + dialog.style.borderRadius = '8px'; + dialog.style.padding = '0'; + dialog.style.minWidth = '600px'; + dialog.style.maxWidth = '800px'; + dialog.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.3)'; + dialog.style.fontFamily = 'var(--vscode-font-family)'; + + // Get PR info from page + const prTitle = document.querySelector('.pr-title') ? document.querySelector('.pr-title').textContent : 'Pull Request'; + const prNumber = prTitle.match(/#(\\d+)/)?.[1] || '?'; + const branchInfo = document.querySelector('.branch-info') ? document.querySelector('.branch-info').textContent.trim() : ''; + const branches = branchInfo.split(' → '); + const sourceBranch = branches[0] || 'source-branch'; + const targetBranch = branches[1] || 'target-branch'; + + dialog.innerHTML = + '
' + + '

Merge Pull Request

' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + 'Reviewed-on: ' + window.location.origin + window.location.pathname + + '
' + + '
' + + '' + + '
' + + '
' + + '' + + '' + + '
' + + '
'; + + overlay.appendChild(dialog); + document.body.appendChild(overlay); + + // Focus the message textarea + setTimeout(() => { + const textarea = dialog.querySelector('#mergeMessage'); + if (textarea) { + textarea.focus(); + // Select the default text for easy editing + textarea.select(); + } + }, 100); + + // Handle button clicks + dialog.querySelector('#cancelMergeBtn').onclick = function() { + document.body.removeChild(overlay); + }; + + dialog.querySelector('#confirmMergeBtn').onclick = function() { + const mergeMessage = document.getElementById('mergeMessage').value.trim(); + const deleteBranch = document.getElementById('deleteBranch').checked; + + document.body.removeChild(overlay); + + // Send merge command with custom message + vscode.postMessage({ + command: 'mergePR', + mergeMessage: mergeMessage || 'Merge pull request', + deleteBranch: deleteBranch + }); + }; + + // Close on overlay click + overlay.onclick = function(e) { + if (e.target === overlay) { + document.body.removeChild(overlay); + } + }; + + // Handle Escape key + const handleEscape = function(e) { + if (e.key === 'Escape') { + document.body.removeChild(overlay); + document.removeEventListener('keydown', handleEscape); + } + }; + document.addEventListener('keydown', handleEscape); + } + + function showSimpleAlert(title, message, type) { + // Create modal overlay + const overlay = document.createElement('div'); + overlay.style.position = 'fixed'; + overlay.style.top = '0'; + overlay.style.left = '0'; + overlay.style.width = '100%'; + overlay.style.height = '100%'; + overlay.style.background = 'rgba(0, 0, 0, 0.5)'; + overlay.style.display = 'flex'; + overlay.style.justifyContent = 'center'; + overlay.style.alignItems = 'center'; + overlay.style.zIndex = '1000'; + + const dialog = document.createElement('div'); + dialog.style.background = 'var(--vscode-editor-background)'; + dialog.style.border = '1px solid var(--vscode-panel-border)'; + dialog.style.borderRadius = '8px'; + dialog.style.padding = '0'; + dialog.style.minWidth = '400px'; + dialog.style.maxWidth = '500px'; + dialog.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.3)'; + + const iconColor = type === 'warning' ? 'var(--vscode-inputValidation-warningBorder, #ffc107)' : 'var(--vscode-inputValidation-errorBorder, #dc3545)'; + const icon = type === 'warning' ? + '' : + ''; + + dialog.innerHTML = + '
' + + '
' + icon + '
' + + '

' + title + '

' + + '

' + message + '

' + + '' + + '
'; + + overlay.appendChild(dialog); + document.body.appendChild(overlay); + + // Handle OK click + dialog.querySelector('#alertOkBtn').onclick = function() { + document.body.removeChild(overlay); + }; + + // Close on overlay click + overlay.onclick = function(e) { + if (e.target === overlay) { + document.body.removeChild(overlay); + } + }; + + // Handle Escape key + const handleEscape = function(e) { + if (e.key === 'Escape') { + document.body.removeChild(overlay); + document.removeEventListener('keydown', handleEscape); + } + }; + document.addEventListener('keydown', handleEscape); } function addComment() { @@ -3519,7 +4121,32 @@ function getPullRequestViewerContent(pr: WizGitPRData, comments: WizGitCommentDa break; case 'merged': if (message.success) { - location.reload(); + // Show success message and disable merge button + const mergeBtn = document.querySelector('button[onclick="mergePR()"]'); + if (mergeBtn) { + mergeBtn.textContent = 'Merged Successfully'; + mergeBtn.disabled = true; + mergeBtn.style.backgroundColor = '#28a745'; + mergeBtn.style.cursor = 'not-allowed'; + } + // Update PR state indicator + const stateIndicator = document.querySelector('.pr-state'); + if (stateIndicator) { + stateIndicator.textContent = 'MERGED'; + stateIndicator.className = 'pr-state state-merged'; + } + } else { + // Show error message + const mergeBtn = document.querySelector('button[onclick="mergePR()"]'); + if (mergeBtn) { + const originalText = mergeBtn.textContent; + mergeBtn.textContent = 'Merge Failed - Retry'; + mergeBtn.style.backgroundColor = '#dc3545'; + setTimeout(() => { + mergeBtn.textContent = originalText; + mergeBtn.style.backgroundColor = ''; + }, 3000); + } } break; } diff --git a/src/features/statusBar.ts b/src/features/statusBar.ts index da3c74a..cd9f7f9 100644 --- a/src/features/statusBar.ts +++ b/src/features/statusBar.ts @@ -52,7 +52,8 @@ export function createStatusBarItem(context: vscode.ExtensionContext): vscode.St updateStatusBar(statusBarItem, { configured: false, hasRepositories: false, - notificationCount: 0 + notificationCount: 0, + accountName: undefined }); context.subscriptions.push(statusBarItem); @@ -63,6 +64,7 @@ export interface StatusBarData { configured: boolean; hasRepositories: boolean; notificationCount: number; + accountName?: string; } /** @@ -75,10 +77,14 @@ export function updateStatusBar(statusBarItem: vscode.StatusBarItem, data: Statu return; } - let text = '$(git-branch) WizGIT'; + let text = '$(git-branch) WizGIT: '; + + if (data.accountName) { + text += `${data.accountName} `; + } if (data.hasRepositories) { - text += ' ✓'; + text += '✓'; } if (data.notificationCount > 0) {