1
0
Fork 0

Merge branch 'freesewing:develop' into develop

This commit is contained in:
Zee 2022-05-16 13:35:00 +02:00 committed by GitHub
commit 99adb6dd17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
134 changed files with 2590 additions and 610 deletions

View file

@ -5,7 +5,7 @@ title: "Tamiko top: Cutting Instructions"
- **Tissu principal**
- Couper **1 Tamiko dessus** sur le pli
Tamiko is a zero-waste pattern. It's a rectangle that's cut out on the fold. Not more than that. There's only one part to this top, we will simply refer to it at the **Tamiko top**. It needs to be cut on the fold along the bottom.
Le Tamiko est un modèle zéro déchets. C'est un rectangle découpé sur la pliure. Rien de plus. C'est un modèle en une seule pièce, que nous appellerons simplement le **Tamiko top**. Elle doit être coupée sur la pliure, le long du bas.
<Note>

View file

@ -2,6 +2,6 @@
title: "Tamiko top: Fabric Options"
- - -
This pattern has a lot of unfinished edges, so you need something that does not ravel. And you want to go for something with a nice drape.
This pattern has a lot of unfinished edges, so you need something that does not ravel. Et vous voulez quelque chose avec un joli drapé.
Long story short, go for a knitted fabric, or some light scuba or something. No wovens.
Bref, choisissez un tissu à mailles, ou un scuba léger, quelque chose dans le genre. Pas de tissu tissé.

View file

@ -2,25 +2,25 @@
title: "Tamiko top: Sewing Instructions"
- - -
### Étape 1 : Terminer la couture des armoiries
### Étape 1 : Terminer l'emmanchure
![Finsh the armhole seam](step03.png)
![Étape : Terminer la couture de la manche](step03.png)
- Finish the armhole seam with a narrow hem.
- Terminez la couture de la manche avec un ourlet étroit.
### Étape 2 : Terminer le haut
![Sew the three seamlines that are marked on your draft](step04.png)
![Coudre les trois lignes de couture qui sont marquées sur votre patron](step04.png)
<Note>
Comme votre dessus est plié, c'est une bonne idée de relier les deux moitiés.
De cette façon, votre top ne peut pas se déplacer pendant que nous le finissons.
Comme votre top est plié, c'est une bonne idée d'épingler les deux moitiés ensemble.
Comme ça, votre top ne va pas bouger pendant que nous le finissons.
</Note>
- Sew the three seamlines that are marked on your draft. F-H, C and I-J in the diagram, represented by lines and notches on the pattern.
- Coudre les trois lignes de couture qui sont marquées sur votre patron. F-H, C et I-J dans le diagramme, représentés par des lignes et des encoches sur le modèle.
### Step 3: Enjoy!
Now enjoy your new top and it's zero waste properties!
Vous pouvez maintenant enfiler votre nouveau top, qui a généré zéro déchets!

View file

@ -1,5 +1,5 @@
- - -
title: "Tamiko top: Required Measurements"
title: "top: Tamiko : Mensurations requises"
- - -
<PatternMeasurements pattern='tamiko' />

View file

@ -1,8 +1,8 @@
- - -
title: "Tamiko top: What You Need"
title: "Top Tamiko : ce dont vous avez besoin"
- - -
To make Tamiko, you will need the following:
Pour faire Tamiko, vous aurez besoin des éléments suivants :
- Fourniture de base pour la couture
- About 1 meter (1.1 yards) of a suitable fabric ([see Fabric options](/docs/patterns/tamiko/fabric))
- Environ 1 mètre (1.1 yards) d'un tissu adapté ([voir Options de tissu](/docs/patterns/tamiko/fabric))

View file

@ -1,6 +1,6 @@
Contrôle la profondeur de votre armure, en tant que facteur de mesure de votre épaule à épaule.
Contrôle la profondeur de votre manche, en fonction de la mensuration de votre épaule à épaule.
## Effet de cette option sur le motif
## Effet de cette option sur le patron
![Cette image montre l'effet de cette option en superposant plusieurs variantes qui ont une valeur différente pour cette option](tamiko_armholedepthfactor_sample.svg "Effet de cette option sur le motif")

View file

@ -1,5 +1,5 @@
La quantité d'aisance à votre poitrine.
L'aisance au niveau de la poitrine.
## Effet de cette option sur le motif

View file

@ -1,5 +1,5 @@
L'angle par lequel le vêtement sort de votre poitrine vers le bas.
L'angle par lequel le vêtement s'évase de votre poitrine vers le bas.
## Effet de cette option sur le motif

View file

@ -1,5 +1,5 @@
La longueur de la couture d'épaule, en tant que facteur de la mesure d'épaule à épaule.
La longueur de la couture d'épaule, en fonction de la mensuration d'épaule à épaule.
## Effet de cette option sur le motif

View file

@ -24,7 +24,7 @@ const social = {
const Footer = ({ app }) => (
<footer className="bg-neutral">
<div className={`theme-gradient h-1 w-full relative ${app.loading ? 'loading' : ''}`}></div>
<div className={`theme-gradient h-14 ${app.loading ? 'loading' : ''}`} />
<div className="p-4 py-16 flex flex-row bg-neutral -mt-1 z-0 gap-8 flex-wrap justify-around text-neutral-content">
<div className="w-64 mt-2">
<div className="px-4 mb-4"><CreativeCommonsLogo /></div>
@ -159,6 +159,7 @@ const Footer = ({ app }) => (
</a>
</div>
<div className={`theme-gradient h-14 ${app.loading ? 'loading' : ''}`} />
</footer>
)

View file

@ -46,6 +46,8 @@ const Header = ({ app, setSearch }) => {
z-30
transition-transform
${show ? '': 'fixed top-0 left-0 -translate-y-20'}
drop-shadow-xl
${app.loading ? "theme-gradient loading" : ""}
`}>
<div className="max-w-6xl m-auto">
<div className="p-2 flex flex-row gap-2 justify-between text-neutral-content">
@ -55,7 +57,7 @@ const Header = ({ app, setSearch }) => {
text-neutral-content bg-transparent
border border-transparent
hover:bg-transparent hover:border-base-100
sm:hidden
md:hidden
h-12
`}
onClick={app.togglePrimaryMenu}>
@ -79,14 +81,14 @@ const Header = ({ app, setSearch }) => {
)
}
</button>
<div className="flex flex-row gap-2 sm:hidden">
<div className="flex flex-row gap-2 md:hidden">
<button className="btn btn-sm btn h-12 px-12" onClick={() => setSearch(true)}>
<SearchIcon />
</button>
</div>
<button className={`
btn btn-sm h-12
hidden sm:flex
hidden md:flex
flex-row gap-1 mr-4 w-64 px-2
bg-base-100 text-base-content
hover:bg-base-100 hover:text-base-content
@ -110,15 +112,11 @@ const Header = ({ app, setSearch }) => {
</a>
</Link>
</div>
<div className="hidden sm:flex flex-row items-center">
<div className="hidden md:flex flex-row items-center">
<ThemePicker app={app} />
</div>
</div>
</div>
<div className={`
theme-gradient h-1 w-full z-10 relative -mb-1
${app.loading ? 'loading' : ''}
`}></div>
</header>
)
}

View file

@ -0,0 +1,68 @@
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'
const PageTitle = ({ app, slug, title }) => {
if (title) return <h1>{title}</h1>
if (slug) return <h1>{get(app.navigation, slug.split('/')).__title}</h1>
return <h1>FIXME: This page has no title</h1>
}
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 (
<ul className="flex flex-row flex-wrap gap-2 font-bold">
<li>
<Link href="/">
<a title="To the homepage" className="text-base-content">
<Logo size={24} fill="currentColor" stroke={false}/>
</a>
</Link>
</li>
{crumbs.map(crumb => (
<React.Fragment key={crumb[1]}>
<li className="text-base-content">&raquo;</li>
<li>
{crumb[2]
? (
<Link href={crumb[1]}>
<a title={crumb[0]} className="text-secondary hover:text-secondary-focus">
{crumb[0]}
</a>
</Link>
)
: <span className="text-base-content">{crumb[0]}</span>
}
</li>
</React.Fragment>
))}
</ul>
)
}
const DefaultLayout = ({ app, title=false, children=[] }) => {
const router = useRouter()
const slug = router.asPath.slice(1)
return (
<>
<Aside app={app} slug={slug} mobileOnly />
{children}
</>
)
}
export default DefaultLayout

View file

@ -0,0 +1,78 @@
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'
const PageTitle = ({ app, slug, title }) => {
if (title) return <h1>{title}</h1>
if (slug) return <h1>{get(app.navigation, slug.split('/')).__title}</h1>
return <h1>FIXME: This page has no title</h1>
}
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 (
<ul className="flex flex-row flex-wrap gap-2 font-bold">
<li>
<Link href="/">
<a title="To the homepage" className="text-base-content">
<Logo size={24} fill="currentColor" stroke={false}/>
</a>
</Link>
</li>
{crumbs.map(crumb => (
<React.Fragment key={crumb[1]}>
<li className="text-base-content">&raquo;</li>
<li>
{crumb[2]
? (
<Link href={crumb[1]}>
<a title={crumb[0]} className="text-secondary hover:text-secondary-focus">
{crumb[0]}
</a>
</Link>
)
: <span className="text-base-content">{crumb[0]}</span>
}
</li>
</React.Fragment>
))}
</ul>
)
}
const DefaultLayout = ({ app, title=false, children=[] }) => {
const router = useRouter()
const slug = router.asPath.slice(1)
return (
<div className="m-auto flex flex-row justify-center">
<Aside app={app} slug={slug} />
<section className="py-28 md:py-36">
<div>
{title && (
<div className="px-8 xl:pl-8 2xl:pl-16">
<Breadcrumbs app={app} slug={slug} title={title} />
<PageTitle app={app} slug={slug} title={title} />
</div>
)}
{children}
</div>
</section>
</div>
)
}
export default DefaultLayout

View file

@ -0,0 +1,115 @@
import React from 'react'
import { useState } from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'
// Shared components
import Logo from 'shared/components/logos/freesewing.js'
import PrimaryNavigation from 'shared/components/navigation/primary'
import get from 'lodash.get'
import Right from 'shared/components/icons/right.js'
import Left from 'shared/components/icons/left.js'
// Site components
import Header from 'site/components/header'
import Footer from 'site/components/footer'
import Search from 'site/components/search'
const PageTitle = ({ app, slug, title }) => {
if (title) return <h1>{title}</h1>
if (slug) return <h1>{get(app.navigation, slug.split('/')).__title}</h1>
return <h1>FIXME: This page has no title</h1>
}
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 (
<ul className="flex flex-row flex-wrap gap-2 font-bold">
<li>
<Link href="/">
<a title="To the homepage" className="text-base-content">
<Logo size={24} fill="currentColor" stroke={false}/>
</a>
</Link>
</li>
{crumbs.map(crumb => (
<React.Fragment key={crumb[1]}>
<li className="text-base-content">&raquo;</li>
<li>
{crumb[2]
? (
<Link href={crumb[1]}>
<a title={crumb[0]} className="text-secondary hover:text-secondary-focus">
{crumb[0]}
</a>
</Link>
)
: <span className="text-base-content">{crumb[0]}</span>
}
</li>
</React.Fragment>
))}
</ul>
)
}
const LayoutWrapper = ({
app,
title=false,
children=[],
search,
setSearch,
noSearch=false,
workbench=false,
AltMenu=null,
}) => {
const startNavigation = () => {
app.startLoading()
// Force close of menu on mobile if it is open
if (app.primaryNavigation) app.setPrimaryNavigation(false)
// Force close of search modal if it is open
if (search) setSearch(false)
}
const router = useRouter()
router.events?.on('routeChangeStart', startNavigation)
router.events?.on('routeChangeComplete', () => app.stopLoading())
const slug = router.asPath.slice(1)
const [collapsePrimaryNav, setCollapsePrimaryNav] = useState(workbench || false)
const [collapseAltMenu, setCollapseAltMenu] = useState(false)
return (
<div className={`
flex flex-col justify-between
min-h-screen
bg-base-100
`}>
<Header app={app} setSearch={setSearch} />
<main className="grow">{children}</main>
{!noSearch && search && (
<>
<div className={`
fixed w-full max-h-screen bg-base-100 top-0 z-30 pt-0 pb-16 px-8
md:rounded-lg md:top-24
md:max-w-xl md:m-auto md:inset-x-12
md:max-w-2xl
lg:max-w-4xl
`}>
<Search app={app} search={search} setSearch={setSearch}/>
</div>
<div className="fixed top-0 left-0 w-full min-h-screen bg-neutral z-20 bg-opacity-70"></div>
</>
)}
<Footer app={app} />
</div>
)
}
export default LayoutWrapper

View file

@ -0,0 +1,68 @@
import React, { useState, useEffect } from 'react'
import { useSwipeable } from 'react-swipeable'
import { useRouter } from 'next/router'
import { useHotkeys } from 'react-hotkeys-hook'
// Layouts components
import LayoutWrapper from 'site/components/wrappers/layout'
import Docs from 'site/components/layouts/docs'
const layouts = {
docs: Docs,
}
/* This component should wrap all page content */
const PageWrapper= ({
title="FIXME: No title set",
noSearch=false,
app=false,
layout=Docs,
children=[]
}) => {
const swipeHandlers = useSwipeable({
onSwipedLeft: evt => (app.primaryMenu) ? app.setPrimaryMenu(false) : null,
onSwipedRight: evt => (app.primaryMenu) ? null : app.setPrimaryMenu(true),
trackMouse: true
})
const router = useRouter()
const slug = router.asPath.slice(1)
useEffect(() => app.setSlug(slug), [slug])
// Trigger search with Ctrl+k
useHotkeys('ctrl+k', (evt) => {
evt.preventDefault()
setSearch(true)
})
const [search, setSearch] = useState(false)
const childProps = {
app: app,
title: title,
search, setSearch, toggleSearch: () => setSearch(!search),
noSearch: noSearch,
}
const Layout = layout
return (
<div
ref={swipeHandlers.ref}
onMouseDown={swipeHandlers.onMouseDown}
data-theme={app.theme}
key={app.theme} // Thiis forces the data-theme update
>
<LayoutWrapper {...childProps}>
{Layout
? <Layout {...childProps}>{children}</Layout>
: children
}
</LayoutWrapper>
</div>
)
}
export default PageWrapper

View file

@ -50,8 +50,5 @@
"postcss": "^8.4.4",
"tailwindcss": "^3.0.1",
"yaml-loader": "^0.8.0"
},
"engines": {
"node": ">=14.18.1"
}
}

View file

@ -1,8 +1,9 @@
import Page from 'shared/components/wrappers/page.js'
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import mdxMeta from 'site/prebuild/mdx.en.js'
import mdxLoader from 'shared/mdx/loader'
import MdxWrapper from 'shared/components/wrappers/mdx'
import TocWrapper from 'shared/components/wrappers/toc'
import Head from 'next/head'
import HelpUs from 'site/components/help-us.js'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
@ -36,8 +37,17 @@ const MdxPage = props => {
<meta property="og:locale" content="en_US" key='locale' />
<meta property="og:site_name" content="freesewing.dev" key='site' />
</Head>
<MdxWrapper mdx={props.mdx} app={app}/>
<HelpUs mdx slug={`/${props.page.slug}`} />
<div className="flex flex-row-reverse flex-wrap xl:flex-nowrap justify-end px-8 xl:px-0">
{props.toc && (
<div className="mb-8 px-0 w-full xl:w-80 2xl:w-96 xl:pl-8 2xl:pl-16">
<TocWrapper toc={props.toc} app={app}/>
</div>
)}
<div className="px-0 xl:pl-8 2xl:pl-16">
<MdxWrapper mdx={props.mdx} app={app}/>
<HelpUs mdx slug={`/${props.page.slug}`} />
</div>
</div>
</Page>
)
}
@ -61,11 +71,12 @@ export default MdxPage
*/
export async function getStaticProps({ params, locale }) {
const { mdx, intro } = await mdxLoader('en', 'dev', params.mdxslug.join('/'))
const { mdx, intro, toc } = await mdxLoader('en', 'dev', params.mdxslug.join('/'))
return {
props: {
mdx,
toc,
intro: intro.join(' '),
page: {
slug: params.mdxslug.join('/'),

View file

@ -1,15 +1,16 @@
import Page from 'shared/components/wrappers/page.js'
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import Head from 'next/head'
import HelpUs from 'site/components/help-us.js'
import Link from 'next/link'
import Script from 'next/script'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import Layout from 'site/components/layouts/bare'
import Navigation, { Icons } from 'shared/components/navigation/primary'
const HomePage = (props) => {
const app = useApp()
return (
<Page app={app} title="Welcome to FreeSewing.dev">
<Page app={app} title="Welcome to FreeSewing.dev" layout={Layout}>
<Head>
<meta property="og:title" content="FreeSewing.dev" key="title" />
<meta property="og:type" content="article" key='type' />
@ -23,85 +24,60 @@ const HomePage = (props) => {
<meta property="og:locale" content="en_US" key='locale' />
<meta property="og:site_name" content="freesewing.dev" key='site' />
</Head>
<Script src="/sw.js"></Script>
<div className="max-w-screen-md">
<p>
FreeSewing.dev hosts documentation for contributors and developers alike.
<br />
For our maker site, and to try our platform, go
to <a
href="https://freesewing.org/"
title="Go to FreeSewing.org"
className="text-secondary font-bold"
>freesewing.org</a>.
</p>
<h2 className="mt-8">What is FreeSewing?</h2>
<div className="theme-gradient p-1 -mt-2 mb-2 "></div>
<p className="text-2xl sm:text-3xl">
FreeSewing is an open source platform for made-to-measure sewing patterns.
</p>
<p className="text-xl sm:text-2xl">
<b>@freeSewing/core</b> is a Javascript library for 2D parametric design
</p>
<p>
It has a primary focus is on sewing patterns,
but can be utilized for a variety of similar 2D design tasks.
</p>
<h2 className='mt-8'>How can I try it out?</h2>
<div className="theme-gradient p-1 -mt-2 mb-2 "></div>
<p className="text-2xl sm:text-3xl">
You can try it <Link
href="/howtos/environments/browser">
<a className="text-secondary">in the browser</a>
</Link>, <Link
href="/howtos/environments/node">
<a className="text-secondary">in NodeJS</a>
</Link>,
or on any Javascript runtime.
</p>
<p className="text-xl sm:text-2xl">
The includes Deno, AWS Lamba, Cloudflare workers, Vercel Edge functions, Netlify functions, and so on.
</p>
<p>
Or save yourself the trouble, and check <a
href="https://freesewing.org/"
title="Go to FreeSewing.org"
className="text-secondary font-bold"
>freesewing.org</a> for a showcase of our software.
</p>
<h2 className='mt-8'>
You son of a bitch, I&apos;m in
<sup>
<a
href="https://www.youtube.com/watch?v=nKxvDYHkfSY"
className="text-secondary"
>*</a>
</sup>
</h2>
<div className="theme-gradient p-1 -mt-2 mb-2 "></div>
<p className="text-2xl sm:text-3xl">
We are an <a
href="https://allcontributors.org/"
className="text-secondary"
>all-contributors</a> project
and welcome all contributions.
</p>
<p className="text-xl sm:text-2xl">
<a
href="https://discord.freesewing.org/"
className="text-secondary"
>Come say hi on Discord</a>,
or check out <Link
href="/howtos/ways-to-contribute"><a
className="text-secondary">ways to contribute</a>
</Link> to get inspired.
</p>
<p>
Last but certainly not least, you can also support FreeSewing financially:
</p>
<section
style={{
backgroundImage: "url('/img/splash.jpg')",
backgroundSize: 'cover',
backgroundPosition: '40% 50%',
}}
className="m-0 p-0 shadow drop-shadow-lg w-full mb-8"
>
<div className="mx-auto px-8 flex flex-col items-center justify-center min-h-screen lg:min-h-0 lg:py-96">
<div className="flex flex-col items-end max-w-4xl">
<h1
className={`
text-4xl font-black text-right px-4
sm:text-6xl
md:text-7xl px-6
lg:px-8
bg-secondary
`}
style={{ textShadow: '1px 1px 3px #000', color: 'white' }}
>
FreeSewing
<span className="font-light">.dev</span>
</h1>
<h2
className={`
text-right text-2xl mr-0
sm:text-3xl
md:text-4xl
lg:max-w-1/2 lg:text-4xl xl:pr-0 `}
style={{ textShadow: '1px 1px 3px #000', color: 'white' }}
>
Documentation for FreeSewing contributors & developers
</h2>
</div>
<Icons app={app} active='/'
ulClasses="flex flex-row flex-wrap mt-8 justify-around w-full max-w-6xl"
liClasses="text-neutral-content w-1/3 my-4 lg:mx-2 lg:w-24"
linkClasses={`
text-lg lg:text-xl py-1 text-secondary
hover:text-secondary sm:hover:text-secondary-focus hover:cursor-pointer
flex flex-col items-center capitalize`}
/>
<p className="text-neutral-content text-center mt-8">
To learn more about FreeSewing and try our platform
go to <a
href="https://freesewing.org/"
title="Go to FreeSewing.org"
className="text-secondary font-bold"
>freesewing.org</a>
</p>
</div>
</section>
<div>
<div className="max-w-7xl m-auto my-32">
<div className="bg-cover bg-neutral w-full bg-center rounded-lg shadow p-4 "
style={{backgroundImage: "url(/support.jpg)"}}
>
@ -116,8 +92,25 @@ const HomePage = (props) => {
</p>
<a role="button" className="btn btn-accent btn-wide ml-4 mb-8" href="https://freesewing.org/patrons/join">Become a Patron</a>
</div>
</div>
<div className="max-w-7xl m-auto my-32">
<div className="px-8 text-base-content">
<Icons app={app}
active='/'
ulClasses="flex flex-row flex-wrap mt-8 justify-around w-full max-w-6xl"
liClasses="w-1/3 my-4 lg:mx-2 lg:w-24"
linkClasses={`
text-lg lg:text-xl py-1 text-base-content
hover:text-secondary sm:hover:text-secondary-focus hover:cursor-pointer
flex flex-col items-center capitalize`}
/>
</div>
</div>
<div className="max-w-xl m-auto my-32">
<HelpUs slug='/' />
</div>
</div>
</Page>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View file

@ -1,16 +0,0 @@
/*
* This is here to force-remove the cache of the old Gatsby site
* See: https://github.com/gatsbyjs/gatsby/issues/15623
*/
self.addEventListener("activate", function (event) {
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames
.map(function (cacheName) {
return caches.delete(cacheName);
})
);
})
);
});

View file

@ -0,0 +1,11 @@
#!/bin/bash
if git diff HEAD^ HEAD --quiet ../freesewing.shared || git diff --quiet HEAD^ HEAD . ; then
# We have local changes, go ahead and build
echo "✅ - Changed detected; Let's build this thing"
exit 1;
else
# No changes, do not waste time building this commit
echo "🛑 - No changes detected; Let's just not"
exit 0;
fi

View file

@ -0,0 +1,771 @@
{
"2.20.7": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.20.6": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.20.5": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.20.4": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.20.3": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.20.2": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.20.1": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.20.0": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"plugintest",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.19.9": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.19.0": [
"aaron",
"albert",
"bee",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"lunetius",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"tiberius",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"walburga",
"waralee",
"yuri"
],
"2.18.0": [
"aaron",
"albert",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"waralee",
"yuri"
],
"2.17.1": [
"aaron",
"albert",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"waralee"
],
"2.17.0": [
"aaron",
"albert",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"titan",
"trayvon",
"tutorial",
"ursula",
"wahid",
"waralee"
],
"2.16.2": [
"aaron",
"albert",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"titan",
"trayvon",
"tutorial",
"wahid",
"waralee"
],
"2.16.0": [
"aaron",
"albert",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"titan",
"trayvon",
"tutorial",
"wahid",
"waralee"
],
"2.15.4": [
"aaron",
"albert",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"titan",
"trayvon",
"tutorial",
"wahid",
"waralee"
],
"2.15.0": [
"aaron",
"albert",
"bella",
"benjamin",
"bent",
"breanna",
"brian",
"bruce",
"carlita",
"carlton",
"cathrin",
"charlie",
"cornelius",
"diana",
"examples",
"florence",
"florent",
"holmes",
"hortensia",
"huey",
"hugo",
"jaeger",
"legend",
"paco",
"penelope",
"rendertest",
"sandy",
"shin",
"simon",
"simone",
"sven",
"tamiko",
"teagan",
"theo",
"titan",
"trayvon",
"tutorial",
"wahid",
"waralee"
]
}

View file

@ -28,7 +28,7 @@ const Footer = ({ app }) => {
return (
<footer className="bg-neutral">
<div className={`theme-gradient h-1 w-full relative ${app.loading ? 'loading' : ''}`}></div>
<div className={`theme-gradient h-14 ${app.loading ? 'loading' : ''}`} />
<div className="p-4 py-16 flex flex-row bg-neutral -mt-1 z-0 gap-8 flex-wrap justify-around text-neutral-content">
<div className="w-64 mt-2">
<div className="px-4 mb-4"><CreativeCommonsLogo /></div>
@ -163,6 +163,7 @@ const Footer = ({ app }) => {
</a>
</div>
<div className={`theme-gradient h-14 ${app.loading ? 'loading' : ''}`} />
</footer>
)
}

View file

@ -47,7 +47,9 @@ const Header = ({ app }) => {
w-full
z-30
transition-transform
drop-shadow-xl
${show ? '': 'fixed top-0 left-0 -translate-y-20'}
${app.loading ? "theme-gradient loading" : ""}
`}>
<div className="max-w-6xl m-auto">
<div className="p-2 flex flex-row gap-2 justify-between text-neutral-content">
@ -57,7 +59,7 @@ const Header = ({ app }) => {
text-neutral-content bg-transparent
border border-transparent
hover:bg-transparent hover:border-base-100
sm:hidden
md:hidden
h-12
`}
onClick={app.togglePrimaryMenu}>
@ -66,29 +68,23 @@ const Header = ({ app }) => {
: <><MenuIcon /><span className="opacity-50 pl-2 flex flex-row items-center gap-1"><Right />swipe</span></>
}
</button>
<div className="hidden sm:flex flex-row items-center">
<div className="hidden md:flex flex-row items-center">
<PatternPicker app={app} />
<VersionPicker app={app} />
</div>
<div className="hidden md:flex md:flex-row gap-2">
<Link href="/">
<a className="flex flex-column items-center">
<Logo size={36} fill="currentColor" stroke={false} />
</a>
</Link>
<Link href="/">
<a role="button" className="btn btn-link btn-sm text-neutral-content h-12">
lab.freesewing.dev
<a role="button" className="btn btn-link btn-sm text-neutral-content h-12 font-normal lowercase text-2xl">
lab.<span className="font-black px-1 normal-case">FreeSewing</span>.dev
</a>
</Link>
</div>
<div className="hidden sm:flex flex-row items-center">
<div className="hidden md:flex flex-row items-center">
<ThemePicker app={app} />
<LocalePicker app={app} />
</div>
</div>
</div>
<div className={`theme-gradient h-1 w-full z-10 relative -mb-1 ${app.loading ? 'loading' : ''}`}></div>
</header>
)
}

View file

@ -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 <h1>{title}</h1>
if (slug) return <h1>{get(app.navigation, slug.split('/')).__title}</h1>
return <h1>FIXME: This page has no title</h1>
}
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 (
<ul className="flex flex-row flex-wrap gap-2 font-bold">
<li>
<Link href="/">
<a title="To the homepage" className="text-base-content">
<Logo size={24} fill="currentColor" stroke={false}/>
</a>
</Link>
</li>
{crumbs.map(crumb => (
<React.Fragment key={crumb[1]}>
<li className="text-base-content">&raquo;</li>
<li>
{crumb[2]
? (
<Link href={crumb[1]}>
<a title={crumb[0]} className="text-secondary hover:text-secondary-focus">
{crumb[0]}
</a>
</Link>
)
: <span className="text-base-content">{crumb[0]}</span>
}
</li>
</React.Fragment>
))}
</ul>
)
}
const DefaultLayout = ({ app, title=false, children=[] }) => {
const router = useRouter()
const slug = router.asPath.slice(1)
console.log(BeforeNav)
return (
<>
<Aside app={app} slug={slug} before={<BeforeNav app={app}/>} mobileOnly />
{children}
</>
)
}
export default DefaultLayout

View file

@ -0,0 +1,51 @@
import ThemePicker from 'shared/components/theme-picker.js'
import LocalePicker from 'shared/components/locale-picker.js'
import PatternPicker from 'site/components/pattern-picker.js'
import VersionPicker from 'site/components/version-picker.js'
export const BeforeNav = ({ app }) => (
<>
<div className="md:hidden flex flex-row flex-wrap sm:flex-nowrap gap-2 mb-2">
<ThemePicker app={app} />
<LocalePicker app={app} />
</div>
<div className="md:hidden flex flex-row flex-wrap sm:flex-nowrap gap-2 mb-2">
<PatternPicker app={app} />
<VersionPicker app={app} />
</div>
</>
)
const LabLayout = ({ app, AltMenu, children=[] }) => (
<div className="py-24 lg:py-36 flex flex-row">
<div className="w-full px-8">
{children}
</div>
<aside className={`
fixed top-0 right-0
pt-20 pb-8 px-8
md:pt-0
md:relative md:transform-none
h-screen w-screen
bg-base-100
md:bg-base-50
md:flex
md:sticky
overflow-y-scroll
z-20
bg-base-100 text-base-content
transition-all
xl:w-1/4
${app.primaryMenu ? '' : 'translate-x-[-100%]'} transition-transform
md:flex-row
md:w-80
lg:w-96
shrink-0
`}>
<BeforeNav app={app}/>
{AltMenu}
</aside>
</div>
)
export default LabLayout

View file

@ -10,10 +10,10 @@ const PatternPicker = ({ app }) => {
const version = useVersion()
return (
<div className="dropdown">
<div className="dropdown w-full md:w-auto">
<div tabIndex="0" className={`
m-0 btn btn-neutral flex flex-row gap-2
sm:btn-ghost
m-0 btn btn-neutral flex flex-row gap-2 btn-outline
md:btn-ghost
hover:bg-neutral hover:border-neutral-content
`}>
<DesignIcon />

View file

@ -24,10 +24,10 @@ const PatternPicker = ({ app }) => {
const version = useVersion()
return (
<div className="dropdown">
<div className="dropdown w-full md:w-auto">
<div tabIndex="0" className={`
m-0 btn btn-neutral flex flex-row gap-2
sm:btn-ghost
m-0 btn btn-neutral flex flex-row gap-2 btn-outline
md:btn-ghost
hover:bg-neutral hover:border-neutral-content
`}>
<VersionsIcon />

View file

@ -0,0 +1,88 @@
import React from 'react'
import { useState } from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'
// Shared components
import Logo from 'shared/components/logos/freesewing.js'
import PrimaryNavigation from 'shared/components/navigation/primary'
import get from 'lodash.get'
import Right from 'shared/components/icons/right.js'
import Left from 'shared/components/icons/left.js'
// Site components
import Header from 'site/components/header'
import Footer from 'site/components/footer'
export const PageTitle = ({ app, slug, title }) => {
if (title) return <h1>{title}</h1>
if (slug) return <h1>{get(app.navigation, slug.split('/')).__title}</h1>
return <h1>FIXME: This page has no title</h1>
}
export 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 (
<ul className="flex flex-row flex-wrap gap-2 font-bold">
<li>
<Link href="/">
<a title="To the homepage" className="text-base-content">
<Logo size={24} fill="currentColor" stroke={false}/>
</a>
</Link>
</li>
{crumbs.map(crumb => (
<React.Fragment key={crumb[1]}>
<li className="text-base-content">&raquo;</li>
<li>
{crumb[2]
? (
<Link href={crumb[1]}>
<a title={crumb[0]} className="text-secondary hover:text-secondary-focus">
{crumb[0]}
</a>
</Link>
)
: <span className="text-base-content">{crumb[0]}</span>
}
</li>
</React.Fragment>
))}
</ul>
)
}
const LayoutWrapper = ({ app, title=false, children=[] }) => {
const startNavigation = () => {
app.startLoading()
// Force close of menu on mobile if it is open
if (app.primaryMenu) app.setPrimaryMenu(false)
}
const router = useRouter()
router.events?.on('routeChangeStart', startNavigation)
router.events?.on('routeChangeComplete', app.stopLoading)
const slug = router.asPath.slice(1)
return (
<div className={`
flex flex-col justify-between
min-h-screen
bg-base-100
`}>
<Header app={app}/>
<main className="grow">{children}</main>
<Footer app={app} />
</div>
)
}
export default LayoutWrapper

View file

@ -0,0 +1,52 @@
import React, { useState, useEffect } from 'react'
import { useSwipeable } from 'react-swipeable'
import { useRouter } from 'next/router'
import { useHotkeys } from 'react-hotkeys-hook'
// Layouts components
import LayoutWrapper from 'site/components/wrappers/layout'
/* This component should wrap all page content */
const PageWrapper= ({
title="FIXME: No title set",
app=false,
layout=false,
children=[]
}) => {
const swipeHandlers = useSwipeable({
onSwipedLeft: evt => (app.primaryMenu) ? app.setPrimaryMenu(false) : null,
onSwipedRight: evt => (app.primaryMenu) ? null : app.setPrimaryMenu(true),
trackMouse: true
})
const router = useRouter()
const slug = router.asPath.slice(1)
useEffect(() => app.setSlug(slug), [slug])
const childProps = {
app: app,
title: title,
}
const Layout = layout
return (
<div
ref={swipeHandlers.ref}
onMouseDown={swipeHandlers.onMouseDown}
data-theme={app.theme}
key={app.theme} // Thiis forces the data-theme update
>
<LayoutWrapper {...childProps}>
{Layout
? <Layout {...childProps}>{children}</Layout>
: children
}
</LayoutWrapper>
</div>
)
}
export default PageWrapper

View file

@ -6,6 +6,7 @@
"dev": "next dev -p 3002",
"develop": "next dev -p 3002",
"prebuild": "cd ../utils && npm run build && cd - && SITE=lab node ../freesewing.shared/prebuild/index.mjs",
"cibuild": "yarn prebuild && next build",
"build": "next build",
"export": "yarn prebuild && next build && next export",
"start": "next start -p 3002",
@ -52,8 +53,5 @@
"postcss": "^8.4.4",
"tailwindcss": "^3.0.1"
},
"engines": {
"node": ">=14.18.1"
},
"browserslist": [ "last 2 versions" ]
}

View file

@ -1,10 +1,16 @@
import Page from 'shared/components/wrappers/page.js'
import React from 'react'
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import Head from 'next/head'
import Link from 'next/link'
import About from 'site/components/about.js'
import { useTranslation } from 'next-i18next'
import { defaultVersion, formatVersionTitle, formatVersionUri } from 'site/components/version-picker.js'
import TutorialIcon from 'shared/components/icons/tutorial.js'
import DesignIcon from 'shared/components/icons/design.js'
import BoxIcon from 'shared/components/icons/box.js'
import CogIcon from 'shared/components/icons/cog.js'
import Layout from 'site/components/layouts/bare'
import { PageTitle, Breadcrumbs } from 'site/components/wrappers/layout'
const links = (section, list, version) => list.map(design => (
<li key={design} className="">
@ -14,13 +20,49 @@ const links = (section, list, version) => list.map(design => (
</li>
))
const PatternListPageTemplate = ({ sections=false, version=false }) => {
const icons = {
accessories: (className='') => <TutorialIcon className={className}/>,
blocks: (className='') => <BoxIcon className={className}/>,
garments: (className='') => <DesignIcon className={className}/>,
utilities: (className='') => <CogIcon className={className}/>,
}
const Section = ({ section, version, patterns }) => {
const { t } = useTranslation(['patterns'])
return patterns.map(design => (
<Link href={formatVersionUri(version, design)}>
<a className={`
text-secondary border rounded-lg
flex flex-col gap-1 px-4 py-2 grow justify-between text-2xl
md:text-4xl
lg:text-4xl
xl:text-6xl
2xl:text-7xl
hover:border hover:border-secondary hover:bg-secondary hover:bg-opacity-10
shadow
`}>
<div className="flex flex-row items-center justify-items-start w-full">
<span className="text-2xl md:text-3xl lg:text-4xl xl:text-4xl 2xl:text-5xl font-bold grow capitalize">
{t(`patterns:${design}.t`)}
</span>
{icons[section]("w-12 h-12 md:h-20 md:w-20 xl:w-32 xl:h-32 shrink-0")}
</div>
<span className="text-xl md:text-2xl xl:text-3xl pb-2 xl:pb-4 2xl:text-4xl">{t(`patterns:${design}.d`)}</span>
</a>
</Link>
))
}
const PatternListPageTemplate = ({ section=false, version=false }) => {
const app = useApp()
const { t } = useTranslation(['app'])
if (sections === false) sections = Object.keys(app.patterns)
const title = section
? app.navigation[section].__title
: t('designs')
return (
<Page app={app} title={`FreeSewing Lab: ${formatVersionTitle(version)}`}>
<Page app={app} title={`FreeSewing Lab: ${formatVersionTitle(version)}`} layout={Layout}>
<Head>
<meta property="og:title" content="lab.FreeSewing.dev" key="title" />
<meta property="og:type" content="article" key='type' />
@ -34,22 +76,30 @@ const PatternListPageTemplate = ({ sections=false, version=false }) => {
<meta property="og:locale" content="en_US" key='locale' />
<meta property="og:site_name" content="lab.freesewing.dev" key='site' />
</Head>
<div className="max-w-screen-md">
{Object.keys(app.navigation).map(section => {
if (sections.indexOf(section) !== -1) return (
<div key={section}>
<h2>{t(app.navigation[section].__title)}</h2>
<ul className="flex flex-row flex-wrap gap-2">
{links(section, app.patterns[section], version)}
</ul>
</div>
)
else return null
})}
<About />
<div className="max-w-7xl m-auto py-20 md:py-36 min-h-screen">
<section className="px-8">
<PageTitle app={app} slug={'/'+section} title={title} />
{section
? (
<div className="flex flex-row flex-wrap gap-4 items-center justify-center my-8">
<Section section={section} version={version} patterns={app.patterns[section]} />
</div>
)
: Object.keys(app.patterns).map(section => (
<div key={section} className="mb-12">
<h2 className="pb-0">{app.navigation[section].__title}</h2>
<div className="flex flex-row flex-wrap gap-4 items-center justify-center my-8">
<Section {...{section, version}} patterns={app.patterns[section]} />
</div>
</div>
))
}
</section>
</div>
</Page>
)
}
export default PatternListPageTemplate

View file

@ -1,7 +1,8 @@
import Page from 'shared/components/wrappers/page.js'
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import WorkbenchWrapper from 'shared/components/wrappers/workbench.js'
import { useRouter } from 'next/router'
import Layout from 'site/components/layouts/lab'
const WorkbenchPage = ({ pattern }) => {
const app = useApp()
@ -9,8 +10,8 @@ const WorkbenchPage = ({ pattern }) => {
const { preload, from } = router.query
return (
<Page app={app} noLayout>
<WorkbenchWrapper {...{ app, pattern, preload, from }} />
<Page app={app}>
<WorkbenchWrapper {...{ app, pattern, preload, from }} layout={Layout} />
</Page>
)
}

View file

@ -0,0 +1,16 @@
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import PageTemplate from 'site/page-templates/pattern-list.js'
const Page = props => <PageTemplate section='accessories' />
export default Page
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale)),
}
}
}

View file

@ -0,0 +1,16 @@
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import PageTemplate from 'site/page-templates/pattern-list.js'
const Page = props => <PageTemplate section='blocks' />
export default Page
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale)),
}
}
}

View file

@ -0,0 +1,16 @@
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import PageTemplate from 'site/page-templates/pattern-list.js'
const Page = props => <PageTemplate section='garments' />
export default Page
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale)),
}
}
}

View file

@ -1,7 +1,134 @@
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import Head from 'next/head'
import Link from 'next/link'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import Page from 'site/page-templates/pattern-list.js'
import Layout from 'site/components/layouts/bare'
import { useTranslation } from 'next-i18next'
import Navigation, { Icons } from 'shared/components/navigation/primary'
export default Page
const HomePage = (props) => {
const app = useApp()
const { t } = useTranslation(['lab'])
return (
<Page app={app} title="Welcome to FreeSewing.dev" layout={Layout}>
<Head>
<meta property="og:title" content="FreeSewing.dev" key="title" />
<meta property="og:type" content="article" key='type' />
<meta property="og:description" content="Documentation and tutorials for FreeSewing developers and contributors. Plus our Developers Blog" key='description' />
<meta property="og:article:author" content='Joost De Cock' key='author' />
<meta property="og:image" content="https://canary.backend.freesewing.org/og-img/en/dev/" key='image' />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://freesewing.dev/" key='url' />
<meta property="og:locale" content="en_US" key='locale' />
<meta property="og:site_name" content="freesewing.dev" key='site' />
</Head>
<section
style={{
backgroundImage: "url('/img/splash.jpg')",
backgroundSize: 'cover',
backgroundPosition: '40% 50%',
}}
className="m-0 p-0 shadow drop-shadow-lg w-full mb-8"
>
<div className="mx-auto px-8 flex flex-col items-center justify-center min-h-screen py-24 lg:min-h-0 lg:py-96">
<div className="flex flex-col items-end max-w-4xl">
<h1
className={`
text-3xl font-black text-right px-4
sm:text-6xl
md:text-7xl px-6
lg:px-8
bg-accent
`}
style={{ textShadow: '1px 1px 3px #000', color: 'white' }}
>
<span className="font-light">lab.</span>
FreeSewing
<span className="font-light">.dev</span>
</h1>
<h2
className={`
text-xl mr-0 mt-4 font-bold
sm:text-3xl
md:text-4xl
lg:max-w-1/2 lg:text-4xl
xl:pr-0 `}
style={{ textShadow: '1px 1px 3px #000', color: 'white' }}
>{t('slogan')}:</h2>
<ul
className={`
text-xl mr-8 font-bold list list-inside list-disc
sm:text-3xl
md:text-4xl
lg:max-w-1/2 lg:text-3xl
xl:pr-0 `}
style={{ textShadow: '1px 1px 3px #000', color: 'white' }}
>
<li>{t('slogan1')}</li>
<li>{t('slogan2')}</li>
<li>{t('slogan3')}</li>
<li>{t('slogan4')}</li>
</ul>
</div>
<Icons app={app} active='/'
ulClasses="flex flex-row flex-wrap mt-8 justify-around w-full max-w-6xl"
liClasses="text-neutral-content w-1/2 my-4 lg:mx-2 lg:w-24"
linkClasses={`
text-lg lg:text-xl py-1 text-secondary text-center
hover:text-secondary sm:hover:text-secondary-focus hover:cursor-pointer
flex flex-col items-center capitalize`}
/>
<p className="text-neutral-content text-center mt-8">
To learn more about FreeSewing and try our platform
go to <a
href="https://freesewing.org/"
title="Go to FreeSewing.org"
className="text-secondary font-bold"
>freesewing.org</a>
</p>
</div>
</section>
<div>
<div className="max-w-7xl m-auto my-32">
<div className="bg-cover bg-neutral w-full bg-center rounded-lg shadow p-4 "
style={{backgroundImage: "url(/support.jpg)"}}
>
<h2 className="text-neutral-content p-4 text-4xl font-bold sm:font-light sm:text-6xl drop-shadow">Support FreeSewing</h2>
<p className="text-neutral-content p-4 font-bold max-w-md text-lg">
FreeSewing is fuelled by a voluntary subscription model
</p>
<p className="text-neutral-content p-4 font-bold max-w-md text-lg">
If you think what we do is worthwhile,
and if you can spare a few coins each month without hardship,
please support our work
</p>
<a role="button" className="btn btn-accent btn-wide ml-4 mb-8" href="https://freesewing.org/patrons/join">Become a Patron</a>
</div>
</div>
<div className="max-w-7xl m-auto my-32">
<div className="px-8 text-base-content">
<Icons app={app}
active='/'
ulClasses="flex flex-row flex-wrap mt-8 justify-around w-full max-w-6xl"
liClasses="w-1/3 my-4 lg:mx-2 lg:w-24"
linkClasses={`
text-lg lg:text-xl py-1 text-base-content text-center
hover:text-secondary sm:hover:text-secondary-focus hover:cursor-pointer
flex flex-col items-center capitalize`}
/>
</div>
</div>
</div>
</Page>
)
}
export default HomePage
export async function getStaticProps({ locale }) {
return {
@ -12,3 +139,4 @@ export async function getStaticProps({ locale }) {
}

View file

@ -0,0 +1,16 @@
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import PageTemplate from 'site/page-templates/pattern-list.js'
const Page = props => <PageTemplate section='utilities' />
export default Page
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale)),
}
}
}

View file

@ -0,0 +1 @@
../../../freesewing.dev/public/img/splash.jpg

View file

@ -8,14 +8,13 @@
"2.20.1",
"2.20.0",
"2.19.9",
"2.19.8",
"2.19.7",
"2.19.6",
"2.19.5",
"2.19.4",
"2.19.3",
"2.19.2",
"2.19.1",
"2.19.0"
"2.19.0",
"2.18.0",
"2.17.1",
"2.17.0",
"2.16.2",
"2.16.0",
"2.15.4",
"2.15.0"
]

View file

@ -0,0 +1,7 @@
const BoxIcon = ({ className="m-6 h-6" }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
)
export default BoxIcon

View file

@ -0,0 +1,8 @@
const CogIcon = ({ className='h-6 m-6' }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
)
export default CogIcon

View file

@ -1,5 +1,5 @@
const Design = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" stroke="currentColor">
const Design = ({ className='h-6 w-6' }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="currentColor" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={0.75} d="m11.975 2.9104c-1.5285 0-2.7845 1.2563-2.7845 2.7848 0 0.7494 0.30048 1.4389 0.78637 1.9394a0.79437 0.79437 0 0 0 0.0084 0.00839c0.38087 0.38087 0.74541 0.62517 0.94538 0.82483 0.19998 0.19966 0.25013 0.2645 0.25013 0.51907v0.65964l-9.1217 5.2665c-0.28478 0.16442-0.83603 0.46612-1.3165 0.9611-0.48047 0.49498-0.92451 1.3399-0.66684 2.2585 0.22026 0.78524 0.7746 1.3486 1.3416 1.5878 0.56697 0.23928 1.0982 0.23415 1.4685 0.23415h18.041c0.37033 0 0.90158 0.0051 1.4686-0.23415 0.56697-0.23928 1.1215-0.80261 1.3418-1.5878 0.25767-0.91859-0.18662-1.7636-0.66709-2.2585-0.48046-0.49498-1.0315-0.79669-1.3162-0.9611l-8.9844-5.1873v-0.73889c0-0.70372-0.35623-1.2837-0.71653-1.6435-0.35778-0.3572-0.70316-0.58503-0.93768-0.81789-0.20864-0.21601-0.33607-0.50298-0.33607-0.83033 0-0.67 0.52595-1.1962 1.1959-1.1962 0.67001 0 1.1962 0.5262 1.1962 1.1962a0.79429 0.79429 0 0 0 0.79434 0.79427 0.79429 0.79429 0 0 0 0.79427-0.79427c0-1.5285-1.2563-2.7848-2.7848-2.7848zm-0.06859 8.2927 8.9919 5.1914c0.28947 0.16712 0.69347 0.41336 0.94393 0.67138 0.25046 0.25803 0.31301 0.3714 0.24754 0.60483-0.10289 0.36677-0.19003 0.40213-0.35969 0.47373-0.16967 0.07161-0.47013 0.09952-0.80336 0.09952h-18.041c-0.33323 0-0.6337-0.02792-0.80336-0.09952-0.16967-0.07161-0.25675-0.10696-0.35963-0.47373-0.06548-0.23342-0.00303-0.3468 0.24748-0.60483 0.25046-0.25803 0.65471-0.50426 0.94418-0.67138z" />
</svg>
)

View file

@ -1,6 +1,6 @@
/* Sourced from heroicons.com - Thanks guys! */
const Docs = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
const Docs = ({ className="h-6 w-6" }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
)

View file

@ -1,5 +1,5 @@
const Guides = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
const Guides = ({ className="h-6 w-6" }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 3L3 10.53v.98l6.84 2.65L12.48 21h.98L21 3z" />
</svg>
)

View file

@ -1,6 +1,6 @@
/* Sourced from heroicons.com - Thanks guys! */
const Help = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
const Help = ({ className="w-6 h-6" }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
)

View file

@ -1,6 +1,6 @@
/* Sourced from heroicons.com - Thanks guys! */
const Rss = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
const Rss = ({ className='h-6 w-6' }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 5c7.18 0 13 5.82 13 13M6 11a7 7 0 017 7m-6 0a1 1 0 11-2 0 1 1 0 012 0z" />
</svg>
)

View file

@ -1,5 +1,5 @@
const Tutorial = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
const Tutorial = ({ className="w-6 h-6" }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path d="M12 14l9-5-9-5-9 5 9 5z" />
<path d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222" />

View file

@ -74,8 +74,9 @@ const asideClasses = `
overflow-y-scroll
z-20
bg-base-100 text-base-content
sm:bg-neutral sm:bg-opacity-95 sm:text-neutral-content
transition-all `
transition-all
xl:w-1/4
`
const DefaultLayout = ({
@ -110,53 +111,58 @@ const DefaultLayout = ({
bg-base-100
`}>
<Header app={app} setSearch={setSearch} />
<main className={`
grow flex flex-row
gap-2
${!workbench ? 'lg:gap-8 xl:gap-16' : ''}
`}>
<aside className={`
${asideClasses}
${app.primaryMenu ? '' : 'translate-x-[-100%]'} transition-transform
sm:flex-row-reverse
${workbench && collapsePrimaryNav
? 'sm:px-0 sm:w-16'
: 'sm:px-1 md:px-4 lg:px-8 sm:w-[38.2%]'
}
`}>
{workbench && (
<div className={`hidden sm:flex`}>
<button
className="text-secondary-focus h-full px-2 pl-4 hover:animate-pulse"
onClick={() => setCollapsePrimaryNav(!collapsePrimaryNav)}
>
{collapsePrimaryNav
? <><Right /><Right /><Right /></>
: <><Left /><Left /><Left /></>
}
</button>
</div>
)}
<PrimaryNavigation app={app} active={slug}/>
</aside>
<section className={`
p-4 pt-24 sm:pt-28 w-full
sm:px-1 md:px-4 lg:px-8
${workbench && collapsePrimaryNav
? ''
: 'max-w-61.8%'
}
`}>
<div className={workbench ? '' : "max-w-5xl"}>
{title && (
<>
<Breadcrumbs app={app} slug={slug} title={title} />
<PageTitle app={app} slug={slug} title={title} />
</>
<main className="grow bg-base-100">
<div className="m-auto flex flex-row justify-center">
<aside className={`
${asideClasses}
${app.primaryMenu ? '' : 'translate-x-[-100%]'} transition-transform
sm:flex-row-reverse
${workbench && collapsePrimaryNav
? 'sm:px-0 sm:w-16'
: 'sm:px-1 md:px-4 lg:px-8'
}
w-96
`}>
{workbench && (
<div className={`hidden sm:flex`}>
<button
className="text-secondary-focus h-full px-2 pl-4 hover:animate-pulse"
onClick={() => setCollapsePrimaryNav(!collapsePrimaryNav)}
>
{collapsePrimaryNav
? <><Right /><Right /><Right /></>
: <><Left /><Left /><Left /></>
}
</button>
</div>
)}
{children}
<PrimaryNavigation app={app} active={slug}/>
</aside>
<div className="p-0 m-0 bg-base-100">
<section className={`
p-4 pt-24 sm:pt-28
sm:px-1 md:px-4 lg:px-8
${workbench && collapsePrimaryNav
? ''
: 'max-w-7xl'
}
`}>
<div>
{title && (
<>
<Breadcrumbs app={app} slug={slug} title={title} />
<PageTitle app={app} slug={slug} title={title} />
</>
)}
{children}
</div>
</section>
</div>
</section>
</div>
{workbench && AltMenu && (
<aside className={`
${asideClasses}

View file

@ -9,10 +9,10 @@ const LocalePicker = ({ app }) => {
const router = useRouter()
return (
<div className="dropdown">
<div className="dropdown w-full md:w-auto">
<div tabIndex="0" className={`
m-0 btn btn-neutral flex flex-row gap-2
sm:btn-ghost
m-0 btn btn-neutral flex flex-row gap-2 btn-outline
md:btn-ghost
hover:bg-neutral hover:border-neutral-content
`}>
<LocaleIcon />

View file

@ -17,7 +17,7 @@ const Highlight = (props) => {
: 'txt'
const preProps = {
className: `language-${language} hljs text-base lg:text-lg whitespace-pre-wrap break-words`
className: `language-${language} hljs text-base lg:text-lg whitespace-pre-wrap break-all`
}
if (props.raw) preProps.dangerouslySetInnerHTML = { __html: props.raw }

View file

@ -0,0 +1,26 @@
import PrimaryNavigation from './primary'
const Aside = ({ app, slug, mobileOnly=false, before=[], after=[]}) => (
<aside className={`
fixed top-0 right-0 h-screen w-screen
overflow-y-auto z-20
bg-base-100 text-base-content md:bg-base-50
transition-all
${app.primaryMenu ? '' : 'translate-x-[-100%]'} transition-transform
md:flex md:sticky md:flex-row-reverse
md:relative md:transform-none
px-8 py-24
shrink-0
md:w-24 md:px-2 md:justify-center
lg:w-max lg:pr-2 lg:border-r-2
xl:w-max xl:border-0
2xl:pr-8
${mobileOnly ? 'block md:hidden' : ''}
`}>
{before}
<PrimaryNavigation app={app} active={slug}/>
{after}
</aside>
)
export default Aside

View file

@ -1,11 +1,13 @@
import Link from 'next/link'
import orderBy from 'lodash.orderby'
import ThemePicker from 'shared/components/theme-picker.js'
import RssIcon from 'shared/components/icons/rss.js'
import TutorialIcon from 'shared/components/icons/tutorial.js'
import GuideIcon from 'shared/components/icons/guide.js'
import HelpIcon from 'shared/components/icons/help.js'
import DocsIcon from 'shared/components/icons/docs.js'
import DesignIcon from 'shared/components/icons/design.js'
import BoxIcon from 'shared/components/icons/box.js'
import CogIcon from 'shared/components/icons/cog.js'
// Don't show children for blog and showcase posts
const keepClosed = ['blog', 'showcase', ]
@ -19,11 +21,15 @@ const force = [
// List of icons matched to top-level slug
const icons = {
blog: <RssIcon />,
tutorials: <TutorialIcon />,
guides: <GuideIcon />,
howtos: <HelpIcon />,
reference: <DocsIcon />
blog: (className='') => <RssIcon className={className}/>,
tutorials: (className='') => <TutorialIcon className={className}/>,
guides: (className='') => <GuideIcon className={className}/>,
howtos: (className='') => <HelpIcon className={className}/>,
reference: (className='') => <DocsIcon className={className}/>,
accessories: (className='') => <TutorialIcon className={className}/>,
blocks: (className='') => <BoxIcon className={className}/>,
garments: (className='') => <DesignIcon className={className}/>,
utilities: (className='') => <CogIcon className={className}/>,
}
/* helper method to order nav entries */
@ -33,7 +39,7 @@ const order = obj => orderBy(obj, ['__order', '__title'], ['asc', 'asc'])
// Exported for re-use
export const Chevron = ({w=8, m=2}) => <svg className={`
fill-current opacity-75 w-${w} h-${w} mr-${m}
details-toggle hover:text-secondary sm:hover:text-secondary-focus
details-toggle hover:text-secondary sm:hover:text-secondary
`}
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/>
@ -47,9 +53,9 @@ const currentChildren = current => Object.values(order(current))
// Exported for re-use
export const linkClasses = `text-lg lg:text-xl
py-1
text-base-content sm:text-neutral-content
text-base-content sm:text-base-content
hover:text-secondary
sm:hover:text-secondary-focus
sm:hover:text-secondary
`
// Figure out whether a page is on the path to the active page
@ -76,7 +82,7 @@ const SubLevel = ({ nodes={}, active }) => (
flex flex-row
px-2
text-base-content
sm:text-neutral-content
sm:text-base-content
hover:cursor-row-resize
items-center
`}>
@ -86,16 +92,16 @@ const SubLevel = ({ nodes={}, active }) => (
${linkClasses}
hover:cursor-pointer
hover:border-secondary
sm:hover:border-secondary-focus
sm:hover:border-secondary
${child.__slug === active
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
: 'text-base-content sm:text-neutral-content'
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
: 'text-base-content sm:text-base-content'
}
`}>
<span className={`
text-3xl mr-2 inline-block p-0 leading-3
${child.__slug === active
? 'text-secondary sm:text-secondary-focus translate-y-1'
? 'text-secondary sm:text-secondary translate-y-1'
: 'translate-y-3'
}
`}>
@ -120,15 +126,15 @@ const SubLevel = ({ nodes={}, active }) => (
${linkClasses}
hover:cursor-pointer
hover:border-secondary
sm:hover:border-secondary-focus
sm:hover:border-secondary
${child.__slug === active
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
: 'text-base-content sm:text-neutral-content'
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
: 'text-base-content sm:text-base-content'
}`}>
<span className={`
text-3xl mr-2 inline-block p-0 leading-3
${child.__slug === active
? 'text-secondary sm:text-secondary-focus translate-y-1'
? 'text-secondary sm:text-secondary translate-y-1'
: 'translate-y-3'
}
`}>
@ -154,15 +160,15 @@ const TopLevel = ({ icon, title, nav, current, slug, hasChildren=false, active }
hover:cursor-row-resize
p-2
text-base-content
sm:text-neutral-content
sm:text-base-content
items-center
`}>
<span className="text-secondary-focus">{icon}</span>
<span className="text-secondary">{icon}</span>
<Link href={`/${slug}`}>
<a className={`
grow ${linkClasses} hover:cursor-pointer
${slug === active
? 'text-secondary sm:text-secondary-focus'
? 'text-secondary sm:text-secondary'
: ''
}`}
>
@ -175,12 +181,15 @@ const TopLevel = ({ icon, title, nav, current, slug, hasChildren=false, active }
</details>
)
const Navigation = ({ app, active }) => {
const Navigation = ({ app, active, className='' }) => {
if (!app.navigation) return null
const output = []
for (const page of order(app.navigation)) output.push(<TopLevel
key={page.__slug}
icon={icons[page.__slug] || <span className="text-3xl mr-2 translate-y-3 inline-block p-0 leading-3">&deg;</span>}
icon={icons[page.__slug]
? icons[page.__slug]('w-6 h-6')
: <span className="text-3xl mr-2 translate-y-3 inline-block p-0 leading-3">&deg;</span>
}
title={page.__title}
slug={page.__slug}
hasChildren={keepClosed.indexOf(page.__slug) === -1}
@ -189,13 +198,44 @@ const Navigation = ({ app, active }) => {
active={active}
/>)
return <div className='pb-20'>{output}</div>
return <div className={`pb-20 ${className}`}>{output}</div>
}
const PrimaryMenu = ({ app, active }) => (
<nav className="sm:max-w-lg grow mb-12">
<ThemePicker app={app} className="w-full sm:hidden"/>
<Navigation app={app} active={active} />
export const Icons = ({
app, active,
ulClasses='',
liClasses='',
linkClasses=`grow text-lg lg:text-xl py-1 text-base-content sm:text-base-content
hover:text-secondary sm:hover:text-secondary hover:cursor-pointer
flex flex-col items-center`
}) => {
if (!app.navigation) return null
const output = []
for (const page of order(app.navigation)) {
output.push(
<li key={page.__slug} className={liClasses}>
<Link href={`/${page.__slug}`}>
<a className={linkClasses} title={page.__title}>
{icons[page.__slug]
? icons[page.__slug]('w-14 h-14')
: <HelpIcon />
}
<span className='font-bold'>{page.__title}</span>
</a>
</Link>
</li>
)
}
return <ul className={ulClasses}>{output}</ul>
}
const PrimaryMenu = ({ app, active, before=[], after=[] }) => (
<nav className="mb-12">
{before}
<Icons app={app} ulClasses="hidden md:block lg:hidden flex flex-col items-center"/>
<Navigation app={app} active={active} className="md:hidden lg:block"/>
{after}
</nav>
)

View file

@ -6,10 +6,10 @@ const ThemePicker = ({ app, className }) => {
const { t } = useTranslation(['themes'])
return (
<div className={`dropdown ${className}`}>
<div className={`dropdown ${className} w-full md:w-auto`}>
<div tabIndex="0" className={`
m-0 btn btn-neutral flex flex-row gap-2
sm:btn-ghost
m-0 btn btn-neutral flex flex-row gap-2 btn-outline
md:btn-ghost
hover:bg-neutral hover:border-neutral-content
`}>
<ThemeIcon />

View file

@ -15,7 +15,7 @@ import Part from './part'
* where the browser will render this as zero width and height.
*
* To avoid that, we use the SizeMe which will report the size of the
* grandparent element, and then we wraper our SVG in a div that we
* grandparent element, and then we wrap our SVG in a div that we
* set to this size. This will cause the SVG to fill in that entire
* space, and the pan and zoom to adapt to this size.
*
@ -32,6 +32,7 @@ const SvgWrapper = props => {
<TransformWrapper
minScale={0.1}
centerZoomedOut={true}
wheel={{ activationKeys: ['Control'] }}
>
<TransformComponent>
<div style={{ width: size.width+'px', }}>

View file

@ -6,9 +6,9 @@ import { useTranslation } from 'next-i18next'
const EditCount = props => (
<div className="form-control mb-2 w-full">
<label className="label">
<span className="label-text text-neutral-content">{props.min}</span>
<span className="label-text font-bold text-neutral-content">{props.value}</span>
<span className="label-text text-neutral-content">{props.max}</span>
<span className="label-text text-base-content">{props.min}</span>
<span className="label-text font-bold text-base-content">{props.value}</span>
<span className="label-text text-base-content">{props.max}</span>
</label>
<label className="input-group input-group-sm">
<input
@ -19,7 +19,7 @@ const EditCount = props => (
value={props.value}
onChange={props.handleChange}
/>
<span className="text-neutral-content font-bold">#</span>
<span className="text-base-content font-bold">#</span>
</label>
</div>
)

View file

@ -36,7 +36,7 @@ const DesignOptionList = props => {
? choice === dflt
? 'text-secondary'
: 'text-accent'
: 'text-neutral-content'
: 'text-base-content'
}
`}
>
@ -56,7 +56,7 @@ const DesignOptionList = props => {
disabled={val === dflt}
onClick={reset}
>
<span className={val===dflt ? 'text-neutral' : 'text-accent'}><ClearIcon /></span>
<span className={val===dflt ? 'text-base' : 'text-accent'}><ClearIcon /></span>
</button>
</div>
</div>

View file

@ -7,9 +7,9 @@ import { useTranslation } from 'next-i18next'
const EditOption = props => (
<div className="form-control mb-2 w-full">
<label className="label">
<span className="label-text text-neutral-content">{props.min}{props.suffix}</span>
<span className="label-text font-bold text-neutral-content">{props.value}{props.suffix}</span>
<span className="label-text text-neutral-content">{props.max}{props.suffix}</span>
<span className="label-text text-base-content">{props.min}{props.suffix}</span>
<span className="label-text font-bold text-base-content">{props.value}{props.suffix}</span>
<span className="label-text text-base-content">{props.max}{props.suffix}</span>
</label>
<label className="input-group input-group-sm">
<input
@ -20,7 +20,7 @@ const EditOption = props => (
value={props.value}
onChange={props.handleChange}
/>
<span className="text-neutral-content font-bold">{props.suffix}</span>
<span className="text-base-content font-bold">{props.suffix}</span>
</label>
</div>
)
@ -51,7 +51,7 @@ const DesignOptionPctDeg = props => {
return (
<div className="py-4 mx-6 border-l-2 pl-2">
<p className="m-0 p-0 px-2 mb-2 text-neutral-content opacity-60 italic">
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
{props.ot(`${props.option}.d`)}
</p>
<div className="flex flex-row justify-between">

View file

@ -48,8 +48,7 @@ const WorkbenchMeasurements = ({ app, pattern, gist, updateGist }) => {
<h1>
<span className='capitalize mr-4 opacity-70'>
{pattern.config.name}:
</span>
{t('measurements')}
</span> {t('measurements')}
</h1>
<details open className="my-2">
<summary><h2 className="inline pl-1">{t('cfp:preloadMeasurements')}</h2></summary>

View file

@ -24,7 +24,7 @@ const CoreSettingList = props => {
return (
<div className="py-4 mx-6 border-l-2 pl-2">
<p className="m-0 p-0 px-2 mb-2 text-neutral-content opacity-60 italic">
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
{t(`settings:${props.setting}.d`)}
</p>
<div className="flex flex-row">

View file

@ -27,7 +27,7 @@ const CoreSettingMm = props => {
return (
<div className="py-4 mx-6 border-l-2 pl-2">
<p className="m-0 p-0 px-2 mb-2 text-neutral-content opacity-60 italic">
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
{t(`settings:${props.setting}.d`)}
</p>
<div className="flex flex-row justify-between">

View file

@ -27,7 +27,7 @@ const CoreSettingNr = props => {
return (
<div className="py-4 mx-6 border-l-2 pl-2">
<p className="m-0 p-0 px-2 mb-2 text-neutral-content opacity-60 italic">
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
{t(`settings:${props.setting}.d`)}
</p>
<div className="flex flex-row justify-between">

View file

@ -22,7 +22,7 @@ const CoreSettingOnly = props => {
return (
<div className="py-4 mx-6 border-l-2 pl-2">
<p className="m-0 p-0 px-2 mb-2 text-neutral-content opacity-60 italic">
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
{t(`settings:only.d`)}
</p>
<div className="flex flex-row">

View file

@ -29,7 +29,7 @@ const CoreSettingMm = props => {
return (
<div className="py-4 mx-6 border-l-2 pl-2">
<p className="m-0 p-0 px-2 mb-2 text-neutral-content opacity-60 italic">
<p className="m-0 p-0 px-2 mb-2 text-base-content opacity-60 italic">
{t(`settings:sa.d`)}
</p>
<div className="flex flex-row justify-between">

View file

@ -24,7 +24,7 @@ export const NoSumDiv = props => (
hover:cursor-resize
hover:border-secondary
sm:hover:border-secondary-focus
text-base-content sm:text-neutral-content
text-base-content sm:text-base-content
`}>{props.children}</div>
)
export const SumDiv = props => (
@ -34,7 +34,7 @@ export const SumDiv = props => (
hover:cursor-resize
hover:border-secondary
sm:hover:border-secondary-focus
text-base-content sm:text-neutral-content
text-base-content sm:text-base-content
`}>{props.children}</div>
)
export const Summary = props => (
@ -42,7 +42,7 @@ export const Summary = props => (
flex flex-row
px-2
text-base-content
sm:text-neutral-content
sm:text-base-content
hover:cursor-row-resize
items-center
`}>{props.children}</summary>
@ -53,7 +53,7 @@ export const TopSummary = props => (
hover:cursor-row-resize
p-2
text-base-content
sm:text-neutral-content
sm:text-base-content
items-center
`}>
<span className="text-secondary-focus mr-4">{props.icon || null}</span>
@ -67,7 +67,7 @@ export const SumButton = props => (
w-full justify-between
text-left
text-base-content
sm:text-neutral-content
sm:text-base-content
hover:cursor-pointer
items-center
mr-4
@ -84,7 +84,7 @@ export const SecText = props => props.raw
const WorkbenchMenu = props => {
return (
<nav className="smmax-w-96 grow mb-12">
<nav className="grow mb-12">
<ViewMenu {...props} />
{props.gist?._state?.view === 'draft' && (
<>

View file

@ -9,7 +9,7 @@ const SumButton = props => (
w-full justify-between
text-left
text-base-content
sm:text-neutral-content
sm:text-base-content
hover:cursor-pointer
items-center
mr-4
@ -22,7 +22,7 @@ const SumDiv = (props) => (
hover:cursor-resize
hover:border-secondary
sm:hover:border-secondary-focus
text-base-content sm:text-neutral-content
text-base-content sm:text-base-content
${props.active && 'border-secondary-focus'}
`}>{props.children}</div>
@ -42,7 +42,8 @@ const Option = props => {
{
type: 'option',
option: props.option
}
},
true // Close navigation on mobile
)}>
<SumDiv active={active}>
<span className={`

View file

@ -8,54 +8,54 @@ const View = props => {
{
name: 'measurements',
title: t('measurements'),
onClick: () => props.updateGist(['_state', 'view'], 'measurements')
onClick: () => props.updateGist(['_state', 'view'], 'measurements', true)
},
{
name: 'draft',
title: t('draftPattern', { pattern: props.pattern.config.name }),
onClick: () => props.updateGist(['_state', 'view'], 'draft')
onClick: () => props.updateGist(['_state', 'view'], 'draft', true)
},
{
name: 'test',
title: t('testPattern', { pattern: props.pattern.config.name }),
onClick: () => props.updateGist(['_state', 'view'], 'test')
onClick: () => props.updateGist(['_state', 'view'], 'test', true)
},
{
name: 'printingLayout',
title: t('layoutThing', { thing: props.pattern.config.name })
+ ': ' + t('forPrinting'),
onClick: () => props.updateGist(['_state', 'view'], 'printingLayout')
onClick: () => props.updateGist(['_state', 'view'], 'printingLayout', true)
},
{
name: 'cuttingLayout',
title: t('layoutThing', { thing: props.pattern.config.name })
+ ': ' + t('forCutting'),
onClick: () => props.updateGist(['_state', 'view'], 'cuttingLayout')
onClick: () => props.updateGist(['_state', 'view'], 'cuttingLayout', true)
},
{
name: 'export',
title: t('exportThing', { thing: props.pattern.config.name }),
onClick: () => props.updateGist(['_state', 'view'], 'export')
onClick: () => props.updateGist(['_state', 'view'], 'export', true)
},
{
name: 'events',
title: t('events'),
onClick: () => props.updateGist(['_state', 'view'], 'events')
onClick: () => props.updateGist(['_state', 'view'], 'events', true)
},
{
name: 'yaml',
title: t('YAML'),
onClick: () => props.updateGist(['_state', 'view'], 'yaml')
onClick: () => props.updateGist(['_state', 'view'], 'yaml', true)
},
{
name: 'json',
title: t('JSON'),
onClick: () => props.updateGist(['_state', 'view'], 'json')
onClick: () => props.updateGist(['_state', 'view'], 'json', true)
},
{
name: 'edit',
title: t('editThing', { thing: 'YAML' }),
onClick: () => props.updateGist(['_state', 'view'], 'edit')
onClick: () => props.updateGist(['_state', 'view'], 'edit', true)
},
{
name: 'clear',
@ -71,7 +71,7 @@ const View = props => {
hover:cursor-row-resize
p-2
text-base-content
sm:text-neutral-content
sm:text-base-content
items-center
`}>
<span className="text-secondary-focus mr-4"><MenuIcon /></span>
@ -93,7 +93,7 @@ const View = props => {
capitalize
${entry.name === props.gist?._state?.view
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
: 'text-base-content sm:text-neutral-content'
: 'text-base-content sm:text-base-content'
}
`} onClick={entry.onClick}>
<span className={`

View file

@ -0,0 +1,42 @@
/*
* This is used to wrap a Table of Contents (toc) as returned
* from the mdxLoader method (see shared/mdx/loader.js)
* It is NOT for wrapping plain markdown/mdx
*/
import { useState, useEffect, Fragment } from 'react'
// See: https://mdxjs.com/guides/mdx-on-demand/
import { run } from '@mdx-js/mdx'
import * as runtime from 'react/jsx-runtime.js'
// Components that are available in all MDX
import customComponents from 'shared/components/mdx'
const TocWrapper = ({toc, app}) => {
const [mdxModule, setMdxModule] = useState()
useEffect(() => {
;(async () => {
setMdxModule(await run(toc, runtime))
})()
}, [toc])
// React component for MDX content
const MdxContent = mdxModule ? mdxModule.default : Fragment
return (
<div className={`
mdx mdx-toc text-base-content text-lg lg:text-xl
sticky top-8 max-h-screen overflow-y-auto
max-w-prose
md:border-l-4 md:pl-4 md:mb-8 md:border-base-200
`}
>
{mdxModule && <MdxContent components={customComponents(app)}/>}
</div>
)
}
export default TocWrapper

View file

@ -55,10 +55,10 @@ const hasRequiredMeasurements = (pattern, gist) => {
/*
* This component wraps the workbench and is in charge of
* keeping the gist state, which will trickly down
* keeping the gist state, which will trickle down
* to all workbench subcomponents
*/
const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
const WorkbenchWrapper = ({ app, pattern, preload=false, from=false, layout=false }) => {
// State for gist
const [gist, setGist] = useLocalStorage(`${pattern.config.name}_gist`, defaultGist(pattern, app.locale))
@ -82,10 +82,12 @@ const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
}, [preload, from])
// Helper methods to manage the gist state
const updateGist = (path, content) => {
const updateGist = (path, content, closeNav=false) => {
const newGist = {...gist}
set(newGist, path, content)
setGist(newGist)
// Force close of menu on mobile if it is open
if (closeNav && app.primaryMenu) app.setPrimaryMenu(false)
}
const unsetGist = (path) => {
const newGist = {...gist}
@ -128,14 +130,19 @@ const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
AltMenu: <Menu {...componentProps }/>
}
// Layout to use
const LayoutComponent = layout
? layout
: Layout
const Component = views[gist?._state?.view]
? views[gist._state.view]
: views.welcome
return <Layout {...layoutProps}>
return <LayoutComponent {...layoutProps}>
{messages}
<Component {...componentProps} />
</Layout>
</LayoutComponent>
}
export default WorkbenchWrapper

View file

@ -7,6 +7,7 @@ module.exports = {
'./pages/**/*.js',
'./components/*.js',
'./components/**/*.js',
'./page-templates/*.js',
'../freesewing.shared/components/**/*.js',
],
plugins: [require('daisyui'), require('tailwindcss/nesting')],

View file

@ -10,6 +10,7 @@ import remarkFrontmatter from 'remark-frontmatter'
import remarkGfm from 'remark-gfm'
import remarkCopyLinkedFiles from 'remark-copy-linked-files'
import { remarkIntroPlugin } from './remark-intro-plugin.mjs'
import mdxPluginToc from './mdx-plugin-toc.mjs'
import smartypants from 'remark-smartypants'
// Rehype plugins we want to use
import rehypeHighlight from 'rehype-highlight'
@ -61,7 +62,27 @@ const mdxLoader = async (language, site, slug) => {
})
)
return {mdx, intro}
// This is not ideal as we're adding a second pass but for now it will do
// See: https://github.com/remarkjs/remark-toc/issues/37
const toc = String(
await compile(md, {
outputFormat: 'function-body',
remarkPlugins: [
remarkFrontmatter,
remarkGfm,
smartypants,
[
mdxPluginToc,
{ language: 'en' }
]
],
rehypePlugins: [
rehypeSlug,
],
})
)
return {mdx, intro, toc}
}
export default mdxLoader

View file

@ -0,0 +1,30 @@
import {toc} from 'mdast-util-toc'
const headings = {
en: 'Table of contents',
fr: 'Table des matières',
nl: 'Inhoudsopgave',
es: 'Tabla de contenido',
de: 'Inhaltsverzeichnis'
}
export default function mdxToc(options = {}) {
return (node) => {
const result = toc(node, { heading: false })
if (result.map) node.children = [
{
type: 'heading',
depth: 4,
children: [{
type: 'text',
value: headings[options.language || 'en']
}]
},
result.map
]
else node.children = []
return
}
}

View file

@ -22,6 +22,7 @@
"highlight.js": "^11.4.0",
"lodash.orderby": "^4.6.0",
"lodash.unset": "^4.5.2",
"mdast-util-toc": "^6.1.0",
"react-markdown": "^8.0.0",
"react-sizeme": "^3.0.2",
"react-timeago": "^6.2.1",

View file

@ -89,6 +89,7 @@ export async function getStaticProps({ locale }) {
const l = 'prebuild'
export const prebuildLab = async (site) => {
const promises = []
const availableVersions = {}
// Load config
const versions = JSON.parse(await fs.readFile(
path.resolve('..', 'freesewing.lab', 'versions.json'),
@ -130,6 +131,7 @@ export const prebuildLab = async (site) => {
if (process.env.BUILD_ALL_VERSIONS) {
// Download published versions from unpkg
for (const version of versions) {
if (typeof availableVersions[version] === 'undefined') availableVersions[version] = new Set()
// Assume that if the file is on disk, it's good to go (caching)
const file = path.resolve('..', `freesewing.lab`, 'lib', version, `${design}.mjs`)
let cached
@ -145,6 +147,7 @@ export const prebuildLab = async (site) => {
await fs.mkdir(path.resolve('..', `freesewing.lab`, 'pages', 'v', version), { recursive: true })
const code = (await loadFromUnpkg(design, version))
if (code) {
availableVersions[version].add(design)
promises.push(
fs.writeFile(
path.resolve('..', `freesewing.lab`, 'lib', version, `${design}.mjs`),
@ -156,6 +159,8 @@ export const prebuildLab = async (site) => {
),
)
} else console.log(`No ${version} for ${design}`)
} else {
availableVersions[version].add(design)
}
}
}
@ -163,6 +168,15 @@ export const prebuildLab = async (site) => {
}
if (process.env.BUILD_ALL_VERSIONS) {
// Write available versions file
const av = {}
for (const [v, set] of Object.entries(availableVersions)) av[v] = [...set].sort()
promises.push(
fs.writeFile(
path.resolve('..', `freesewing.lab`, 'available-versions.json'),
JSON.stringify(av, null, 2)
)
)
// Also add version overview pages
for (const version of versions) {
// Assume that if the file is on disk, it's good to go (caching)

View file

@ -35,6 +35,18 @@
.mdx a[aria-hidden="true"] {
text-decoration: none;
}
/* Watch out of P tags in the toc list */
.mdx-toc ul li p {
margin: 0;
padding: 0;
display: inline;
}
/* Prevent inline code style from applying to code blocks */
.mdx code.hljs {
color: inherit;
background-color: inherit;
background-opacity: inherit;
}
/* FreeSewing SVG output styles */
.fs-stroke-fabric { stroke: var(--pattern-fabric); }
@ -206,7 +218,7 @@
}
.theme-gradient.loading {
animation-name: MOVE-BG;
animation-duration: 2s;
animation-duration: 4s;
animation-timing-function: linear;
animation-iteration-count: infinite;
margin-top: 0;
@ -217,7 +229,7 @@
background-position-x: 0;
}
to {
background-position-x: -300px;
background-position-x: -200vw;
}
}

View file

@ -34,11 +34,12 @@ module.exports = {
'--btn-error-content': colors.neutral[50],
'--theme-gradient': `repeating-linear-gradient(
-45deg,
${colors.neutral[300]},
${colors.neutral[300]} 10px,
${colors.neutral[800]} 10px,
${colors.neutral[800]} 20px
90deg,
${colors.violet[900]},
${colors.violet[900]} 40%,
${colors.pink[700]} 65%,
${colors.violet[600]} 90%,
${colors.violet[900]} 100%
)`,
'--code-background-color': '#111',
@ -50,7 +51,7 @@ module.exports = {
'--code-border-width': 2,
'--code-outer-padding': '0 0.5rem',
'--code-inner-padding': '1rem',
'--code-color-keyword': colors.pink['400'],
'--code-color-keyword': colors.yellow['500'],
'--code-font-weight-keyword': 'bold',
'--code-color-entity': colors.violet['400'],
'--code-font-weight-entity': 'bold',

View file

@ -36,11 +36,10 @@ module.exports = {
'--rounded-btn': '0',
'--theme-gradient': `repeating-linear-gradient(
-45deg,
${colors.lime['700']},
${colors.lime['700']} 15px,
${bg} 15px,
${bg} 30px
90deg,
${colors.lime['900']},
${colors.lime['500']} 50%,
${colors.lime['900']} 100%
)`,
'--code-background-color': '#002407',

View file

@ -29,19 +29,19 @@ module.exports = {
'error': colors.red['600'],
'--theme-gradient': `repeating-linear-gradient(
-45deg,
${colors.red[500]},
${colors.red[500]} 20px,
${colors.orange[500]} 20px,
${colors.orange[500]} 40px,
${colors.yellow[400]} 40px,
${colors.yellow[400]} 60px,
${colors.green[500]} 60px,
${colors.green[500]} 80px,
${colors.blue[500]} 80px,
${colors.blue[500]} 100px,
${colors.violet[500]} 100px,
${colors.violet[500]} 120px
90deg,
${colors.red[500]},
${colors.red[500]} 16.66%,
${colors.orange[500]} 16.66%,
${colors.orange[500]} 33.33%,
${colors.yellow[400]} 33.33%,
${colors.yellow[400]} 50%,
${colors.green[500]} 50%,
${colors.green[500]} 66.66%,
${colors.blue[500]} 66.66%,
${colors.blue[500]} 83.33%,
${colors.violet[500]} 83.33%,
${colors.violet[500]} 100%
)`,
'--code-background-color': colors.neutral['800'],

View file

@ -131,11 +131,12 @@ module.exports = {
* This is used as a border & loading indicator
*/
'--theme-gradient': `repeating-linear-gradient(
-45deg,
${colors.neutral[900]} 0,
${colors.neutral[900]} 10px,
${colors.neutral[50]} 10px,
${colors.neutral[50]} 20px
90deg,
${colors.violet[600]} 0,
${colors.violet[500]} 30%,
${colors.yellow[500]} 50%,
${colors.purple[500]} 75%,
${colors.violet[600]} 100%
)`,
/* CODE HIGHLIGHTING COLORS
@ -158,7 +159,7 @@ module.exports = {
/*
* These variables are used to style the highlighted tokens themselves
*/
'--code-color-keyword': colors.pink['400'],
'--code-color-keyword': colors.yellow['500'],
'--code-font-weight-keyword': 'bold',
'--code-color-entity': colors.violet['400'],
'--code-font-weight-entity': 'bold',
@ -168,7 +169,7 @@ module.exports = {
'--code-color-variable': colors.indigo['400'],
'--code-color-comment': colors.neutral['400'],
'--code-color-tag': colors.green['400'],
'--code-color-property': 'inherit',
'--code-color-property': colors.yellow['200'],
'--code-font-weight-property': 'bold',
/* FREESEWING PATTERN THEMEING

View file

@ -14,7 +14,7 @@ module.exports = {
'primary': pink,
'primary-focus': blue,
'primary-content': colors.neutral['900'],
'secondary': blue,
'secondary': colors.sky['500'],
'secondary-focus': pink,
'secondary-content': colors.neutral['900'],
@ -32,17 +32,17 @@ module.exports = {
'error': colors.red['600'],
'--theme-gradient': `repeating-linear-gradient(
-45deg,
90deg,
#77cbf9,
#77cbf9 20px,
#ecadb9 20px,
#ecadb9 40px,
${colors.neutral['50']} 40px,
${colors.neutral['50']} 60px,
#ecadb9 60px,
#ecadb9 80px,
#77cbf9 80px,
#77cbf9 100px
#77cbf9 20%,
#ecadb9 20%,
#ecadb9 40%,
${colors.neutral['50']} 40%,
${colors.neutral['50']} 60%,
#ecadb9 60%,
#ecadb9 80%,
#77cbf9 80%,
#77cbf9 100%
)`,
'--code-background-color': colors.neutral['800'],

View file

@ -9,6 +9,7 @@ import gdpr from './gdpr.yaml'
import i18n from './i18n.yaml'
import intro from './intro.yaml'
import measurements from './measurements.yaml'
import lab from './lab.yaml'
import options from './options/'
import optiongroups from './optiongroups.yaml'
import parts from './parts.yaml'
@ -31,6 +32,7 @@ const topics = {
i18n,
intro,
measurements,
lab,
options,
optiongroups,
parts,

View file

@ -0,0 +1,5 @@
slogan: The FreeSewing lab provides
slogan1: All our pattern designs
slogan2: New & old version
slogan3: Bleeding edge code
slogan4: Unreleased designs

View file

@ -9,6 +9,7 @@ import gdpr from './gdpr.yaml'
import i18n from './i18n.yaml'
import intro from './intro.yaml'
import measurements from './measurements.yaml'
import lab from './lab.yaml'
import options from './options/'
import optiongroups from './optiongroups.yaml'
import parts from './parts.yaml'
@ -31,6 +32,7 @@ const topics = {
i18n,
intro,
measurements,
lab,
options,
optiongroups,
parts,

View file

@ -0,0 +1,5 @@
slogan: The FreeSewing lab provides
slogan1: All our pattern designs
slogan2: New & old version
slogan3: Bleeding edge code
slogan4: Unreleased designs

View file

@ -9,6 +9,7 @@ import gdpr from './gdpr.yaml'
import i18n from './i18n.yaml'
import intro from './intro.yaml'
import measurements from './measurements.yaml'
import lab from './lab.yaml'
import options from './options/'
import optiongroups from './optiongroups.yaml'
import parts from './parts.yaml'
@ -31,6 +32,7 @@ const topics = {
i18n,
intro,
measurements,
lab,
options,
optiongroups,
parts,

View file

@ -0,0 +1,5 @@
slogan: The FreeSewing lab provides
slogan1: All our pattern designs
slogan2: New & old version
slogan3: Bleeding edge code
slogan4: Unreleased designs

View file

@ -9,6 +9,7 @@ import gdpr from './gdpr.yaml'
import i18n from './i18n.yaml'
import intro from './intro.yaml'
import measurements from './measurements.yaml'
import lab from './lab.yaml'
import options from './options/'
import optiongroups from './optiongroups.yaml'
import parts from './parts.yaml'
@ -31,6 +32,7 @@ const topics = {
i18n,
intro,
measurements,
lab,
options,
optiongroups,
parts,

View file

@ -0,0 +1,5 @@
slogan: The FreeSewing lab provides
slogan1: All our pattern designs
slogan2: New & old version
slogan3: Bleeding edge code
slogan4: Unreleased designs

View file

@ -9,6 +9,7 @@ import gdpr from './gdpr.yaml'
import i18n from './i18n.yaml'
import intro from './intro.yaml'
import measurements from './measurements.yaml'
import lab from './lab.yaml'
import options from './options/'
import optiongroups from './optiongroups.yaml'
import parts from './parts.yaml'
@ -31,6 +32,7 @@ const topics = {
i18n,
intro,
measurements,
lab,
options,
optiongroups,
parts,

View file

@ -0,0 +1,5 @@
slogan: The FreeSewing lab provides
slogan1: All our pattern designs
slogan2: New & old version
slogan3: Bleeding edge code
slogan4: Unreleased designs

View file

@ -70,7 +70,7 @@ const app = {
"documentationForEditors": "Dokumentation für Redakteure",
"documentationForTranslators": "Dokumentation für Übersetzer/-innen",
"documentationOverview": "Überblick über die Dokumentation",
"dolls": "Dolls",
"dolls": "Puppen",
"download": "Herunterladen",
"draft": "Entwurf",
"draftPattern": "{pattern} erstellen",
@ -127,7 +127,7 @@ const app = {
"newThing": "Neu {thing}",
"newPatternForModel": "{pattern} für {model} neu erstellen",
"noChanges": "Keine Änderungen",
"no": "No",
"no": "Nein",
"noPasswordPolicy": "Wir haben keine strikten Passwort-Rictlinien",
"noSeamAllowance": "Keine Nahtzugabe",
"notAllOfThisContentIsAvailableInLanguage": "Nicht alle Inhalte sind auf Deutsch verfügbar",
@ -228,7 +228,7 @@ const app = {
"withBreasts": "Mit Brüsten",
"withoutBreasts": "Ohne Brüste",
"yay": "Juhuu!",
"yes": "Yes",
"yes": "Ja",
"youAreAPatron": "Du bist ein/e Förder/in",
"youAreNotAPatron": "Du bist kein/e Förder/in",
"youAreNotLoggedIn": "Du bist nicht eingeloggt",

View file

@ -3,9 +3,9 @@
* All edits will be overwritten on the next (pre)build
*/
const common = {
"designs": "Designs",
"sloganCome": "Come for the sewing patterns",
"sloganStay": "Stay for the community"
"designs": "Entwurf",
"sloganCome": "Komm für die Schnittmuster",
"sloganStay": "Bleib für die Community"
}
export default common

View file

@ -16,6 +16,7 @@ import gdpr from "./gdpr.mjs"
import i18n from "./i18n.mjs"
import intro from "./intro.mjs"
import jargon from "./jargon.mjs"
import lab from "./lab.mjs"
import measurements from "./measurements.mjs"
import optiongroups from "./optiongroups.mjs"
import o_aaron from "./o_aaron.mjs"
@ -79,6 +80,7 @@ const allNamespaces = {
i18n,
intro,
jargon,
lab,
measurements,
optiongroups,
o_aaron,

View file

@ -9,7 +9,7 @@ const jargon = {
"darts.d": "<a href='/docs/sewing/darts/'>Abnäher</a> in der <a href='/docs/sewing'>Dokumentation zum Nähen</a>",
"doubleWeltPockets.d": "Siehe <a href='/docs/sewing/double-welt-pockets/'>Doppelpaspeltasche</a> in der <a href='/docs/sewing'>Dokumentation zum Nähen</a>",
"ease.d": "Siehe <a href='/docs/sewing/ease/'>Zugabe</a> in der <a href='/docs/sewing'>Dokumentation zum Nähen</a>",
"edgestitch.d": "See <a href='/docs/sewing/edgestitching/'>Edgestitching</a> in the <a href='/docs/sewing'>Sewing documentation</a>",
"edgestitch.d": "Siehe <a href='/docs/sewing/edgestitching/'>Nähte absteppen</a> in der <a href='/docs/sewing'>Dokumentation zum Nähen</a>",
"fabricGrain.d": "Siehe <a href='/docs/sewing/fabric-grain/'>Fadenlauf</a> in der <a href='/docs/sewing'>Dokumentation zum Nähen</a>",
"goodSidesTogether.d": "Siehe <a href='/docs/sewing/good-sides-together/'>rechts auf rechts</a> in der <a href='/docs/sewing/'>Dokumentation zum Nähen</a>",
"onTheFold.d": "Siehe <a href='/docs/sewing/on-the-fold/'>Im Stoffbruch</a> in der <a href='/docs/sewing/'>Dokumentation zum Nähen</a>",

View file

@ -0,0 +1,13 @@
/*
* This file is auto-generated by the prebuild script
* All edits will be overwritten on the next (pre)build
*/
const lab = {
"slogan": "The FreeSewing lab provides",
"slogan1": "All our pattern designs",
"slogan2": "New & old version",
"slogan3": "Bleeding edge code",
"slogan4": "Unreleased designs"
}
export default lab

View file

@ -33,7 +33,7 @@ const measurements = {
"upperLeg": "Oberschenkelumfang",
"waist": "Taillenweite",
"waistBack": "Taillenweite hinten",
"waistToArmhole": "Waist to armhole",
"waistToArmhole": "Taille zu Armloch",
"waistToFloor": "Taille bis Boden",
"waistToHips": "Taille bis Hüfte",
"waistToKnee": "Taille bis Knie",

View file

@ -5,7 +5,7 @@
const o_aaron = {
"armholeDrop.t": "Armlochabsenkung",
"armholeDrop.d": "Senkt das Armloch um diesen Wert. Negative Werte erhöhen es.",
"backlineBend.t": "Armlochform hinten",
"backlineBend.t": "Hintere Armlochform",
"backlineBend.d": "Bestimmt die Form / Krümmung der Rückseite der Armlöcher.",
"hipsEase.t": "Bequemlichkeitszugabe Hüfte",
"hipsEase.d": "Die Menge an Bequemlichkeits-/Bewegungszugabe an deinen Hüften.",

View file

@ -4,55 +4,57 @@
*/
const o_bee = {
"chestEase.t": "Brustzugabe",
"chestEase.d": "Controls the chest ease in the underlying Bella block Bee is based on",
"chestEase.d": "Kontrolliert die Brustzugabe des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert",
"waistEase.t": "Taillenzugabe",
"waistEase.d": "Controls the waist ease in the underlying Bella block Bee is based on",
"bustSpanEase.t": "Büstenumfang leicht",
"bustSpanEase.d": "Controls the bust span ease in the underlying Bella block Bee is based on",
"topDepth.t": "Top Depth",
"topDepth.d": "Controls how far the bikini cup extends upwards",
"bottomCupDepth.t": "Bottom depth",
"bottomCupDepth.d": "Controls how far the bikini cup extends downwards",
"sideDepth.t": "Side depth",
"sideDepth.d": "Controls how far the bikini cup extends towards the side",
"sideCurve.t": "Side curve",
"sideCurve.d": "Controls the curvature of the side of the bikini cup",
"frontCurve.t": "Front curve",
"frontCurve.d": "Controls the curvature of the front of the bikini cup",
"bellaGuide.t": "Show Bella",
"bellaGuide.d": "Shows the outline of the Bella block Bee is based on",
"ties.t": "Ties",
"ties.d": "Whether to includes ties, yes or no",
"bandTieWidth.t": "Band (chest) tie width",
"bandTieWidth.d": "Controls the width of the ties around your chest",
"bandTieLength.t": "Band (chest) tie length",
"bandTieLength.d": "Controls the length of the ties around your chest",
"bandTieEnds.t": "Band (chest) tie ends",
"bandTieEnds.d": "Whether you like straight or pointy ends on the ties around your chest",
"bandTieColours.t": "Band (chest) tie length colours",
"bandTieColours.d": "Whether you want single color ties around your chest, or dual-coloured ones",
"neckTieWidth.t": "Neck tie width",
"neckTieWidth.d": "Controls the width of the ties around your chest",
"neckTieLength.t": "Neck tie length",
"neckTieLength.d": "Controls the length of the ties around your chest",
"neckTieEnds.t": "Neck tie ends",
"neckTieEnds.d": "Whether you like straight or pointy ends on the ties around your chest",
"neckTieColours.t": "Neck tie colours",
"neckTieColours.d": "Whether you want single color ties around your chest, or dual-coloured ones",
"crossBackTies.t": "Cross back ties",
"crossBackTies.d": "Whether you'd like to use the cross back tie variation of Bee",
"bandLength.t": "Band Length (Cross back ties)",
"bandLength.d": "Controls the length of the band around your chest for the cross back ties variation of Bee",
"backDartHeight.t": "Back dart height (Bella)",
"backDartHeight.d": "Controls the back dart height in the underlying Bella block Bee is based on",
"armholeDepth.t": "Armhole depth (Bella)",
"armholeDepth.d": "Controls the armhole depth in the underlying Bella block Bee is based on",
"frontArmholePitchDepth.t": "Front armhole pitch depth (Bella)",
"frontArmholePitchDepth.d": "Controls the front armhole pitch depth in the underlying Bella block Bee is based on",
"frontShoulderWidth.t": "Front shoulder width (Bella)",
"frontShoulderWidth.d": "Controls the front shoulder width in the underlying Bella block Bee is based on",
"highBustWidth.t": "High bust width (Bella)",
"highBustWidth.d": "Controls the high bust width in the underlying Bella block Bee is based on"
"waistEase.d": "Kontrolliert die Taillenzugabe des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert",
"bustSpanEase.t": "Zugabe im Abstand der Brustpunkte",
"bustSpanEase.d": "Kontrolliert die Zugabe des Abstandes der Brustpunkte des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert",
"topDepth.t": "Obere Tiefe",
"topDepth.d": "Kontrolliert, wie weit sich der Bikini-Cup nach oben erstreckt",
"bottomCupDepth.t": "Untere Tiefe",
"bottomCupDepth.d": "Kontrolliert, wie weit sich der Bikini-Cup nach unten erstreckt",
"sideDepth.t": "Seitliche Tiefe",
"sideDepth.d": "Kontrolliert, wie weit sich der Bikini-Cup zur Seite erstreckt",
"sideCurve.t": "Seitliche Kurve",
"sideCurve.d": "Kontrolliert die Krümmung der Seite des Bikini-Cups",
"frontCurve.t": "Vordere Kurve",
"frontCurve.d": "Kontrolliert die Krümmung der Vorderseite des Bikini-Cups",
"bellaGuide.t": "Bella anzeigen",
"bellaGuide.d": "Zeigt die Umrisse des Bella-Grundschnittes an, auf dem Bee basiert",
"ties.t": "Bänder",
"ties.d": "Sollen Bänder hinzugefügt werden, ja oder nein",
"bandTieWidth.t": "Breite des Brustbandes",
"bandTieWidth.d": "Kontrolliert die Breite des Bandes um deinen Brustkorb",
"bandTieLength.t": "Länge des Brustbandes",
"bandTieLength.d": "Kontrolliert die Länge des Bandes um deinen Brustkorb",
"bandTieEnds.t": "Enden des Brustbandes",
"bandTieEnds.d": "Möchtest du gerade oder spitze Enden des Brustbandes?",
"bandTieColours.t": "Farben des Brustbandes",
"bandTieColours.d": "Möchtest du ein- oder mehrfarbige Brustbänder?",
"neckTieWidth.t": "Breite des Nackenträgers",
"neckTieWidth.d": "Steuert die Breite des Nackenträgers",
"neckTieLength.t": "Länge des Nackenträgers",
"neckTieLength.d": "Steuert die Länge des Nackenträgers",
"neckTieEnds.t": "Enden des Nackenträgers",
"neckTieEnds.d": "Möchtest du gerade oder spitze Enden des Nackenträgers?",
"neckTieColours.t": "Farben des Nackenträgers",
"neckTieColours.d": "Möchtest du ein- oder mehrfarbige Nackenträger?",
"crossBackTies.t": "Gekreuzte Rückenträger",
"crossBackTies.d": "Möchtest du die Version von Bee mit gekreuzten Rückenträgern?",
"bandLength.t": "Länge der gekreuzten Rückenträger",
"bandLength.d": "Kontrolliert die Länge Brustbandes für die Variante von Bee mit gekreuzten Rückenträgern",
"backDartHeight.t": "Höhe der Rückenabnäher (Bella)",
"backDartHeight.d": "Kontrolliert die Höhe der Rückenabnäher des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert",
"armholeDepth.t": "Tiefen des Armloches (Bella)",
"armholeDepth.d": "Kontrolliert die Tiefe des Armloches des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert",
"frontArmholePitchDepth.t": "Tiefe des vorderen Armlochdrehpunktes (Bella)",
"frontArmholePitchDepth.d": "Kontrolliert die Tiefe des vorderen Armlochdrehpunktes des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert",
"frontShoulderWidth.t": "Vordere Schulterbreite (Bella)",
"frontShoulderWidth.d": "Kontrolliert die vordere Schulterbreite des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert",
"fullChestEaseReduction.t": "Full chest reduction (Bella)",
"fullChestEaseReduction.d": "Controls the full chest reduction in the underlying Bella block Bee is based on",
"highBustWidth.t": "Hohe Brustweite (Bella)",
"highBustWidth.d": "Kontrolliert die hohe Brustbreite des zugrunde liegenden Grundschnittes \"Bella\" auf welchem Bee basiert"
}
export default o_bee

View file

@ -9,10 +9,10 @@ const o_bella = {
"waistEase.d": "Steuert die Menge an Leichtigkeit in deiner Taille",
"bustSpanEase.t": "Zugabe seitlicher Brustbereich",
"bustSpanEase.d": "Steuert die Größe der (horizontalen) Zugabe, zu deinem seitlichen Brustbereich, \nwenn Brustpunkt berechnet wird.",
"shoulderToShoulderEase.t": "Shoulder to Shoulder ease",
"shoulderToShoulderEase.d": "Controls the amount of ease between your shoulders. Initially set to -.5% because Bella implements a block that is used in the industry.",
"fullChestEaseReduction.t": "Full chest ease reduction",
"fullChestEaseReduction.d": "Allows you to independently reduce the ease around the chest to make it fit tight(er) in that area",
"shoulderToShoulderEase.t": "Zugabe des Schulterabstandes",
"shoulderToShoulderEase.d": "Kontrolliert die Menge an Zugabe zwischen deinen Schultern. Liegt inital bei -0,5%, da Bella ein Grundschnitt erfüllt, welcher in der Branche verwendet wird.",
"fullChestEaseReduction.t": "Verringerung der Brustumfangszugabe",
"fullChestEaseReduction.d": "Ermöglicht es dir den Spielraum an der Brust zu verringern, um dort einen engeren Sitz zu ermöglichen",
"backDartHeight.t": "Höhe Rückenabnäher",
"backDartHeight.d": "Steuert die Höhe des Rückenabnähers",
"bustDartLength.t": "Länge des Brustabnähers",
@ -24,18 +24,18 @@ const o_bella = {
"armholeDepth.t": "Armlochtiefe",
"armholeDepth.d": "Steuert die Tiefe des Armloches",
"backArmholeSlant.t": "Hintere Armlochschiebung",
"backArmholeSlant.d": "Das Rüstungsloch dreht sich leicht um seinen Pitchpunkt",
"backArmholeSlant.d": "Dreht das Armloch leicht in seinem Drehpunkt",
"frontArmholeCurvature.t": "Vordere Armlochkrümmung",
"frontArmholeCurvature.d": "Steuert wie tief das Armloch nach vorne unten ausgeschnitten ist",
"backArmholeCurvature.t": "Hintere Armlochkrümmung",
"backArmholeCurvature.d": "Steuert wie tief das Armloch nach hinten unten ausgeschnitten ist",
"frontArmholePitchDepth.t": "Front-Armloch-Tiefe",
"frontArmholePitchDepth.t": "Vordere Armlochtiefe",
"frontArmholePitchDepth.d": "Stellt die horizontale Position des Vorderarmlochpunktes fest",
"backArmholePitchDepth.t": "Rückenarmloch-Tiefe",
"backArmholePitchDepth.d": "Stellt die horizontale Position des Rückenlochpunktes fest",
"backNeckCutout.t": "Ausschnitt im Nacken",
"backNeckCutout.d": "Steuert wie tief die Halsöffnung auf der Rückseite abgesogen wird",
"backHemSlope.t": "Hinterer Saum Hang",
"backNeckCutout.d": "Steuert, wie tief der Nackenausschnit am Rücken ausfällt",
"backHemSlope.t": "Neigung des hinteren Saumes",
"backHemSlope.d": "Steuert den Hang des Saum auf der Rückseite",
"frontShoulderWidth.t": "Vordere Schulterbreite",
"frontShoulderWidth.d": "Steuert die Schmalheit der vorderen Schultern relativ zum Rücken",

View file

@ -11,8 +11,8 @@ const o_charlie = {
"backPocketWidth.d": "Steuert die Breite der hinteren Tasche",
"backPocketDepth.t": "Tiefe der hinteren Tasche",
"backPocketDepth.d": "Steuert die Tiefe der hinteren Tasche",
"backPocketFacing.t": "Back pocket facing",
"backPocketFacing.d": "Controls whether or not to include facing on the back pockets",
"backPocketFacing.t": "Besatz der hinteren Tasche",
"backPocketFacing.d": "Legt fest, ob die hinteren Taschen Besatz haben sollen oder nicht",
"frontPocketSlantDepth.t": "Vordertasche Schrägtiefe",
"frontPocketSlantDepth.d": "Steuert die Tiefe des (vorder) Taschenschlitzes",
"frontPocketSlantWidth.t": "Fronttasche Schrägbreite",

Some files were not shown because too many files have changed in this diff Show more