Skip to content

Commit

Permalink
Change default keyboard shortcuts; update Readme and add link in Conf…
Browse files Browse the repository at this point in the history
…ig tab to change the shortcuts
  • Loading branch information
petrgazarov committed Jan 8, 2024
1 parent 40bba02 commit aca0246
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 113 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Install on the [Chrome web store](https://chromewebstore.google.com/detail/cumul
- AI Contextual Understanding: Pass screenshots as context to the language model.
- Conversation History: View previous messages and resend with edits.
- Adaptive UI: Dark and light mode theme selection is automatic based on your system settings.
- Convenient layout: Toggle Chrome's Side panel with a keyboard shortcut (Ctrl/Cmd + B suggested).

### Security & Privacy

Expand Down Expand Up @@ -55,19 +54,19 @@ More comparisons below:

1. Activate the extension

Click on the extension icon or use a keyboard shortcut (Mac: `Cmd + B`, Windows/Linux: `Ctrl + B`) to open the Side panel. The extension can only be activated on AWS pages.
Click on the extension icon to open the Side panel. The extension can only be activated on AWS pages. Keyboard shortcut for the activation can be configured in chrome://extensions/shortcuts.

2. Add your OpenAI API key in the "Config" tab

3. Start chatting

`Enter` key sends a text-only message. The model does _not_ get context about your screen by default. To include a screenshot with the message, press `Cmd + I` (Mac) or `Ctrl + I` (Linux/Windows).
`Enter` key sends a text-only message. The model does _not_ get context about your screen by default. To include a screenshot with the message, press `Shift + Cmd + I` (Mac) or `Shift + Ctrl + I` (Linux/Windows).

### FAQ

- Why doesn't the keyboard shortcut work?

The provided keyboard shortcuts are suggested defaults, and the bindings are handled by the browser. You can visit chrome://extensions/shortcuts and verify that the correct shortcuts are set for this extension. If there is a conflict with another extension, you can change the shortcuts to something else.
The provided keyboard shortcuts are suggested defaults, and the bindings are handled by the browser. You can visit chrome://extensions/shortcuts and verify that the correct shortcuts are set for this extension. If there is a conflict, you can change the shortcuts to something else.

- I'm getting `` 404 The model `gpt-4-vision-preview` does not exist or you do not have access to it `` error.

Expand All @@ -81,8 +80,8 @@ More comparisons below:
- `amazonaws-us-gov.com`
- `amazonaws.cn`
- `aws.training`
- `aws` (top-level domain)
- `aws` (top-level domain)

It is disabled on all other pages.

- I have another issue not listed here or want to provide feedback.
Expand Down
6 changes: 2 additions & 4 deletions public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@
],
"commands": {
"submit_with_screenshot": {
"suggested_key": "Ctrl+I",
"suggested_key": "Shift+Ctrl+I",
"description": "Submit message with screenshot"
},
"_execute_action": {
"suggested_key": "Ctrl+B"
}
"_execute_action": {}
}
}
2 changes: 1 addition & 1 deletion src/serviceWorker/openai/systemPrompt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ You are an AI assistant that helps users navigate the AWS management console and

Here are the features of the chat interface:
- The chat interface is a Chrome extension that is available on the Chrome Web Store.
- Users may take screenshots of the AWS console by using "Cmd/Ctrl + I" (this is the default binding that can be changed in user settings). This will take a screenshot of the current tab, insert it into the chat, and submit it to the assistant.
- Users may take screenshots of the AWS console by using "Shift + Cmd/Ctrl + I". This is the default binding that can be changed in extension settings (chrome://extensions/shortcuts). This will take a screenshot of the current tab, insert it into the chat, and submit it to the assistant.
- Alternatively, users can press "Enter" to submit a text message to the assistant without the screenshot.
- Assistant is built using OpenAI GPT-4 Turbo with vision.
- Users may view and edit their previous conversations with the assistant by navigating to the "History" tab.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useAtom } from "jotai";
import { useCallback, useRef, useState } from "react";

import { Button } from "sidePanel/components/Button";
import TextInput from "sidePanel/components/TextInput";
import { openaiApiKeyAtom } from "sidePanel/utils/atoms";
import { getAllNonCharacterKeys } from "sidePanel/utils/helpers";
import { saveOpenaiApiKey } from "utils/helpers";

import { SectionContainer, SubmitButtonContainer } from "./styled";

const ApiKeySection = () => {
const [openaiApiKey, setOpenaiApiKey] = useAtom(openaiApiKeyAtom);
const [inputValue, setInputValue] = useState(openaiApiKey);
const textInputRef = useRef<HTMLInputElement>(null);

const saveApiKey = useCallback(() => {
const value = textInputRef.current?.value || "";

saveOpenaiApiKey(value.trim()).then((maskedKey) => {
setInputValue(maskedKey);
setOpenaiApiKey(maskedKey);
});
}, [setInputValue, setOpenaiApiKey]);

const inputMasked = inputValue.includes("...") && inputValue === openaiApiKey;

const isApiKeySubmitDisabled = useCallback(
() => inputMasked || inputValue === openaiApiKey,
[openaiApiKey, inputMasked, inputValue]
);

const onKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && !isApiKeySubmitDisabled()) {
e.preventDefault();
saveApiKey();
}

const characterKeyPressed =
!getAllNonCharacterKeys().includes(e.key) && !e.ctrlKey && !e.metaKey;

if (inputMasked && characterKeyPressed) {
try {
/* document.execCommand is used to allow the built-in "undo" action to revert this input change.
* It is deprecated, but works for now. Might need to replaced in the future. */
textInputRef.current?.focus();
textInputRef.current?.select();
document.execCommand("delete");
} catch (err) {
console.debug(
"[Cumuli] Failed to call document.execCommand('delete') on the input"
);
setInputValue("");
}
}
},
[isApiKeySubmitDisabled, inputMasked, saveApiKey]
);

const onChange = useCallback((value: string) => setInputValue(value), []);

return (
<SectionContainer>
<TextInput
label="OpenAI API Key"
value={inputValue}
onChange={onChange}
onKeyDown={onKeyDown}
placeholder="Enter OpenAI API Key"
showSavedStatus={inputValue === openaiApiKey}
textInputRef={textInputRef}
/>
<SubmitButtonContainer>
<Button onClick={saveApiKey} disabled={isApiKeySubmitDisabled()}>
Submit
</Button>
</SubmitButtonContainer>
</SectionContainer>
);
};

export default ApiKeySection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styled from "styled-components";

export const SectionContainer = styled.div`
display: flex;
align-items: end;
justify-content: space-between;
`;

export const SubmitButtonContainer = styled.div`
margin-left: 8px;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ButtonLabel, ChangeShortcutsButton, SectionContainer } from "./styled";

const ChangeKeyboardShortcutsSection = () => {
return (
<SectionContainer>
<ButtonLabel>
Keyboard shortcuts are customizable in the extension settings
</ButtonLabel>
<ChangeShortcutsButton
onClick={() =>
chrome.tabs.create({ url: "chrome://extensions/shortcuts" })
}
>
Change keyboard shortcuts
</ChangeShortcutsButton>
</SectionContainer>
);
};

export default ChangeKeyboardShortcutsSection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import styled from "styled-components";

import { SecondaryButton } from "sidePanel/components/Button";
import { FONT_SIZE_SECONDARY } from "sidePanel/globalStyles";

export const SectionContainer = styled.div`
margin-top: 35px;
`;

export const ButtonLabel = styled.div`
margin-bottom: 5px;
color: ${({ theme }) => theme.colors.HELP_TEXT};
font-size: ${FONT_SIZE_SECONDARY};
`;

export const ChangeShortcutsButton = styled(SecondaryButton)`
width: 247px;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const ClearDataSection = () => {
);
case SectionState.after_delete:
return (
<ClearDataButton disabled>Cleared all conversations</ClearDataButton>
<ClearDataButton disabled>Cleared</ClearDataButton>
);
default:
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import styled from "styled-components";
import { PrimaryButton, SecondaryButton } from "sidePanel/components/Button";
import { FONT_SIZE_SECONDARY } from "sidePanel/globalStyles";

export const SectionContainer = styled.div`
margin-top: 35px;
`;

export const ButtonsContainer = styled.div`
display: flex;
width: 247px;
Expand All @@ -14,10 +18,6 @@ export const ButtonLabel = styled.div`
font-size: ${FONT_SIZE_SECONDARY};
`;

export const SectionContainer = styled.div`
margin-top: 35px;
`;

export const ClearDataButton = styled(PrimaryButton)`
flex-grow: 1;
`;
Expand Down
85 changes: 5 additions & 80 deletions src/sidePanel/components/SidePanel/tabs/ConfigTab/index.tsx
Original file line number Diff line number Diff line change
@@ -1,89 +1,14 @@
import { useAtom } from "jotai";
import { useCallback, useRef, useState } from "react";

import { Button } from "sidePanel/components/Button";
import TextInput from "sidePanel/components/TextInput";
import { openaiApiKeyAtom } from "sidePanel/utils/atoms";
import { getAllNonCharacterKeys } from "sidePanel/utils/helpers";
import { saveOpenaiApiKey } from "utils/helpers";

import ApiKeySection from "./ApiKeySection";
import ChangeKeyboardShortcutsSection from "./ChangeKeyboardShortcutsSection";
import ClearDataSection from "./ClearDataSection";
import Footer from "./Footer";
import {
ApiKeySection,
ConfigTabContent,
SubmitApiKeyButtonContainer,
} from "./styled";
import { ConfigTabContent } from "./styled";

const ConfigTab = () => {
const [openaiApiKey, setOpenaiApiKey] = useAtom(openaiApiKeyAtom);
const [inputValue, setInputValue] = useState(openaiApiKey);
const textInputRef = useRef<HTMLInputElement>(null);

const saveApiKey = useCallback(() => {
const value = textInputRef.current?.value || "";

saveOpenaiApiKey(value.trim()).then((maskedKey) => {
setInputValue(maskedKey);
setOpenaiApiKey(maskedKey);
});
}, [setInputValue, setOpenaiApiKey]);

const inputMasked = inputValue.includes("...") && inputValue === openaiApiKey;

const isApiKeySubmitDisabled = useCallback(
() => inputMasked || inputValue === openaiApiKey,
[openaiApiKey, inputMasked, inputValue]
);

const onKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && !isApiKeySubmitDisabled()) {
e.preventDefault();
saveApiKey();
}

const characterKeyPressed =
!getAllNonCharacterKeys().includes(e.key) && !e.ctrlKey && !e.metaKey;

if (inputMasked && characterKeyPressed) {
try {
/* document.execCommand is used to allow the built-in "undo" action to revert this input change.
* It is deprecated, but works for now. Might need to replaced in the future. */
textInputRef.current?.focus();
textInputRef.current?.select();
document.execCommand("delete");
} catch (err) {
console.debug(
"[Cumuli] Failed to call document.execCommand('delete') on the input"
);
setInputValue("");
}
}
},
[isApiKeySubmitDisabled, inputMasked, saveApiKey]
);

const onChange = useCallback((value: string) => setInputValue(value), []);

return (
<ConfigTabContent>
<ApiKeySection>
<TextInput
label="OpenAI API Key"
value={inputValue}
onChange={onChange}
onKeyDown={onKeyDown}
placeholder="Enter OpenAI API Key"
showSavedStatus={inputValue === openaiApiKey}
textInputRef={textInputRef}
/>
<SubmitApiKeyButtonContainer>
<Button onClick={saveApiKey} disabled={isApiKeySubmitDisabled()}>
Submit
</Button>
</SubmitApiKeyButtonContainer>
</ApiKeySection>
<ApiKeySection />
<ChangeKeyboardShortcutsSection />
<ClearDataSection />
<Footer />
</ConfigTabContent>
Expand Down
10 changes: 0 additions & 10 deletions src/sidePanel/components/SidePanel/tabs/ConfigTab/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,6 @@ export const ConfigTabContent = styled.div`
height: 100%;
`;

export const SubmitApiKeyButtonContainer = styled.div`
margin-left: 8px;
`;

export const ApiKeySection = styled.div`
display: flex;
align-items: end;
justify-content: space-between;
`;

export const StyledFooter = styled.div`
display: flex;
flex-grow: 1;
Expand Down
Loading

0 comments on commit aca0246

Please sign in to comment.