Skip to content

Commit

Permalink
v0.0.18 (#28)
Browse files Browse the repository at this point in the history
v0.0.18
  • Loading branch information
albertocubeddu authored Aug 15, 2024
1 parent 95bf851 commit 8893b05
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 30 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ Move it somewhere else ASAP:

# Changelog

### 0.0.18
- SelectionMenu: Now you can choose to enable/disable
- SelectionMenu: When a key is pressed (e.g backspace for remove, or CTRL/CMD + C for copying) the menu automatically disappear


### 0.0.17
- Development: Integrated Playwright for testing and added a suite of automated tests

Expand Down
17 changes: 14 additions & 3 deletions contents/SelectionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { createCall } from "~lib/vapiOutbound"
import { Storage } from "@plasmohq/storage";
import { sendToBackground } from "@plasmohq/messaging"
import { adjustXYSelectionMenu, getRealXY } from "~lib/calculationXY"
import { useStorage } from "@plasmohq/storage/hook"
import { defaultGlobalConfig, setGlobalConfig } from "~lib/configurations/globalConfig"
import deepmerge from "deepmerge"
const storage = new Storage();

// We enable the extension to be used in anywebsite with an http/https protocol.
Expand All @@ -34,6 +37,8 @@ const SelectionMenu = () => {
const [selectedText, setSelectedText] = useState("")
const [menuPosition, setMenuPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 })
const [menuItems, setMenuItems] = useState<chrome.contextMenus.CreateProperties[]>([]) // Initialize with an empty array
let [config] = useStorage("globalConfig", defaultGlobalConfig)
config = deepmerge(defaultGlobalConfig, config)


const handleMouseUp = useCallback((event: MouseEvent) => { // Use useCallback
Expand All @@ -55,6 +60,10 @@ const SelectionMenu = () => {
}
}, []);

const handleKeyDown = useCallback((event: MouseEvent) => { // Use useCallback
setMenuPosition({ x: 0, y: 0 })
}, []);

// Separate functions for handling different actions
const handleCopyClipboard = async (element) => {
const r = await sendToBackground({
Expand Down Expand Up @@ -133,6 +142,7 @@ const SelectionMenu = () => {

useEffect(() => {
document.addEventListener("mouseup", handleMouseUp)
document.addEventListener("keydown", handleKeyDown)

const initialize = async () => {
const contextConfigItems =
Expand All @@ -152,14 +162,15 @@ const SelectionMenu = () => {

return () => {
document.removeEventListener("mouseup", handleMouseUp)
document.removeEventListener("keydown", handleKeyDown)
}
}, [])

return (
<>
{menuPosition.x !== 0 && menuPosition.y !== 0 && ( // Check if .x and .y are not equal to 0
{config.selectionMenu.display && menuPosition.x !== 0 && menuPosition.y !== 0 && ( // Check if .x and .y are not equal to 0
<div id="extension-os-selection-menu">
<Command className="rounded-2xl shadow-lg p-0 bg-[#161616] border border-[#505050] dark:border-[#fff] translate-x-1 translate-y-1 text-white " style={{
<Command className="rounded-2xl shadow-lg p-0 bg-[#161616] border border-[#505050] dark:border-[#fff] translate-x-1 translate-y-1" style={{
position: "relative",
top: `${menuPosition.y}px`,
left: `${menuPosition.x}px`,
Expand All @@ -172,7 +183,7 @@ const SelectionMenu = () => {
<CommandGroup >
{menuItems.map((item) => (
<CommandItem className="cursor-pointer opacity-50 hover:opacity-100 hover:bg-[#505050] font-bold m-1 rounded-[5px] py-1 text-[0.9rem]" key={item.id} value={item.title} onSelect={() => handleMenuItemClick(item)}>
<span>{item.title}</span>
<span className="text-white">{item.title}</span>
</CommandItem>
))}
</CommandGroup>
Expand Down
43 changes: 43 additions & 0 deletions lib/configurations/globalConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import deepmerge from "deepmerge";
import { Storage } from "@plasmohq/storage";
const storage = new Storage();

//We want to have everything as Partial (deep too) as the deepmerge will take care of matching with the default/stored configuration.
type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};

export const defaultGlobalConfig = {
selectionMenu: {
display: true,
},
};

export async function getGlobalConfig() {
let storagedConfig = {};
try {
storagedConfig = await storage.get("globalConfig");
} catch (error) {}
return deepmerge(
{},
defaultGlobalConfig,
storagedConfig
) as typeof defaultGlobalConfig;
}

export async function setGlobalConfig(
config: DeepPartial<typeof defaultGlobalConfig>
) {
let storagedConfig: DeepPartial<typeof defaultGlobalConfig>;
try {
storagedConfig = (await storage.get("globalConfig")) as Partial<
typeof defaultGlobalConfig
>;
} catch (error) {}
config = deepmerge<
DeepPartial<typeof defaultGlobalConfig>,
DeepPartial<typeof defaultGlobalConfig>
>(storagedConfig, config);
await storage.set("globalConfig", config);
return config;
}
5 changes: 4 additions & 1 deletion options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import OptionsGeneral from "~options/OptionsGeneral"
import OptionsHeader from "~options/OptionsHeader"
import OptionsMixtureOfAgents from "~options/OptionsMixtureOfAgents"
import OptionsPromptFactory from "~options/OptionsPromptFactory"
import OptionsSettings from "~options/OptionsSettings"

// ================================
// MAIN FUNCTION (OPTIONS)
Expand All @@ -26,10 +27,11 @@ export default function Options() {
<nav
className="grid gap-4 text-sm text-muted-foreground"
x-chunk="dashboard-04-chunk-0">
{["general", "promptFactory", "mixtureOfAgents", "about"].map(
{["general", "promptFactory", "mixtureOfAgents", "settings", "about"].map(
(tab) => (
<div
key={tab}
id={tab}
className={` font-light text-xl cursor-pointer rounded-lg ${activeTab === tab ? "bg-black text-[#ff66cc] os-text-gradient" : "text-white"} shadow-md `}
onClick={() => setActiveTab(tab)}
>
Expand All @@ -42,6 +44,7 @@ export default function Options() {
{activeTab === "general" && <OptionsGeneral />}
{activeTab === "promptFactory" && <OptionsPromptFactory />}
{activeTab === "mixtureOfAgents" && <OptionsMixtureOfAgents />}
{activeTab === "settings" && <OptionsSettings />}
{activeTab === "about" && <OptionsAbout />}
</div>
</main>
Expand Down
1 change: 0 additions & 1 deletion options/OptionsPromptFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ export default function OptionsPromptFactory() {
key={key}
className="p-4 pt-6 mb-20 border-t-8 border-l-8 border-2 rounded-lg shadow-lg"
>
{contextMenuItems[key].id}
<div className="flex flex-row justify-between gap-4 px-4">
<div className="flex flex-col gap-1 w-3/4">
<LabelWithTooltip keyTooltip={key} labelText="Display Name" tooltipText="The name displayed in the menu visualised when the user clicks the right-click" />
Expand Down
55 changes: 55 additions & 0 deletions options/OptionsSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
Card,
CardContent,
CardHeader,
} from "@/components/ui/card"
import { useStorage } from "@plasmohq/storage/hook"
import deepmerge from "deepmerge"
import CardHeaderIntro from "~components/blocks/CardHeaderIntro"
import { Checkbox } from "~components/ui/checkbox"
import { defaultGlobalConfig, setGlobalConfig } from "~lib/configurations/globalConfig"

export default function OptionsSettings() {
//We're setting to the default if nothing exists.
let [config] = useStorage("globalConfig", defaultGlobalConfig)
config = deepmerge(defaultGlobalConfig, config)

return (
<div className="grid gap-6 text-lg">
<Card x-chunk="dashboard-04-chunk-2">
<CardHeader>
<CardHeaderIntro title={"Settings"} description={"Customise the look and feel of Extension | OS"} />
</CardHeader>
<CardContent>
Experience the perfect blend of simplicity and power with our tool—designed to be user-friendly while effortlessly handling complex tasks.
</CardContent>
</Card>

<Card x-chunk="dashboard-04-chunk-2">
<CardHeader>
<CardHeaderIntro title={"Selection Menu"} description={"The Selection menu shows when you select a text in any webpage"} />
</CardHeader>
<CardContent>
<div className="flex items-center space-x-2">
<Checkbox id="display-selection-menu" checked={config.selectionMenu.display}
onCheckedChange={(checked) => {
const result = checked === true ? true : false
setGlobalConfig({
selectionMenu: {
display: result,
}
})
}} />
<label
htmlFor="display-selection-menu"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Display Selection Menu when two words are underlined.
</label>
</div>

</CardContent>
</Card>
</div>
)
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "extension-os",
"displayName": "Extension-OS: Your AI Partner",
"version": "0.0.17",
"version": "0.0.18",
"description": "AI at Your Fingertips, Anytime, Anywhere.",
"author": "[email protected]",
"scripts": {
Expand All @@ -24,6 +24,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"deepmerge": "^4.3.1",
"install": "^0.13.0",
"lucide-react": "^0.414.0",
"plasmo": "0.88.0",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 0 additions & 23 deletions tests/basic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,6 @@ test("option page loaded at the beginning", async ({ page }) => {
await expect(pageTitle).toBe("Extension-OS: Your AI Partner");
});

test("open a page, the menu should show with all the elements", async ({
page,
}) => {
await page.waitForTimeout(1000);
await page.goto("https://www.york.ac.uk/teaching/cws/wws/webpage1.html");
const pageTitle = await page.title();
await expect(pageTitle).toBe("webpage1");
await page.mouse.move(100, 100); // Move the mouse to the starting position (x: 100, y: 100)
await page.mouse.down(); // Press the mouse button down to start selecting
await page.mouse.move(300, 300); // Move the mouse to the end position (x: 300, y: 300) to select text
await page.mouse.up(); // Release the mouse button to complete the selection

const options = await page.getByRole("option");
const optionsCount = await options.count();
//Must be 7 as you have to count the +2 (separator + Setup Your Own Prompt)
expect(optionsCount).toBe(7);

const isGrammarFixerPresent = await page
.getByRole("option", { name: "❗Grammar Fixer" })
.isVisible();
expect(isGrammarFixerPresent).toBe(true);
});

test("open a page, the menu should show when the right click is pressed", async ({
page,
}) => {
Expand Down
1 change: 0 additions & 1 deletion tests/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export const test = base.extend<{
}>({
context: async ({}, use) => {
const pathToExtension = path.join(__dirname, "../build/chrome-mv3-prod");
console.log(pathToExtension);
const context = await chromium.launchPersistentContext("", {
headless: false,
args: [
Expand Down
44 changes: 44 additions & 0 deletions tests/selectionMenu.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { test, expect } from "./fixtures";

test("Selection Menu: Must show with the default config", async ({ page }) => {
await page.waitForTimeout(1000);
await page.goto("https://www.york.ac.uk/teaching/cws/wws/webpage1.html");
const pageTitle = await page.title();
await expect(pageTitle).toBe("webpage1");
await page.mouse.move(100, 100); // Move the mouse to the starting position (x: 100, y: 100)
await page.mouse.down(); // Press the mouse button down to start selecting
await page.mouse.move(300, 300); // Move the mouse to the end position (x: 300, y: 300) to select text
await page.mouse.up(); // Release the mouse button to complete the selection

const options = await page.getByRole("option");
const optionsCount = await options.count();
//Must be 7 as you have to count the +2 (separator + Setup Your Own Prompt)
expect(optionsCount).toBe(7);

const isGrammarFixerPresent = await page
.getByRole("option", { name: "❗Grammar Fixer" })
.isVisible();
expect(isGrammarFixerPresent).toBe(true);
});

test("Selection Menu: Must NOT show when the config is set to false", async ({
page,
}) => {
await page.waitForTimeout(1000);

await page.click("#settings");
await page.waitForTimeout(1000);
await page.uncheck("#display-selection-menu");
await page.goto("https://www.york.ac.uk/teaching/cws/wws/webpage1.html");
const pageTitle = await page.title();
await expect(pageTitle).toBe("webpage1");
await page.mouse.move(100, 100); // Move the mouse to the starting position (x: 100, y: 100)
await page.mouse.down(); // Press the mouse button down to start selecting
await page.mouse.move(300, 300); // Move the mouse to the end position (x: 300, y: 300) to select text
await page.mouse.up(); // Release the mouse button to complete the selection

const options = await page.getByRole("option");
const optionsCount = await options.count();
//Must be 0 as the configuration is NOT Showing the menu!
expect(optionsCount).toBe(0);
});

0 comments on commit 8893b05

Please sign in to comment.