Skip to content

Commit

Permalink
Merge pull request #1 from yikuansun/dev
Browse files Browse the repository at this point in the history
Use webpack
  • Loading branch information
yikuansun authored Jul 18, 2024
2 parents 83dfedc + 20d4bb2 commit 6a26fb6
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 54 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,5 @@ typings/

# FuseBox cache
.fusebox/

package-lock.json
56 changes: 55 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,58 @@
# photopea.js
A JS-based wrapper for the [Photopea API](https://www.photopea.com/api/).

## [Documentation](https://github.com/yikuansun/photopeaapi/wiki)
## Installation
The easiest way to install photopea.js is through a CDN.
```
haven't set it up yet... come back later.
```
You can also download [`photopea.min.js`](./dist/photopea.min.js) and host it yourself:
```
<script src="./photopea.min.js"></script>
```
If you're using a Node framework, like Webpack, Rollup, or Vite, simply install with npm:
```
npm install photopea
```
You can then import the module in your code:
```
import Photopea from "photopea";
```

## Usage
`Photopea` is a class with methods that can interact with any instance of Photopea.
### Constructors
For plugins, use window.parent as the Photopea content window:
```
let pea = new Photopea(window.parent);
```
To create a new Photopea embed, use `Photopea.createEmbed`:
```
Photopea.createEmbed(container).then(async (pea) => {
// photopea initialized
// pea is the new Photopea object
// you can also use async/await:
/*
let pea = await Photopea.createEmbed(container);
*/
});
```
`container` is the parent DOM element and should be a `div` with a set width and height.
### Methods
These are methods for objects of the Photopea class, created with the constructors above. These are all Promises, so be sure to use `.then()` or `await`.
#### `async runScript(script)`
- `script` (string): the [script](https://www.photopea.com/learn/scripts) to run in Photopea.
- Returns: an array containing all of the scripts outputs, ending with `"done"`.
#### `async loadAsset(asset)`
- `asset` (ArrayBuffer): a buffer of the asset to load in Photopea.
- Returns: `[ "done" ]`.
#### `async openFromURL(url, asSmart=true)`
- `url` (string): The URL of the image/psd file to open.
- `asSmart` (boolean): whether to open the image as a layer. Set to `false` if you are opening an image or psd file in a new document.
- Returns: `[ "done" ]`.
#### `async exportImage(type="png")`
- `type` (string): export image filetype. Can be png or jpg.
- Returns: a Blob of the exported image. To get the image URL, use `URL.createObjectURL`.

## Demo
See [dist/test.html](./dist/test.html)
1 change: 1 addition & 0 deletions dist/photopea.min.js

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

31 changes: 31 additions & 0 deletions dist/test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<script src="./photopea.min.js"></script>
</head>
<body>
<div id="container" style="width: 800px; height: 400px;"></div>
<script>
let container = document.getElementById("container");
Photopea.createEmbed(container).then(async (pea) => {
let output = await pea.runScript(`
app.echoToOE("hello world");
`);
console.log(output)
await pea.openFromURL("https://www.photopea.com/api/img2/pug.png", false);
await pea.runScript(`app.activeDocument.activeLayer.blendMode = "lddg";`);
await pea.openFromURL("https://www.photopea.com/api/img2/pug.png", true);
await pea.runScript(`app.activeDocument.activeLayer.blendMode = "scrn";`);
await pea.runScript(`app.activeDocument.activeLayer.translate(20, 20);`);
output = await pea.runScript(`
app.echoToOE("hello world 2");
`);
console.log(output)
let finalImage = await pea.exportImage();
let img = new Image();
img.src = URL.createObjectURL(finalImage);
document.body.appendChild(img);
});
</script>
</body>
</html>
104 changes: 104 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"name": "photopea",
"version": "0.0.1",
"description": "JS wrapper for the Photopea API.",
"type": "module",
"main": "src/index.js",
"dependencies": {
"acorn": "^8.12.1",
"acorn-import-attributes": "^1.9.5",
"ajv": "^6.12.6",
"ajv-keywords": "^3.5.2",
"browserslist": "^4.23.2",
"buffer-from": "^1.1.2",
"caniuse-lite": "^1.0.30001642",
"chrome-trace-event": "^1.0.4",
"clone-deep": "^4.0.1",
"colorette": "^2.0.20",
"commander": "^2.20.3",
"cross-spawn": "^7.0.3",
"electron-to-chromium": "^1.4.829",
"enhanced-resolve": "^5.17.0",
"envinfo": "^7.13.0",
"es-module-lexer": "^1.5.4",
"escalade": "^3.1.2",
"eslint-scope": "^5.1.1",
"esrecurse": "^4.3.0",
"estraverse": "^4.3.0",
"events": "^3.3.0",
"fast-deep-equal": "^3.1.3",
"fast-json-stable-stringify": "^2.1.0",
"fastest-levenshtein": "^1.0.16",
"find-up": "^4.1.0",
"flat": "^5.0.2",
"function-bind": "^1.1.2",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.11",
"has-flag": "^4.0.0",
"hasown": "^2.0.2",
"import-local": "^3.1.0",
"interpret": "^3.1.1",
"is-core-module": "^2.15.0",
"is-plain-object": "^2.0.4",
"isexe": "^2.0.0",
"isobject": "^3.0.1",
"jest-worker": "^27.5.1",
"json-parse-even-better-errors": "^2.3.1",
"json-schema-traverse": "^0.4.1",
"kind-of": "^6.0.3",
"loader-runner": "^4.3.0",
"locate-path": "^5.0.0",
"merge-stream": "^2.0.0",
"mime-db": "^1.52.0",
"mime-types": "^2.1.35",
"neo-async": "^2.6.2",
"node-releases": "^2.0.17",
"p-limit": "^2.3.0",
"p-locate": "^4.1.0",
"p-try": "^2.2.0",
"path-exists": "^4.0.0",
"path-key": "^3.1.1",
"path-parse": "^1.0.7",
"picocolors": "^1.0.1",
"pkg-dir": "^4.2.0",
"punycode": "^2.3.1",
"randombytes": "^2.1.0",
"rechoir": "^0.8.0",
"resolve": "^1.22.8",
"resolve-cwd": "^3.0.0",
"resolve-from": "^5.0.0",
"safe-buffer": "^5.2.1",
"schema-utils": "^3.3.0",
"serialize-javascript": "^6.0.2",
"shallow-clone": "^3.0.1",
"shebang-command": "^2.0.0",
"shebang-regex": "^3.0.0",
"source-map": "^0.6.1",
"source-map-support": "^0.5.21",
"supports-color": "^8.1.1",
"supports-preserve-symlinks-flag": "^1.0.0",
"tapable": "^2.2.1",
"terser": "^5.31.3",
"terser-webpack-plugin": "^5.3.10",
"undici-types": "^5.26.5",
"update-browserslist-db": "^1.1.0",
"uri-js": "^4.4.1",
"watchpack": "^2.4.1",
"webpack-merge": "^5.10.0",
"webpack-sources": "^3.2.3",
"which": "^2.0.2",
"wildcard": "^2.0.1"
},
"devDependencies": {
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4"
},
"scripts": {
"build": "webpack"
},
"keywords": [
"photopea"
],
"author": "Yikuan Sun",
"license": "MIT"
}
52 changes: 0 additions & 52 deletions photopea.js

This file was deleted.

1 change: 0 additions & 1 deletion photopea.min.js

This file was deleted.

102 changes: 102 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
export default class Photopea {

static async createEmbed(parentElement, config) {
let _config = "";
if (typeof(config) == "object") _config = JSON.stringify(config);
else if (typeof(config) == "string") _config = config;
let frame = document.createElement("iframe");
frame.style.border = "0";
frame.style.width = "100%";
frame.style.height = "100%";
if (config) frame.src = "https://www.photopea.com#" + encodeURI(_config);
else frame.src = "https://www.photopea.com";
parentElement.appendChild(frame);
let waitForInit = new Promise(function(res, rej) {
let messageHandle = (e) => {
if (e.source == frame.contentWindow && e.data == "done") {
let pea = new Photopea(frame.contentWindow);
window.removeEventListener("message", messageHandle);
res(pea);
}
};
window.addEventListener("message", messageHandle);
});
return await waitForInit;
}

contentWindow;
constructor(contentWindow) {
this.contentWindow = contentWindow;
}

async runScript(script) {
await this._pause();
let waitForMessage = new Promise((res, rej) => {
let outputs = [];
let messageHandle = (e) => {
if (e.source == this.contentWindow) {
outputs.push(e.data);
if (e.data == "done") {
window.removeEventListener("message", messageHandle);
res(outputs);
}
}
};
window.addEventListener("message", messageHandle);

this.contentWindow.postMessage(script, "*");
});
return await waitForMessage;
}

async loadAsset(asset) {
await this._pause();
let waitForMessage = new Promise((res, rej) => {
let outputs = [];
let messageHandle = (e) => {
if (e.source == this.contentWindow) {
outputs.push(e.data);
if (e.data == "done") {
window.removeEventListener("message", messageHandle);
res(outputs);
}
}
};
window.addEventListener("message", messageHandle);

this.contentWindow.postMessage(asset, "*");
});
return await waitForMessage;
}

async openFromURL(url, asSmart=true) {
await this._pause();
let layerCountOld = "done";
while (layerCountOld == "done") layerCountOld = (await this.runScript(`app.echoToOE(${asSmart?"app.activeDocument.layers.length":"app.documents.length"})`))[0];
let layerCountNew = layerCountOld;
await this.runScript(`app.open("${url}", null, ${asSmart});`);
while (layerCountNew == layerCountOld || layerCountNew == "done") {
layerCountNew = (await this.runScript(`app.echoToOE(${asSmart?"app.activeDocument.layers.length":"app.documents.length"})`))[0];
}
return [ "done" ];
}

async exportImage(type="png") {
await this._pause();
let buffer = "done";
while (buffer == "done") {
let data = await this.runScript(`app.activeDocument.saveToOE("${type}");`);
buffer = data[0];
}
return new Blob([ buffer ], {
type: "image/" + type,
});

}

async _pause(ms=10) {
return await new Promise((res, rej) => {
setTimeout(() => { res(); }, ms);
});
}
}
17 changes: 17 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
entry: "./src/index.js",
output: {
filename: "photopea.min.js",
path: path.resolve(__dirname, "dist"),
library: "Photopea",
libraryExport: "default",
libraryTarget: "umd",
globalObject: "this",
},
};

0 comments on commit 6a26fb6

Please sign in to comment.