Merge pull request #6590 from penpot/niwinz-develop-library-fixes

 Add minor enhancements to penpot library
This commit is contained in:
Andrey Antukh
2025-05-30 10:35:41 +02:00
committed by GitHub
8 changed files with 298 additions and 133 deletions

View File

@@ -1,5 +1,60 @@
version: 2.1 version: 2.1
jobs: jobs:
lint:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
steps:
- checkout
- run:
name: "fmt check"
working_directory: "."
command: |
yarn install
yarn run fmt:clj:check
- run:
name: "lint clj common"
working_directory: "."
command: |
yarn run lint:clj:common
- run:
name: "lint clj frontend"
working_directory: "."
command: |
yarn run lint:clj:frontend
- run:
name: "lint clj backend"
working_directory: "."
command: |
yarn run lint:clj:backend
- run:
name: "lint clj exporter"
working_directory: "."
command: |
yarn run lint:clj:exporter
- run:
name: "lint clj library"
working_directory: "."
command: |
yarn run lint:clj:library
- run:
name: "lint scss on frontend"
working_directory: "./frontend"
command: |
yarn install
yarn run lint:scss
test-common: test-common:
docker: docker:
- image: penpotapp/devenv:latest - image: penpotapp/devenv:latest
@@ -19,14 +74,6 @@ jobs:
keys: keys:
- v1-dependencies-{{ checksum "common/deps.edn"}} - v1-dependencies-{{ checksum "common/deps.edn"}}
- run:
name: "fmt check & linter"
working_directory: "./common"
command: |
yarn install
yarn run fmt:clj:check
yarn run lint:clj
- run: - run:
name: "JVM tests" name: "JVM tests"
working_directory: "./common" working_directory: "./common"
@@ -37,6 +84,7 @@ jobs:
name: "NODE tests" name: "NODE tests"
working_directory: "./common" working_directory: "./common"
command: | command: |
yarn install
yarn run test yarn run test
- save_cache: - save_cache:
@@ -63,23 +111,6 @@ jobs:
keys: keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}} - v1-dependencies-{{ checksum "frontend/deps.edn"}}
- run:
name: "prepopulate linter cache"
working_directory: "./common"
command: |
yarn install
yarn run lint:clj
- run:
name: "fmt check & linter"
working_directory: "./frontend"
command: |
yarn install
yarn run fmt:clj:check
yarn run fmt:js:check
yarn run lint:scss
yarn run lint:clj
- run: - run:
name: "unit tests" name: "unit tests"
working_directory: "./frontend" working_directory: "./frontend"
@@ -115,8 +146,8 @@ jobs:
name: Install dependencies name: Install dependencies
working_directory: "./frontend" working_directory: "./frontend"
command: | command: |
yarn yarn install
npx playwright install --with-deps yarn run playwright install --with-deps chromium
- run: - run:
name: Build Storybook name: Build Storybook
@@ -185,21 +216,6 @@ jobs:
keys: keys:
- v1-dependencies-{{ checksum "backend/deps.edn" }} - v1-dependencies-{{ checksum "backend/deps.edn" }}
- run:
name: "prepopulate linter cache"
working_directory: "./common"
command: |
yarn install
yarn run lint:clj
- run:
name: "fmt check & linter"
working_directory: "./backend"
command: |
yarn install
yarn run fmt:clj:check
yarn run lint:clj
- run: - run:
name: "tests" name: "tests"
working_directory: "./backend" working_directory: "./backend"
@@ -217,35 +233,6 @@ jobs:
- ~/.m2 - ~/.m2
key: v1-dependencies-{{ checksum "backend/deps.edn" }} key: v1-dependencies-{{ checksum "backend/deps.edn" }}
test-exporter:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
- run:
name: "prepopulate linter cache"
working_directory: "./common"
command: |
yarn install
yarn run lint:clj
- run:
name: "fmt check & linter"
working_directory: "./exporter"
command: |
yarn install
yarn run fmt:clj:check
yarn run lint:clj
test-render-wasm: test-render-wasm:
docker: docker:
- image: penpotapp/devenv:latest - image: penpotapp/devenv:latest
@@ -278,10 +265,25 @@ jobs:
workflows: workflows:
penpot: penpot:
jobs: jobs:
- test-frontend - lint
- test-components - test-frontend:
- test-integration requires:
- test-backend - lint: success
- test-common
- test-exporter - test-components:
requires:
- lint: success
- test-integration:
requires:
- lint: success
- test-backend:
requires:
- lint: success
- test-common:
requires:
- lint: success
- test-render-wasm - test-render-wasm

View File

@@ -10,7 +10,7 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.features :as cfeat] ;; [app.common.features :as cfeat]
[app.common.files.changes :as ch] [app.common.files.changes :as ch]
[app.common.files.migrations :as fmig] [app.common.files.migrations :as fmig]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
@@ -133,20 +133,6 @@
(def decode-add-component (def decode-add-component
(sm/decode-fn schema:add-component sm/json-transformer)) (sm/decode-fn schema:add-component sm/json-transformer))
(def schema:add-component-instance
[:map
[:component-id ::sm/uuid]
[:file-id {:optional true} ::sm/uuid]
[:frame-id {:optional true} ::sm/uuid]
[:page-id {:optional true} ::sm/uuid]])
(def ^:private check-add-component-instance
(sm/check-fn schema:add-component-instance
:hint "invalid arguments passed for add-component-instance"))
(def decode-add-component-instance
(sm/decode-fn schema:add-component-instance sm/json-transformer))
(def schema:add-bool (def schema:add-bool
[:map [:map
[:group-id ::sm/uuid] [:group-id ::sm/uuid]
@@ -198,11 +184,31 @@
(-> (get-current-objects state) (-> (get-current-objects state)
(get shape-id))) (get shape-id)))
;; WORKAROUND: A copy of features from staging for make the library
;; generate files compatible with version released right now. This
;; should be removed and replaced with cfeat/default-features when 2.8
;; version is released
(def default-features
#{"fdata/shape-data-type"
"styles/v2"
"layout/grid"
"components/v2"
"plugins/runtime"
"design-tokens/v1"})
;; WORKAROUND: the same as features
(def available-migrations
(-> fmig/available-migrations
(disj "003-convert-path-content")
(disj "0002-clean-shape-interactions")
(disj "0003-fix-root-shape")))
(defn add-file (defn add-file
[state params] [state params]
(let [params (-> params (let [params (-> params
(assoc :features cfeat/default-features) (assoc :features default-features)
(assoc :migrations fmig/available-migrations) (assoc :migrations available-migrations)
(update :id default-uuid)) (update :id default-uuid))
file (types.file/make-file params :create-page false)] file (types.file/make-file params :create-page false)]
(-> state (-> state
@@ -432,33 +438,6 @@
(commit-change change1) (commit-change change1)
(commit-change change2)))) (commit-change change2))))
(defn add-component-instance
[state params]
(let [{:keys [component-id file-id frame-id page-id]}
(check-add-component-instance params)
file-id
(or file-id (get state ::current-file-id))
frame-id
(or frame-id (get state ::current-frame-id))
page-id
(or page-id (get state ::current-page-id))
change
{:type :mod-obj
:id frame-id
:page-id page-id
:operations
[{:type :set :attr :component-root :val true}
{:type :set :attr :component-id :val component-id}
{:type :set :attr :component-file :val file-id}]}]
(commit-change state change)))
(defn delete-shape (defn delete-shape
[file id] [file id]
(commit-change (commit-change

12
library/CHANGES.md Normal file
View File

@@ -0,0 +1,12 @@
# CHANGELOG
## 1.0.1
- Make the library generate a .penpot file compatible with penpot 2.7.x
- Remove useless method `addComponentInstance`
## 1.0.0
- Initial release after big refactor (from the first MVP prototype)

View File

@@ -1,6 +1,6 @@
{ {
"name": "@penpot/library", "name": "@penpot/library",
"version": "1.0.0", "version": "1.0.1",
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538", "packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",

View File

@@ -0,0 +1,106 @@
import * as penpot from "#self";
import { writeFile, readFile } from "fs/promises";
import { createWriteStream } from "fs";
import { Writable } from "stream";
// console.log(penpot);
(async function () {
const context = penpot.createBuildContext();
{
context.addFile({ name: "Test File 1" });
context.addPage({ name: "Foo Page" });
// Add image media
const buffer = await readFile("./playground/sample.jpg");
const blob = new Blob([buffer], { type: "image/jpeg" });
const mediaId = context.addFileMedia(
{
name: "avatar.jpg",
width: 512,
height: 512,
},
blob
);
// Add image color asset
const assetColorId = context.addLibraryColor({
name: "Avatar",
opacity: 1,
image: {
...context.getMediaAsImage(mediaId),
keepAspectRatio: true,
},
});
const boardId = context.addBoard({
name: "Foo Board",
x: 0,
y: 0,
width: 500,
height: 1000,
});
const fill = {
fillColorRefId: assetColorId,
fillColorRefFile: context.currentFileId,
fillImage: {
...context.getMediaAsImage(mediaId),
keepAspectRatio: true,
},
};
const stroke = {
strokeColorRefId: assetColorId,
strokeColorRefFile: context.currentFileId,
strokeWidth: 48,
strokeAlignment: "inner",
strokeStyle: "solid",
strokeOpacity: 1,
strokeImage: {
...context.getMediaAsImage(mediaId),
keepAspectRatio: true,
},
};
context.addRect({
name: "Rect 1",
x: 20,
y: 20,
width: 500,
height: 1000,
fills: [fill],
strokes: [stroke],
});
context.closeBoard();
context.closeFile();
}
{
let result = await penpot.exportAsBytes(context);
await writeFile("sample-sync.zip", result);
}
// {
// // Create a file stream to write the zip to
// const output = createWriteStream('sample-stream.zip');
// // Wrap Node's stream in a WHATWG WritableStream
// const writable = Writable.toWeb(output);
// await penpot.exportStream(context, writable);
// }
})()
.catch((cause) => {
console.error(cause);
const innerCause = cause.cause;
if (innerCause) {
console.error("Inner cause:", innerCause);
}
process.exit(-1);
})
.finally(() => {
process.exit(0);
});

View File

@@ -0,0 +1,68 @@
import * as penpot from "#self";
import { writeFile, readFile } from "fs/promises";
(async function () {
const context = penpot.createBuildContext();
{
context.addFile({ name: "Test File 1" });
context.addPage({ name: "Foo Page" });
const pathContent = [
{
"command": "move-to",
"params": {
"x": 480.0,
"y": 839.0
}
},
{
"command": "line-to",
"params": {
"x": 439.0,
"y": 802.0
}
},
{
"command": "curve-to",
"params": {
"c1x": 368.0,
"c1y": 737.0,
"c2x": 310.0,
"c2y": 681.0,
"x": 264.0,
"y": 634.0
}
},
{
"command": "close-path",
"params": {}
}
];
context.addPath({
name: "Path 1",
content: pathContent
});
context.closeBoard();
context.closeFile();
}
{
let result = await penpot.exportAsBytes(context);
await writeFile("sample-path.zip", result);
}
})()
.catch((cause) => {
console.error(cause);
const innerCause = cause.cause;
if (innerCause) {
console.error("Inner cause:", innerCause);
}
process.exit(-1);
})
.finally(() => {
process.exit(0);
});

View File

@@ -230,16 +230,6 @@
(catch :default cause (catch :default cause
(handle-exception cause)))) (handle-exception cause))))
:addComponentInstance
(fn [params]
(try
(let [params (-> (decode-params params)
(fb/decode-add-component-instance))]
(-> (swap! state fb/add-component-instance params)
(get-last-id)))
(catch :default cause
(handle-exception cause))))
:addFileMedia :addFileMedia
(fn [params blob] (fn [params blob]

View File

@@ -82,6 +82,14 @@
:is-shared :is-shared
:version}) :version})
(defn- encode-shape*
[{:keys [type] :as shape}]
(let [shape (if (or (= type :path)
(= type :bool))
(update shape :content vec)
shape)]
(-> shape encode-shape json/encode)))
(defn- generate-file-export-procs (defn- generate-file-export-procs
[{:keys [id data] :as file}] [{:keys [id data] :as file}]
(cons (cons
@@ -109,7 +117,7 @@
(map (fn [[shape-id shape]] (map (fn [[shape-id shape]]
(let [shape (assoc shape :page-id page-id)] (let [shape (assoc shape :page-id page-id)]
[(str "files/" id "/pages/" page-id "/" shape-id ".json") [(str "files/" id "/pages/" page-id "/" shape-id ".json")
(delay (-> shape encode-shape json/encode))])) (delay (encode-shape* shape))]))
objects))))))) objects)))))))
(->> (get data :components) (->> (get data :components)