-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Pass populated AWS_CA_BUNDLE env var to Flare (#341) * Add UI notification to alert user of deprecated manifest version (#312) * Add webview dependency missing logic to ViewRouter * Revert commit 'Add UI notification to alert user of deprecated manifest version' * Revert 'Pass populated AWS_CA_BUNDLE env var to Flare' * Rebase changes from Browser Provider PR * Fix ViewRouter --------- Co-authored-by: Jonathan Breedlove <[email protected]> Co-authored-by: Nicolas <[email protected]>
- Loading branch information
1 parent
4ec1937
commit 5dc56b9
Showing
10 changed files
with
412 additions
and
260 deletions.
There are no files selected for viewing
194 changes: 194 additions & 0 deletions
194
.../src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package software.aws.toolkits.eclipse.amazonq.providers.assets; | ||
|
||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
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; | ||
import software.aws.toolkits.eclipse.amazonq.lsp.model.QuickActions; | ||
import software.aws.toolkits.eclipse.amazonq.lsp.model.QuickActionsCommandGroup; | ||
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.WebviewAssetServer; | ||
|
||
public class ChatWebViewAssetProvider extends WebViewAssetProvider { | ||
|
||
private WebviewAssetServer webviewAssetServer; | ||
|
||
public ChatWebViewAssetProvider() { | ||
Optional<String> content = getContent(); | ||
Activator.getEventBroker().post(WebViewAssetState.class, | ||
content.isPresent() ? WebViewAssetState.RESOLVED : WebViewAssetState.DEPENDENCY_MISSING); | ||
} | ||
|
||
@Override | ||
public Optional<String> getContent() { | ||
var chatAsset = resolveJsPath(); | ||
if (!chatAsset.isPresent()) { | ||
return Optional.empty(); | ||
} | ||
|
||
String chatJsPath = chatAsset.get(); | ||
|
||
return Optional.of(String.format(""" | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<meta | ||
http-equiv="Content-Security-Policy" | ||
content="default-src 'none'; script-src %s 'unsafe-inline'; style-src %s 'unsafe-inline'; | ||
img-src 'self' data:; object-src 'none'; base-uri 'none'; connect-src swt:;" | ||
> | ||
<title>Amazon Q Chat</title> | ||
%s | ||
</head> | ||
<body> | ||
%s | ||
</body> | ||
</html> | ||
""", chatJsPath, chatJsPath, generateCss(), generateJS(chatJsPath))); | ||
} | ||
|
||
private String generateCss() { | ||
return """ | ||
<style> | ||
body, | ||
html { | ||
background-color: var(--mynah-color-bg); | ||
color: var(--mynah-color-text-default); | ||
height: 100vh; | ||
width: 100%%; | ||
overflow: hidden; | ||
margin: 0; | ||
padding: 0; | ||
} | ||
.mynah-ui-icon-plus, | ||
.mynah-ui-icon-cancel { | ||
-webkit-mask-size: 155% !important; | ||
mask-size: 155% !important; | ||
mask-position: center; | ||
scale: 60%; | ||
} | ||
.mynah-ui-icon-tabs { | ||
-webkit-mask-size: 102% !important; | ||
mask-size: 102% !important; | ||
mask-position: center; | ||
} | ||
textarea:placeholder-shown { | ||
line-height: 1.5rem; | ||
} | ||
</style> | ||
"""; | ||
} | ||
|
||
private String generateJS(final String jsEntrypoint) { | ||
var chatQuickActionConfig = generateQuickActionConfig(); | ||
var disclaimerAcknowledged = Activator.getPluginStore().get(PluginStoreKeys.CHAT_DISCLAIMER_ACKNOWLEDGED); | ||
return String.format(""" | ||
<script type="text/javascript" src="%s" defer></script> | ||
<script type="text/javascript"> | ||
%s | ||
const init = () => { | ||
waitForFunction('ideCommand') | ||
.then(() => { | ||
amazonQChat.createChat({ | ||
postMessage: (message) => { | ||
ideCommand(JSON.stringify(message)); | ||
} | ||
}, { | ||
quickActionCommands: %s, | ||
disclaimerAcknowledged: %b | ||
}); | ||
}) | ||
.catch(error => console.error('Error initializing chat:', error)); | ||
} | ||
window.addEventListener('load', init); | ||
</script> | ||
""", jsEntrypoint, getWaitFunction(), chatQuickActionConfig, "true".equals(disclaimerAcknowledged)); | ||
} | ||
|
||
/* | ||
* Generates javascript for chat options to be supplied to Chat UI defined here | ||
* https://github.com/aws/language-servers/blob/ | ||
* 785f8dee86e9f716fcfa29b2e27eb07a02387557/chat-client/src/client/chat.ts#L87 | ||
*/ | ||
private String generateQuickActionConfig() { | ||
return Optional.ofNullable(AwsServerCapabiltiesProvider.getInstance().getChatOptions()) | ||
.map(ChatOptions::quickActions).map(QuickActions::quickActionsCommandGroups) | ||
.map(this::serializeQuickActionCommands).orElse("[]"); | ||
} | ||
|
||
private String serializeQuickActionCommands(final List<QuickActionsCommandGroup> quickActionCommands) { | ||
try { | ||
ObjectMapper mapper = ObjectMapperFactory.getInstance(); | ||
return mapper.writeValueAsString(quickActionCommands); | ||
} catch (Exception e) { | ||
Activator.getLogger().warn("Error occurred when json serializing quick action commands", e); | ||
return ""; | ||
} | ||
} | ||
|
||
public Optional<String> resolveJsPath() { | ||
var chatUiDirectory = getChatUiDirectory(); | ||
|
||
if (!isValid(chatUiDirectory)) { | ||
Activator.getLogger().error( | ||
"Error loading Chat UI. If override used, please verify the override env variables else restart Eclipse"); | ||
return Optional.empty(); | ||
} | ||
|
||
String jsFile = Paths.get(chatUiDirectory.get()).resolve("amazonq-ui.js").toString(); | ||
var jsParent = Path.of(jsFile).getParent(); | ||
var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); | ||
|
||
if (webviewAssetServer == null) { | ||
webviewAssetServer = new WebviewAssetServer(); | ||
} | ||
|
||
var result = webviewAssetServer.resolve(jsDirectoryPath); | ||
if (!result) { | ||
Activator.getLogger().error(String.format( | ||
"Error loading Chat UI. Unable to find the `amazonq-ui.js` file in the directory: %s. Please verify and restart", | ||
chatUiDirectory.get())); | ||
return Optional.empty(); | ||
} | ||
|
||
String chatJsPath = webviewAssetServer.getUri() + "amazonq-ui.js"; | ||
|
||
return Optional.ofNullable(chatJsPath); | ||
} | ||
|
||
private Optional<String> getChatUiDirectory() { | ||
try { | ||
return Optional.of(LspManagerProvider.getInstance().getLspInstallation().getClientDirectory()); | ||
} catch (Exception e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
private boolean isValid(final Optional<String> chatUiDirectory) { | ||
return chatUiDirectory.isPresent() && Files.exists(Paths.get(chatUiDirectory.get())); | ||
} | ||
|
||
@Override | ||
public void dispose() { | ||
if (webviewAssetServer != null) { | ||
webviewAssetServer.stop(); | ||
} | ||
webviewAssetServer = null; | ||
} | ||
|
||
} |
110 changes: 110 additions & 0 deletions
110
...tware/aws/toolkits/eclipse/amazonq/providers/assets/ToolkitLoginWebViewAssetProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package software.aws.toolkits.eclipse.amazonq.providers.assets; | ||
|
||
import java.io.IOException; | ||
import java.net.URL; | ||
import java.net.URLDecoder; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.Optional; | ||
|
||
import software.aws.toolkits.eclipse.amazonq.plugin.Activator; | ||
import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; | ||
import software.aws.toolkits.eclipse.amazonq.util.ThemeDetector; | ||
import software.aws.toolkits.eclipse.amazonq.util.WebviewAssetServer; | ||
|
||
public final class ToolkitLoginWebViewAssetProvider extends WebViewAssetProvider { | ||
|
||
private WebviewAssetServer webviewAssetServer; | ||
private static final ThemeDetector THEME_DETECTOR = new ThemeDetector(); | ||
|
||
public ToolkitLoginWebViewAssetProvider() { | ||
Optional<String> content = getContent(); | ||
Activator.getEventBroker().post(WebViewAssetState.class, | ||
content.isPresent() ? WebViewAssetState.RESOLVED : WebViewAssetState.DEPENDENCY_MISSING); | ||
} | ||
|
||
@Override | ||
public Optional<String> getContent() { | ||
try { | ||
URL jsFile = PluginUtils.getResource("webview/build/assets/js/getStart.js"); | ||
String decodedPath = URLDecoder.decode(jsFile.getPath(), StandardCharsets.UTF_8); | ||
|
||
// Remove leading slash for Windows paths | ||
decodedPath = decodedPath.replaceFirst("^/([A-Za-z]:)", "$1"); | ||
|
||
Path jsParent = Paths.get(decodedPath).getParent(); | ||
String jsDirectoryPath = jsParent.normalize().toString(); | ||
|
||
webviewAssetServer = new WebviewAssetServer(); | ||
var result = webviewAssetServer.resolve(jsDirectoryPath); | ||
if (!result) { | ||
return Optional.of("Failed to load JS"); | ||
} | ||
var loginJsPath = webviewAssetServer.getUri() + "getStart.js"; | ||
boolean isDarkTheme = THEME_DETECTOR.isDarkTheme(); | ||
return Optional.of(String.format( | ||
""" | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta | ||
http-equiv="Content-Security-Policy" | ||
content="default-src 'none'; script-src %s 'unsafe-inline'; style-src %s 'unsafe-inline'; | ||
img-src 'self' data:; object-src 'none'; base-uri 'none'; connect-src swt:;" | ||
> | ||
<title>AWS Q</title> | ||
</head> | ||
<body class="jb-light"> | ||
<div id="app"></div> | ||
<script type="text/javascript" src="%s" defer></script> | ||
<script type="text/javascript"> | ||
%s | ||
const init = () => { | ||
changeTheme(%b); | ||
Promise.all([ | ||
waitForFunction('ideCommand'), | ||
waitForFunction('telemetryEvent') | ||
]) | ||
.then(([ideCommand, telemetryEvent]) => { | ||
const ideApi = { | ||
postMessage(message) { | ||
ideCommand(JSON.stringify(message)); | ||
} | ||
}; | ||
window.ideApi = ideApi; | ||
const telemetryApi = { | ||
postClickEvent(event) { | ||
telemetryEvent(event); | ||
} | ||
}; | ||
window.telemetryApi = telemetryApi; | ||
ideCommand(JSON.stringify({"command":"onLoad"})); | ||
}) | ||
.catch(error => console.error('Error in initialization:', error)); | ||
}; | ||
window.addEventListener('load', init); | ||
</script> | ||
</body> | ||
</html> | ||
""", | ||
loginJsPath, loginJsPath, loginJsPath, getWaitFunction(), isDarkTheme)); | ||
} catch (IOException e) { | ||
return Optional.of("Failed to load JS"); | ||
} | ||
} | ||
|
||
@Override | ||
public void dispose() { | ||
if (webviewAssetServer != null) { | ||
webviewAssetServer.stop(); | ||
} | ||
webviewAssetServer = null; | ||
} | ||
|
||
} |
34 changes: 34 additions & 0 deletions
34
plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/WebViewAssetProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package software.aws.toolkits.eclipse.amazonq.providers.assets; | ||
|
||
import java.util.Optional; | ||
|
||
public abstract class WebViewAssetProvider { | ||
|
||
public abstract Optional<String> getContent(); | ||
|
||
public abstract void dispose(); | ||
|
||
protected final String getWaitFunction() { | ||
return """ | ||
function waitForFunction(functionName, timeout = 30000) { | ||
return new Promise((resolve, reject) => { | ||
const startTime = Date.now(); | ||
const checkFunction = () => { | ||
if (typeof window[functionName] === 'function') { | ||
resolve(window[functionName]); | ||
} else if (Date.now() - startTime > timeout) { | ||
reject(new Error(`Timeout waiting for ${functionName}`)); | ||
} else { | ||
setTimeout(checkFunction, 100); | ||
} | ||
}; | ||
checkFunction(); | ||
}); | ||
} | ||
"""; | ||
} | ||
|
||
} |
13 changes: 13 additions & 0 deletions
13
plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/WebViewAssetState.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package software.aws.toolkits.eclipse.amazonq.providers.assets; | ||
|
||
public enum WebViewAssetState { | ||
RESOLVED, DEPENDENCY_MISSING; | ||
|
||
public boolean isDependencyMissing() { | ||
return this == DEPENDENCY_MISSING; | ||
} | ||
|
||
} |
Oops, something went wrong.