a Chrome Manifest V3 extension that reads the current page, and offers a popup UI that has the page title+content and a textarea for a prompt (with a default value we specify). When the user hits submit, it sends the page title+content to the Anthropic Claude API along with the up to date prompt to summarize it. The user can modify that prompt and re-send the prompt+content to get another summary view of the content.
- Only when clicked:
- it injects a content script
content_script.js
on the currently open tab, and accesses the titlepageTitle
and main content (innerText)pageContent
of the currently open page (extracted via an injected content script, and sent over using astorePageContent
action) - in the background, receives the
storePageContent
data and stores it - only once the new page content is stored, then it pops up a full height window with a minimalistic styled html popup
- in the popup script
- the popup should display a 10px tall rounded css animated red and white candy stripe loading indicator
loadingIndicator
, while waiting for the anthropic api to return- with the currently fetching page title and a running timer in the center showing time elapsed since call started
- do not show it until the api call begins, and hide it when it ends.
- retrieves the page content data using a
getPageContent
action (and the background listens for thegetPageContent
action and retrieves that data) and displays the title at the top of the popup - check extension storage for an
apiKey
, and if it isn't stored, asks for an API key to Anthropic Claude and stores it. - at the bottom of the popup, show a vertically resizable form that has:
- a 2 line textarea with an id and label of
userPrompt
userPrompt
has a default value ofdefaultPrompt = `Please provide a detailed, easy to read HTML summary of the given content`; ```js
- a 4 line textarea with an id and label of
stylePrompt
stylePrompt
has a default value ofdefaultStyle = `Respond with 3-4 highlights per section with important keywords, people, numbers, and facts bolded in this HTML format: <h1>{title here}</h1> <h3>{section title here}</h3> <details> <summary>{summary of the section with <strong>important keywords, people, numbers, and facts bolded</strong> and key quotes repeated}</summary> <ul> <li><strong>{first point}</strong>: {short explanation with <strong>important keywords, people, numbers, and facts bolded</strong>}</li> <li><strong>{second point}</strong>: {same as above}</li> <li><strong>{third point}</strong>: {same as above}</li> <!-- a fourth point if warranted --> </ul> </details> <h3>{second section here}</h3> <p>{summary of the section with <strong>important keywords, people, numbers, and facts bolded</strong> and key quotes repeated}</p> <details> <summary>{summary of the section with <strong>important keywords, people, numbers, and facts bolded</strong> and key quotes repeated}</summary> <ul> <!-- as many points as warranted in the same format as above --> </ul> </details> <h3>{third section here}</h3> <!-- and so on, as many sections and details/summary subpoints as warranted --> With all the words in brackets replaced by the summary of the content. sanitize non visual HTML tags with HTML entities, so <template> becomes <template> but <strong> stays the same. Only draw from the source content, do not hallucinate. Finally, end with other questions that the user might want answered based on this source content: <hr> <h2>Next prompts</h2> <ul> <li>{question 1}</li> <li>{question 2}</li> <li>{question 3}</li> </ul>`; ```js
- and in the last row, on either side,
- and a nicely styled submit button with an id of
sendButton
(tactile styling that "depresses" on click)
- and a nicely styled submit button with an id of
- only when
sendButton
is clicked, calls the Anthropic model endpoint https://api.anthropic.com/v1/complete with:- append the page title
- append the page content
- add the prompt which is a concatenation of
finalPrompt = `Human: ${userPrompt} \n\n ${stylePrompt} \n\n Assistant:`
- and use the
claude-instant-v1
model (ifpageContent
is <70k words) or theclaude-instant-v1-100k
model (if more) - requesting max tokens = the higher of (25% of the length of the page content, or 750 words)
- if another submit event is hit while the previous api call is still inflight, cancel that and start the new one
- a 2 line textarea with an id and label of
- renders the Anthropic-generated result at the top of the popup in a div with an id of
content
- the popup should display a 10px tall rounded css animated red and white candy stripe loading indicator
- it injects a content script
Important Details:
-
It has to run in a browser environment, so no Nodejs APIs allowed.
-
the return signature of the anthropic api is curl https://api.anthropic.com/v1/complete\ -H "x-api-key: $API_KEY"
-H 'content-type: application/json'
-d '{ "prompt": "\n\nHuman: Tell me a haiku about trees\n\nAssistant: ", "model": "claude-v1", "max_tokens_to_sample": 1000, "stop_sequences": ["\n\nHuman:"] }' {"completion":" Here is a haiku about trees:\n\nSilent sentinels, \nStanding solemn in the woods,\nBranches reaching sky.","stop":"\n\nHuman:","stop_reason":"stop_sequence","truncated":false,"log_id":"f5d95cf326a4ac39ee36a35f434a59d5","model":"claude-v1","exception":null} -
in the string prompt sent to Anthropic, first include the page title and page content, and finally append the prompt, clearly vertically separated by spacing.
-
if the Anthropic api call is a 401, handle that by clearing the stored anthropic api key and asking for it again.
-
add styles to make sure the popup's styling follows the basic rules of web design, for example having margins around the body, and a system font stack.
-
style the popup body with but insist on body margins of 16 and a minimum width of 400 and height of 600.
inside of background.js, just take the getPageContent response directly
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'storePageContent') {
// dont access request.pageContent
chrome.storage.local.set({ pageContent: request }, () => {
sendResponse({ success: true });
});
} else if (request.action === 'getPageContent') {
chrome.storage.local.get(['pageContent'], (result) => {
// dont access request.pageContent
sendResponse(result);
});
}
return true;
});
inside of popup.js, Update the function calls to requestAnthropicSummary
in popup.js
to pass the apiKey
:
chrome.storage.local.get(['apiKey'], (result) => {
const apiKey = result.apiKey;
requestAnthropicSummary(defaultPrompt, apiKey);
});
sendButton.addEventListener('click', () => {
chrome.storage.local.get(['apiKey'], (result) => {
const apiKey = result.apiKey;
requestAnthropicSummary(userPrompt.value, apiKey);
});
});
in popup.js
, store the defaultPrompt at the top level.
also, give a HTML format to the anthropic prompt