Skip to content

Commit

Permalink
Refactor asset providers (#383)
Browse files Browse the repository at this point in the history
* Refactor asset provider to handle JS asset setup

* Move progress listener for flicker bug out of asset providers

* Pre-fetch assets to publish asset state on event broker
  • Loading branch information
taldekar authored Feb 27, 2025
1 parent db14c48 commit a4a0a5c
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,17 @@

package software.aws.toolkits.eclipse.amazonq.chat;

import java.util.Optional;

import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.PlatformUI;

import software.aws.toolkits.eclipse.amazonq.providers.assets.ChatWebViewAssetProvider;

public final class ChatStateManager {
private static ChatStateManager instance;
private ChatWebViewAssetProvider chatAssetProvider;
private Browser browser;
private Composite dummyParent;
private volatile boolean hasPreservedState = false;

private ChatStateManager() {
chatAssetProvider = new ChatWebViewAssetProvider();
}

public static synchronized ChatStateManager getInstance() {
if (instance == null) {
instance = new ChatStateManager();
Expand All @@ -48,10 +39,6 @@ public synchronized void updateBrowser(final Browser browser) {
this.browser = browser;
}

public synchronized Optional<String> getContent() {
return chatAssetProvider.getContent();
}

public synchronized boolean hasPreservedState() {
return hasPreservedState;
}
Expand Down Expand Up @@ -83,7 +70,6 @@ public void dispose() {
browser = null;
}
disposeDummyParent();
chatAssetProvider.dispose();
hasPreservedState = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@

package software.aws.toolkits.eclipse.amazonq.providers.assets;

import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;

import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.BrowserFunction;
import org.eclipse.swt.browser.ProgressAdapter;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.widgets.Display;

import com.fasterxml.jackson.databind.ObjectMapper;

import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager;
import software.aws.toolkits.eclipse.amazonq.chat.ChatTheme;
import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys;
import software.aws.toolkits.eclipse.amazonq.lsp.AwsServerCapabiltiesProvider;
import software.aws.toolkits.eclipse.amazonq.lsp.model.ChatOptions;
Expand All @@ -19,11 +29,77 @@
import software.aws.toolkits.eclipse.amazonq.plugin.Activator;
import software.aws.toolkits.eclipse.amazonq.providers.lsp.LspManagerProvider;
import software.aws.toolkits.eclipse.amazonq.util.ObjectMapperFactory;
import software.aws.toolkits.eclipse.amazonq.util.PluginPlatform;
import software.aws.toolkits.eclipse.amazonq.util.PluginUtils;
import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils;
import software.aws.toolkits.eclipse.amazonq.util.WebviewAssetServer;
import software.aws.toolkits.eclipse.amazonq.views.AmazonQChatViewActionHandler;
import software.aws.toolkits.eclipse.amazonq.views.LoginViewCommandParser;
import software.aws.toolkits.eclipse.amazonq.views.ViewActionHandler;
import software.aws.toolkits.eclipse.amazonq.views.ViewCommandParser;

public final class ChatWebViewAssetProvider extends WebViewAssetProvider {

private WebviewAssetServer webviewAssetServer;
private final ChatTheme chatTheme;
private final ViewCommandParser commandParser;
private final ViewActionHandler actionHandler;
private final ChatCommunicationManager chatCommunicationManager;

public ChatWebViewAssetProvider() {
chatTheme = new ChatTheme();
commandParser = new LoginViewCommandParser();
chatCommunicationManager = ChatCommunicationManager.getInstance();
actionHandler = new AmazonQChatViewActionHandler(chatCommunicationManager);
}

@Override
public void injectAssets(final Browser browser) {
new BrowserFunction(browser, "ideCommand") {
@Override
public Object function(final Object[] arguments) {
ThreadingUtils.executeAsyncTask(() -> {
handleMessageFromUI(browser, arguments);
});
return null;
}
};

new BrowserFunction(browser, "isMacOs") {
@Override
public Object function(final Object[] arguments) {
return Boolean.TRUE.equals(PluginUtils.getPlatform() == PluginPlatform.MAC);
}
};

new BrowserFunction(browser, "copyToClipboard") {
@Override
public Object function(final Object[] arguments) {
if (arguments.length > 0 && arguments[0] instanceof String) {
StringSelection stringSelection = new StringSelection((String) arguments[0]);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null);
}
return null;
}
};

// Inject chat theme after mynah-ui has loaded
browser.addProgressListener(new ProgressAdapter() {
@Override
public void completed(final ProgressEvent event) {
Display.getDefault().syncExec(() -> {
try {
chatTheme.injectTheme(browser);
disableBrowserContextMenu(browser);
} catch (Exception e) {
Activator.getLogger().info("Error occurred while injecting theme into Q chat", e);
}
});
}
});

browser.setText(getContent().get());
}

@Override
public Optional<String> getContent() {
Expand Down Expand Up @@ -298,6 +374,15 @@ private String serializeQuickActionCommands(final List<QuickActionsCommandGroup>
}
}

private void handleMessageFromUI(final Browser browser, final Object[] arguments) {
try {
commandParser.parseCommand(arguments)
.ifPresent(parsedCommand -> actionHandler.handleCommand(parsedCommand, browser));
} catch (Exception e) {
Activator.getLogger().error("Error processing message from Amazon Q chat", e);
}
}

public Optional<String> resolveJsPath() {
var chatUiDirectory = getChatUiDirectory();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,53 @@
import java.nio.file.Paths;
import java.util.Optional;

import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.BrowserFunction;

import software.aws.toolkits.eclipse.amazonq.plugin.Activator;
import software.aws.toolkits.eclipse.amazonq.telemetry.UiTelemetryProvider;
import software.aws.toolkits.eclipse.amazonq.util.PluginUtils;
import software.aws.toolkits.eclipse.amazonq.util.ThemeDetector;
import software.aws.toolkits.eclipse.amazonq.util.WebviewAssetServer;
import software.aws.toolkits.eclipse.amazonq.views.LoginViewActionHandler;
import software.aws.toolkits.eclipse.amazonq.views.LoginViewCommandParser;
import software.aws.toolkits.eclipse.amazonq.views.ViewActionHandler;
import software.aws.toolkits.eclipse.amazonq.views.ViewCommandParser;
import software.aws.toolkits.eclipse.amazonq.views.ViewConstants;

public final class ToolkitLoginWebViewAssetProvider extends WebViewAssetProvider {

private WebviewAssetServer webviewAssetServer;
private static final ThemeDetector THEME_DETECTOR = new ThemeDetector();
private final ViewCommandParser commandParser;
private final ViewActionHandler actionHandler;

public ToolkitLoginWebViewAssetProvider() {
this.commandParser = new LoginViewCommandParser();
this.actionHandler = new LoginViewActionHandler();
}

@Override
public void injectAssets(final Browser browser) {
new BrowserFunction(browser, ViewConstants.COMMAND_FUNCTION_NAME) {
@Override
public Object function(final Object[] arguments) {
commandParser.parseCommand(arguments)
.ifPresent(command -> actionHandler.handleCommand(command, browser));
return null;
}
};
new BrowserFunction(browser, "telemetryEvent") {
@Override
public Object function(final Object[] arguments) {
String clickEvent = (String) arguments[0];
UiTelemetryProvider.emitClickEventMetric("auth_" + clickEvent);
return null;
}
};

browser.setText(getContent().get());
}

@Override
public Optional<String> getContent() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@

import java.util.Optional;

import org.eclipse.swt.browser.Browser;

public abstract class WebViewAssetProvider {

public abstract void injectAssets(final Browser browser);

public abstract Optional<String> getContent();

public abstract void dispose();
Expand All @@ -31,4 +35,8 @@ function waitForFunction(functionName, timeout = 30000) {
""";
}

protected final void disableBrowserContextMenu(final Browser browser) {
browser.execute("document.oncontextmenu = e => e.preventDefault();");
}

}

This file was deleted.

Loading

0 comments on commit a4a0a5c

Please sign in to comment.