Skip to content

Commit

Permalink
Merge refactor branch with settings page (#50)
Browse files Browse the repository at this point in the history
* move refactor changes from private repo

* Update src/editors.ts

Co-authored-by: Tommy MacWilliam <[email protected]>

* inject script tag to documentElement, use mutationObserver for all DOM changes, not just the ReactDOM, shorten timeout for monaco listeners

* remove stray print, remove alert for use on copy clickables and replace with div overlay

* style fixes, simplify scroll command response logic

* remove debug print statements

* fix bug in test page and ace editor language determination

* fix bug in codemirror language determination

* Add popup and "Always Show Clickables" setting (#48)

* add popup page and match styling to client

* implement message passing between popup and background, store settings

* first functional prototype

* add missing await in message passing from content to injected script, don't return immediately if always show clickables is true

* add show inputs and links, change UI phrasing

* remove debug prints

Co-authored-by: Tommy MacWilliam <[email protected]>
  • Loading branch information
sam-dixon and tmacwill authored Feb 23, 2022
1 parent d7630e8 commit 0e4280a
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 69 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ build
*.log
.idea
package-lock.json
.vscode
6 changes: 5 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
"name": "Serenade",
"version": "2.0.0",
"description": "Code with voice. Learn more at https://serenade.ai.",
"permissions": ["tabs", "scripting"],
"permissions": ["tabs", "scripting", "storage"],
"host_permissions": ["ws://localhost:17373/", "*://*/*"],
"action": {
"default_title": "Serenade for Chrome",
"default_popup": "src/popup.html"
},
"background": {
"service_worker": "build/extension.js"
},
Expand Down
39 changes: 25 additions & 14 deletions src/content-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,34 @@ document.addEventListener(`serenade-injected-script-command-response`, (e: any)
}
});

async function sendMessageToInjectedScript(data: any) {
const id = Math.random();
const response = await new Promise((resolve) => {
resolvers[id] = resolve;
document.dispatchEvent(
new CustomEvent(`serenade-injected-script-command-request`, {
detail: {
id,
data: data,
},
})
);
});
return response
}

chrome.runtime.onMessage.addListener(async (request, _sender, sendResponse) => {
if (request.type == "injected-script-command-request") {
const id = Math.random();
const response = await new Promise((resolve) => {
resolvers[id] = resolve;
document.dispatchEvent(
new CustomEvent(`serenade-injected-script-command-request`, {
detail: {
id,
data: request.data,
},
})
);
});

const response = await sendMessageToInjectedScript(request.data)
sendResponse(response);
}

return true;
});

document.addEventListener("DOMContentLoaded", async () => {
const settings = await chrome.storage.sync.get(["alwaysShowClickables"]);
sendMessageToInjectedScript({
type: "updateSettings",
...settings,
})
});
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const ipc = new IPC(
navigator.userAgent.indexOf("Brave") != -1
? "brave"
: navigator.userAgent.indexOf("Edg") != -1
? "edge"
: "chrome",
? "edge"
: "chrome",
extensionCommandHandler
);

Expand Down
92 changes: 42 additions & 50 deletions src/injected-command-handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as editors from "./editors";

export default class InjectedCommandHandler {
overlays: { node: Node, type: string }[] = [];
private overlays: { node: Node; type: string }[] = [];
private settings = {
alwaysShowClickables: false,
};

private clickNode(node: Node) {
const element = node as HTMLElement;
Expand Down Expand Up @@ -31,47 +34,13 @@ export default class InjectedCommandHandler {
private inViewport(element: HTMLElement) {
const bounding = element.getBoundingClientRect();

// If all four of the corners are covered by another element that's not a parent, no need to show
if (
!element.contains(
document.elementFromPoint(bounding.left + 1, bounding.top + 1)
) &&
!element.contains(
document.elementFromPoint(bounding.right - 1, bounding.top + 1)
) &&
!element.contains(
document.elementFromPoint(bounding.left + 1, bounding.bottom - 1)
) &&
!element.contains(
document.elementFromPoint(bounding.right - 1, bounding.bottom - 1)
) &&
!document
.elementFromPoint(bounding.left + 1, bounding.top + 1)
?.contains(element) &&
!document
.elementFromPoint(bounding.right - 1, bounding.top + 1)
?.contains(element) &&
!document
.elementFromPoint(bounding.left + 1, bounding.bottom - 1)
?.contains(element) &&
!document
.elementFromPoint(bounding.right - 1, bounding.bottom - 1)
?.contains(element)
) {
return false;
}

// Check that this is in the viewport and has some dimensions
return (
((bounding.top >= 0 && bounding.top <= window.innerHeight) ||
(bounding.bottom >= 0 && bounding.bottom <= window.innerHeight)) &&
((bounding.left >= 0 && bounding.left <= window.innerWidth) ||
(bounding.right >= 0 && bounding.right <= window.innerWidth)) &&
!!(
element.offsetWidth ||
element.offsetHeight ||
element.getClientRects().length
)
!!(element.offsetWidth || element.offsetHeight || element.getClientRects().length)
);
}

Expand Down Expand Up @@ -124,15 +93,22 @@ export default class InjectedCommandHandler {
}

private elementIsScrollable(element: HTMLElement, direction: string): boolean {
if (direction === "up" || direction === "down" || direction === "bottom" || direction === "top") {
if (
direction === "up" ||
direction === "down" ||
direction === "bottom" ||
direction === "top"
) {
const overflowStyle = window.getComputedStyle(element).overflowY;
return (
element.scrollHeight > element.clientHeight && (overflowStyle === "scroll" || overflowStyle === "auto")
element.scrollHeight > element.clientHeight &&
(overflowStyle === "scroll" || overflowStyle === "auto")
);
} else if (direction === "left" || direction === "right") {
const overflowStyle = window.getComputedStyle(element).overflowX;
return (
element.scrollWidth > element.clientWidth && (overflowStyle === "scroll" || overflowStyle === "auto")
element.scrollWidth > element.clientWidth &&
(overflowStyle === "scroll" || overflowStyle === "auto")
);
}
return false;
Expand Down Expand Up @@ -191,13 +167,15 @@ export default class InjectedCommandHandler {

private async scrollInDirection(direction: string) {
let hoveredElements = document.querySelectorAll("*:hover");
let lastHoveredElement = hoveredElements.length ? hoveredElements[hoveredElements.length - 1] as HTMLElement : null;
let lastHoveredElement = hoveredElements.length
? (hoveredElements[hoveredElements.length - 1] as HTMLElement)
: null;
let scrolled = false;
while (lastHoveredElement && !scrolled) {
if (this.elementIsScrollable(lastHoveredElement, direction)) {
let options = this.scrollOptions(lastHoveredElement, direction);
if (direction === "top" || direction === "bottom") {
lastHoveredElement.scrollTo(options)
lastHoveredElement.scrollTo(options);
} else {
lastHoveredElement.scrollBy(options);
}
Expand All @@ -209,7 +187,7 @@ export default class InjectedCommandHandler {
if (!scrolled) {
let options = this.scrollOptions(window, direction);
if (direction === "top" || direction === "bottom") {
window.scrollTo(options)
window.scrollTo(options);
} else {
window.scrollBy(options);
}
Expand Down Expand Up @@ -238,7 +216,7 @@ export default class InjectedCommandHandler {
}
// Use first match
if (!target) {
target = matches[0]
target = matches[0];
}

const style = window.getComputedStyle(target as Element);
Expand Down Expand Up @@ -271,7 +249,9 @@ export default class InjectedCommandHandler {
overlay.style.fontFamily =
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif';
document.body.appendChild(overlay);
setTimeout(() => {document.body.removeChild(overlay)}, 1000);
setTimeout(() => {
document.body.removeChild(overlay);
}, 1000);
}

private showOverlays(nodes: Node[], overlayType: string) {
Expand All @@ -288,7 +268,7 @@ export default class InjectedCommandHandler {
overlay.style.position = "absolute";
overlay.style.zIndex = "999";
overlay.style.top = elementRect.top - bodyRect.top + "px";
overlay.style.left = elementRect.left - bodyRect.left + "px";
overlay.style.left = elementRect.left - bodyRect.left - overlay.clientWidth + "px";
overlay.style.padding = "3px";
overlay.style.textAlign = "center";
overlay.style.color = "#e6ecf2";
Expand Down Expand Up @@ -338,8 +318,7 @@ export default class InjectedCommandHandler {
const pathNumber = parseInt(data.path, 10);
if (!isNaN(pathNumber)) {
// if path is a number, check that it is available
response.clickable =
pathNumber - 1 >= 0 && pathNumber - 1 < this.overlays.length;
response.clickable = pathNumber - 1 >= 0 && pathNumber - 1 < this.overlays.length;
} else {
// otherwise, search for matching nodes
let matches = this.nodesMatchingPath(data.path);
Expand Down Expand Up @@ -404,6 +383,9 @@ export default class InjectedCommandHandler {
}

async COMMAND_TYPE_GET_EDITOR_STATE(_data: any): Promise<any> {
if (this.settings.alwaysShowClickables) {
this.COMMAND_TYPE_SHOW({ text: "all" });
}
const editor = await editors.active();
if (!editor) {
return;
Expand Down Expand Up @@ -438,10 +420,11 @@ export default class InjectedCommandHandler {
if (data.text == "links") {
selector = 'a, button, summary, [role="link"], [role="button"]';
} else if (data.text == "inputs") {
selector =
'input, textarea, [role="checkbox"], [role="radio"], .CodeMirror';
selector = 'input, textarea, [role="checkbox"], [role="radio"]';
} else if (data.text == "code") {
selector = "pre, code";
} else if (data.text == "all") {
selector = 'a, button, summary, [role="link"], [role="button"], input, textarea, [role="checkbox"], [role="radio"]';
} else {
return;
}
Expand All @@ -456,12 +439,21 @@ export default class InjectedCommandHandler {

async COMMAND_TYPE_USE(data: any): Promise<any> {
let overlay = this.overlays[data.index - 1];
if (overlay.type === "links" || overlay.type === "inputs") {
if (overlay.type === "links" || overlay.type === "inputs" || overlay.type === "all") {
this.clickNode(overlay.node);
} else if (overlay.type === "code") {
await this.copyCode(overlay.node);
this.showCopyOverlay(data.index);
}
this.clearOverlays();
}

async updateSettings(data: any): Promise<void> {
this.settings = {
alwaysShowClickables: data.alwaysShowClickables,
};
if (!this.settings.alwaysShowClickables) {
this.clearOverlays();
}
}
}
7 changes: 5 additions & 2 deletions src/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,20 @@ export default class IPC {
} catch (e) { }
}

private async tab(): Promise<number> {
private async tab(): Promise<any> {
const [result] = await chrome.tabs.query({
active: true,
currentWindow: true,
});

return result.id!;
return result?.id;
}

private async sendMessageToContentScript(message: any): Promise<any> {
let tabId = await this.tab();
if (!tabId) {
return;
}
return new Promise((resolve) => {
chrome.tabs.sendMessage(tabId, message, (response) => {
resolve(response);
Expand Down
92 changes: 92 additions & 0 deletions src/popup.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
html {
min-width: 400px;
}

body {
font-family: aktiv-grotesk, sans-serif;
font-size: medium;
color: #475569;
text-align: center;
padding-bottom: 1em;
}

h1 {
font-size: medium;
}

a {
color: #fff;
background-color: rgb(59 130 246);
display: inline-block;
text-decoration: none;
padding: 0.5em 1.5em;
margin-bottom: 1em;
font-weight: normal;
text-align: center;
vertical-align: middle;
border-radius: 1em;
box-shadow: 0 4px 6px -1px #3b82f688;
}

a:hover {
background-color: rgb(37 99 235);
box-shadow: 0 4px 6px -1px #3b82f688;
}

/* The switch - the box around the slider */
.switch {
--width: 40px;
--height: calc(var(--width) / 1.8);
position: relative;
display: inline-block;
width: var(--width);
height: var(--height);
vertical-align: middle;
}

/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}

/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgb(29 78 216);
}

.slider:before {
position: absolute;
content: "";
height: calc(0.8 * var(--height));
width: calc(0.8 * var(--height));
top: calc(0.1 * var(--height));
left: calc(0.1 * var(--height));
border-radius: calc(var(--height) / 2);
background-color: #fff;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3);
}

input:checked + .slider {
background-color: rgb(59 130 246);
}

input:focus + .slider {
box-shadow: 0 0 1px rgb(59 130 246);
}

input:checked + .slider:before {
transform: translateX(calc(var(--width) - var(--height)));
}

/* Rounded sliders */
.slider.round {
border-radius: var(--height);
}
Loading

0 comments on commit 0e4280a

Please sign in to comment.