Skip to content

Commit

Permalink
Mynah UI fixes (#379)
Browse files Browse the repository at this point in the history
  • Loading branch information
taldekar authored Feb 20, 2025
1 parent 924c905 commit e19db25
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ private String getCssForDarkTheme() {
// Card
themeMap.put(QChatCssVariable.CardBackground, cardBackgroundColor);

themeMap.put(QChatCssVariable.LineHeight, "1.25em");

return getCss(themeMap);
}

Expand Down Expand Up @@ -150,6 +152,8 @@ private String getCssForLightTheme() {
// Card
themeMap.put(QChatCssVariable.CardBackground, cardBackgroundColor);

themeMap.put(QChatCssVariable.LineHeight, "1.25em");

return getCss(themeMap);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ public enum QChatCssVariable {
AlternateForeground("--mynah-color-alternate-reverse"),

// Card
CardBackground("--mynah-card-bg");
CardBackground("--mynah-card-bg"),

// Line height
LineHeight("--mynah-line-height");

private String value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

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

import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.util.List;
import java.util.Optional;

Expand All @@ -26,6 +28,8 @@
import software.aws.toolkits.eclipse.amazonq.lsp.model.QuickActionsCommandGroup;
import software.aws.toolkits.eclipse.amazonq.plugin.Activator;
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.views.actions.AmazonQCommonActions;

Expand Down Expand Up @@ -91,6 +95,7 @@ public void completed(final ProgressEvent event) {
amazonQCommonActions = getAmazonQCommonActions();

chatCommunicationManager.setChatUiRequestListener(this);

new BrowserFunction(browser, "ideCommand") {
@Override
public Object function(final Object[] arguments) {
Expand All @@ -101,6 +106,24 @@ public Object function(final Object[] arguments) {
}
};

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
Expand Down Expand Up @@ -210,6 +233,16 @@ private String generateCss() {
mask-position: center;
scale: 60%;
}
.code-snippet-close-button i.mynah-ui-icon-cancel,
.mynah-chat-item-card-related-content-show-more i.mynah-ui-icon-down-open {
-webkit-mask-size: 195.5% !important;
mask-size: 195.5% !important;
mask-position: center;
aspect-ratio: 1/1;
width: 15px;
height: 15px;
scale: 50%
}
.mynah-ui-icon-tabs {
-webkit-mask-size: 102% !important;
mask-size: 102% !important;
Expand All @@ -218,6 +251,9 @@ private String generateCss() {
textarea:placeholder-shown {
line-height: 1.5rem;
}
.mynah-ui-spinner-container > span.mynah-ui-spinner-logo-part > .mynah-ui-spinner-logo-mask.text {
opacity: 1 !important;
}
</style>
""";
}
Expand All @@ -236,7 +272,8 @@ private String generateJS(final String jsEntrypoint) {
postMessage: (message) => {
ideCommand(JSON.stringify(message));
}
}, {
},
{
quickActionCommands: %s,
disclaimerAcknowledged: %b
});
Expand All @@ -245,9 +282,19 @@ private String generateJS(final String jsEntrypoint) {
}
window.addEventListener('load', init);
%s
%s
%s
%s
</script>
""", jsEntrypoint, getWaitFunction(), chatQuickActionConfig,
"true".equals(disclaimerAcknowledged));
"true".equals(disclaimerAcknowledged), getArrowKeyBlockingFunction(),
getSelectAllAndCopySupportFunctions(), getPreventEmptyPopupFunction(), getFocusOnChatPromptFunction());
}

/*
Expand All @@ -271,6 +318,137 @@ private String serializeQuickActionCommands(final List<QuickActionsCommandGroup>
}
}

private String getArrowKeyBlockingFunction() {
return """
window.addEventListener('load', () => {
const textarea = document.querySelector('textarea.mynah-chat-prompt-input');
if (textarea) {
textarea.addEventListener('keydown', (event) => {
const cursorPosition = textarea.selectionStart;
const hasText = textarea.value.length > 0;
// block arrow keys on empty text area
switch (event.key) {
case 'ArrowLeft':
if (!hasText || cursorPosition === 0) {
event.preventDefault();
event.stopPropagation();
}
break;
case 'ArrowRight':
if (!hasText || cursorPosition === textarea.value.length) {
event.preventDefault();
event.stopPropagation();
}
break;
}
});
}
});
""";
}

private String getSelectAllAndCopySupportFunctions() {
return """
window.addEventListener('load', () => {
const textarea = document.querySelector('textarea.mynah-chat-prompt-input');
if (textarea) {
textarea.addEventListener("keydown", (event) => {
if (((isMacOs() && event.metaKey) || (!isMacOs() && event.ctrlKey))
&& event.key === 'a') {
textarea.select();
event.preventDefault();
event.stopPropagation();
}
});
}
});
window.addEventListener('load', () => {
const textarea = document.querySelector('textarea.mynah-chat-prompt-input');
if (textarea) {
textarea.addEventListener("keydown", (event) => {
if (((isMacOs() && event.metaKey) || (!isMacOs() && event.ctrlKey))
&& event.key === 'c') {
copyToClipboard(textarea.value);
event.preventDefault();
event.stopPropagation();
}
});
}
});
""";
}

private String getPreventEmptyPopupFunction() {
String selector = ".mynah-button" + ".mynah-button-secondary.mynah-button-border" + ".fill-state-always"
+ ".mynah-chat-item-followup-question-option" + ".mynah-ui-clickable-item";

return """
const observer = new MutationObserver((mutations) => {
try {
const selector = '%s';
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) { // Check if it's an element node
// Check for direct match
if (node.matches && node.matches(selector)) {
attachEventListeners(node);
}
// Check for nested matches
if (node.querySelectorAll) {
const buttons = node.querySelectorAll(selector); // Missing selector parameter
buttons.forEach(attachEventListeners);
}
}
});
});
} catch (error) {
console.error('Error in mutation observer:', error);
}
});
function attachEventListeners(element) {
if (!element || element.dataset.hasListener) return; // Prevent duplicate listeners
const handleMouseOver = function(event) {
const textSpan = this.querySelector('span.mynah-button-label');
if (textSpan && textSpan.scrollWidth <= textSpan.offsetWidth) {
event.stopImmediatePropagation();
event.stopPropagation();
event.preventDefault();
}
};
element.addEventListener('mouseover', handleMouseOver, true);
element.dataset.hasListener = 'true';
}
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
""".formatted(selector);
}

private String getFocusOnChatPromptFunction() {
return """
window.addEventListener('load', () => {
const chatContainer = document.querySelector('.mynah-chat-prompt');
if (chatContainer) {
chatContainer.addEventListener('click', (event) => {
if (!event.target.closest('.mynah-chat-prompt-input')) {
keepFocusOnPrompt();
}
});
}
});
""";
}

@Override
public final void onSendToChatUi(final String message) {
String script = "window.postMessage(" + message + ");";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class QChatCssVariableTest {

@Test
void testEnumValues() {
assertEquals(27, QChatCssVariable.values().length);
assertEquals(28, QChatCssVariable.values().length);
}

@Test
Expand Down Expand Up @@ -72,6 +72,11 @@ void testCardValues() {
assertEquals("--mynah-card-bg", QChatCssVariable.CardBackground.getValue());
}

@Test
void testLineHeighValues() {
assertEquals("--mynah-line-height", QChatCssVariable.LineHeight.getValue());
}

@Test
void testInvalidEnum() {
assertThrows(IllegalArgumentException.class, () -> QChatCssVariable.valueOf("NonExistentVariable"));
Expand Down

0 comments on commit e19db25

Please sign in to comment.