diff --git a/packages/create-mechanic/CHANGELOG.md b/packages/create-mechanic/CHANGELOG.md
index d9ca7602..40e588f0 100644
--- a/packages/create-mechanic/CHANGELOG.md
+++ b/packages/create-mechanic/CHANGELOG.md
@@ -7,9 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+### Added
+
+- Ability to copy template or examples that use custom inputs.
+- New Adaptive Grid design function (by @munusshih) added to examples
+- Adds base .gitignore to new projects
+- Tries to initialize a git repository for new projects
+- Right before trying to install dependencies of new project, it will list out which dependencies are being installed
+
### Fixed
- Missing line break was added finalizing creating a DF.
+- Adds some line breaks across the CLI logs for reading room.
## 2.0.0-beta.10 - 2023-02-10
diff --git a/packages/create-mechanic/function-blank/index.js b/packages/create-mechanic/function-blank/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-blank/index.js
rename to packages/create-mechanic/function-blank/function/index.js
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/dependencies.json b/packages/create-mechanic/function-examples/adaptive-grid/dependencies.json
new file mode 100644
index 00000000..81bb91dd
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/dependencies.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "@mechanic-design/engine-react": "^2.0.0-beta.10"
+ }
+}
diff --git a/packages/create-mechanic/function-examples/business-card-generator/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf b/packages/create-mechanic/function-examples/adaptive-grid/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
similarity index 100%
rename from packages/create-mechanic/function-examples/business-card-generator/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
rename to packages/create-mechanic/function-examples/adaptive-grid/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
diff --git a/packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-Heavy.otf b/packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-Heavy.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-Heavy.otf
rename to packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-Heavy.otf
diff --git a/packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-HeavySlanted.otf b/packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-HeavySlanted.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-HeavySlanted.otf
rename to packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-HeavySlanted.otf
diff --git a/packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-Regular.otf b/packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-Regular.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-Regular.otf
rename to packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-Regular.otf
diff --git a/packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-Slanted.otf b/packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-Slanted.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/business-card-generator/assets/PPObjectSans-Slanted.otf
rename to packages/create-mechanic/function-examples/adaptive-grid/function/assets/PPObjectSans-Slanted.otf
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/function/components/Column.js b/packages/create-mechanic/function-examples/adaptive-grid/function/components/Column.js
new file mode 100644
index 00000000..93ea7bb2
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/function/components/Column.js
@@ -0,0 +1,23 @@
+import React from "react";
+
+export const Column = ({ width, height, x, y, showGrid, children }) => {
+ return (
+ <>
+
+ {children}
+
+
+ {showGrid && (
+
+ )}
+ >
+ );
+};
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/function/components/Image.js b/packages/create-mechanic/function-examples/adaptive-grid/function/components/Image.js
new file mode 100644
index 00000000..b6b2b256
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/function/components/Image.js
@@ -0,0 +1,69 @@
+import React from "react";
+
+const defaultUrl =
+ "https://images.unsplash.com/photo-1568214697537-ace27ffd6cf3?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1888&q=80";
+
+export const Image = ({
+ image,
+ href,
+ x,
+ width,
+ height,
+ gutterRatio,
+ filterOpacity,
+ gridColor
+}) => {
+ const imageHref = image ? (href ? href : "") : defaultUrl;
+
+ return (
+ <>
+
+ {/* mask to crop the image into rectangle */}
+
+
+
+
+
+ {/* the image that will be cropped */}
+
+
+ {/* filter applied over image */}
+
+
+ {/* lines surrounding image */}
+
+
+
+
+ >
+ );
+};
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/function/hooks.js b/packages/create-mechanic/function-examples/adaptive-grid/function/hooks.js
new file mode 100644
index 00000000..cc39c1db
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/function/hooks.js
@@ -0,0 +1,39 @@
+import { useEffect, useState, useRef } from "react";
+
+export const usePotentialRandomValue = (
+ randomGenerator,
+ fixedFallback,
+ isRandom
+) => {
+ const randomValue = randomGenerator();
+ const value = useRef(isRandom ? randomValue : fixedFallback);
+ return value.current;
+};
+
+export const useImageHref = image => {
+ const [href, setHref] = useState("");
+
+ useEffect(() => {
+ let reader;
+ if (image) {
+ reader = new FileReader();
+
+ reader.readAsDataURL(image);
+
+ reader.onload = function () {
+ setHref(reader.result);
+ };
+
+ reader.onerror = function () {
+ console.error(reader.error);
+ };
+ }
+ return () => {
+ if (reader) {
+ reader.abort();
+ }
+ };
+ }, [image]);
+
+ return href;
+};
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/function/index.js b/packages/create-mechanic/function-examples/adaptive-grid/function/index.js
new file mode 100644
index 00000000..e3b2fd62
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/function/index.js
@@ -0,0 +1,411 @@
+import React, { useEffect } from "react";
+
+import { Image } from "./components/Image";
+import { Column } from "./components/Column";
+import { useImageHref, usePotentialRandomValue } from "./hooks";
+import {
+ getRandomInt,
+ getRandomSign,
+ getRandomColor,
+ brightnessByColor
+} from "./utils";
+
+import "./styles.css";
+
+export const handler = ({ inputs, mechanic }) => {
+ const {
+ width,
+ height,
+ randomRatio,
+ grid,
+ textSize,
+ randomColor,
+ textOne,
+ textTwo,
+ textThree,
+ textFour,
+ title,
+ image,
+ filterOpacity,
+ titleSizeAdjust
+ } = inputs;
+
+ const canvasRatio = width / height;
+ const columnOptions = canvasRatio >= 0.5 ? (canvasRatio >= 0.75 ? 3 : 2) : 1;
+
+ // Colors
+ const textColor = usePotentialRandomValue(
+ getRandomColor,
+ randomColor.textColor,
+ randomColor.show
+ );
+ const titleColor = usePotentialRandomValue(
+ getRandomColor,
+ randomColor.titleColor,
+ randomColor.show
+ );
+ const backgroundColor = usePotentialRandomValue(
+ getRandomColor,
+ randomColor.backgroundColor,
+ randomColor.show
+ );
+
+ // Column ratios
+ const columnOneRatio = usePotentialRandomValue(
+ () => getRandomInt(2, 10),
+ randomRatio.columnOneRatio,
+ randomRatio.show
+ );
+ const columnTwoRatio = usePotentialRandomValue(
+ () => getRandomInt(2, 10),
+ canvasRatio >= 0.5 ? randomRatio.columnTwoRatio : 0,
+ canvasRatio >= 0.5 && randomRatio.show
+ );
+ const columnThreeRatio = usePotentialRandomValue(
+ () => getRandomInt(2, 10),
+ canvasRatio >= 0.75 ? randomRatio.columnThreeRatio : 0,
+ canvasRatio >= 0.75 && randomRatio.show
+ );
+ const imageColumn = usePotentialRandomValue(
+ () => getRandomInt(1, columnOptions + 1),
+ randomRatio.imageColumn,
+ randomRatio.show
+ );
+
+ // Other spacing
+ const border = usePotentialRandomValue(
+ () => getRandomInt(15, 65),
+ randomRatio.border,
+ randomRatio.show
+ );
+ const gutter = usePotentialRandomValue(
+ () => getRandomInt(15, 45),
+ randomRatio.gutter,
+ randomRatio.show
+ );
+
+ // Horizontal space
+ const borderRatio = (border * width) / 1080;
+ const gutterRatio = (gutter * width) / 1080;
+ const ratioSum =
+ (width - borderRatio * 2 - gutterRatio * (columnOptions - 1)) /
+ (columnOneRatio + columnTwoRatio + columnThreeRatio);
+ const oneWidth = ratioSum * columnOneRatio;
+ const twoWidth = ratioSum * columnTwoRatio;
+ const threeWidth = ratioSum * columnThreeRatio;
+
+ const chooseW = [
+ canvasRatio >= 0.5
+ ? oneWidth + borderRatio + gutterRatio * 0.5
+ : oneWidth + borderRatio * 2,
+ canvasRatio >= 0.75
+ ? twoWidth + gutterRatio
+ : twoWidth + gutterRatio * 0.5 + borderRatio,
+ threeWidth + borderRatio + borderRatio * 0.5
+ ];
+ const chooseX = [
+ 0,
+ borderRatio + gutterRatio * 0.5 + oneWidth,
+ borderRatio + gutterRatio * 1.5 + oneWidth + twoWidth
+ ];
+ const cropWidth = chooseW[imageColumn - 1];
+ const cropX = chooseX[imageColumn - 1];
+
+ const fullHeight = height - borderRatio * 2;
+ const textSizeRatio =
+ ((textSize - (canvasRatio - 0.5) * 2) * width) /
+ 1080 /
+ Math.min(canvasRatio, 1);
+ const titleSize =
+ ((textSize - title.length * 0.8 + titleSizeAdjust) * 10 * width) /
+ 1080 /
+ Math.min(canvasRatio, 1);
+ const titleAngle = usePotentialRandomValue(
+ () => getRandomSign() * Math.random() * (60 / canvasRatio),
+ null,
+ true
+ );
+
+ const gridColor = brightnessByColor(backgroundColor) > 127 ? "#000" : "#fff";
+
+ const bigTextStyle = {
+ color: titleColor,
+ fontSize: textSizeRatio * 1.5,
+ fontFamily: "Object Sans",
+ whiteSpace: "pre-wrap",
+ overflowWrap: "anywhere",
+ hyphens: "auto"
+ };
+
+ const textStyle = {
+ color: textColor,
+ fontSize: textSizeRatio,
+ fontFamily: "Object Sans",
+ whiteSpace: "pre-wrap",
+ overflowWrap: "anywhere",
+ hyphens: "auto"
+ };
+
+ const href = useImageHref(image);
+
+ useEffect(() => {
+ if (!image || href !== "") {
+ mechanic.done();
+ }
+ }, [image, href]);
+
+ return (
+
+
+
+
+
+
+
+
{textOne}
+ {canvasRatio < 0.5 && (
+ <>
+
+
{textFour}
+ >
+ )}
+
+
+
+
{textTwo}
+ {canvasRatio < 0.5 && (
+ <>
+
+
{textThree}
+ >
+ )}
+
+
+
+
+ {canvasRatio < 0.75 && (
+
+ )}
+
+
+
+
+
+
+
+
+ {/* title */}
+
+ {title.toUpperCase()}
+
+
+ );
+};
+
+export const inputs = {
+ width: {
+ type: "number",
+ default: 1000
+ },
+ height: {
+ type: "number",
+ default: 1000
+ },
+ randomRatio: {
+ type: "groupToggle",
+ default: true,
+ label: "Random Ratio",
+ inputs: {
+ columnOneRatio: {
+ type: "number",
+ slider: true,
+ default: 2,
+ min: 1,
+ max: 10,
+ step: 1
+ },
+ columnTwoRatio: {
+ type: "number",
+ slider: true,
+ default: 2,
+ min: 1,
+ max: 10,
+ step: 1
+ },
+ columnThreeRatio: {
+ type: "number",
+ slider: true,
+ default: 1,
+ min: 1,
+ max: 10,
+ step: 1
+ },
+ border: {
+ type: "number",
+ slider: true,
+ default: 15,
+ min: 1,
+ max: 100,
+ step: 1
+ },
+ gutter: {
+ type: "number",
+ slider: true,
+ default: 15,
+ min: 1,
+ max: 100,
+ step: 1
+ },
+ imageColumn: {
+ type: "number",
+ slider: true,
+ default: 1,
+ min: 1,
+ max: 3,
+ step: 1
+ }
+ }
+ },
+ randomColor: {
+ type: "groupToggle",
+ default: false,
+ label: "Random Color",
+ inputs: {
+ backgroundColor: {
+ type: "color",
+ model: "hex",
+ default: "#000000"
+ },
+ textColor: {
+ type: "color",
+ model: "hex",
+ default: "#ffffff"
+ },
+ titleColor: {
+ type: "color",
+ model: "hex",
+ default: "#E94825"
+ }
+ }
+ },
+ image: {
+ type: "image",
+ multiple: false
+ },
+ filterOpacity: {
+ type: "number",
+ default: 20,
+ min: 0,
+ max: 100,
+ step: 1,
+ slider: true
+ },
+ textSize: {
+ type: "number",
+ default: 20
+ },
+ titleSizeAdjust: {
+ type: "number",
+ slider: true,
+ default: 0,
+ min: -5,
+ max: 15,
+ step: 1
+ },
+ title: {
+ type: "text",
+ default: "Mechanic"
+ },
+ textOne: {
+ type: "text",
+ default: "MUNUS SHIH"
+ },
+ textTwo: {
+ type: "text",
+ default: "MECHANIC.DESIGN INFO@MECHANIC.DESIGN"
+ },
+ textThree: {
+ type: "text",
+ default: "@MECHANIC 781 12TH ST, 8A, NEW YORK, NY 10003"
+ },
+ textFour: {
+ type: "text",
+ default: "MUNUS@MECHANIC.DESIGN"
+ },
+ grid: {
+ type: "boolean",
+ default: false
+ }
+};
+
+export const presets = {
+ "Instagram Story": {
+ width: 1080,
+ height: 1920
+ },
+ "Instagram Post": {
+ width: 1080,
+ height: 1080
+ },
+ Poster: {
+ width: 812,
+ height: 1148
+ },
+ Banner: {
+ width: 1640,
+ height: 624
+ },
+ Ticket: {
+ width: 394,
+ height: 1126
+ }
+};
+
+export const settings = {
+ engine: require("@mechanic-design/engine-react"),
+ showMultipleExports: true
+};
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/function/styles.css b/packages/create-mechanic/function-examples/adaptive-grid/function/styles.css
new file mode 100644
index 00000000..1d1b1791
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/function/styles.css
@@ -0,0 +1,63 @@
+/* Object Sans
+ * This beautiful font was designed by Alex Slobzheninov.
+ * ---------------------------------------------------------------------------
+ * Object Sans is a contemporary type family that puts together the best
+ * qualities of Swiss neo-grotesks and geometric fonts. It’s a multifunctional
+ * workhorse designed to work best in any printed and on-screen contexts.
+ * It is free to use for personal purposes. If you would like to use it
+ * for commercial purposes, you will need to purchase a license.
+ * for more information visit https://pangrampangram.com/products/object-sans
+ * or check ./assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
+ * Alex Slobzheninov https://www.behance.net/slobzheninov
+ * ---------------------------------------------------------------------------
+*/
+
+@font-face {
+ font-family: "Object Sans";
+ src: url("./assets/PPObjectSans-Regular.otf") format("opentype");
+}
+
+@font-face {
+ font-family: "Object Sans Italic";
+ src: url("./assets/PPObjectSans-Slanted.otf") format("opentype");
+ font-style: italic;
+}
+
+@font-face {
+ font-family: "Object Sans Heavy";
+ src: url("./assets/PPObjectSans-Heavy.otf") format("opentype");
+ font-weight: bold;
+}
+
+@font-face {
+ font-family: "Object Sans Slanted";
+ src: url("./assets/PPObjectSans-HeavySlanted.otf") format("opentype");
+ font-weight: bold;
+ font-style: italic;
+}
+
+* {
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+}
+
+.column {
+ height: 100%;
+ position: relative;
+ padding: 10px;
+}
+
+.top {
+ position: absolute;
+ top: 0;
+ padding-top: 10px;
+ padding-right: 10px;
+}
+
+.bottom {
+ position: absolute;
+ bottom: 0;
+ padding-bottom: 10px;
+ padding-right: 10px;
+}
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/function/utils.js b/packages/create-mechanic/function-examples/adaptive-grid/function/utils.js
new file mode 100644
index 00000000..ac354dc7
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/function/utils.js
@@ -0,0 +1,36 @@
+export function getRandomColor() {
+ return (
+ "#" + (0x1000000 + Math.random() * 0xffffff).toString(16).substring(1, 7)
+ );
+}
+
+export function getRandomInt(from, to) {
+ return Math.floor(from + Math.random() * (to - from));
+}
+
+export function getRandomSign() {
+ return 2 * Math.round(Math.random()) - 1;
+}
+
+export function brightnessByColor(color) {
+ let r, g, b;
+ const isHEX = color.indexOf("#") === 0;
+ const isRGB = color.indexOf("rgb") === 0;
+ if (isHEX) {
+ const hasFullSpec = color.length === 7;
+ const m = color.substring(1).match(hasFullSpec ? /(\S{2})/g : /(\S{1})/g);
+ if (m) {
+ r = parseInt(m[0] + (hasFullSpec ? "" : m[0]), 16);
+ g = parseInt(m[1] + (hasFullSpec ? "" : m[1]), 16);
+ b = parseInt(m[2] + (hasFullSpec ? "" : m[2]), 16);
+ }
+ } else if (isRGB) {
+ const m = color.match(/(\d+){3}/g);
+ if (m) {
+ r = m[0];
+ g = m[1];
+ b = m[2];
+ }
+ }
+ if (r) return (r * 299 + g * 587 + b * 114) / 1000;
+}
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/inputs/group/index.js b/packages/create-mechanic/function-examples/adaptive-grid/inputs/group/index.js
new file mode 100644
index 00000000..ee84687c
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/inputs/group/index.js
@@ -0,0 +1,87 @@
+import React from "react";
+import {
+ BooleanInput,
+ NumberInput,
+ OptionInput,
+ ColorInput
+} from "@mechanic-design/ui-components";
+
+import * as css from "./style.module.css";
+
+export const typeName = "groupToggle";
+
+export const initValue = input => ({
+ show: input.default,
+ ...Object.fromEntries(
+ Object.entries(input.inputs).map(([name, input]) => [name, input.default])
+ )
+});
+
+export const prepareValue = (value, input) => {
+ const v =
+ value === undefined ||
+ value === null ||
+ !Object.keys(input.inputs).every(k => value.hasOwnProperty(k))
+ ? initValue(input)
+ : value;
+ return v;
+};
+
+export const Input = props => {
+ const { name, values, inputDef, onChange } = props;
+ const { show, ...value } = values[name] ?? initValue(inputDef);
+
+ return (
+
+ onChange(e, name, { ...value, show: v })}
+ />
+ {!show && (
+ <>
+ {Object.entries(inputDef.inputs).map(([inputName, input], index) =>
+ input.type === "number" ? (
+
+ onChange(e, name, { show, ...value, [inputName]: v })
+ }
+ />
+ ) : input.type === "color" ? (
+
+ onChange(e, name, { show, ...value, [inputName]: v })
+ }
+ >
+ ) : (
+
+ onChange(e, name, { show, ...value, [inputName]: v })
+ }
+ />
+ )
+ )}
+ >
+ )}
+
+ );
+};
diff --git a/packages/create-mechanic/function-examples/adaptive-grid/inputs/group/style.module.css b/packages/create-mechanic/function-examples/adaptive-grid/inputs/group/style.module.css
new file mode 100644
index 00000000..4bb9fdfa
--- /dev/null
+++ b/packages/create-mechanic/function-examples/adaptive-grid/inputs/group/style.module.css
@@ -0,0 +1,7 @@
+.root {
+ margin: 1em 0;
+}
+
+.toggle {
+ margin: 1em 0;
+}
\ No newline at end of file
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf b/packages/create-mechanic/function-examples/business-card-generator/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
rename to packages/create-mechanic/function-examples/business-card-generator/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-Heavy.otf b/packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-Heavy.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-Heavy.otf
rename to packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-Heavy.otf
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-HeavySlanted.otf b/packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-HeavySlanted.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-HeavySlanted.otf
rename to packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-HeavySlanted.otf
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-Regular.otf b/packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-Regular.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-Regular.otf
rename to packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-Regular.otf
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-Slanted.otf b/packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-Slanted.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/assets/PPObjectSans-Slanted.otf
rename to packages/create-mechanic/function-examples/business-card-generator/function/assets/PPObjectSans-Slanted.otf
diff --git a/packages/create-mechanic/function-examples/business-card-generator/index.js b/packages/create-mechanic/function-examples/business-card-generator/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-examples/business-card-generator/index.js
rename to packages/create-mechanic/function-examples/business-card-generator/function/index.js
diff --git a/packages/create-mechanic/function-examples/business-card-generator/styles.css b/packages/create-mechanic/function-examples/business-card-generator/function/styles.css
similarity index 100%
rename from packages/create-mechanic/function-examples/business-card-generator/styles.css
rename to packages/create-mechanic/function-examples/business-card-generator/function/styles.css
diff --git a/packages/create-mechanic/function-examples/index.js b/packages/create-mechanic/function-examples/index.js
index c287427f..cf51188e 100644
--- a/packages/create-mechanic/function-examples/index.js
+++ b/packages/create-mechanic/function-examples/index.js
@@ -13,6 +13,11 @@ const options = [
name: "Poster Generator",
type: "Canvas",
dir: "poster-generator"
+ },
+ {
+ name: "Adaptive Grid",
+ type: "Canvas",
+ dir: "adaptive-grid"
}
];
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/Circle.js b/packages/create-mechanic/function-examples/instagram-story-generator/function/Circle.js
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/Circle.js
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/Circle.js
diff --git a/packages/create-mechanic/function-examples/poster-generator/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf b/packages/create-mechanic/function-examples/instagram-story-generator/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
similarity index 100%
rename from packages/create-mechanic/function-examples/poster-generator/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
diff --git a/packages/create-mechanic/function-examples/poster-generator/assets/PPObjectSans-Heavy.otf b/packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-Heavy.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/poster-generator/assets/PPObjectSans-Heavy.otf
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-Heavy.otf
diff --git a/packages/create-mechanic/function-examples/poster-generator/assets/PPObjectSans-HeavySlanted.otf b/packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-HeavySlanted.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/poster-generator/assets/PPObjectSans-HeavySlanted.otf
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-HeavySlanted.otf
diff --git a/packages/create-mechanic/function-examples/poster-generator/assets/PPObjectSans-Regular.otf b/packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-Regular.otf
similarity index 100%
rename from packages/create-mechanic/function-examples/poster-generator/assets/PPObjectSans-Regular.otf
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-Regular.otf
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-Slanted.otf b/packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-Slanted.otf
new file mode 100644
index 00000000..739f0d8e
Binary files /dev/null and b/packages/create-mechanic/function-examples/instagram-story-generator/function/assets/PPObjectSans-Slanted.otf differ
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/index.js b/packages/create-mechanic/function-examples/instagram-story-generator/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/index.js
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/index.js
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/styles.css b/packages/create-mechanic/function-examples/instagram-story-generator/function/styles.css
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/styles.css
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/styles.css
diff --git a/packages/create-mechanic/function-examples/instagram-story-generator/utils.js b/packages/create-mechanic/function-examples/instagram-story-generator/function/utils.js
similarity index 100%
rename from packages/create-mechanic/function-examples/instagram-story-generator/utils.js
rename to packages/create-mechanic/function-examples/instagram-story-generator/function/utils.js
diff --git a/packages/create-mechanic/function-examples/poster-generator/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf b/packages/create-mechanic/function-examples/poster-generator/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf
new file mode 100644
index 00000000..7afcff96
Binary files /dev/null and b/packages/create-mechanic/function-examples/poster-generator/function/assets/EULA-PangramPangram-FreeForPersonalUse-MAY2021.pdf differ
diff --git a/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-Heavy.otf b/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-Heavy.otf
new file mode 100644
index 00000000..de186532
Binary files /dev/null and b/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-Heavy.otf differ
diff --git a/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-HeavySlanted.otf b/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-HeavySlanted.otf
new file mode 100644
index 00000000..247c2760
Binary files /dev/null and b/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-HeavySlanted.otf differ
diff --git a/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-Regular.otf b/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-Regular.otf
new file mode 100644
index 00000000..3e8b681c
Binary files /dev/null and b/packages/create-mechanic/function-examples/poster-generator/function/assets/PPObjectSans-Regular.otf differ
diff --git a/packages/create-mechanic/function-examples/poster-generator/index.js b/packages/create-mechanic/function-examples/poster-generator/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-examples/poster-generator/index.js
rename to packages/create-mechanic/function-examples/poster-generator/function/index.js
diff --git a/packages/create-mechanic/function-examples/poster-generator/utils.js b/packages/create-mechanic/function-examples/poster-generator/function/utils.js
similarity index 100%
rename from packages/create-mechanic/function-examples/poster-generator/utils.js
rename to packages/create-mechanic/function-examples/poster-generator/function/utils.js
diff --git a/packages/create-mechanic/function-templates/canvas-image/index.js b/packages/create-mechanic/function-templates/canvas-image/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/canvas-image/index.js
rename to packages/create-mechanic/function-templates/canvas-image/function/index.js
diff --git a/packages/create-mechanic/function-templates/canvas-video/index.js b/packages/create-mechanic/function-templates/canvas-video/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/canvas-video/index.js
rename to packages/create-mechanic/function-templates/canvas-video/function/index.js
diff --git a/packages/create-mechanic/function-templates/d3-image/index.js b/packages/create-mechanic/function-templates/d3-image/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/d3-image/index.js
rename to packages/create-mechanic/function-templates/d3-image/function/index.js
diff --git a/packages/create-mechanic/function-templates/p5-image/index.js b/packages/create-mechanic/function-templates/p5-image/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/p5-image/index.js
rename to packages/create-mechanic/function-templates/p5-image/function/index.js
diff --git a/packages/create-mechanic/function-templates/p5-video/index.js b/packages/create-mechanic/function-templates/p5-video/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/p5-video/index.js
rename to packages/create-mechanic/function-templates/p5-video/function/index.js
diff --git a/packages/create-mechanic/function-templates/react-image/index.js b/packages/create-mechanic/function-templates/react-image/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/react-image/index.js
rename to packages/create-mechanic/function-templates/react-image/function/index.js
diff --git a/packages/create-mechanic/function-templates/react-video/index.js b/packages/create-mechanic/function-templates/react-video/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/react-video/index.js
rename to packages/create-mechanic/function-templates/react-video/function/index.js
diff --git a/packages/create-mechanic/function-templates/svg-image/index.js b/packages/create-mechanic/function-templates/svg-image/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/svg-image/index.js
rename to packages/create-mechanic/function-templates/svg-image/function/index.js
diff --git a/packages/create-mechanic/function-templates/svg-video/index.js b/packages/create-mechanic/function-templates/svg-video/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/svg-video/index.js
rename to packages/create-mechanic/function-templates/svg-video/function/index.js
diff --git a/packages/create-mechanic/function-templates/svgjs-image/index.js b/packages/create-mechanic/function-templates/svgjs-image/function/index.js
similarity index 100%
rename from packages/create-mechanic/function-templates/svgjs-image/index.js
rename to packages/create-mechanic/function-templates/svgjs-image/function/index.js
diff --git a/packages/create-mechanic/index.js b/packages/create-mechanic/index.js
index 5edf0686..f0a191d1 100644
--- a/packages/create-mechanic/index.js
+++ b/packages/create-mechanic/index.js
@@ -12,6 +12,7 @@ const {
installationMethodQuestion,
generateProjectTemplate,
installDependencies,
+ tryGitInit,
checkLockFile
} = require("./new-project");
const {
@@ -106,6 +107,7 @@ const command = async argv => {
const { confirmContinue } = await inquirer.prompt(confirmDFQuestion);
await sleep();
if (confirmContinue) {
+ log();
log(content.designFunctionBasesDescription);
} else {
skipFunctions = true;
@@ -149,6 +151,9 @@ const command = async argv => {
// Install dependencies in new project directory
const install = await askToInstall(projectName);
+ // Try initializing git repository
+ await tryGitInit(projectName);
+
// Done!
log(content.doneAndNextStepsMessage(projectName, install));
log(content.bye);
diff --git a/packages/create-mechanic/new-function.js b/packages/create-mechanic/new-function.js
index 3247a199..5cc581a5 100644
--- a/packages/create-mechanic/new-function.js
+++ b/packages/create-mechanic/new-function.js
@@ -13,29 +13,42 @@ const log = console.log;
// https://gist.github.com/lovasoa/8691344#gistcomment-3299018
const walk = (dir, fileCallback, directoryCallback) => {
const files = fs.readdirSync(dir);
+ let wasSuccessful = true;
files.forEach(file => {
const filepath = path.join(dir, file);
const stats = fs.statSync(filepath);
if (stats.isDirectory()) {
- directoryCallback(file, filepath, stats);
- walk(filepath, fileCallback, directoryCallback);
+ const success = directoryCallback(file, filepath, stats);
+ if (wasSuccessful && !success) wasSuccessful = success;
+ if (success) {
+ wasSuccessful = walk(filepath, fileCallback, directoryCallback);
+ }
} else if (stats.isFile()) {
- fileCallback(file, filepath, stats);
+ const success = fileCallback(file, filepath, stats);
+ if (wasSuccessful && !success) wasSuccessful = success;
}
});
+ return wasSuccessful;
};
-const copyDirAndContents = (baseFunctionDir, newFunctionDir) => {
+const copyDirAndContents = (originDir, targetDir) => {
const copyFile = (_, filepath) => {
- if (path.join(baseFunctionDir, "dependencies.json") === filepath) return;
- const relativePath = path.relative(baseFunctionDir, filepath);
- fs.copyFileSync(filepath, path.join(newFunctionDir, relativePath));
+ const relativePath = path.relative(originDir, filepath);
+ const target = path.join(targetDir, relativePath);
+ if (!fs.pathExistsSync(target)) {
+ fs.copyFileSync(filepath, target);
+ return true;
+ } else return false;
};
- const copyDir = (_, filepath) => {
- const relativePath = path.relative(baseFunctionDir, filepath);
- fs.mkdirSync(path.join(newFunctionDir, relativePath));
+ const copyDir = (_, dirPath) => {
+ const relativePath = path.relative(originDir, dirPath);
+ const target = path.join(targetDir, relativePath);
+ if (!fs.pathExistsSync(target)) {
+ fs.mkdirSync(target);
+ return true;
+ } else return false;
};
- walk(baseFunctionDir, copyFile, copyDir);
+ return walk(originDir, copyFile, copyDir);
};
const baseExists = (typeOfBaseUsed, base) => {
@@ -110,40 +123,49 @@ const generateFunctionTemplate = async (
spinner.start(content.generateFunctionStart);
// Create design function folder
- const directory = path.resolve(projectName);
+ const projectDir = path.resolve(projectName);
+ const projectPackagePath = path.join(projectDir, "package.json");
const newFunctionDir = path.join(
- directory,
+ projectDir,
config.functionsPath || "functions",
functionName
);
await fs.mkdir(newFunctionDir);
// Path of template directory to copy
- const baseFunctionDir = path.join(
- __dirname,
+ const functionTypeDirectory =
typeOfBaseUsed === "Template"
? "function-templates"
: typeOfBaseUsed === "Example"
? "function-examples"
- : "function-blank",
+ : "function-blank";
+
+ const functionDir =
typeOfBaseUsed === "Template"
? functionTemplateOptions[base].dir
: typeOfBaseUsed === "Example"
? functionExampleOptions[base].dir
- : ""
+ : "";
+ const baseFunctionDir = path.join(
+ __dirname,
+ functionTypeDirectory,
+ functionDir
+ );
+ const functionSrcDir = path.join(baseFunctionDir, "function");
+ const functionDependenciesPath = path.join(
+ baseFunctionDir,
+ "dependencies.json"
);
+ const inputsSrcDic = path.join(baseFunctionDir, "inputs");
// Add dependencies and copy basic files
await Promise.all([
(async () => {
const packageObj = JSON.parse(
- await fs.readFile(path.join(directory, "package.json"), "utf8")
+ await fs.readFile(projectPackagePath, "utf8")
);
const baseDependencies = JSON.parse(
- await fs.readFile(
- path.join(baseFunctionDir, "dependencies.json"),
- "utf8"
- )
+ await fs.readFile(functionDependenciesPath, "utf8")
);
// Add dependencies
for (const depType in baseDependencies) {
@@ -156,16 +178,39 @@ const generateFunctionTemplate = async (
}
// Write the resulting package
await fs.writeFile(
- path.join(directory, "package.json"),
+ projectPackagePath,
JSON.stringify(packageObj, null, 2)
);
})()
]);
+
+ // Add any custom inputs
+ const templateHasInputs = await fs.pathExists(inputsSrcDic);
+ const generatedCustomInputs = { tried: false, success: false };
+ if (templateHasInputs) {
+ const projectInputDir = path.join(projectDir, "inputs");
+ const projectInputDirExists = await fs.pathExists(projectInputDir);
+ if (!projectInputDirExists) {
+ await fs.mkdir(projectInputDir);
+ }
+
+ const copiedEverything = copyDirAndContents(inputsSrcDic, projectInputDir);
+ generatedCustomInputs.tried = true;
+ generatedCustomInputs.success = copiedEverything;
+ }
+
// Copy all files in base dir
- copyDirAndContents(baseFunctionDir, newFunctionDir);
+ copyDirAndContents(functionSrcDir, newFunctionDir);
spinner.succeed(content.generateFunctionSuccess(functionName));
- log(content.functionCreationDetails(functionName));
+ log();
+ log(
+ content.functionCreationDetails(
+ { functionName, functionTypeDirectory, functionDir },
+ generatedCustomInputs
+ )
+ );
+
return newFunctionDir;
};
diff --git a/packages/create-mechanic/new-project.js b/packages/create-mechanic/new-project.js
index b2176e46..35f1cbd7 100644
--- a/packages/create-mechanic/new-project.js
+++ b/packages/create-mechanic/new-project.js
@@ -63,7 +63,7 @@ const generateProjectTemplate = async (projectName, typeOfBaseUsed) => {
// Copying content promises
await Promise.all([
// Copy array of files that get duplicated without change
- ...["mechanic.config.js", "README.md"].map(filename =>
+ ...["mechanic.config.js", "README.md", "_gitignore"].map(filename =>
fs.copyFile(
path.join(projectTemplateDir, filename),
path.join(directory, filename.replace(/^_/, "."))
@@ -88,6 +88,7 @@ const generateProjectTemplate = async (projectName, typeOfBaseUsed) => {
]);
spinner.succeed(content.generateProjectSuccess(typeOfBaseUsed, projectName));
+ log();
log(content.projectContents(path.dirname(directory)));
};
@@ -110,6 +111,17 @@ const installDependencies = async (projectName, installingMethod) => {
// Project directory
const cwd = path.resolve(projectName);
+ // List out dependencies being installed
+ const packageJsonPath = path.join(cwd, "package.json");
+ const packageObj = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
+ log(content.installingDependenciesMessage);
+ for (const depType of ["devDependencies", "dependencies"]) {
+ for (const dep in packageObj[depType]) {
+ log(content.dependencyItem(dep));
+ }
+ }
+ log();
+
try {
spinner.start(content.installTry(installingMethod));
// Install
@@ -126,6 +138,59 @@ const installDependencies = async (projectName, installingMethod) => {
}
};
+async function isInGitRepository(cwd) {
+ try {
+ await execa("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
+ return true;
+ } catch (_) {}
+ return false;
+}
+
+async function isInMercurialRepository(cwd) {
+ try {
+ await execa("hg", ["--cwd", ".", "root"], { cwd });
+ return true;
+ } catch (_) {}
+ return false;
+}
+
+// Adaptation of https://github.com/vercel/next.js/blob/canary/packages/create-next-app/helpers/git.ts
+const tryGitInit = async projectName => {
+ // Project directory
+ const cwd = path.resolve(projectName);
+ let didInit = false;
+ try {
+ await execa("git", ["--version"], { cwd });
+ if (
+ (await isInGitRepository(cwd)) ||
+ (await isInMercurialRepository(cwd))
+ ) {
+ return false;
+ }
+
+ await execa("git", ["init"], { cwd });
+ didInit = true;
+
+ await execa("git", ["checkout", "-b", "main"], { cwd });
+
+ await execa("git", ["add", "-A"], { cwd });
+ await execa(
+ "git",
+ ["commit", "-m", "Initial commit from Create Mechanic"],
+ { cwd }
+ );
+
+ return true;
+ } catch (e) {
+ if (didInit) {
+ try {
+ fs.rmSync(path.join(cwd, ".git"));
+ } catch (_) {}
+ }
+ return false;
+ }
+};
+
module.exports = {
getProjectQuestion,
confirmDFQuestion,
@@ -133,5 +198,6 @@ module.exports = {
installationMethodQuestion,
generateProjectTemplate,
checkLockFile,
- installDependencies
+ installDependencies,
+ tryGitInit
};
diff --git a/packages/create-mechanic/project-template/_gitignore b/packages/create-mechanic/project-template/_gitignore
index e69de29b..5c787228 100644
--- a/packages/create-mechanic/project-template/_gitignore
+++ b/packages/create-mechanic/project-template/_gitignore
@@ -0,0 +1,15 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+
+# production
+/dist
+
+# misc
+.DS_Store
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/packages/create-mechanic/script-content.js b/packages/create-mechanic/script-content.js
index ccc220b4..ce97f4ba 100644
--- a/packages/create-mechanic/script-content.js
+++ b/packages/create-mechanic/script-content.js
@@ -1,9 +1,11 @@
const {
logo: { mechanic, mechanicInverse },
- colors: { success, bgRed, bgBlue }
+ colors: { success, bgRed, bgBlue, fail }
} = require("@mechanic-design/utils");
const mechanicPackage = "@mechanic-design/core";
+const sourceCodeUrl = "https://github.com/designsystemsinternational/mechanic";
+const sourceCodeMainBranchUrl = `${sourceCodeUrl}/tree/main`;
module.exports = {
welcome: `${mechanic}
@@ -81,7 +83,10 @@ ${bgBlue(
generateFunctionStart: "Adding design function to project...",
generateFunctionSuccess: functionName =>
`Design function "${functionName}" added to project!`,
- functionCreationDetails: functionName =>
+ functionCreationDetails: (
+ { functionName, functionDir, functionTypeDirectory },
+ customInputGeneration
+ ) =>
`This just:
> Created a folder inside functions/, called ${success(
functionName
@@ -89,13 +94,31 @@ ${bgBlue(
"index.js"
)} file where the design function is defined.
> Added some other dependencies into your project to make your design function work.
-
-`,
+${
+ customInputGeneration.tried
+ ? customInputGeneration.success
+ ? "> Added a custom input used in the function. You can find it in the " +
+ success("inputs/") +
+ " folder \n"
+ : "> " +
+ fail("Tried") +
+ " adding a needed custom input, but it wasn't possible. " +
+ "Check " +
+ `${sourceCodeMainBranchUrl}/packages/create-mechanic/${functionTypeDirectory}${
+ functionDir !== "" ? "/" + functionDir : ""
+ }/inputs for the input's source code.\n`
+ : ""
+}`,
confirmInstallQuestion:
"Do you wish to install dependencies for your project right away?",
installationMethodQuestion:
"Do you wish to install dependencies using npm or yarn?",
+ installingDependenciesMessage: "\nDependencies to install:",
+ dependencyItem: dep =>
+ !dep.includes("@mechanic-design")
+ ? `- ${dep}`
+ : `- ${bgRed("@mechanic-design")}/${bgBlue(dep.split("/")[1])}`,
installTry: method => `Trying with ${method}.`,
installSucceed: method => `Installed dependencies with ${method}.`,
installFailed: method =>