Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Make SVGs "Penrose-editable" #1171

Merged
merged 27 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eda905e
chore: autocomplete shape props inside shape constructors
wodeni Sep 19, 2022
1a19623
fix: import
wodeni Nov 30, 2022
6ea24aa
added code for creating <penrose> medatadata field and subfields
mzhou08 Dec 4, 2022
5417cc1
added code for creating <penrose> medatadata field and subfields
mzhou08 Dec 4, 2022
f192a15
Merge pull request #1 from YonahGoldberg/feat/svg-metadata-tags
YonahGoldberg Dec 7, 2022
9b78790
Revert "added code for creating <penrose> medatadata field and subfie…
mzhou08 Dec 7, 2022
014e27f
Merge pull request #2 from YonahGoldberg/feat/svg-metadata-tags
YonahGoldberg Dec 7, 2022
facbb87
Svg drop zone (#3)
YonahGoldberg Dec 7, 2022
e79b756
retrieved all data to svg output
lzx0406 Dec 9, 2022
0951b4f
fixed SVG exporting and reverted styleconfig.ts
YonahGoldberg Dec 9, 2022
157e3c8
fixed bug of escaped xml characters being loaded incorrectly
YonahGoldberg Dec 9, 2022
8493d2b
Merge branch 'penrose:main' into penrose-editable-svgs
YonahGoldberg Dec 9, 2022
cb02cfe
deleted unused import in renderer
lmxlucy Dec 10, 2022
3bb3b7c
removed completed TODO
lmxlucy Dec 10, 2022
2387db3
removed irrelevant changes to Penrose Editable PR
lmxlucy Dec 10, 2022
9ae407c
move svg upload tab to the left sidebar
lmxlucy Dec 10, 2022
34330ae
set variation when recreating uploaded svg
lmxlucy Dec 10, 2022
c8bec3b
fixed small typo on create variation tag
lmxlucy Dec 11, 2022
f73c84b
set variation from the uploaded svg instead of current workspace
lmxlucy Dec 11, 2022
34180e9
small edit on trim
lmxlucy Dec 13, 2022
ed8d30b
added auto compile uploaded svg feature
lmxlucy Dec 13, 2022
49ba364
Update compileDiagram in SvgUploader
lmxlucy Dec 14, 2022
3bf8897
reformat
lmxlucy Dec 14, 2022
ae2282a
removed package-lock.json
lmxlucy Dec 14, 2022
87c71ca
reformat
lmxlucy Dec 14, 2022
6e2a120
reformat with prettier 2.3.0
lmxlucy Dec 14, 2022
1388c70
reformat after refresh build
lmxlucy Dec 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/renderer/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export const RenderStatic = async (
svg.setAttribute("version", "1.2");
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svg.setAttribute("viewBox", `0 0 ${canvas.width} ${canvas.height}`);

return Promise.all(
computeShapes(varyingValues).map((shape) =>
RenderShape({
Expand Down
1 change: 1 addition & 0 deletions packages/editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"react": "^18.0.0",
"react-data-table-component": "^6.11.7",
"react-dom": "^18.0.0",
"react-drag-drop-files": "^2.3.8",
"react-hot-toast": "^2.2.0",
"react-inspector": "^4.0.1",
"react-responsive": "^9.0.0",
Expand Down
8 changes: 8 additions & 0 deletions packages/editor/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import RogerPanel from "./components/RogerPanel";
import SavedFilesBrowser from "./components/SavedBrowser";
import Settings from "./components/Settings";
import StateInspector from "./components/StateInspector";
import SvgUploader from "./components/SvgUploader";
import TopBar from "./components/TopBar";
import {
currentRogerState,
Expand Down Expand Up @@ -112,6 +113,11 @@ export const layoutModel = Model.fromJson({
name: "examples",
component: "examplesPanel",
},
{
type: "tab",
name: "upload",
component: "svgUploader",
},
{
type: "tab",
name: "settings",
Expand Down Expand Up @@ -152,6 +158,8 @@ function App() {
switch (node.getComponent()) {
case "programEditor":
return <ProgramEditor kind={node.getConfig().kind} />;
case "svgUploader":
return <SvgUploader />;
case "diagram":
return <DiagramPanel />;
case "savedFiles":
Expand Down
100 changes: 98 additions & 2 deletions packages/editor/src/components/DiagramPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import { useRecoilCallback, useRecoilState, useRecoilValue } from "recoil";
import { v4 as uuid } from "uuid";
import {
currentRogerState,
DiagramMetadata,
diagramMetadataSelector,
diagramState,
fileContentsSelector,
ProgramFile,
WorkspaceMetadata,
workspaceMetadataSelector,
} from "../state/atoms";
Expand All @@ -27,8 +30,14 @@ import BlueButton from "./BlueButton";
*/
export const DownloadSVG = (
svg: SVGSVGElement,
title = "illustration"
title = "illustration",
dslStr: string,
subStr: string,
styleStr: string,
versionStr: string,
variationStr: string
): void => {
SVGaddCode(svg, dslStr, subStr, styleStr, versionStr, variationStr);
const blob = new Blob([svg.outerHTML], {
type: "image/svg+xml;charset=utf-8",
});
Expand All @@ -41,6 +50,76 @@ export const DownloadSVG = (
document.body.removeChild(downloadLink);
};

/**
* Given an SVG, program triple, and version and variation strings,
* appends penrose tags to the SVG so the SVG can be reuploaded and edited.
*
* @param svg
* @param dslStr the domain file
* @param subStr the substance file
* @param styleStr the style file
* @param versionStr
* @param variationStr
*/
const SVGaddCode = (
svg: SVGSVGElement,
dslStr: string,
subStr: string,
styleStr: string,
versionStr: string,
variationStr: string
): void => {
svg.setAttribute("penrose", "0");

// Create custom <penrose> tag to store metadata
const metadata = document.createElementNS(
"https://penrose.cs.cmu.edu/metadata",
"penrose"
);

// Create <version> tag for penrose version
const version = document.createElementNS(
"https://penrose.cs.cmu.edu/version",
"version"
);
version.insertAdjacentText("afterbegin", versionStr);

// Create <variation> tag for variation string
const variation = document.createElementNS(
wodeni marked this conversation as resolved.
Show resolved Hide resolved
"https://penrose.cs.cmu.edu/version",
"variation"
);
variation.insertAdjacentText("afterbegin", variationStr);

// Create <sub> tag to store substance (.sub) code
const substance = document.createElementNS(
"https://penrose.cs.cmu.edu/substance",
"sub"
);
substance.insertAdjacentText("afterbegin", subStr);

// Create <sty> tag to store style (.sty) code
const style = document.createElementNS(
"https://penrose.cs.cmu.edu/style",
"sty"
);
style.insertAdjacentText("afterbegin", styleStr);

// Create <dsl> tag to store .dsl code
const dsl = document.createElementNS("https://penrose.cs.cmu.edu/dsl", "dsl");
dsl.insertAdjacentText("afterbegin", dslStr);

// Add these new tags under the <penrose> metadata tag
metadata.appendChild(version);
metadata.appendChild(variation);
metadata.appendChild(substance);
metadata.appendChild(style);
metadata.appendChild(dsl);

// Add the <penrose> metadata tag to the parent <svg> tag
svg.appendChild(metadata);
};

/**
* (browser-only) Downloads any given exported PNG to the user's computer
* @param svg
Expand Down Expand Up @@ -167,7 +246,24 @@ export default function DiagramPanel() {
if (svg !== null) {
const metadata = snapshot.getLoadable(workspaceMetadataSelector)
.contents as WorkspaceMetadata;
DownloadSVG(svg, metadata.name);
const diagram = snapshot.getLoadable(diagramMetadataSelector)
.contents as DiagramMetadata;
const domain = snapshot.getLoadable(fileContentsSelector("domain"))
.contents as ProgramFile;
const substance = snapshot.getLoadable(
fileContentsSelector("substance")
).contents as ProgramFile;
const style = snapshot.getLoadable(fileContentsSelector("style"))
.contents as ProgramFile;
DownloadSVG(
svg,
metadata.name,
domain.contents,
substance.contents,
style.contents,
metadata.editorVersion.toString(),
diagram.variation
);
}
}
});
Expand Down
94 changes: 94 additions & 0 deletions packages/editor/src/components/SvgUploader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { FileUploader } from "react-drag-drop-files";
import toast from "react-hot-toast";
import { useRecoilState } from "recoil";
import { diagramMetadataSelector, fileContentsSelector } from "../state/atoms";

export default function SvgUploader() {
const setDomain = useRecoilState(fileContentsSelector("domain"))[1];
const setSubstance = useRecoilState(fileContentsSelector("substance"))[1];
const setStyle = useRecoilState(fileContentsSelector("style"))[1];
const [diagramMetadata, setDiagramMetadata] = useRecoilState(
diagramMetadataSelector
);

const handleChange = (svg: File) => {
const reader = new FileReader();
reader.readAsText(svg);
reader.onabort = () => console.log("file reading was aborted");
reader.onerror = () => console.log("file reading has failed");
reader.onload = () => {
const svgText = reader.result?.toString();
if (!svgText) {
return;
}

const parser = new DOMParser();
const xmlDoc = parser.parseFromString(svgText, "text/xml");
const styElem = xmlDoc.getElementsByTagName("sty");
wodeni marked this conversation as resolved.
Show resolved Hide resolved
const subElem = xmlDoc.getElementsByTagName("sub");
const dslElem = xmlDoc.getElementsByTagName("dsl");

setDiagramMetadata((metadata) => ({
...metadata,
variation: diagramMetadata.variation,
wodeni marked this conversation as resolved.
Show resolved Hide resolved
}));

if (styElem.length === 0) {
toast.error(
"Could not load SVG. Make sure the SVG was exported from Penrose."
);
return;
}

setStyle({
name: "SVG import",
contents: styElem[0].textContent ?? "".trim(),
lmxlucy marked this conversation as resolved.
Show resolved Hide resolved
});

if (subElem.length === 0) {
toast.error(
"Could not load SVG. Make sure the SVG was exported from Penrose."
);
return;
}

setSubstance({
name: "SVG import",
contents: subElem[0].textContent ?? "".trim(),
lmxlucy marked this conversation as resolved.
Show resolved Hide resolved
});

if (dslElem.length === 0) {
toast.error(
"Could not load SVG. Make sure the SVG was exported from Penrose."
);
return;
}

setDomain({
name: "SVG import",
contents: dslElem[0].textContent ?? "".trim(),
});

toast.success("Sucessfully uploaded SVG to editor");
wodeni marked this conversation as resolved.
Show resolved Hide resolved
};
};

return (
<FileUploader
handleChange={handleChange}
name="file"
types={["SVG"]}
multiple={false}
label="Upload or drop a Penrose exported SVG here"
>
<div
style={{ border: "2px dashed", borderColor: "darkgrey", padding: 10 }}
>
<p>
<span style={{ textDecoration: "underline" }}>Upload</span> or drop a
Penrose exported SVG here
</p>
</div>
</FileUploader>
);
}
24 changes: 24 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -19710,6 +19710,14 @@ react-dom@^18.0.0:
loose-envify "^1.1.0"
scheduler "^0.22.0"

react-drag-drop-files@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/react-drag-drop-files/-/react-drag-drop-files-2.3.8.tgz#909e78de96e3300d6b2bdca5fdb3c93140d09b7b"
integrity sha512-/tA1FEGhw6IwG5DlogYu0y3uLkyZ6np8cbzihW7Hy/k3b1T022T7zjn/4elZ6+M69GwnJnUUO0Utisu1xZFATA==
dependencies:
prop-types "^15.7.2"
styled-components "^5.3.0"

react-element-to-jsx-string@^14.3.4:
version "14.3.4"
resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz#709125bc72f06800b68f9f4db485f2c7d31218a8"
Expand Down Expand Up @@ -22025,6 +22033,22 @@ style-to-object@0.3.0, style-to-object@^0.3.0:
dependencies:
inline-style-parser "0.1.1"

styled-components@^5.3.0:
version "5.3.6"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.6.tgz#27753c8c27c650bee9358e343fc927966bfd00d1"
integrity sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/traverse" "^7.4.5"
"@emotion/is-prop-valid" "^1.1.0"
"@emotion/stylis" "^0.8.4"
"@emotion/unitless" "^0.7.4"
babel-plugin-styled-components ">= 1.12.0"
css-to-react-native "^3.0.0"
hoist-non-react-statics "^3.0.0"
shallowequal "^1.1.0"
supports-color "^5.5.0"

styled-components@^5.3.5:
version "5.3.5"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.5.tgz#a750a398d01f1ca73af16a241dec3da6deae5ec4"
Expand Down