clipper(web): save height/width with clipped data

This commit is contained in:
Abdullah Atta
2022-12-03 08:58:01 +05:00
parent c5faf62398
commit c1674b6f05
11 changed files with 1855 additions and 1516 deletions

View File

@@ -27,7 +27,7 @@ import {
Server,
Clip,
WEB_EXTENSION_CHANNEL_EVENTS
} from "@notesnook/web-clipper/src/common/bridge";
} from "@notesnook/web-clipper/dist/common/bridge";
import { isUserPremium } from "../hooks/use-is-user-premium";
import { store as themestore } from "../stores/theme-store";
import { store as appstore } from "../stores/app-store";
@@ -133,6 +133,8 @@ class WebExtensionServer implements Server {
"data-mime": attachment.type,
src: clip.url,
title: clip.pageTitle || clip.title,
width: clip.width ? `${clip.width}` : undefined,
height: clip.height ? `${clip.height}` : undefined,
class: "web-clip"
}).outerHTML;
}

View File

@@ -40,7 +40,8 @@ const common = {
"tabs",
"storage",
"contextMenus",
"notifications"
"notifications",
"<all_urls>"
],
content_scripts: [
{
@@ -51,14 +52,14 @@ const common = {
js: ["contentScript.bundle.js"],
matches: ["http://*/*", "https://*/*"],
exclude_matches: ["*://app.notesnook.com/*", "*://localhost/*"]
},
}
],
browser_specific_settings: {
gecko: {
strict_min_version: "105.0"
}
},
icons: ICONS,
icons: ICONS
};
const v2 = {

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
"name": "@notesnook/web-clipper",
"version": "0.1.0",
"private": true,
"main": "./dist/index.js",
"dependencies": {
"@emotion/react": "^11.10.0",
"@hot-loader/react-dom": "^17.0.2",
@@ -76,9 +77,9 @@
"fs-extra": "^10.0.0",
"html-loader": "^3.1.0",
"html-webpack-plugin": "^5.5.0",
"node-sass": "^6.0.1",
"node-sass": "^8.0.0",
"prettier": "^2.5.1",
"sass-loader": "^12.4.0",
"sass-loader": "^13.2.0",
"source-map-loader": "^3.0.1",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.1",

View File

@@ -17,12 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
declare global {
interface Window {
WEB_EXTENSION_CHANNEL?: MessageChannel;
}
}
export type ClipArea = "full-page" | "visible" | "selection" | "article";
export type ClipMode = "simplified" | "screenshot" | "complete";
@@ -59,6 +53,8 @@ export type Clip = {
data: string;
area: ClipArea;
mode: ClipMode;
width?: number;
height?: number;
pageTitle?: string;
tags?: string[];
note?: ItemReference;
@@ -77,3 +73,9 @@ export const WEB_EXTENSION_CHANNEL_EVENTS = {
ON_CREATED: "web-extension-channel-created",
ON_READY: "web-extension-channel-ready"
} as const;
export type ClipData = {
height?: number;
width?: number;
data: string;
};

View File

@@ -75,22 +75,24 @@ export const NotePicker = (props: NotePickerProps) => {
</Text>
<Icon path={Icons.chevronDown} color="text" size={18} />
</Button>
<Button
variant="tool"
onClick={() => onSelected(undefined)}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexShrink: 0,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
height: 33
}}
title={"Clear selection"}
>
<Icon path={Icons.close} color="text" size={16} />
</Button>
{selectedNote && (
<Button
variant="tool"
onClick={() => onSelected(undefined)}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexShrink: 0,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
height: 33
}}
title={"Clear selection"}
>
<Icon path={Icons.close} color="text" size={16} />
</Button>
)}
</Flex>
<Picker onClose={close} isOpen={modalVisible}>

View File

@@ -74,22 +74,24 @@ export const NotebookPicker = (props: NotebookPickerProps) => {
</Text>
<Icon path={Icons.chevronDown} color="text" size={18} />
</Button>
<Button
variant="tool"
onClick={() => onSelected(undefined)}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexShrink: 0,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
height: 33
}}
title={"Clear selection"}
>
<Icon path={Icons.close} color="text" size={16} />
</Button>
{selectedNotebook && (
<Button
variant="tool"
onClick={() => onSelected(undefined)}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexShrink: 0,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
height: 33
}}
title={"Clear selection"}
>
<Icon path={Icons.close} color="text" size={16} />
</Button>
)}
</Flex>
<Picker onClose={close} isOpen={modalVisible}>

View File

@@ -26,22 +26,41 @@ import {
} from "@notesnook/clipper";
import { ClipArea, ClipMode } from "../common/bridge";
browser.runtime.onMessage.addListener((request) => {
const message = request as {
type?: string;
mode?: ClipMode;
area?: ClipArea;
};
type ClipMessage = {
type: "clip";
mode?: ClipMode;
area?: ClipArea;
};
if (message.type === "viewport") {
return Promise.resolve({
x: 0,
y: 0,
height: document.body.clientHeight,
width: document.body.clientWidth
});
type ViewportMessage = {
type: "viewport";
};
browser.runtime.onMessage.addListener(async (request) => {
const message = request as ClipMessage | ViewportMessage;
switch (message.type) {
case "clip": {
const sizeable =
message.area === "full-page" &&
(message.mode === "complete" || message.mode === "screenshot");
return {
height: sizeable ? document.body.clientHeight : 0,
width: sizeable ? document.body.clientWidth : 0,
data: await clip(request)
};
}
case "viewport":
return {
x: 0,
y: 0,
height: document.body.clientHeight,
width: document.body.clientWidth
};
}
});
function clip(message: ClipMessage) {
try {
const isScreenshot = message.mode === "screenshot";
const withStyles = message.mode === "complete" || isScreenshot;
@@ -64,4 +83,4 @@ browser.runtime.onMessage.addListener((request) => {
} finally {
if (message.area !== "selection") cleanup();
}
});
}

View File

@@ -28,7 +28,8 @@ import {
ItemReference,
SelectedNotebook,
ClipArea,
ClipMode
ClipMode,
ClipData,
} from "../common/bridge";
import { usePersistentState } from "../hooks/use-persistent-state";
import { deleteClip, getClip } from "../utils/storage";
@@ -101,7 +102,7 @@ export function Main() {
const [notebook, setNotebook] =
usePersistentState<SelectedNotebook>("notebook");
const [tags, setTags] = usePersistentState<string[]>("tags", []);
const [clipData, setClipData] = useState<string>();
const [clipData, setClipData] = useState<ClipData>();
const pageTitle = useRef<string>();
useEffect(() => {
@@ -165,6 +166,7 @@ export function Main() {
setClipArea(item.id);
setClipNonce((s) => ++s);
}}
disabled={isClipping}
sx={{
display: "flex",
borderRadius: "default",
@@ -207,7 +209,7 @@ export function Main() {
setClipMode(item.id);
setClipNonce((s) => ++s);
}}
disabled={item.pro && !isPremium}
disabled={isClipping || (item.pro && !isPremium)}
sx={{
display: "flex",
borderRadius: "default",
@@ -253,7 +255,7 @@ export function Main() {
onClick={async () => {
if (!clipData) return;
const winUrl = URL.createObjectURL(
new Blob(["\ufeff", clipData], { type: "text/html" })
new Blob(["\ufeff", clipData.data], { type: "text/html" })
);
await browser.windows.create({
url: winUrl
@@ -266,7 +268,7 @@ export function Main() {
{error && (
<Text
variant="text"
variant="body"
sx={{
mt: 1,
bg: "errorBg",
@@ -346,17 +348,16 @@ export function Main() {
setError("You are not connected to Notesnook.");
return;
}
await notesnook.saveClip({
url,
title,
data: clipData,
area: clipArea,
mode: clipMode,
tags,
note,
notebook,
pageTitle: pageTitle.current
pageTitle: pageTitle.current,
...clipData
});
setClipData(undefined);
@@ -386,11 +387,11 @@ export function Main() {
export async function clip(
area: ClipArea,
mode: ClipMode
): Promise<string | undefined> {
): Promise<ClipData | undefined> {
const clipData = await getClip();
if (area === "selection" && typeof clipData === "string") {
await deleteClip();
return clipData;
return { data: clipData };
}
const [tab] = await browser.tabs.query({ active: true });
@@ -403,8 +404,11 @@ export async function clip(
format: "jpeg",
quality: 100
});
return `<img src="${result}" width="${tab.width}px" height="${tab.height}px"/>`;
return {
data: `<img src="${result}" width="${tab.width}px" height="${tab.height}px"/>`
};
}
return await browser.tabs.sendMessage(tab.id, { mode, area });
return await browser.tabs.sendMessage(tab.id, { type: "clip", mode, area });
}

View File

@@ -5,7 +5,7 @@
"allowJs": true,
"jsx": "react-jsx",
"maxNodeModuleJsDepth": 5,
"declaration": false
"outDir": "./dist"
},
"include": ["src", "global.d.ts"]
}

View File

@@ -133,7 +133,10 @@ var options = {
.concat([".js", ".jsx", ".ts", ".tsx", ".css"])
},
plugins: [
new CleanWebpackPlugin({ verbose: false }),
new CleanWebpackPlugin({
verbose: false,
dangerouslyAllowCleanPatternsOutsideProject: true
}),
new webpack.ProgressPlugin(),
// expose and write the allowed env vars on the compiled bundle
new webpack.EnvironmentPlugin(["NODE_ENV"]),