diff --git a/.gitignore b/.gitignore
index 76d7e3e9186..3dbc88d0866 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,7 @@ sites/strapi/.cache
# Sites prebuild artifacts
sites/*/public/locales/*/*.json
sites/*/public/feeds/*
+packages/new-design/shared/public/locales/*/*.json
# Lab auto-generated pages
sites/lab/lib
diff --git a/config/software/packages.json b/config/software/packages.json
index ef958c22502..546a6c48098 100644
--- a/config/software/packages.json
+++ b/config/software/packages.json
@@ -8,6 +8,7 @@
"i18n": "Translations for the FreeSewing project",
"models": "Body measurements data for a range of default sizes",
"mui-theme": "A Material-UI theme for FreeSewing web UIs",
+ "new-design": "Initializer package for a new FreeSewing design: npx @freesewing/new-design",
"pattern-info": "Information about available freesewing patterns",
"prettier-config": "FreeSewing's shared configuration for prettier",
"remark-jargon": "A Remark plugin for jargon terms",
diff --git a/packages/i18n/src/locales/en/components/common.yaml b/packages/i18n/src/locales/en/components/common.yaml
index 51a7b891738..9a875c4143c 100644
--- a/packages/i18n/src/locales/en/components/common.yaml
+++ b/packages/i18n/src/locales/en/components/common.yaml
@@ -9,3 +9,5 @@ requiredMeasurements: Required measurements
showcase: Showcase
sloganCome: Come for the sewing patterns
sloganStay: Stay for the community
+support: Support
+
diff --git a/packages/i18n/src/locales/en/components/patrons.yaml b/packages/i18n/src/locales/en/components/patrons.yaml
index f2b47874c01..a92f87d638b 100644
--- a/packages/i18n/src/locales/en/components/patrons.yaml
+++ b/packages/i18n/src/locales/en/components/patrons.yaml
@@ -1,2 +1,5 @@
becomeAPatron: Become a patron
supportFreesewing: Support FreeSewing
+patronLead: FreeSewing is fuelled by a voluntary subscription model
+patronPitch: If you think what we do is worthwhile, and if you can spare a few coins each month without hardship, please support our work
+
diff --git a/packages/i18n/src/next/en/common.mjs b/packages/i18n/src/next/en/common.mjs
index 2b11a7858cc..1ed218daaf8 100644
--- a/packages/i18n/src/next/en/common.mjs
+++ b/packages/i18n/src/next/en/common.mjs
@@ -13,7 +13,8 @@ const common = {
"requiredMeasurements": "Required measurements",
"showcase": "Showcase",
"sloganCome": "Come for the sewing patterns",
- "sloganStay": "Stay for the community"
+ "sloganStay": "Stay for the community",
+ "support": "Support"
}
export default common
diff --git a/packages/i18n/src/next/en/patrons.mjs b/packages/i18n/src/next/en/patrons.mjs
index 3f9e4ed30a1..ebe13e2b382 100644
--- a/packages/i18n/src/next/en/patrons.mjs
+++ b/packages/i18n/src/next/en/patrons.mjs
@@ -4,7 +4,9 @@
*/
const patrons = {
"becomeAPatron": "Become a patron",
- "supportFreesewing": "Support FreeSewing"
+ "supportFreesewing": "Support FreeSewing",
+ "patronLead": "FreeSewing is fuelled by a voluntary subscription model",
+ "patronPitch": "If you think what we do is worthwhile, and if you can spare a few coins each month without hardship, please support our work"
}
export default patrons
diff --git a/packages/new-design/CHANGELOG.md b/packages/new-design/CHANGELOG.md
new file mode 100644
index 00000000000..7274b9130ec
--- /dev/null
+++ b/packages/new-design/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Change log for: @freesewing/new-design
+
+
+
+This is the **initial release**, and the start of this change log.
+
+> Prior to version 2, FreeSewing was not a JavaScript project.
+> As such, that history is out of scope for this change log.
+
diff --git a/packages/new-design/README.md b/packages/new-design/README.md
new file mode 100644
index 00000000000..cd7325f2d84
--- /dev/null
+++ b/packages/new-design/README.md
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+
+
+
+
+
+# @freesewing/new-design
+
+Initializer package for a new FreeSewing design: npx @freesewing/new-design
+
+
+
+## What am I looking at? π€
+
+This repository is our *monorepo*
+holding [all our NPM packages](https://freesewing.dev/reference/packages/).
+
+This folder holds: @freesewing/new-design
+
+If you're not entirely sure what to do or how to start, type this command:
+
+```
+npm run tips
+```
+
+## About FreeSewing π
+
+Where the world of makers and developers collide, that's where you'll find FreeSewing.
+
+If you're a maker, checkout [freesewing.org](https://freesewing.org/) where you can generate
+our sewing patterns adapted to your measurements.
+
+If you're a developer, our documentation is on [freesewing.dev](https://freesewing.dev/).
+Our [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox
+for parametric design of sewing patterns. But we also provide a range
+of [plugins](https://freesewing.dev/reference/plugins/) that further extend the
+functionality of the platform.
+
+If you have NodeJS installed, you can try it right now by running:
+
+```bash
+npx create-freesewing-pattern
+```
+
+Or, consult our getting started guides
+for [Linux](https://freesewing.dev/tutorials/getting-started-linux/),
+[MacOS](https://freesewing.dev/tutorials/getting-started-mac/),
+or [Windows](https://freesewing.dev/tutorials/getting-started-windows/).
+
+We also have a [pattern design tutorial](https://freesewing.dev/tutorials/pattern-design/) that
+walks you through your first parametric design,
+and [a friendly community](https://freesewing.org/community/where/) with
+people who can help you when you get stuck.
+
+## Support FreeSewing: Become a patron π₯°
+
+FreeSewing is an open source project run by a community,
+and financially supported by our patrons.
+
+If you feel what we do is worthwhile, and you can spend a few coind without
+hardship, then you should [join us and become a patron](https://freesewing.org/community/join).
+
+## Links π©βπ»
+
+ - π» Makers website: [freesewing.org](https://freesewing.org)
+ - π» Developers website: [freesewing.dev](https://freesewing.dev)
+ - π¬ Chat: On Discord via [discord.freesewing.org](https://discord.freesewing.org/)
+ - β
Todo list/Kanban board: On Github via [todo.freesewing.org](https://todo.freesewing.org/)
+ - π¦ Twitter: [@freesewing_org](https://twitter.com/freesewing_org)
+ - π· Instagram: [@freesewing_org](https://instagram.com/freesewing_org)
+
+## License: MIT π€
+
+Β© [Joost De Cock](https://github.com/joostdecock).
+See [the license file](https://github.com/freesewing/freesewing/blob/develop/LICENSE) for details.
+
+## Where to get help π€―
+
+Our [chatrooms on Discord](https://chat.freesewing.org/) are the best place to ask questions,
+share your feedback, or just hang out.
+
+If you want to report a problem, please [create an issue](https://github.com/freesewing/freesewing/issues/new).
+
+
+
+## Contributors β¨
+
+Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+
+
+
+
+
+
+
+
+
+
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
+
diff --git a/packages/new-design/build.js b/packages/new-design/build.js
new file mode 100644
index 00000000000..67c80e27137
--- /dev/null
+++ b/packages/new-design/build.js
@@ -0,0 +1,55 @@
+/* This script will build the package with esbuild */
+const esbuild = require('esbuild')
+const pkg = require('./package.json')
+
+// Create banner based on package info
+const banner = `/**
+ * ${pkg.name} | v${pkg.version}
+ * ${pkg.description}
+ * (c) ${new Date().getFullYear()} ${pkg.author}
+ * @license ${pkg.license}
+ */`
+
+// Shared esbuild options
+const options = {
+ banner: { js: banner },
+ bundle: true,
+ entryPoints: ['index.mjs'],
+ external: ["@freesewing"],
+ metafile: process.env.VERBOSE ? true : false,
+ minify: process.env.NO_MINIFY ? false : true,
+ sourcemap: true,
+}
+
+// Different formats
+const formats = {
+ cjs: "dist/index.js",
+ esm: "dist/index.mjs",
+}
+
+// Let esbuild generate different formats
+let result
+(async () => {
+ for (const [format, outfile] of Object.entries(formats)) {
+ result = await esbuild
+ .build({ ...options, outfile, format })
+ .catch(() => process.exit(1))
+ }
+
+ if (process.env.VERBOSE) {
+ const info = await esbuild.analyzeMetafile(result.metafile)
+ console.log(info)
+ }
+
+ // Also build a version that has all dependencies bundled
+ // This makes it easy to run tests
+ await esbuild
+ .build({
+ ...options,
+ outfile: 'tests/dist/index.mjs',
+ format: 'esm',
+ external: [],
+ })
+ .catch(() => process.exit(1))
+
+})()
diff --git a/packages/new-design/index.mjs b/packages/new-design/index.mjs
new file mode 100644
index 00000000000..bd79ef34527
--- /dev/null
+++ b/packages/new-design/index.mjs
@@ -0,0 +1,18 @@
+#!/usr/bin/env node
+import { cli } from './lib/cli.mjs'
+
+cli()
+
+/*
+
+ ___ ___ _
+ | __| _ ___ ___/ __| _____ __ _(_)_ _ __ _
+ | _| '_/ -_) -_)__ \/ -_) V V / | ' \/ _` |
+ |_||_| \___\___|___/\___|\_/\_/|_|_||_\__, |
+ β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘|___/
+ Come for the sewing patterns
+ Stay for the community
+
+
+ */
+
diff --git a/packages/new-design/lib/cli.mjs b/packages/new-design/lib/cli.mjs
new file mode 100644
index 00000000000..7b48b730ab3
--- /dev/null
+++ b/packages/new-design/lib/cli.mjs
@@ -0,0 +1,54 @@
+import path from 'path'
+import chalk from 'chalk'
+import commander from 'commander'
+import { banner } from '../../../scripts/banner.mjs'
+import {
+ checkNodeVersion,
+ getChoices,
+ createEnvironment,
+} from './utils.mjs'
+
+
+export const cli = async () => {
+
+ // Make it pretty
+ console.log(banner+"\n")
+
+ // Make sure we have a valid NodeJS version
+ checkNodeVersion()
+
+ // Get user input
+ const choices = await getChoices()
+
+ // Create environment from template
+ const result = createEnvironment(choices)
+}
+
+/*
+
+
+ const dest = await createLibrary(params)
+
+ console.log(`
+π ${strings[params.language]['cfp.patternCreated']} ${chalk.bold(dest)}
+
+${strings[params.language]['cfp.runTheseCommands']}:
+
+ π ${chalk.cyan(`cd ${path.join(params.shortName, 'example')} && ${params.manager} start`)}
+
+${strings[params.language]['cfp.startWebpack']}
+
+${strings[params.language]['cfp.devDocsAvailableAt']}
+ ${chalk.bold('https://freesewing.dev/')}
+
+${strings[params.language]['cfp.talkToUs']}
+ ${chalk.bold('https://discord.freesewing.org/')}
+
+`
+ )
+
+ return dest
+}
+
+
+*/
diff --git a/packages/new-design/lib/config.mjs b/packages/new-design/lib/config.mjs
new file mode 100644
index 00000000000..35637b47f56
--- /dev/null
+++ b/packages/new-design/lib/config.mjs
@@ -0,0 +1,191 @@
+export const config = {
+ node: 14, // Minimum node version
+ repo: 'freesewing/freesewing', // Repository to download from
+ branch: 'develop', // Branch to download from
+ i18n: [
+ 'account',
+ 'common',
+ 'patrons',
+ 'themes',
+ 'workbench',
+ 'errors',
+ 'i18n',
+ 'lab',
+ 'measurements',
+ 'optiongroups',
+ 'o_bella',
+ 'o_bent',
+ 'o_breanna',
+ 'o_brian',
+ 'o_titan',
+ 'parts',
+ 'plugin',
+ 'settings',
+ 'welcome',
+ ],
+ fetch: {
+ sites: [
+ "shared/utils.mjs",
+ "shared/config/freesewing.mjs",
+ "shared/config/i18n.config.mjs",
+ "shared/config/next.mjs",
+ "shared/config/postcss.config.js",
+ "shared/config/tailwind.config.js",
+ "shared/prebuild/contributors.mjs",
+ "shared/hooks/useLocalStorage.js",
+ "shared/hooks/useTheme.js",
+ "shared/styles/code.css",
+ "shared/styles/globals.css",
+ "shared/styles/svg-freesewing-draft.css",
+ "shared/themes/dark.js",
+ "shared/themes/hax0r.js",
+ "shared/themes/index.js",
+ "shared/themes/lgbtq.js",
+ "shared/themes/light.js",
+ "shared/themes/runtime.js",
+ "shared/themes/trans.js",
+ "shared/components/copy-to-clipboard.js",
+ "shared/components/json.js",
+ "shared/components/lightbox.js",
+ "shared/components/locale-picker.js",
+ "shared/components/modal.js",
+ "shared/components/page-link.js",
+ "shared/components/pinked-ribbon.js",
+ "shared/components/popout.js",
+ "shared/components/theme-picker.js",
+ "shared/components/web-link.js",
+ "shared/components/yaml.js",
+ "shared/components/layouts/default.js",
+ "shared/components/icons/box.js",
+ "shared/components/icons/camera.js",
+ "shared/components/icons/clear.js",
+ "shared/components/icons/close.js",
+ "shared/components/icons/cog.js",
+ "shared/components/icons/community.js",
+ "shared/components/icons/copy.js",
+ "shared/components/icons/design.js",
+ "shared/components/icons/discord.js",
+ "shared/components/icons/docs.js",
+ "shared/components/icons/down.js",
+ "shared/components/icons/edit.js",
+ "shared/components/icons/export.js",
+ "shared/components/icons/facebook.js",
+ "shared/components/icons/filter.js",
+ "shared/components/icons/freesewing.js",
+ "shared/components/icons/github.js",
+ "shared/components/icons/google.js",
+ "shared/components/icons/guide.js",
+ "shared/components/icons/heart.js",
+ "shared/components/icons/help.js",
+ "shared/components/icons/i18n.js",
+ "shared/components/icons/instagram.js",
+ "shared/components/icons/left.js",
+ "shared/components/icons/menu.js",
+ "shared/components/icons/note.js",
+ "shared/components/icons/options.js",
+ "shared/components/icons/page-size.js",
+ "shared/components/icons/page.js",
+ "shared/components/icons/print.js",
+ "shared/components/icons/reddit.js",
+ "shared/components/icons/right.js",
+ "shared/components/icons/rss.js",
+ "shared/components/icons/search.js",
+ "shared/components/icons/settings.js",
+ "shared/components/icons/theme.js",
+ "shared/components/icons/tip.js",
+ "shared/components/icons/tutorial.js",
+ "shared/components/icons/twitter.js",
+ "shared/components/icons/user.js",
+ "shared/components/icons/versions.js",
+ "shared/components/icons/with-breasts.js",
+ "shared/components/icons/without-breasts.js",
+ "shared/components/icons/xray.js",
+ "shared/components/logos/cc-by.js",
+ "shared/components/logos/cc.js",
+ "shared/components/logos/freesewing.js",
+ "shared/components/logos/osi.js",
+ "shared/components/navigation/aside.js",
+ "shared/components/navigation/primary.js",
+ "shared/components/robot/index.js",
+ "shared/components/robot/poses.js",
+ "shared/components/wrappers/img.js",
+ "shared/components/wrappers/mdx.js",
+ "shared/components/wrappers/page.js",
+ "shared/components/wrappers/toc.js",
+ "shared/components/wrappers/workbench.js",
+ "shared/components/mdx/index.js",
+ "shared/components/workbench/default-settings.js",
+ "shared/components/workbench/events.js",
+ "shared/components/workbench/export.js",
+ "shared/components/workbench/json.js",
+ "shared/components/workbench/preload.js",
+ "shared/components/workbench/sample.js",
+ "shared/components/workbench/yaml.js",
+ "shared/components/workbench/inputs/design-option-count.js",
+ "shared/components/workbench/inputs/design-option-list.js",
+ "shared/components/workbench/inputs/design-option-pct-deg.js",
+ "shared/components/workbench/inputs/measurement.js",
+ "shared/components/workbench/measurements/index.js",
+ "shared/components/workbench/measurements/non-human.js",
+ "shared/components/workbench/draft/error.js",
+ "shared/components/workbench/draft/index.js",
+ "shared/components/workbench/draft/svg-wrapper.js",
+ "shared/components/workbench/draft/utils.js",
+ "shared/components/workbench/draft/circle/index.js",
+ "shared/components/workbench/draft/defs/index.js",
+ "shared/components/workbench/draft/part/index.js",
+ "shared/components/workbench/draft/path/index.js",
+ "shared/components/workbench/draft/point/index.js",
+ "shared/components/workbench/draft/snippet/index.js",
+ "shared/components/workbench/draft/svg/index.js",
+ "shared/components/workbench/draft/text/index.js",
+ "shared/components/workbench/draft/text-on-path/index.js",
+ "shared/components/workbench/layout/draft.js",
+ "shared/components/workbench/layout/cut/index.js",
+ "shared/components/workbench/layout/cut/settings.js",
+ "shared/components/workbench/layout/print/index.js",
+ "shared/components/workbench/layout/print/orientation-picker.js",
+ "shared/components/workbench/layout/print/pagesize-picker.js",
+ "shared/components/workbench/layout/print/plugin.js",
+ "shared/components/workbench/layout/print/settings.js",
+ "shared/components/workbench/menu/index.js",
+ "shared/components/workbench/menu/view.js",
+ "shared/components/workbench/menu/core-settings/core-setting-bool.js",
+ "shared/components/workbench/menu/core-settings/core-setting-list.js",
+ "shared/components/workbench/menu/core-settings/core-setting-mm.js",
+ "shared/components/workbench/menu/core-settings/core-setting-nr.js",
+ "shared/components/workbench/menu/core-settings/core-setting-only.js",
+ "shared/components/workbench/menu/core-settings/core-setting-sa-bool.js",
+ "shared/components/workbench/menu/core-settings/core-setting-sa-mm.js",
+ "shared/components/workbench/menu/core-settings/index.js",
+ "shared/components/workbench/menu/core-settings/setting.js",
+ "shared/components/workbench/menu/design-options/index.js",
+ "shared/components/workbench/menu/design-options/option-group.js",
+ "shared/components/workbench/menu/design-options/option-input.js",
+ "shared/components/workbench/menu/design-options/option-value.js",
+ "shared/components/workbench/menu/design-options/option.js",
+ "shared/components/workbench/menu/test-design-options/index.js",
+ "shared/components/workbench/menu/test-design-options/option-group.js",
+ "shared/components/workbench/menu/test-design-options/option-sub-group.js",
+ "shared/components/workbench/menu/test-design-options/option.js",
+ "shared/components/workbench/menu/xray/attributes.js",
+ "shared/components/workbench/menu/xray/disable.js",
+ "shared/components/workbench/menu/xray/index.js",
+ "shared/components/workbench/menu/xray/list.js",
+ "shared/components/workbench/menu/xray/log.js",
+ "shared/components/workbench/menu/xray/path-ops.js",
+ "shared/components/workbench/menu/xray/path.js",
+ "shared/components/workbench/menu/xray/point.js",
+ "shared/components/workbench/menu/xray/reset.js",
+ "lab/components/footer.js",
+ "lab/components/wrappers/layout.js",
+ "lab/components/wrappers/page.js"
+ ],
+ config: [
+ {
+ from: 'measurements.mjs',
+ to: 'shared/config/measurements.mjs'
+ }
+ ],
+ }
+}
diff --git a/packages/new-design/lib/download-list.js b/packages/new-design/lib/download-list.js
new file mode 100644
index 00000000000..c3cab9a77b0
--- /dev/null
+++ b/packages/new-design/lib/download-list.js
@@ -0,0 +1,43 @@
+import rdir from 'recursive-readdir'
+import { join, basename } from 'path'
+
+// Files to not download
+const avoid = {
+ files: [
+ 'README.md',
+ 'package.json',
+ 'CHANGELOG.md',
+ 'version-pickers.js',
+ 'pattern-picker.js',
+ 'header.js',
+ ],
+ dirs: [ 'node_modules', 'layouts' ]
+}
+
+
+// Method to check what files to keep
+const keep = file => {
+ if (avoid.files.indexOf(basename(file)) !== -1) return false
+ for (const dir of avoid.dirs) {
+ if (file.indexOf(dir) !== -1) return false
+ }
+
+ return true
+}
+
+const getFiles = async (site) => {
+ const all = await rdir(join('..', '..', 'sites', site))
+
+ return all
+ .filter(file => keep(file))
+ .map(file => file.slice(12))
+}
+
+getFiles('shared').then(shared => {
+ getFiles(join('lab', 'components')).then(lab => {
+ console.log(JSON.stringify([
+ ...shared,
+ ...lab,
+ ], null, 2))
+ })
+})
diff --git a/packages/new-design/lib/utils.mjs b/packages/new-design/lib/utils.mjs
new file mode 100644
index 00000000000..3d90eec9162
--- /dev/null
+++ b/packages/new-design/lib/utils.mjs
@@ -0,0 +1,259 @@
+import { config } from './config.mjs'
+import { mkdir, readFile, writeFile, copyFile } from 'node:fs/promises'
+import { join, resolve, dirname, extname } from 'path'
+import mustache from 'mustache'
+import rdir from 'recursive-readdir'
+import chalk from 'chalk'
+import prompts from 'prompts'
+import {oraPromise} from 'ora'
+import { execa } from 'execa'
+import axios from 'axios'
+
+const nl = "\n"
+const tab = " "
+const nlt = nl+tab
+
+// Checks for node 14 or higher
+export const checkNodeVersion = () => {
+ const node_version = process.version.slice(1).split('.')[0]
+ if (parseInt(node_version) < config.node) {
+ console.log(
+ chalk.yellow(nlt+`β οΈ FreeSewing requires Node v${config.node} or newer`) +
+ nl+nlt+'We hightly recommend using NVM to manage your Node versions:' +
+ nlt+chalk.blue('https://github.com/nvm-sh/nvm') +
+ nl+nlt+'When in doubt, pick an active LTS version:' +
+ nlt+chalk.blue('https://nodejs.org/en/about/releases/')+nl+nl
+ )
+ process.exit(1)
+ }
+}
+
+// Helper method to validate the design name
+const validateDesignName = (name) => {
+ if (/^([a-z]+)$/.test(name)) return true
+ else return ' π Please use only [a-z], no spaces, no capitals, no nothing π€·'
+}
+
+// Gets user input to figure out what to do
+export const getChoices = async () => {
+
+ const { template } = await prompts({
+ type: 'select',
+ name: 'template',
+ message: 'What template would you like to use? π',
+ choices: [
+ { title: 'From Scratch', value: 'scratch', description: 'Create a design from scratch' },
+ { title: 'Extend Brian', value: 'brian', description: "Extend the Brian design (basic torso block for menswear)" },
+ { title: 'Extend Bent', value: 'bent', description: "Extend the Bent design (like brian with added two-part sleeve)" },
+ { title: 'Extend Bella', value: 'bella', description: "Extend the Bella design (womenswear torso block)" },
+ { title: 'Extend Breanna', value: 'breanna', description: "Extend the Breanna design (womenswear torso block - YMMV)" },
+ { title: 'Extend Titan', value: 'titan', description: "Extend the Titan design (gender-neutral trouser block)" },
+ ],
+ initial: 0,
+ })
+
+ const { name } = await prompts({
+ type: 'text',
+ name: 'name',
+ message: 'What name would you like the design to have? π·οΈ ([a-z] only)',
+ validate: validateDesignName,
+ })
+
+ const { manager } = await prompts({
+ type: 'select',
+ name: 'manager',
+ message: 'Last but not least, what package manager do you use? π¦',
+ choices: [
+ { title: 'yarn', value: 'yarn', description: 'Yarn - Nice if you have it' },
+ { title: 'npm', value: 'npm', description: "NPM - Comes with NodeJS" },
+ ],
+ initial: 0,
+ })
+
+ return { template, name, manager }
+}
+
+// Keep track of directories that need to be created
+const dirs = {}
+const ensureDir = async (file, suppress=false) => {
+ const dir = suppress
+ ? dirname(file.replace(suppress))
+ : dirname(file)
+ if (!dirs[dir]) {
+ await mkdir(dir, { recursive: true })
+ dirs[dir] = true
+ }
+}
+
+// Helper method to copy template files
+const copyTemplate = async (config, choices) => {
+
+ // Copy files in parallel rather than using await
+ const promises = []
+
+ // Copy shared files
+ for (const from of config.files.shared) {
+ const to = config.dest + from.slice(config.source.shared.length)
+ if (!dirs[to]) await ensureDir(to)
+ console.log(to)
+ promises.push(copyFile(from, to))
+ }
+
+ // Template files
+ for (const from of config.files.template) {
+ const to = config.dest + from.slice(config.source.template.length)
+ if (!dirs[to]) await ensureDir(to)
+ if (extname(from) === '.json') {
+ // Template out package.json
+ const src = await readFile(from, 'utf-8')
+ promises.push(
+ writeFile(to, mustache.render(src, { name: choices.name }))
+ )
+ } else {
+ // Just copy the file
+ promises.push(copyFile(from, to))
+ }
+ }
+
+ await Promise.all(promises)
+
+ return
+}
+
+// Helper method to run [yarn|npm] install
+const installDependencies = async (config, choices) => await execa(
+ `${choices.manager} install`,
+ {
+ cwd: config.dest,
+ shell: true
+ }
+)
+
+// Helper method to download web environment
+const downloadLabFiles = async (config) => {
+ const base = 'https://raw.githubusercontent.com'
+ const promises = []
+ for (const dir in config.fetch) {
+ for (const file of config.fetch[dir]) {
+ const to = (typeof file === 'string')
+ ? join(config.dest, file)
+ : join(config.dest, file.to)
+ if (!dirs[to]) await ensureDir(to)
+ console.log(to)
+ promises.push(
+ axios.get(`${base}/${config.repo}/${config.branch}/${dir}/${typeof file === 'string' ? file : file.from}`)
+ .catch(err => console.log(err))
+ .then(res => promises.push(writeFile(to, res.data)))
+ )
+ }
+ }
+
+ await Promise.all(promises)
+
+ return
+}
+
+// Creates the environment based on the user's choices
+export const createEnvironment = async (choices) => {
+
+ // Store directories for re-use
+ config.cwd = process.cwd()
+ config.source = {
+ root: dirname(process.argv[1]),
+ template: dirname(process.argv[1]) + `/templates/from-${choices.template}`,
+ shared: dirname(process.argv[1]) + `/shared`
+ }
+ config.dest = join(config.cwd, choices.name)
+
+ // Create target directory
+ await mkdir(config.dest, { recursive: true })
+
+ // Find files
+ config.files = {
+ template: await rdir(config.source.template),
+ shared: await rdir(config.source.shared),
+ }
+
+ // Copy/Template files
+ await copyTemplate(config, choices)
+
+ // Install dependencies
+ await oraPromise(
+ installDependencies(config, choices),
+ chalk.white.bold('Installing dependencies')+chalk.white.dim(' (This will take a while)')
+ )
+
+ // Fetch web components
+ await oraPromise(
+ downloadLabFiles(config),
+ chalk.white.bold('Downloading web components')+chalk.white.dim(' (This too will take a while)')
+ )
+
+}
+
+//const handlebars = require('handlebars')
+//const execa = require('execa')
+//const fs = require('fs')
+//const globby = require('globby')
+//const normalize = require('normalize-path')
+//const mkdirp = require('make-dir')
+//const ora = require('ora')
+//const pEachSeries = require('p-each-series')
+
+//const pkg = require('../package')
+/*
+const templateBlacklist = new Set([path.join('example', 'public', 'favicon.ico')])
+
+module.exports = async (info) => {
+
+
+
+
+
+ if (git) {
+ const promise = module.exports.initGitRepo({ dest })
+ ora.promise(promise, 'Initializing git repo')
+ await promise
+ }
+
+
+ return dest
+}
+
+module.exports.initGitRepo = async (opts) => {
+ const { dest } = opts
+
+ const gitIgnorePath = path.join(dest, '.gitignore')
+ fs.writeFileSync(
+ gitIgnorePath,
+ `
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+node_modules
+
+# builds
+build
+dist
+.rpt2_cache
+
+# misc
+.DS_Store
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+`,
+ 'utf8'
+ )
+
+ const cmd = `git init && git add . && git commit -m ":tada: Initialized ${pkg.name}@${pkg.version} with create-freesewing-pattern"`
+ return execa.sync(cmd, { cwd: dest, shell: true })
+}
+
+*/
diff --git a/packages/new-design/package.json b/packages/new-design/package.json
new file mode 100644
index 00000000000..f06ca245e09
--- /dev/null
+++ b/packages/new-design/package.json
@@ -0,0 +1,57 @@
+{
+ "name": "@freesewing/new-design",
+ "version": "2.21.0-rc.0",
+ "description": "Initializer package for a new FreeSewing design: npx @freesewing/new-design",
+ "author": "Joost De Cock (https://github.com/joostdecock)",
+ "homepage": "https://freesewing.org/",
+ "repository": "github:freesewing/freesewing",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/freesewing/freesewing/issues"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://freesewing.org/patrons/join"
+ },
+ "keywords": [
+ "freesewing",
+ "freesewing"
+ ],
+ "main": "dist/index.js",
+ "module": "dist/index.mjs",
+ "scripts": {
+ "build": "SITE=new-design/shared node ../../sites/shared/prebuild/i18n-only.mjs",
+ "clean": "rimraf dist",
+ "mbuild": "NO_MINIFY=1 node build.js",
+ "symlink": "mkdir -p ./node_modules/@freesewing && cd ./node_modules/@freesewing && ln -s -f ../../../* . && cd -",
+ "test": "echo \"new-design: No tests configured. Perhaps you'd like to do this?\" && exit 0",
+ "vbuild": "VERBOSE=1 node build.js",
+ "lab": "cd ../../sites/lab && yarn start",
+ "tips": "node ../../scripts/help.mjs",
+ "cibuild_step6": "node build.js"
+ },
+ "peerDependencies": {},
+ "dependencies": {
+ "axios": "^0.27.2",
+ "chalk": "^5.0.1",
+ "execa": "^6.1.0",
+ "mustache": "^4.2.0",
+ "ora": "^6.1.0",
+ "prompts": "^2.4.2",
+ "recursive-readdir": "^2.2.2"
+ },
+ "devDependencies": {},
+ "files": [
+ "dist/*",
+ "README.md",
+ "package.json"
+ ],
+ "publishConfig": {
+ "access": "public",
+ "tag": "next"
+ },
+ "engines": {
+ "node": ">=14.0.0",
+ "npm": ">=6"
+ }
+}
diff --git a/packages/new-design/shared/lab/components/header.js b/packages/new-design/shared/lab/components/header.js
new file mode 100644
index 00000000000..9730bd9b8b9
--- /dev/null
+++ b/packages/new-design/shared/lab/components/header.js
@@ -0,0 +1,100 @@
+import { useState, useEffect } from 'react'
+import Logo from 'shared/components/logos/freesewing.js'
+import Link from 'next/link'
+import ThemePicker from 'shared/components/theme-picker.js'
+import LocalePicker from 'shared/components/locale-picker.js'
+import CloseIcon from 'shared/components/icons/close.js'
+import MenuIcon from 'shared/components/icons/menu.js'
+import DocsIcon from 'shared/components/icons/docs.js'
+import HelpIcon from 'shared/components/icons/help.js'
+import { useTranslation } from 'next-i18next'
+
+const Right = props => (
+
+
+
+)
+const Left = props => (
+
+
+
+)
+
+const Header = ({ app }) => {
+ const { t } = useTranslation(['common'])
+
+ const [prevScrollPos, setPrevScrollPos] = useState(0)
+ const [show, setShow] = useState(true)
+
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ const handleScroll = () => {
+ const curScrollPos = (typeof window !== 'undefined') ? window.pageYOffset : 0
+ if (curScrollPos >= prevScrollPos) {
+ if (show && curScrollPos > 20) setShow(false)
+ }
+ else setShow(true)
+ setPrevScrollPos(curScrollPos)
+ }
+ window.addEventListener('scroll', handleScroll)
+ return () => window.removeEventListener('scroll', handleScroll)
+ }
+ }, [prevScrollPos, show])
+
+
+ return (
+
+
+
+
+ {app.primaryMenu
+ ? <> swipe >
+ : <> swipe >
+ }
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Header
diff --git a/packages/new-design/shared/lab/components/layouts/bare.js b/packages/new-design/shared/lab/components/layouts/bare.js
new file mode 100644
index 00000000000..f7a390a90eb
--- /dev/null
+++ b/packages/new-design/shared/lab/components/layouts/bare.js
@@ -0,0 +1,69 @@
+import React from 'react'
+import { useRouter } from 'next/router'
+import Link from 'next/link'
+// Shared components
+import Logo from 'shared/components/logos/freesewing.js'
+import Aside from 'shared/components/navigation/aside'
+import get from 'lodash.get'
+import { BeforeNav } from './lab'
+
+const PageTitle = ({ app, slug, title }) => {
+ if (title) return {title}
+ if (slug) return {get(app.navigation, slug.split('/')).__title}
+
+ return FIXME: This page has no title
+}
+
+const Breadcrumbs = ({ app, slug=false, title }) => {
+ if (!slug) return null
+ const crumbs = []
+ const chunks = slug.split('/')
+ for (const i in chunks) {
+ const j = parseInt(i)+parseInt(1)
+ const page = get(app.navigation, chunks.slice(0,j))
+ if (page) crumbs.push([page.__linktitle, '/'+chunks.slice(0,j).join('/'), (j < chunks.length)])
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ {crumbs.map(crumb => (
+
+ »
+
+ {crumb[2]
+ ? (
+
+
+ {crumb[0]}
+
+
+ )
+ : {crumb[0]}
+ }
+
+
+ ))}
+
+ )
+}
+
+const DefaultLayout = ({ app, title=false, children=[] }) => {
+ const router = useRouter()
+ const slug = router.asPath.slice(1)
+
+ return (
+ <>
+ } mobileOnly />
+ {children}
+ >
+ )
+}
+
+export default DefaultLayout
diff --git a/packages/new-design/shared/lab/components/layouts/docs.js b/packages/new-design/shared/lab/components/layouts/docs.js
new file mode 100644
index 00000000000..3c239c4d118
--- /dev/null
+++ b/packages/new-design/shared/lab/components/layouts/docs.js
@@ -0,0 +1,14 @@
+import React from 'react'
+import { useRouter } from 'next/router'
+import Link from 'next/link'
+import Aside from 'shared/components/navigation/aside'
+
+const DocsLayout = ({ app, title=false, children=[] }) => (
+
+ {title &&
{title} }
+
+ {children}
+
+)
+
+export default DocsLayout
diff --git a/packages/new-design/shared/lab/components/layouts/lab.js b/packages/new-design/shared/lab/components/layouts/lab.js
new file mode 100644
index 00000000000..e269676042c
--- /dev/null
+++ b/packages/new-design/shared/lab/components/layouts/lab.js
@@ -0,0 +1,47 @@
+import ThemePicker from 'shared/components/theme-picker.js'
+import LocalePicker from 'shared/components/locale-picker.js'
+
+export const BeforeNav = ({ app }) => (
+ <>
+
+
+
+
+
+
+ >
+)
+
+const LabLayout = ({ app, AltMenu, children=[] }) => (
+
+)
+
+export default LabLayout
diff --git a/packages/new-design/shared/lab/components/mdx/dot.js b/packages/new-design/shared/lab/components/mdx/dot.js
new file mode 100644
index 00000000000..944782a8f28
--- /dev/null
+++ b/packages/new-design/shared/lab/components/mdx/dot.js
@@ -0,0 +1,5 @@
+// Noop placeholder
+const Noop = props => null
+
+export default Noop
+
diff --git a/packages/new-design/shared/lab/components/search.js b/packages/new-design/shared/lab/components/search.js
new file mode 100644
index 00000000000..e5b204c70b1
--- /dev/null
+++ b/packages/new-design/shared/lab/components/search.js
@@ -0,0 +1,4 @@
+// Noop placeholder
+const Noop = props => null
+
+export default Noop
diff --git a/packages/new-design/shared/lab/hooks/useApp.js b/packages/new-design/shared/lab/hooks/useApp.js
new file mode 100644
index 00000000000..d46f09c9c04
--- /dev/null
+++ b/packages/new-design/shared/lab/hooks/useApp.js
@@ -0,0 +1,58 @@
+import { useState } from 'react'
+// Stores state in local storage
+import useLocalStorage from 'shared/hooks/useLocalStorage.js'
+// Locale and translation
+import { useRouter } from 'next/router'
+import { useTranslation } from 'next-i18next'
+import { capitalize } from 'shared/utils.mjs'
+import useTheme from 'shared/hooks/useTheme'
+
+function useApp(full = true) {
+
+ // Load translation method
+ const locale = useRouter().locale
+ const { t } = useTranslation(['app'])
+
+ // Persistent state
+ const [account, setAccount] = useLocalStorage('account', { username: false })
+ const [theme, setTheme] = useTheme();
+
+ // React State
+ const [primaryMenu, setPrimaryMenu] = useState(false)
+ const [navigation, setNavigation] = useState({})
+ const [slug, setSlug] = useState('/')
+ const [loading, setLoading] = useState(false)
+
+ // State methods
+ const togglePrimaryMenu = () => setPrimaryMenu(!primaryMenu)
+
+ return {
+ // Static vars
+ site: 'lab',
+
+ // i18n
+ locale,
+
+ // State
+ loading,
+ navigation,
+ primaryMenu,
+ slug,
+ theme,
+
+ // State setters
+ setLoading,
+ setNavigation,
+ setPrimaryMenu,
+ setSlug,
+ setTheme,
+ startLoading: () => { setLoading(true); setPrimaryMenu(false) }, // Always close menu when navigating
+ stopLoading: () => setLoading(false),
+
+ // State handlers
+ togglePrimaryMenu,
+ }
+}
+
+export default useApp
+
diff --git a/packages/new-design/shared/next-i18next.config.js b/packages/new-design/shared/next-i18next.config.js
new file mode 100644
index 00000000000..ab2c71ab36b
--- /dev/null
+++ b/packages/new-design/shared/next-i18next.config.js
@@ -0,0 +1,13 @@
+// See: https://github.com/isaachinman/next-i18next
+module.exports = {
+ i18n: {
+ defaultLocale: 'en',
+ locales: ['en', 'de', 'es', 'fr', 'nl'],
+ defaultNS: 'common',
+ },
+ interpolation: {
+ prefix: '{',
+ suffix: '}',
+ },
+ localeStructure: '{lng}/{ns}',
+}
diff --git a/packages/new-design/shared/next.config.mjs b/packages/new-design/shared/next.config.mjs
new file mode 100644
index 00000000000..aab57d6399d
--- /dev/null
+++ b/packages/new-design/shared/next.config.mjs
@@ -0,0 +1,26 @@
+import path from 'path'
+import i18nConfig from './next-i18next.config.js'
+
+const config = {
+ experimental: {
+ externalDir: true,
+ },
+ i18n: i18nConfig.i18n,
+ pageExtensions: [ 'js', 'mjs' ],
+ webpack: (config, options) => {
+
+ // Suppress warnings about importing version from package.json
+ // We'll deal with it in v3 of FreeSewing
+ config.ignoreWarnings = [
+ /only default export is available soon/
+ ]
+
+ // Aliases
+ config.resolve.alias.shared = path.resolve('./shared/')
+ config.resolve.alias.site = path.resolve('./lab/')
+ config.resolve.alias.design = path.resolve('./')
+
+ return config
+ }
+}
+export default config
diff --git a/packages/new-design/shared/pages/_app.js b/packages/new-design/shared/pages/_app.js
new file mode 100644
index 00000000000..69d2eceefc5
--- /dev/null
+++ b/packages/new-design/shared/pages/_app.js
@@ -0,0 +1,6 @@
+import 'shared/styles/globals.css'
+import { appWithTranslation } from 'next-i18next'
+
+const FreeSewingLab = ({ Component, pageProps }) =>
+
+export default appWithTranslation(FreeSewingLab)
diff --git a/packages/new-design/shared/pages/design.js b/packages/new-design/shared/pages/design.js
new file mode 100644
index 00000000000..df9b518bd28
--- /dev/null
+++ b/packages/new-design/shared/pages/design.js
@@ -0,0 +1,27 @@
+import design from 'design/src/index.js'
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
+
+import Page from 'site/components/wrappers/page.js'
+import useApp from 'site/hooks/useApp.js'
+import WorkbenchWrapper from 'shared/components/wrappers/workbench.js'
+import Layout from 'site/components/layouts/lab'
+
+const WorkbenchPage = props => {
+ const app = useApp()
+
+ return (
+
+
+
+ )
+}
+
+export default WorkbenchPage
+
+export async function getStaticProps({ locale }) {
+ return {
+ props: {
+ ...(await serverSideTranslations(locale)),
+ }
+ }
+}
diff --git a/packages/new-design/shared/pages/index.js b/packages/new-design/shared/pages/index.js
new file mode 100644
index 00000000000..688eea976ee
--- /dev/null
+++ b/packages/new-design/shared/pages/index.js
@@ -0,0 +1,112 @@
+import Page from 'site/components/wrappers/page.js'
+import useApp from 'site/hooks/useApp.js'
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
+import Layout from 'site/components/layouts/docs'
+import { useTranslation } from 'next-i18next'
+import ConfigIcon from 'shared/components/icons/settings'
+import CogIcon from 'shared/components/icons/cog'
+import HeartIcon from 'shared/components/icons/heart'
+import FsIcon from 'shared/components/icons/freesewing'
+import Link from 'next/link'
+import Popout from 'shared/components/popout'
+import LocaleIcon from 'shared/components/icons/i18n.js'
+import { useRouter } from 'next/router'
+import themes from 'shared/themes/index.js'
+
+const gh = `freesewing/freesewing `
+const fsd = `freesewing.dev `
+
+const translations = {
+ sade: {
+ en: `Stand-alone development environment`,
+ nl: `Vrijstaande ontwikkeling omgeving`,
+ },
+ load: {
+ en: `To your design`,
+ nl: `Naar jouw ontwerp`,
+ },
+ tips: {
+ en: Edit the files in the design folder, and we'll auto-update your design ,
+ nl: Bewerk de bestanden in de design map, en we passen je ontwerp automatisch aan ,
+ },
+}
+
+const HomePage = (props) => {
+ const app = useApp()
+ const router = useRouter()
+ const { t } = useTranslation(['common', 'patrons', 'locales', 'themes'])
+
+ return (
+
+
+
+
+ {Object.keys(themes).map(theme => (
+ app.setTheme(theme)}
+ className="btn btn-ghost hover:bg-base-200"
+ >
+
+ {t(`themes:${theme}Theme`)}
+
+
+ ))}
+
+
+
+
+
{t('patrons:supportFreesewing')}
+
+
+
+
+ )
+}
+
+export default HomePage
+
+export async function getStaticProps({ locale }) {
+ return {
+ props: {
+ ...(await serverSideTranslations(locale)),
+ }
+ }
+}
+
+
+
diff --git a/packages/new-design/shared/pages/support.js b/packages/new-design/shared/pages/support.js
new file mode 100644
index 00000000000..160fe7e85c7
--- /dev/null
+++ b/packages/new-design/shared/pages/support.js
@@ -0,0 +1,139 @@
+import Page from 'site/components/wrappers/page.js'
+import useApp from 'site/hooks/useApp.js'
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
+import Layout from 'site/components/layouts/docs'
+import { useTranslation } from 'next-i18next'
+import DiscordIcon from 'shared/components/icons/discord'
+import Popout from 'shared/components/popout'
+import GithubIcon from 'shared/components/icons/github'
+import CcIcon from 'shared/components/icons/community'
+import HeartIcon from 'shared/components/icons/heart'
+import DocsIcon from 'shared/components/icons/docs'
+
+const gh = `freesewing/freesewing `
+const fsd = `freesewing.dev `
+
+const translations = {
+ discord: {
+ en: `Our Discord server on is the best place to ask questions, or hang
+ out with other members of the FreeSewing community.`,
+ nl: `Onze Discord server is de best plek om vragen te stellen, hulp te ontvangen,
+ of gewoon een leuke tijd te spenderen met andere leden van de FreeSewing gemeenschap.`,
+ },
+ github: {
+ en: `For bug reports, please create an issue in the ${gh} repository on Github.
+ This is also where you'll find all our source code.`,
+ nl: `Een bug gevonden? Maak dan een issue aan in de ${gh} repository op Github.
+ Dit is ook waar je al de FreeSewing broncode kan vinden.`,
+ },
+ cc: {
+ en: `Every two weeks, there's the FreeSewing contributor call, which is when we discuss
+ ongoing issues, future plans, and news big and small about FreeSewing and its community.`,
+ nl: `Elke twee weken is er de FreeSewing contributor call (Engelstalig), waar de FreeSewing
+ vrijwilligers de lopende zaken bespreken. Ook de plannen voor de toekomst en groot en klein
+ nieuws over FreeSewing en de gemeenschap komen aan bod.`
+ },
+ cc: {
+ en: `Every two weeks, there's the FreeSewing contributor call, which is when we discuss
+ ongoing issues, future plans, and news big and small about FreeSewing and its community.`,
+ nl: `Elke twee weken is er de FreeSewing contributor call (Engelstalig), waar de FreeSewing
+ vrijwilligers de lopende zaken bespreken. Ook de plannen voor de toekomst en groot en klein
+ nieuws over FreeSewing en de gemeenschap komen aan bod.`
+ },
+ docs: {
+ en: `Our documentation for developers hosted on ${fsd}. You can find guides and how-to's
+ there, as well as reference documantation for FreeSewing's core API.
+
+ We stive to provide excellent documentation. So if something is not clear please, let us know.`,
+ nl: `Onze documentatie voor ontwikkelaars is beschikbaar op ${fsd}. Je vindt er guides en how-to's,
+ alsook de referentie documentatie voor FreeSewing's core API.
+
+ We streven ernaar om uitstekende documentatie te voorzien. Dus als er iets niet duidelijk is, laat
+ het ons dan zeker weten.`,
+ },
+}
+
+const SupportPage = (props) => {
+ const app = useApp()
+ const { t } = useTranslation(['common', 'patrons'])
+ return (
+
+
+ Discord
+
+
+ Github
+
+
+ {t('docs')}
+
+
+ Contributor Calls
+
+
+
+
+
{t('patrons:supportFreesewing')}
+
+
+
+
+ )
+}
+
+export default SupportPage
+
+export async function getStaticProps({ locale }) {
+ return {
+ props: {
+ ...(await serverSideTranslations(locale)),
+ }
+ }
+}
+
+
+
diff --git a/packages/new-design/shared/postcss.config.js b/packages/new-design/shared/postcss.config.js
new file mode 100644
index 00000000000..c0842acaa4b
--- /dev/null
+++ b/packages/new-design/shared/postcss.config.js
@@ -0,0 +1,5 @@
+// Can't seem to make this work as ESM
+const config = require('./shared/config/postcss.config.js')
+
+module.exports = config
+
diff --git a/packages/new-design/shared/public/locales/de/.gitkeep b/packages/new-design/shared/public/locales/de/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/new-design/shared/public/locales/en/.gitkeep b/packages/new-design/shared/public/locales/en/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/new-design/shared/public/locales/es/.gitkeep b/packages/new-design/shared/public/locales/es/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/new-design/shared/public/locales/fr/.gitkeep b/packages/new-design/shared/public/locales/fr/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/new-design/shared/public/locales/nl/.gitkeep b/packages/new-design/shared/public/locales/nl/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/new-design/shared/shared/components/mdx/highlight.js b/packages/new-design/shared/shared/components/mdx/highlight.js
new file mode 100644
index 00000000000..6966eaffc99
--- /dev/null
+++ b/packages/new-design/shared/shared/components/mdx/highlight.js
@@ -0,0 +1,40 @@
+import CopyToClipboard from 'shared/components/copy-to-clipboard'
+
+const names = {
+ json: 'JSON',
+ yaml: 'YAML',
+}
+
+const Highlight = (props) => {
+
+ let language = 'txt'
+ if (props.language) language = props.language
+ if (props.children?.props?.className) {
+ language = props.children.props.className.split('-').pop()
+ }
+ const preProps = {
+ className: `language-${language} hljs text-base lg:text-lg whitespace-pre overflow-scroll pr-4`
+ }
+ if (props.raw) preProps.dangerouslySetInnerHTML = { __html: props.raw }
+
+ return (
+
+
+
+ {names[language] ? names[language] : language}
+
+
+
+ {props.children}
+
+
+ )
+}
+
+export default Highlight
+
diff --git a/packages/new-design/shared/src/box.js b/packages/new-design/shared/src/box.js
new file mode 100644
index 00000000000..897d18e7032
--- /dev/null
+++ b/packages/new-design/shared/src/box.js
@@ -0,0 +1,63 @@
+export default function (part) {
+ const {
+ Point, points,
+ Path, paths,
+ Snippet, snippets,
+ complete,
+ options,
+ sa,
+ paperless,
+ macro
+ } = part.shorthand()
+
+ // Add points to make a box
+ const w = 500 * options.size
+ points.topLeft = new Point(0, 0)
+ points.topRight = new Point(w, 0)
+ points.bottomLeft = new Point(0, w / 2)
+ points.bottomRight = new Point(w, w / 2)
+
+ // Create a path for the box outline
+ paths.seam = new Path()
+ .move(points.topLeft)
+ .line(points.bottomLeft)
+ .line(points.bottomRight)
+ .line(points.topRight)
+ .line(points.topLeft)
+ .close()
+ .attr('class', 'fabric')
+
+ // Complete?
+ if (complete) {
+ // Add a logo
+ points.logo = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5)
+ snippets.logo = new Snippet('logo', points.logo)
+
+ // Add some text
+ points.text = points.logo
+ .shift(-90, w / 8)
+ .attr('data-text', 'hello')
+ .attr('data-text-class', 'center')
+
+ if (sa) {
+ // Add seam allowance
+ paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa')
+ }
+ }
+
+ // Add dimensions for paperless mode
+ if (paperless) {
+ macro('hd', {
+ from: points.bottomLeft,
+ to: points.bottomRight,
+ y: points.bottomLeft.y + sa + 15,
+ })
+ macro('vd', {
+ from: points.bottomRight,
+ to: points.topRight,
+ x: points.topRight.x + sa + 15,
+ })
+ }
+
+ return part
+}
diff --git a/packages/new-design/shared/tailwind.config.js b/packages/new-design/shared/tailwind.config.js
new file mode 100644
index 00000000000..b9a5ad0304d
--- /dev/null
+++ b/packages/new-design/shared/tailwind.config.js
@@ -0,0 +1,27 @@
+// Handle themes
+const themes = require('./shared/themes/index.js')
+
+module.exports = {
+ content: [
+ './pages/*.js',
+ './pages/**/*.js',
+ './shared/**/*.js',
+ './lab/**/*.js',
+ ],
+ plugins: [require('daisyui'), require('tailwindcss/nesting')],
+ daisyui: {
+ styled: true,
+ themes: [themes],
+ base: true,
+ utils: true,
+ logs: true,
+ rtl: false,
+ },
+ theme: {
+ extend: {
+ aspectRatio: {
+ '9/16': '9 / 16'
+ }
+ }
+ }
+}
diff --git a/packages/new-design/templates/from-scratch/config.js b/packages/new-design/templates/from-scratch/config.js
new file mode 100644
index 00000000000..9a6793c9207
--- /dev/null
+++ b/packages/new-design/templates/from-scratch/config.js
@@ -0,0 +1,18 @@
+import { version } from './package.json'
+
+export default {
+ name: '{{name}}',
+ version,
+ optionGroups: {
+ fit: ['size'],
+ },
+ measurements: [],
+ dependencies: {},
+ inject: {},
+ hide: [],
+ parts: ['box'],
+ options: {
+ size: { pct: 50, min: 10, max: 100 },
+ },
+}
+
diff --git a/packages/new-design/templates/from-scratch/package.json b/packages/new-design/templates/from-scratch/package.json
new file mode 100644
index 00000000000..a85752e8f20
--- /dev/null
+++ b/packages/new-design/templates/from-scratch/package.json
@@ -0,0 +1,93 @@
+{
+ "name": "@freesewing/{{name}}",
+ "version": "0.0.1",
+ "description": "A new FreeSewing design",
+ "author": "Joost De Cock (https://github.com/joostdecock)",
+ "homepage": "https://freesewing.org/",
+ "repository": "github:freesewing/freesewing",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/freesewing/freesewing/issues"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://freesewing.org/patrons/join"
+ },
+ "keywords": [
+ "freesewing",
+ "design",
+ "diy",
+ "fashion",
+ "parametric design",
+ "sewing",
+ "sewing pattern"
+ ],
+ "main": "dist/index.js",
+ "module": "dist/index.mjs",
+ "scripts": {
+ "dev": "node --experimental-json-modules ./node_modules/.bin/next dev -p 8000",
+ "build": "node build.js",
+ "clean": "rimraf dist",
+ "mbuild": "NO_MINIFY=1 node build.js",
+ "test": "BABEL_ENV=production npx mocha tests/*.test.mjs --require @babel/register",
+ "vbuild": "VERBOSE=1 node build.js"
+ },
+ "dependencies": {
+ "@freesewing/core": "latest",
+ "@freesewing/plugin-bundle": "latest",
+ "@freesewing/config-helpers": "next"
+ },
+ "devDependencies": {
+ "@freesewing/plugin-svgattr": "latest",
+ "@freesewing/plugin-theme": "latest",
+ "@freesewing/plugin-i18n": "latest",
+ "@freesewing/utils": "latest",
+ "@freesewing/models": "latest",
+ "js-yaml": "^4.1.0",
+ "file-saver": "^2.0.5",
+ "axios": "^0.27.2",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "react-sizeme": "^3.0.2",
+ "react-zoom-pan-pinch": "^2.1.3",
+ "react-markdown": "^8.0.3",
+ "roughjs": "^4.5.2",
+ "@tailwindcss/typography": "^0.5.2",
+ "d3-dispatch": "^3.0.1",
+ "d3-drag": "^3.0.0",
+ "d3-selection": "^3.0.0",
+ "daisyui": "^2.0.6",
+ "lodash.get": "^4.4.2",
+ "lodash.orderby": "^4.6.0",
+ "lodash.set": "^4.3.2",
+ "lodash.unset": "^4.5.2",
+ "next": "latest",
+ "next-i18next": "^11.0.0",
+ "react-copy-to-clipboard": "^5.0.4",
+ "react-hotkeys-hook": "^3.4.4",
+ "react-swipeable": "^6.2.0",
+ "react-timeago": "^6.2.1",
+ "mocha": "^9.1.1",
+ "chai": "^4.2.0",
+ "autoprefixer": "^10.4.0",
+ "eslint-config-next": "12.1.6",
+ "highlight.js": "^11.5.1",
+ "js-yaml": "^4.1.0",
+ "postcss": "^8.4.14",
+ "tailwindcss": "^3.1.3",
+ "tailwindcss-open-variant": "^1.0.0"
+ },
+ "files": [
+ "dist/*",
+ "README.md",
+ "package.json"
+ ],
+ "publishConfig": {
+ "access": "public",
+ "tag": "next"
+ },
+ "engines": {
+ "node": ">=14.0.0",
+ "npm": ">=6"
+ }
+}
diff --git a/packages/new-design/templates/from-scratch/src/index.js b/packages/new-design/templates/from-scratch/src/index.js
new file mode 100644
index 00000000000..7c29533fb5b
--- /dev/null
+++ b/packages/new-design/templates/from-scratch/src/index.js
@@ -0,0 +1,16 @@
+// Import dependencies
+import freesewing from '@freesewing/core'
+import plugins from '@freesewing/plugin-bundle'
+// Import configuration
+import config from '../config'
+// Import parts
+import draftBox from './box'
+
+// Create the new design
+const Design = new freesewing.Design(config, plugins)
+
+// Attach the draft methods to the prototype
+Design.prototype.draftBox = draftBox
+
+// Export the new Design
+export default Design
diff --git a/sites/lab/components/footer.js b/sites/lab/components/footer.js
index b0e29b8ba84..f366bcc294b 100644
--- a/sites/lab/components/footer.js
+++ b/sites/lab/components/footer.js
@@ -3,10 +3,7 @@ import OsiLogo from 'shared/components/logos/osi.js'
import CreativeCommonsLogo from 'shared/components/logos/cc.js'
import CcByLogo from 'shared/components/logos/cc-by.js'
import { useTranslation } from 'next-i18next'
-import PageLink from 'shared/components/page-link'
-import DocsLink from 'shared/components/docs-link'
import PinkedRibbon from 'shared/components/pinked-ribbon.js'
-import Worm from 'shared/components/worm.js'
import Link from 'next/link'
import DiscordIcon from 'shared/components/icons/discord.js'
diff --git a/sites/lab/components/layouts/bare.js b/sites/lab/components/layouts/bare.js
index e2c0953cbba..f7a390a90eb 100644
--- a/sites/lab/components/layouts/bare.js
+++ b/sites/lab/components/layouts/bare.js
@@ -57,7 +57,7 @@ const Breadcrumbs = ({ app, slug=false, title }) => {
const DefaultLayout = ({ app, title=false, children=[] }) => {
const router = useRouter()
const slug = router.asPath.slice(1)
-console.log(BeforeNav)
+
return (
<>
} mobileOnly />
diff --git a/sites/shared/components/workbench/measurements/index.js b/sites/shared/components/workbench/measurements/index.js
index 244c03fbcaa..71642cc3ebb 100644
--- a/sites/shared/components/workbench/measurements/index.js
+++ b/sites/shared/components/workbench/measurements/index.js
@@ -1,6 +1,6 @@
import React from 'react'
import MeasurementInput from '../inputs/measurement.js'
-import { withBreasts, withoutBreasts } from 'pkgs/models/src/index.js'
+import { withBreasts, withoutBreasts } from '@freesewing/models'
import nonHuman from './non-human.js'
import WithBreastsIcon from 'shared/components/icons/with-breasts.js'
import WithoutBreastsIcon from 'shared/components/icons/without-breasts.js'
diff --git a/sites/shared/prebuild/i18n-only.mjs b/sites/shared/prebuild/i18n-only.mjs
index f971ac65d83..5fefbb65bca 100644
--- a/sites/shared/prebuild/i18n-only.mjs
+++ b/sites/shared/prebuild/i18n-only.mjs
@@ -1,3 +1,7 @@
import { prebuildI18n } from './i18n.mjs'
+import { config as newDesignConfig } from '../../../packages/new-design/lib/config.mjs'
-prebuildI18n(process.env.SITE)
+prebuildI18n(
+ process.env.SITE,
+ process.env.SITE==='new-design/shared' ? newDesignConfig.i18n : false
+)
diff --git a/sites/shared/prebuild/i18n.mjs b/sites/shared/prebuild/i18n.mjs
index c8363c75658..776277be9d6 100644
--- a/sites/shared/prebuild/i18n.mjs
+++ b/sites/shared/prebuild/i18n.mjs
@@ -19,7 +19,7 @@ const writeJson = (site, locale, namespace, content) => fs.writeFileSync(
/*
* Main method that does what needs doing
*/
-export const prebuildI18n = async (site) => {
+export const prebuildI18n = async (site, only=false) => {
// Iterate over locales
for (const locale in locales) {
// Only English for dev site
@@ -28,10 +28,12 @@ export const prebuildI18n = async (site) => {
const loc = locales[locale]
// Fan out into namespaces
for (const namespace in loc) {
- writeJson(
- site, locale, namespace,
- loc[namespace]
- )
+ if (!only || only.indexOf(namespace) !== -1) {
+ writeJson(
+ site, locale, namespace,
+ loc[namespace]
+ )
+ }
}
writeJson(site, locale, 'locales', languages)
}
diff --git a/yarn.lock b/yarn.lock
index af75c6c1c64..482fba537d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6514,6 +6514,14 @@ axios@^0.25.0:
dependencies:
follow-redirects "^1.14.7"
+axios@^0.27.2:
+ version "0.27.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
+ integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
+ dependencies:
+ follow-redirects "^1.14.9"
+ form-data "^4.0.0"
+
axobject-query@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
@@ -7452,6 +7460,15 @@ bl@^4.0.3, bl@^4.1.0:
inherits "^2.0.4"
readable-stream "^3.4.0"
+bl@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-5.0.0.tgz#6928804a41e9da9034868e1c50ca88f21f57aea2"
+ integrity sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==
+ dependencies:
+ buffer "^6.0.3"
+ inherits "^2.0.4"
+ readable-stream "^3.4.0"
+
bluebird@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
@@ -7809,6 +7826,14 @@ buffer@^5.1.0, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0:
base64-js "^1.3.1"
ieee754 "^1.1.13"
+buffer@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+ integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.2.1"
+
build-array@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/build-array/-/build-array-1.0.0.tgz#385e66f6b05c29ff16870c6e9944ccae77f7f100"
@@ -8247,6 +8272,11 @@ chalk@^5.0.0:
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.0.tgz#bd96c6bb8e02b96e08c0c3ee2a9d90e050c7b832"
integrity sha512-/duVOqst+luxCQRKEo4bNxinsOQtMP80ZYm7mMqzuh5PociNL0PvmHFvREJ9ueYL2TxlHjBcmLCdmocx9Vg+IQ==
+chalk@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6"
+ integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==
+
char-regex@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
@@ -8541,7 +8571,7 @@ cli-progress@^3.7.0:
dependencies:
string-width "^4.2.0"
-cli-spinners@^2.5.0:
+cli-spinners@^2.5.0, cli-spinners@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
@@ -12023,7 +12053,7 @@ execa@^5.0.0, execa@^5.1.0, execa@^5.1.1:
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
-execa@^6.0.0:
+execa@^6.0.0, execa@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20"
integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==
@@ -12684,6 +12714,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.7, fol
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
+follow-redirects@^1.14.9:
+ version "1.15.1"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
+ integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
+
font-awesome@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
@@ -13981,7 +14016,7 @@ highlight.js@^10.4.1:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
-highlight.js@^11.4.0:
+highlight.js@^11.4.0, highlight.js@^11.5.1:
version "11.5.1"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.5.1.tgz#027c24e4509e2f4dcd00b4a6dda542ce0a1f7aea"
integrity sha512-LKzHqnxr4CrD2YsNoIf/o5nJ09j4yi/GcH5BnYz9UnVpZdS4ucMgvP61TDty5xJcFGRjnH4DpujkS9bHT3hq0Q==
@@ -14412,7 +14447,7 @@ identity-obj-proxy@^3.0.0:
dependencies:
harmony-reflect "^1.4.6"
-ieee754@^1.1.13, ieee754@^1.1.4:
+ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
@@ -15088,6 +15123,11 @@ is-interactive@^1.0.0:
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+is-interactive@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90"
+ integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==
+
is-ip@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab"
@@ -15385,6 +15425,11 @@ is-unicode-supported@^1.0.0:
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.1.0.tgz#9127b71f9fa82f52ca5c20e982e7bec0ee31ee1e"
integrity sha512-lDcxivp8TJpLG75/DpatAqNzOpDPSpED8XNtrpBHTdQ2InQ1PbW78jhwSxyxhhu+xbVSast2X38bwj8atwoUQA==
+is-unicode-supported@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz#f4f54f34d8ebc84a46b93559a036763b6d3e1014"
+ integrity sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ==
+
is-url-superb@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2"
@@ -17286,6 +17331,14 @@ log-symbols@^1.0.2:
dependencies:
chalk "^1.0.0"
+log-symbols@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93"
+ integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==
+ dependencies:
+ chalk "^5.0.0"
+ is-unicode-supported "^1.1.0"
+
log-update@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708"
@@ -19288,7 +19341,7 @@ multiparty@^4.2.1:
safe-buffer "5.2.1"
uid-safe "2.1.5"
-mustache@^4.0.1:
+mustache@^4.0.1, mustache@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64"
integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
@@ -20509,6 +20562,21 @@ ora@^5.0.0, ora@^5.4.0, ora@^5.4.1:
strip-ansi "^6.0.0"
wcwidth "^1.0.1"
+ora@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-6.1.0.tgz#86aa07058c4e9fb91444412d103b0d7e01aca973"
+ integrity sha512-CxEP6845hLK+NHFWZ+LplGO4zfw4QSfxTlqMfvlJ988GoiUeZDMzCvqsZkFHv69sPICmJH1MDxZoQFOKXerAVw==
+ dependencies:
+ bl "^5.0.0"
+ chalk "^5.0.0"
+ cli-cursor "^4.0.0"
+ cli-spinners "^2.6.1"
+ is-interactive "^2.0.0"
+ is-unicode-supported "^1.1.0"
+ log-symbols "^5.1.0"
+ strip-ansi "^7.0.1"
+ wcwidth "^1.0.1"
+
original@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"