feat(clipper): Added "Clip as screenshot" switch to allow clipping content as a screenshot and save it to Files

This commit is contained in:
Aman Harwara
2023-05-05 17:03:13 +05:30
parent 74f8f9fafe
commit 0d3b7f7d94
10 changed files with 96 additions and 20 deletions

View File

@@ -1,9 +1,9 @@
import { runtime, action, browserAction, windows, storage } from 'webextension-polyfill'
import { RuntimeMessage, RuntimeMessageTypes } from '../types/message'
import { ClipPayload, RuntimeMessage, RuntimeMessageTypes } from '../types/message'
const isFirefox = navigator.userAgent.indexOf('Firefox/') !== -1
const openPopupAndClipSelection = async (payload: { title: string; content: string }) => {
const openPopupAndClipSelection = async (payload: ClipPayload) => {
await storage.local.set({ clip: payload })
if (isFirefox) {

View File

@@ -1,8 +1,10 @@
import { runtime } from 'webextension-polyfill'
import { Readability } from '@mozilla/readability'
import { RuntimeMessage, RuntimeMessageTypes } from '../types/message'
import { toPng } from 'html-to-image'
let isSelectingNodeForClipping = false
let isScreenshotMode = false
runtime.onMessage.addListener(async (message: RuntimeMessage) => {
switch (message.type) {
@@ -10,6 +12,10 @@ runtime.onMessage.addListener(async (message: RuntimeMessage) => {
isSelectingNodeForClipping = true
return
}
case RuntimeMessageTypes.ToggleScreenshotMode: {
isScreenshotMode = message.enabled
return
}
case RuntimeMessageTypes.HasSelection: {
const selection = window.getSelection()
@@ -40,6 +46,11 @@ runtime.onMessage.addListener(async (message: RuntimeMessage) => {
return { title: document.title, content: result.innerHTML, url: window.location.href }
}
case RuntimeMessageTypes.GetFullPage: {
if (isScreenshotMode) {
const content = await toPng(document.body)
return { title: document.title, content: content, url: window.location.href, isScreenshot: true }
}
return { title: document.title, content: document.body.innerHTML, url: window.location.href }
}
case RuntimeMessageTypes.GetArticle: {
@@ -90,7 +101,7 @@ const disableNodeSelection = () => {
nodeOverlayElement.style.visibility = 'hidden'
}
window.addEventListener('click', (event) => {
window.addEventListener('click', async (event) => {
if (!isSelectingNodeForClipping) {
return
}
@@ -102,10 +113,10 @@ window.addEventListener('click', (event) => {
return
}
const title = document.title
const content = target.outerHTML
const content = isScreenshotMode ? await toPng(target) : target.outerHTML
void runtime.sendMessage({
type: RuntimeMessageTypes.OpenPopupWithSelection,
payload: { title, content, url: window.location.href },
payload: { title, content, url: window.location.href, isScreenshot: isScreenshotMode },
} as RuntimeMessage)
})

View File

@@ -5,6 +5,7 @@ export const RuntimeMessageTypes = {
GetFullPage: 'get-full-page',
OpenPopupWithSelection: 'open-popup-with-selection',
StartNodeSelection: 'start-node-selection',
ToggleScreenshotMode: 'toggle-screenshot-mode',
} as const
export type RuntimeMessageType = typeof RuntimeMessageTypes[keyof typeof RuntimeMessageTypes]
@@ -15,6 +16,7 @@ export type ClipPayload = {
title: string
content: string
url: string
isScreenshot?: boolean
}
export type RuntimeMessageReturnTypes = {
@@ -24,6 +26,7 @@ export type RuntimeMessageReturnTypes = {
[RuntimeMessageTypes.GetFullPage]: ClipPayload
[RuntimeMessageTypes.OpenPopupWithSelection]: void
[RuntimeMessageTypes.StartNodeSelection]: void
[RuntimeMessageTypes.ToggleScreenshotMode]: void
}
export type RuntimeMessage =
@@ -32,5 +35,9 @@ export type RuntimeMessage =
payload: ClipPayload
}
| {
type: Exclude<RuntimeMessageType, MessagesWithClipPayload>
type: typeof RuntimeMessageTypes.ToggleScreenshotMode
enabled: boolean
}
| {
type: Exclude<RuntimeMessageType, MessagesWithClipPayload | typeof RuntimeMessageTypes.ToggleScreenshotMode>
}

View File

@@ -1,14 +1,14 @@
import { tabs } from 'webextension-polyfill'
import { RuntimeMessageReturnTypes, RuntimeMessageType } from '../types/message'
import { RuntimeMessage, RuntimeMessageReturnTypes } from '../types/message'
export default async function sendMessageToActiveTab<T extends RuntimeMessageType>(
type: T,
): Promise<RuntimeMessageReturnTypes[T] | undefined> {
export default async function sendMessageToActiveTab<T extends RuntimeMessage>(
message: T,
): Promise<RuntimeMessageReturnTypes[T['type']] | undefined> {
const [activeTab] = await tabs.query({ active: true, currentWindow: true, windowType: 'normal' })
if (!activeTab || !activeTab.id) {
return
}
return await tabs.sendMessage(activeTab.id, { type })
return await tabs.sendMessage(activeTab.id, message)
}