Skip to content

Commit

Permalink
[extension] Add route to auto start conversation (#8649)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdraier authored Nov 15, 2024
1 parent fc83fa2 commit eabf604
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 50 deletions.
76 changes: 54 additions & 22 deletions extension/app/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ import type {
GetActiveTabBackgroundResponse,
InputBarStatusMessage,
} from "./src/lib/messages";
import { sendAttachSelection as sendAttachSelection } from "./src/lib/messages";
import { generatePKCE } from "./src/lib/utils";

const log = console.error;

const state: {
port: chrome.runtime.Port | undefined;
extensionReady: boolean;
inputBarReady: boolean;
lastHandler: (() => void) | undefined;
} = {
port: undefined,
extensionReady: false,
inputBarReady: false,
lastHandler: undefined,
Expand All @@ -46,6 +47,11 @@ chrome.runtime.onUpdateAvailable.addListener(async (details) => {
*/
chrome.runtime.onInstalled.addListener(() => {
void chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
chrome.contextMenus.create({
id: "ask_dust",
title: "Ask @dust to summarize this page",
contexts: ["all"],
});
chrome.contextMenus.create({
id: "add_tab_content",
title: "Add tab content to conversation",
Expand All @@ -67,10 +73,12 @@ chrome.runtime.onInstalled.addListener(() => {
chrome.runtime.onConnect.addListener((port) => {
if (port.name === "sidepanel-connection") {
console.log("Sidepanel is there");
state.port = port;
state.extensionReady = true;
port.onDisconnect.addListener(() => {
// This fires when sidepanel closes
console.log("Sidepanel was closed");
state.port = undefined;
state.extensionReady = false;
state.inputBarReady = false;
state.lastHandler = undefined;
Expand All @@ -80,27 +88,53 @@ chrome.runtime.onConnect.addListener((port) => {

const getActionHandler = (menuItemId: string | number) => {
switch (menuItemId) {
case "ask_dust":
return () => {
if (state.port) {
const params = JSON.stringify({
includeContent: true,
includeScreenshot: false,
text: ":mention[dust]{sId=dust} summarize this page.",
configurationId: "dust",
});
state.port.postMessage({
type: "ROUTE_CHANGE",
pathname: "/run",
search: `?${params}`,
});
}
};
case "add_tab_content":
return () =>
void sendAttachSelection({
includeContent: true,
includeScreenshot: false,
});
break;
return () => {
if (state.port) {
state.port.postMessage({
type: "ATTACH_TAB",
includeContent: true,
includeScreenshot: false,
});
}
};
case "add_tab_screenshot":
return () =>
void sendAttachSelection({
includeContent: false,
includeScreenshot: true,
});
break;
return () => {
if (state.port) {
state.port.postMessage({
type: "ATTACH_TAB",
includeContent: false,
includeScreenshot: true,
});
}
};
case "add_selection":
return () =>
void sendAttachSelection({
includeContent: true,
includeScreenshot: false,
includeSelectionOnly: true,
});
return () => {
if (state.port) {
state.port.postMessage({
type: "ATTACH_TAB",
includeContent: true,
includeScreenshot: false,
includeSelectionOnly: true,
});
}
};
}
};

Expand All @@ -116,10 +150,8 @@ chrome.contextMenus.onClicked.addListener(async (event, tab) => {
void chrome.sidePanel.open({
windowId: tab.windowId,
});
} else if (state.inputBarReady) {
handler();
} else {
// Extension is loaded but the input bar is not visible - do nothing.
await handler();
}
});

Expand Down
14 changes: 8 additions & 6 deletions extension/app/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ import "./src/css/custom.css";

import { Notification } from "@dust-tt/sparkle";
import { AuthProvider } from "@extension/components/auth/AuthProvider";
import { PortProvider } from "@extension/components/PortContext";
import { routes } from "@extension/pages/routes";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";

const router = createBrowserRouter(routes);

const App = () => {
chrome.runtime.connect({ name: "sidepanel-connection" });
return (
<AuthProvider>
<Notification.Area>
<RouterProvider router={router} />
</Notification.Area>
</AuthProvider>
<PortProvider>
<AuthProvider>
<Notification.Area>
<RouterProvider router={router} />
</Notification.Area>
</AuthProvider>
</PortProvider>
);
};
const rootElement = document.getElementById("root");
Expand Down
19 changes: 19 additions & 0 deletions extension/app/src/components/PortContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createContext, useEffect, useState } from "react";

export const PortContext = createContext<chrome.runtime.Port | null>(null);

export const PortProvider = ({ children }: { children: React.ReactNode }) => {
const [port, setPort] = useState<chrome.runtime.Port | null>(null);

useEffect(() => {
const port = chrome.runtime.connect({ name: "sidepanel-connection" });

setPort(port);

return () => {
port.disconnect();
};
}, []);

return <PortContext.Provider value={port}>{children}</PortContext.Provider>;
};
21 changes: 20 additions & 1 deletion extension/app/src/components/auth/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
Spinner,
} from "@dust-tt/sparkle";
import { useAuth } from "@extension/components/auth/AuthProvider";
import { PortContext } from "@extension/components/PortContext";
import type { RouteChangeMesssage } from "@extension/lib/messages";
import type { StoredUser } from "@extension/lib/storage";
import { getPendingUpdate } from "@extension/lib/storage";
import type { ReactNode } from "react";
import { useEffect, useState } from "react";
import { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

type ProtectedRouteProps = {
Expand All @@ -35,6 +37,23 @@ export const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
const navigate = useNavigate();
const [isLatestVersion, setIsLatestVersion] = useState(true);

const port = useContext(PortContext);
useEffect(() => {
if (port) {
const listener = (message: RouteChangeMesssage) => {
const { type } = message;
if (type === "ROUTE_CHANGE") {
navigate({ pathname: message.pathname, search: message.search });
return false;
}
};
port.onMessage.addListener(listener);
return () => {
port.onMessage.removeListener(listener);
};
}
}, [port, navigate]);

useEffect(() => {
if (!isAuthenticated || !isUserSetup || !user || !workspace) {
navigate("/login");
Expand Down
32 changes: 18 additions & 14 deletions extension/app/src/components/input_bar/InputBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { InputBarCitations } from "@extension/components/input_bar/InputBarCitat
import type { InputBarContainerProps } from "@extension/components/input_bar/InputBarContainer";
import { InputBarContainer } from "@extension/components/input_bar/InputBarContainer";
import { InputBarContext } from "@extension/components/input_bar/InputBarContext";
import { PortContext } from "@extension/components/PortContext";
import { useFileUploaderService } from "@extension/hooks/useFileUploaderService";
import { useDustAPI } from "@extension/lib/dust_api";
import type { AttachSelectionMessage } from "@extension/lib/messages";
Expand Down Expand Up @@ -58,21 +59,24 @@ export function AssistantInputBar({
owner,
});

const port = useContext(PortContext);
useEffect(() => {
void sendInputBarStatus(true);
const listener = (message: AttachSelectionMessage) => {
const { type, ...options } = message;
if (type === "ATTACH_TAB") {
// Handle message
void fileUploaderService.uploadContentTab(options);
}
};
chrome.runtime.onMessage.addListener(listener);
return () => {
void sendInputBarStatus(false);
chrome.runtime.onMessage.removeListener(listener);
};
});
if (port) {
void sendInputBarStatus(true);
const listener = async (message: AttachSelectionMessage) => {
const { type } = message;
if (type === "ATTACH_TAB") {
// Handle message
void fileUploaderService.uploadContentTab(message);
}
};
port.onMessage.addListener(listener);
return () => {
void sendInputBarStatus(false);
port.onMessage.removeListener(listener);
};
}
}, []);

const { droppedFiles, setDroppedFiles } = useFileDrop();

Expand Down
22 changes: 15 additions & 7 deletions extension/app/src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ export type InputBarStatusMessage = {
available: boolean;
};

export type RouteChangeMesssage = {
type: "ROUTE_CHANGE";
pathname: string;
search: string;
};

const sendMessage = <T, U>(message: T): Promise<U> => {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage(message, (response: U | undefined) => {
Expand Down Expand Up @@ -147,6 +153,15 @@ export const sendGetActiveTabMessage = (params: GetActiveTabOptions) => {
});
};

export const sendInputBarStatus = (available: boolean) => {
return sendMessage<InputBarStatusMessage, void>({
type: "INPUT_BAR_STATUS",
available,
});
};

// Messages from background script to content script

export const sendAttachSelection = (
opts: GetActiveTabOptions = { includeContent: true, includeScreenshot: false }
) => {
Expand All @@ -155,10 +170,3 @@ export const sendAttachSelection = (
...opts,
});
};

export const sendInputBarStatus = (available: boolean) => {
return sendMessage<InputBarStatusMessage, void>({
type: "INPUT_BAR_STATUS",
available,
});
};
60 changes: 60 additions & 0 deletions extension/app/src/pages/RunPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { LightWorkspaceType } from "@dust-tt/client";
import { Spinner } from "@dust-tt/sparkle";
import { useFileUploaderService } from "@extension/hooks/useFileUploaderService";
import { postConversation } from "@extension/lib/conversation";
import { useDustAPI } from "@extension/lib/dust_api";
import { useEffect } from "react";
import { useLocation, useNavigate } from "react-router";

export const RunPage = ({ workspace }: { workspace: LightWorkspaceType }) => {
const navigate = useNavigate();
const location = useLocation();
const dustAPI = useDustAPI();

const fileUploaderService = useFileUploaderService({
owner: workspace,
});

useEffect(() => {
const run = async () => {
const params = JSON.parse(decodeURI(location.search.substr(1)));

const files = await fileUploaderService.uploadContentTab({
includeContent: params.includeContent,
includeScreenshot: params.includeScreenshot,
includeSelectionOnly: params.includeSelectionOnly,
updateBlobs: false,
});

const conversationRes = await postConversation({
dustAPI,
messageData: {
input: params.text,
mentions: [{ configurationId: params.configurationId }],
},
contentFragments: files
? files.map((cf) => ({
title: cf.filename,
fileId: cf.fileId || "",
url: cf.publicUrl,
}))
: [],
});

fileUploaderService.resetUpload();

if (conversationRes.isOk()) {
navigate(`/conversations/${conversationRes.value.sId}`);
} else {
navigate("/");
}
};

void run();
}, []);
return (
<div className="w-full h-full flex items-center justify-center">
<Spinner size="xl" />
</div>
);
};
9 changes: 9 additions & 0 deletions extension/app/src/pages/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ConversationPage } from "@extension/pages/ConversationPage";
import { ConversationsPage } from "@extension/pages/ConversationsPage";
import { LoginPage } from "@extension/pages/LoginPage";
import { MainPage } from "@extension/pages/MainPage";
import { RunPage } from "@extension/pages/RunPage";

export const routes = [
{
Expand Down Expand Up @@ -51,4 +52,12 @@ export const routes = [
</ProtectedRoute>
),
},
{
path: "/run",
element: (
<ProtectedRoute>
{({ workspace }) => <RunPage workspace={workspace} />}
</ProtectedRoute>
),
},
];

0 comments on commit eabf604

Please sign in to comment.