From fdb2dfcf8d8ab382338ad257633eb82b2fb3a195 Mon Sep 17 00:00:00 2001 From: ajaykontham Date: Tue, 19 May 2026 20:17:17 -0400 Subject: [PATCH 1/4] fix: update build triggers to only include 'build' branch --- .github/workflows/build-and-package.yml | 2 +- .github/workflows/ci.yml | 86 ++++++++++++------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/workflows/build-and-package.yml b/.github/workflows/build-and-package.yml index 6150e2af..91392a11 100644 --- a/.github/workflows/build-and-package.yml +++ b/.github/workflows/build-and-package.yml @@ -2,7 +2,7 @@ name: Build and Package (p2 ZIP) on: push: - branches: [ build, dev/anypoint-studio ] + branches: [ build ] workflow_dispatch: env: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a77cc802..f11fdcd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,43 +1,43 @@ -name: CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -permissions: - contents: read - -jobs: - build: - - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js environment - uses: actions/setup-node@v4 - with: - node-version: 22 - - - name: Install Copilot agent - working-directory: com.microsoft.copilot.eclipse.core/copilot-agent - run: npm i -f - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: maven - - - name: Build - uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1.0.1 - with: - run: >- - ./mvnw clean verify --batch-mode +# name: CI + +# on: +# push: +# branches: [ "main" ] +# pull_request: +# branches: [ "main" ] + +# permissions: +# contents: read + +# jobs: +# build: + +# runs-on: ${{ matrix.os }} +# strategy: +# matrix: +# os: [ubuntu-latest, macos-latest, windows-latest] + +# steps: +# - uses: actions/checkout@v4 + +# - name: Setup Node.js environment +# uses: actions/setup-node@v4 +# with: +# node-version: 22 + +# - name: Install Copilot agent +# working-directory: com.microsoft.copilot.eclipse.core/copilot-agent +# run: npm i -f + +# - name: Set up JDK 17 +# uses: actions/setup-java@v4 +# with: +# java-version: '17' +# distribution: 'temurin' +# cache: maven + +# - name: Build +# uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1.0.1 +# with: +# run: >- +# ./mvnw clean verify --batch-mode From 4f6cb89ccf15beff7c945e7e7bb5981b4bd26c91 Mon Sep 17 00:00:00 2001 From: ajaykontham Date: Mon, 25 May 2026 22:03:20 -0400 Subject: [PATCH 2/4] feat: add GitHub Actions workflow for building and packaging Copilot for Anypoint Studio --- .github/workflows/build-win-mac-universal.yml | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 .github/workflows/build-win-mac-universal.yml diff --git a/.github/workflows/build-win-mac-universal.yml b/.github/workflows/build-win-mac-universal.yml new file mode 100644 index 00000000..dd40b47a --- /dev/null +++ b/.github/workflows/build-win-mac-universal.yml @@ -0,0 +1,142 @@ +name: Build and Package (p2 ZIP) + +on: + push: + branches: [ main ] + workflow_dispatch: + +env: + MAVEN_OPTS: -Xmx2g + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Extract plugin version + run: | + VERSION=$(grep -oP '(?<=)[^<]+' pom.xml) + TIMESTAMP=$(date -u +%Y%m%d%H%M%S) + DISPLAY_VERSION="${VERSION/-SNAPSHOT/-$TIMESTAMP}" + echo "COPILOT_PLUGIN_VERSION=$VERSION" >> $GITHUB_ENV + echo "COPILOT_DISPLAY_VERSION=$DISPLAY_VERSION" >> $GITHUB_ENV + + - name: Set up Java 21 (Temurin) + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + cache: 'maven' + + - name: Cache Maven local repo + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-m2- + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install Copilot agent + working-directory: com.microsoft.copilot.eclipse.core/copilot-agent + run: | + npm install --force + cp -r node_modules/@github/copilot-language-server/dist ./dist + + - name: Make mvnw executable + run: chmod +x ./mvnw + + - name: Build (Tycho/Maven) + run: | + ./mvnw -DskipTests -Dcheckstyle.skip=true clean verify + + - name: Prepare build artifacts + run: | + mkdir -p build + SRC=com.microsoft.copilot.eclipse.repository/target/com.microsoft.copilot.eclipse.repository-${COPILOT_PLUGIN_VERSION}.zip + if [ ! -f "$SRC" ]; then + echo "ERROR: Expected ZIP not found at $SRC" + ls -la com.microsoft.copilot.eclipse.repository/target || true + exit 1 + fi + if ! unzip -l "$SRC" | grep -qE 'content\.(jar|xml|xml\.xz)|p2\.index'; then + echo "ERROR: p2 repository ZIP is missing p2 metadata (content.jar / content.xml.xz / p2.index)" + unzip -l "$SRC" + exit 1 + fi + find build/ -maxdepth 1 -name "github-copilot-for-anypoint-*.zip" -delete 2>/dev/null || true + + # Create universal p2 repository (all platforms) + OUT_UNIVERSAL=build/github-copilot-for-anypoint-${COPILOT_DISPLAY_VERSION}.zip + cp -f "$SRC" "$OUT_UNIVERSAL" + sha256sum "$OUT_UNIVERSAL" | awk '{print $1 " " $2}' > "${OUT_UNIVERSAL}.sha256" + + # Create Windows-specific package (win32 only) + OUT_WIN=build/github-copilot-for-anypoint-${COPILOT_DISPLAY_VERSION}-win32.zip + unzip -q "$SRC" -d temp_win + rm -rf temp_win/plugins/*linux* temp_win/plugins/*macosx* temp_win/features/*linux* temp_win/features/*macosx* 2>/dev/null || true + cd temp_win && zip -q -r "../${OUT_WIN}" . && cd .. + rm -rf temp_win + sha256sum "$OUT_WIN" | awk '{print $1 " " $2}' > "${OUT_WIN}.sha256" + + # Create macOS-specific package (macosx only) + OUT_MAC=build/github-copilot-for-anypoint-${COPILOT_DISPLAY_VERSION}-macos.zip + unzip -q "$SRC" -d temp_mac + rm -rf temp_mac/plugins/*linux* temp_mac/plugins/*win32* temp_mac/features/*linux* temp_mac/features/*win32* 2>/dev/null || true + cd temp_mac && zip -q -r "../${OUT_MAC}" . && cd .. + rm -rf temp_mac + sha256sum "$OUT_MAC" | awk '{print $1 " " $2}' > "${OUT_MAC}.sha256" + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }} + path: | + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}.zip + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}.zip.sha256 + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-win32.zip + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-win32.zip.sha256 + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-macos.zip + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-macos.zip.sha256 + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: "v${{ env.COPILOT_DISPLAY_VERSION }}" + name: "GitHub Copilot for Anypoint Studio v${{ env.COPILOT_DISPLAY_VERSION }}" + prerelease: true + body: | + Pre-release build of GitHub Copilot for Anypoint Studio. + + **Installation in Anypoint Studio:** + + Select the package appropriate for your platform: + + - **Windows (64-bit)**: Download `github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-win32.zip` + - **macOS (Intel & Apple Silicon)**: Download `github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-macos.zip` + - **All Platforms**: Download `github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}.zip` (universal p2 repository with all platform support) + + **Installation Steps:** + 1. In Anypoint Studio, go to **Help → Install New Software...** + 2. Click **Add...** and select the downloaded ZIP file as a local update site. + 3. Follow the installation wizard and restart Anypoint Studio. + + **Verification:** + Each package includes a `.sha256` file for integrity verification. Use `sha256sum -c file.zip.sha256` to verify the download. + files: | + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}.zip + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}.zip.sha256 + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-win32.zip + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-win32.zip.sha256 + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-macos.zip + build/github-copilot-for-anypoint-${{ env.COPILOT_DISPLAY_VERSION }}-macos.zip.sha256 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From cddd5daea0e2bcbbcc3448c8907009f6a4acf931 Mon Sep 17 00:00:00 2001 From: Darshan Poudel Date: Tue, 26 May 2026 07:58:54 +0545 Subject: [PATCH 3/4] fix: refresh scroller layout when tool confirmation dialog is cancelled (#254) When the user cancels a subagent tool confirmation dialog, the subagent panel stays expanded at its pre-cancel height. This happens because cancelConfirmation() only called parent.layout() (relaying the turn widget's children), but never updated the ScrolledComposite's minimum size via refreshScrollerLayout(). On the Continue path this goes unnoticed because subsequent subagent messages trigger a layout refresh; on the Cancel path no further messages arrive, so the panel is stuck. Fix by adding a dispose listener on the dialog in BaseTurnWidget that walks up to the nearest ChatContentViewer and calls requestRefreshScrollerLayout(), a new coalesced async helper that schedules a single refreshScrollerLayout() after pending SWT mutations settle. InvokeToolConfirmationDialog remains self-contained: both the accept and cancel paths use a private disposeAndRequestParentLayout() helper, keeping scroller-specific behaviour out of the dialog. Fixes #169 --- .../copilot/eclipse/ui/chat/BaseTurnWidget.java | 10 ++++++++++ .../copilot/eclipse/ui/chat/ChatContentViewer.java | 8 ++++++++ .../eclipse/ui/chat/InvokeToolConfirmationDialog.java | 9 +++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/BaseTurnWidget.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/BaseTurnWidget.java index d8a5221a..8890a95b 100644 --- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/BaseTurnWidget.java +++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/BaseTurnWidget.java @@ -650,6 +650,16 @@ public CompletableFuture requestToolExecuti reset(); this.confirmDialog = new InvokeToolConfirmationDialog(this, content, input); + this.confirmDialog.addDisposeListener(e -> { + Composite ancestor = this.getParent(); + while (ancestor != null && !ancestor.isDisposed()) { + if (ancestor instanceof ChatContentViewer) { + ((ChatContentViewer) ancestor).requestRefreshScrollerLayout(); + break; + } + ancestor = ancestor.getParent(); + } + }); CompletableFuture toolConfirmationFuture = this.confirmDialog .getConfirmationFuture(); diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/ChatContentViewer.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/ChatContentViewer.java index 58ee3f5d..7ef32077 100644 --- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/ChatContentViewer.java +++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/ChatContentViewer.java @@ -404,6 +404,14 @@ public void renderErrorMessage(String errorMessage) { scrollToLatestUserTurn(); } + /** + * Schedules a single async {@link #refreshScrollerLayout()} call so that multiple dispose/layout + * events that arrive in the same event-loop tick are coalesced into one pass. + */ + public void requestRefreshScrollerLayout() { + SwtUtils.invokeOnDisplayThreadAsync(() -> refreshScrollerLayout(), this); + } + /** * Update the size of scrolled composite when there are content updates. */ diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/InvokeToolConfirmationDialog.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/InvokeToolConfirmationDialog.java index ebc307a0..1f65aead 100644 --- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/InvokeToolConfirmationDialog.java +++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/InvokeToolConfirmationDialog.java @@ -119,10 +119,7 @@ public void cancelConfirmation() { && StringUtils.isNotEmpty(this.cancelMessage)) { new AgentToolCancelLabel(parent, SWT.NONE, this.cancelMessage); } - this.dispose(); - if (parent != null && !parent.isDisposed()) { - parent.requestLayout(); - } + disposeAndRequestParentLayout(); }, this); } } @@ -318,6 +315,10 @@ private void acceptAndDispose(ConfirmationAction action) { new LanguageModelToolConfirmationResult( ToolConfirmationResult.ACCEPT)); + disposeAndRequestParentLayout(); + } + + private void disposeAndRequestParentLayout() { Composite parent = this.getParent(); this.dispose(); if (parent != null && !parent.isDisposed()) { From dfe6af7edc79d3e53ae4f64e82973b7c71e72fcf Mon Sep 17 00:00:00 2001 From: ajaykontham Date: Tue, 26 May 2026 01:01:44 -0400 Subject: [PATCH 4/4] feat: enhance WarnWidget to support markdown links and improve link handling --- .../copilot/eclipse/ui/chat/WarnWidget.java | 77 +++++++++++++++++-- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/WarnWidget.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/WarnWidget.java index 2582320c..b6029b3c 100644 --- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/WarnWidget.java +++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/WarnWidget.java @@ -4,17 +4,21 @@ package com.microsoft.copilot.eclipse.ui.chat; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; @@ -30,7 +34,12 @@ * and whether to pass a plan. */ public class WarnWidget extends Composite { + private static final Pattern MARKDOWN_LINK_PATTERN = Pattern.compile("\\[([^\\]]+)]\\(([^)]+)\\)"); + private static final Pattern RAW_URL_PATTERN = Pattern.compile("https?://\\S+"); + private int buttonLeftMargin; + private Color darkBackground; + private Color darkForeground; /** * Create the composite. @@ -119,15 +128,71 @@ private void buildWarnLabelWithIcon(String message) { buttonLeftMargin = warnLayout.marginWidth + warnLayout.marginLeft + warnImage.getBounds().width + warnLayout.horizontalSpacing; - ChatMarkupViewer textLabel = new ChatMarkupViewer(composite, SWT.LEFT | SWT.WRAP); - StyledText styledText = textLabel.getTextWidget(); - styledText.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true)); - styledText.setEditable(false); - textLabel.setMarkup(message); + Link messageLink = new Link(composite, SWT.LEFT | SWT.WRAP); + messageLink.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + messageLink.setText(toLinkMarkup(message)); + messageLink.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(org.eclipse.swt.events.SelectionEvent event) { + UiUtils.openLink(event.text); + } + }); requestLayout(); } + private static String toLinkMarkup(String message) { + String text = stripMarkdownEmphasis(message == null ? "" : message); + Matcher markdownLinkMatcher = MARKDOWN_LINK_PATTERN.matcher(text); + StringBuilder result = new StringBuilder(); + int offset = 0; + while (markdownLinkMatcher.find()) { + appendTextWithRawLinks(result, text.substring(offset, markdownLinkMatcher.start())); + appendLink(result, markdownLinkMatcher.group(1), markdownLinkMatcher.group(2)); + offset = markdownLinkMatcher.end(); + } + appendTextWithRawLinks(result, text.substring(offset)); + return result.toString(); + } + + private static void appendTextWithRawLinks(StringBuilder result, String text) { + Matcher rawUrlMatcher = RAW_URL_PATTERN.matcher(text); + int offset = 0; + while (rawUrlMatcher.find()) { + result.append(escapeLinkText(text.substring(offset, rawUrlMatcher.start()))); + String url = rawUrlMatcher.group(); + String trailingPunctuation = ""; + while (!url.isEmpty() && ".,;:".indexOf(url.charAt(url.length() - 1)) >= 0) { + trailingPunctuation = url.charAt(url.length() - 1) + trailingPunctuation; + url = url.substring(0, url.length() - 1); + } + appendLink(result, url, url); + result.append(escapeLinkText(trailingPunctuation)); + offset = rawUrlMatcher.end(); + } + result.append(escapeLinkText(text.substring(offset))); + } + + private static void appendLink(StringBuilder result, String label, String url) { + result.append("") + .append(escapeLinkText(stripMarkdownEmphasis(label == null ? "" : label))) + .append(""); + } + + private static String stripMarkdownEmphasis(String text) { + return text.replace("**", ""); + } + + private static String escapeLinkText(String text) { + return text.replace("&", "&").replace("<", "<").replace(">", ">"); + } + + private static String escapeLinkAttribute(String text) { + return escapeLinkText(text).replace("\"", """).replace("'", "'"); + } + /** * Render plan-driven action buttons for a quota-exceeded warning, kept in sync with the quota {@link StaticBanner}. */