Merge branch 'main' into feat/share-via-link

This commit is contained in:
Judicael 2022-08-25 15:48:30 +03:00 committed by GitHub
commit e1861122f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 211 additions and 58 deletions

View file

@ -75,6 +75,6 @@ When you're finished with the changes, create a pull request, also known as a PR
### Your PR is merged! ### Your PR is merged!
Congratulations :tada::tada: The GitHub team thanks you :sparkles:. Congratulations :tada::tada: The tldraw team thanks you :sparkles:.
Once your PR is merged, your contributions will become part of the next tldraw release, and will be visible in the [tldraw app](https://tldraw.com). Once your PR is merged, your contributions will become part of the next tldraw release, and will be visible in the [tldraw app](https://tldraw.com).

View file

@ -36,11 +36,10 @@ This repository is a monorepo containing two packages:
- [**packages/tldraw**](https://github.com/tldraw/tldraw/tree/main/packages/tldraw) contains the source for the [@tldraw/tldraw](https://www.npmjs.com/package/@tldraw/tldraw) package. This is an editor as a React component named `<Tldraw>`. You can use this package to embed the tldraw editor in any React application. - [**packages/tldraw**](https://github.com/tldraw/tldraw/tree/main/packages/tldraw) contains the source for the [@tldraw/tldraw](https://www.npmjs.com/package/@tldraw/tldraw) package. This is an editor as a React component named `<Tldraw>`. You can use this package to embed the tldraw editor in any React application.
- [**packages/core**](https://github.com/tldraw/tldraw/tree/main/packages/core) contains the source for the [@tldraw/core](https://www.npmjs.com/package/@tldraw/core) package. This is a renderer for React components in a canvas-style UI. It is used by `@tldraw/tldraw` as well as several other projects. - [**packages/core**](https://github.com/tldraw/tldraw/tree/main/packages/core) contains the source for the [@tldraw/core](https://www.npmjs.com/package/@tldraw/core) package. This is a renderer for React components in a canvas-style UI. It is used by `@tldraw/tldraw` as well as several other projects.
...and three apps: ...and two apps:
- [**apps/www**](https://github.com/tldraw/tldraw/tree/main/apps/www) contains the source for the [tldraw.com](https://tldraw.com) website. - [**apps/www**](https://github.com/tldraw/tldraw/tree/main/apps/www) contains the source for the [tldraw.com](https://tldraw.com) website.
- [**apps/vscode**](https://github.com/tldraw/tldraw/tree/main/apps/vscode) contains the source for the [tldraw VS Code extension](https://marketplace.visualstudio.com/items?itemName=tldraw-org.tldraw-vscode). - [**apps/vscode**](https://github.com/tldraw/tldraw/tree/main/apps/vscode) contains the source for the [tldraw VS Code extension](https://marketplace.visualstudio.com/items?itemName=tldraw-org.tldraw-vscode).
- [**apps/electron**](https://github.com/tldraw/tldraw/tree/main/apps/electron) contains the source for an experimental Electron app.
...and three examples: ...and three examples:

View file

@ -24,9 +24,9 @@ npm i @tldraw/core
There are two examples in this repository. There are two examples in this repository.
The **simple** example in the `example` folder shows a minimal use of the library. It does not do much but this should be a good reference for the API without too much else built on top. The **simple** example in [`examples/core-example`](https://github.com/tldraw/tldraw/tree/main/examples/core-example) shows a minimal use of the library. It does not do much but this should be a good reference for the API without too much else built on top.
The **advanced** example in the `example-advanced` folder shows a more realistic use of the library. (Try it [here](https://core-steveruiz.vercel.app/)). While the fundamental patterns are the same, this example contains features such as: panning, pinching, and zooming the camera; creating, cloning, resizing, and deleting shapes; keyboard shortcuts, brush-selection; shape-snapping; undo, redo; and more. Much of the code in the advanced example comes from the [@tldraw/tldraw](https://tldraw.com) codebase. The **advanced** example in [`examples/core-example-advanced`](https://github.com/tldraw/tldraw/tree/main/examples/core-example-advanced) shows a more realistic use of the library. (Try it [here](https://core-steveruiz.vercel.app/)). While the fundamental patterns are the same, this example contains features such as: panning, pinching, and zooming the camera; creating, cloning, resizing, and deleting shapes; keyboard shortcuts, brush-selection; shape-snapping; undo, redo; and more. Much of the code in the advanced example comes from the [@tldraw/tldraw](https://tldraw.com) codebase.
If you're working on an app that uses this library, I recommend referring back to the advanced example for tips on how you might implement these features for your own project. If you're working on an app that uses this library, I recommend referring back to the advanced example for tips on how you might implement these features for your own project.

View file

@ -54,7 +54,7 @@ function addToShapeTree<T extends TLShape, M extends Record<string, unknown>>(
shape.children shape.children
.map((id) => shapes[id]) .map((id) => shapes[id])
.filter(Boolean) // TODO: Find cases where shapes would be missing. .filter((childShape) => shapes[childShape.id]) // TODO: Find cases where shapes would be missing.
.sort((a, b) => a.childIndex - b.childIndex) .sort((a, b) => a.childIndex - b.childIndex)
.forEach((childShape) => .forEach((childShape) =>
addToShapeTree( addToShapeTree(

View file

@ -17,11 +17,10 @@ This repository is a monorepo containing two packages:
- [**packages/tldraw**](https://github.com/tldraw/tldraw/tree/main/packages/tldraw) contains the source for the [@tldraw/tldraw](https://www.npmjs.com/package/@tldraw/tldraw) package. This is an editor as a React component named `<Tldraw>`. You can use this package to embed the tldraw editor in any React application. - [**packages/tldraw**](https://github.com/tldraw/tldraw/tree/main/packages/tldraw) contains the source for the [@tldraw/tldraw](https://www.npmjs.com/package/@tldraw/tldraw) package. This is an editor as a React component named `<Tldraw>`. You can use this package to embed the tldraw editor in any React application.
- [**packages/core**](https://github.com/tldraw/tldraw/tree/main/packages/core) contains the source for the [@tldraw/core](https://www.npmjs.com/package/@tldraw/core) package. This is a renderer for React components in a canvas-style UI. It is used by `@tldraw/tldraw` as well as several other projects. - [**packages/core**](https://github.com/tldraw/tldraw/tree/main/packages/core) contains the source for the [@tldraw/core](https://www.npmjs.com/package/@tldraw/core) package. This is a renderer for React components in a canvas-style UI. It is used by `@tldraw/tldraw` as well as several other projects.
...and three apps: ...and two apps:
- [**apps/www**](https://github.com/tldraw/tldraw/tree/main/apps/www) contains the source for the [tldraw.com](https://tldraw.com) website. - [**apps/www**](https://github.com/tldraw/tldraw/tree/main/apps/www) contains the source for the [tldraw.com](https://tldraw.com) website.
- [**apps/vscode**](https://github.com/tldraw/tldraw/tree/main/apps/vscode) contains the source for the [tldraw VS Code extension](https://marketplace.visualstudio.com/items?itemName=tldraw-org.tldraw-vscode). - [**apps/vscode**](https://github.com/tldraw/tldraw/tree/main/apps/vscode) contains the source for the [tldraw VS Code extension](https://marketplace.visualstudio.com/items?itemName=tldraw-org.tldraw-vscode).
- [**apps/electron**](https://github.com/tldraw/tldraw/tree/main/apps/electron) contains the source for an experimental Electron app.
...and three examples: ...and three examples:

View file

@ -103,6 +103,10 @@ export interface TldrawProps extends TDCallbacks {
disableAssets?: boolean disableAssets?: boolean
} }
const isSystemDarkMode = window.matchMedia
? window.matchMedia('(prefers-color-scheme: dark)').matches
: false
export function Tldraw({ export function Tldraw({
id, id,
document, document,
@ -117,7 +121,7 @@ export function Tldraw({
showUI = true, showUI = true,
readOnly = false, readOnly = false,
disableAssets = false, disableAssets = false,
darkMode = false, darkMode = isSystemDarkMode,
onMount, onMount,
onChange, onChange,
onChangePresence, onChangePresence,

View file

@ -1,5 +1,6 @@
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
import * as React from 'react' import * as React from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { DialogState, useDialog } from '~hooks' import { DialogState, useDialog } from '~hooks'
import { styled } from '~styles' import { styled } from '~styles'
@ -42,13 +43,13 @@ export const AlertDialogDescription = StyledDescription
export const AlertDialogAction = AlertDialogPrimitive.Action export const AlertDialogAction = AlertDialogPrimitive.Action
export const AlertDialogCancel = AlertDialogPrimitive.Cancel export const AlertDialogCancel = AlertDialogPrimitive.Cancel
const descriptions: Record<DialogState, string> = {
saveFirstTime: 'Do you want to save your current project?',
saveAgain: 'Do you want to save changes to your current project?',
}
export const AlertDialog = ({ container }: { container: any }) => { export const AlertDialog = ({ container }: { container: any }) => {
const { setDialogState, dialogState, onCancel, onNo, onYes } = useDialog() const { setDialogState, dialogState, onCancel, onNo, onYes } = useDialog()
const intl = useIntl()
const descriptions: Record<DialogState, string> = {
saveFirstTime: intl.formatMessage({ id: 'dialog.save.firsttime' }),
saveAgain: intl.formatMessage({ id: 'dialog.save.again' }),
}
return ( return (
<AlertDialogRoot open={dialogState !== null}> <AlertDialogRoot open={dialogState !== null}>
@ -73,7 +74,7 @@ export const AlertDialog = ({ container }: { container: any }) => {
setDialogState(null) setDialogState(null)
}} }}
> >
Cancel <FormattedMessage id="dialog.cancel" />
</Button> </Button>
</AlertDialogCancel> </AlertDialogCancel>
)} )}
@ -86,7 +87,7 @@ export const AlertDialog = ({ container }: { container: any }) => {
setDialogState(null) setDialogState(null)
}} }}
> >
No <FormattedMessage id="dialog.no" />
</Button> </Button>
</AlertDialogAction> </AlertDialogAction>
)} )}
@ -99,7 +100,7 @@ export const AlertDialog = ({ container }: { container: any }) => {
setDialogState(null) setDialogState(null)
}} }}
> >
Yes <FormattedMessage id="dialog.yes" />
</Button> </Button>
</AlertDialogAction> </AlertDialogAction>
)} )}

View file

@ -383,7 +383,8 @@ export class TLDR {
data: TDSnapshot, data: TDSnapshot,
ids: string[], ids: string[],
fn: (shape: T, i: number) => Partial<T> | void, fn: (shape: T, i: number) => Partial<T> | void,
pageId: string pageId: string,
forceChildrenTraversal = false
): { ): {
before: Record<string, Partial<T>> before: Record<string, Partial<T>>
after: Record<string, Partial<T>> after: Record<string, Partial<T>>
@ -392,21 +393,11 @@ export class TLDR {
const beforeShapes: Record<string, Partial<T>> = {} const beforeShapes: Record<string, Partial<T>> = {}
const afterShapes: Record<string, Partial<T>> = {} const afterShapes: Record<string, Partial<T>> = {}
const shapes = data.document.pages?.[data.appState.currentPageId].shapes
const groupShape = shapes?.[ids[0]]
if (ids.length === 1 && groupShape?.type === 'group') {
groupShape.children.forEach((id, i) => {
const shape = TLDR.getShape<T>(data, id, pageId)
if (shape.isLocked) return
const change = fn(shape, i)
if (change) {
beforeShapes[id] = TLDR.getBeforeShape(shape, change)
afterShapes[id] = change
}
})
} else {
ids.forEach((id, i) => { ids.forEach((id, i) => {
const shape = TLDR.getShape<T>(data, id, pageId)
if (shape.isLocked) return
if (shape?.type === 'group' && (ids.length === 1 || forceChildrenTraversal)) {
shape.children.forEach((id, i) => {
const shape = TLDR.getShape<T>(data, id, pageId) const shape = TLDR.getShape<T>(data, id, pageId)
if (shape.isLocked) return if (shape.isLocked) return
const change = fn(shape, i) const change = fn(shape, i)
@ -416,6 +407,12 @@ export class TLDR {
} }
}) })
} }
const change = fn(shape, i)
if (change) {
beforeShapes[id] = TLDR.getBeforeShape(shape, change)
afterShapes[id] = change
}
})
const dataWithMutations = Utils.deepMerge(data, { const dataWithMutations = Utils.deepMerge(data, {
document: { document: {

View file

@ -48,7 +48,8 @@ export function alignShapes(app: TldrawApp, ids: string[], type: AlignType): Tld
if (!deltaMap[shape.id]) return shape if (!deltaMap[shape.id]) return shape
return { point: deltaMap[shape.id].next } return { point: deltaMap[shape.id].next }
}, },
currentPageId currentPageId,
false
) )
initialShapes.forEach((shape) => { initialShapes.forEach((shape) => {

View file

@ -18,7 +18,7 @@ export function distributeShapes(
const { before, after } = TLDR.mutateShapes( const { before, after } = TLDR.mutateShapes(
app.state, app.state,
ids.filter((id) => deltaMap[id] !== undefined), ids.filter((id) => deltaMap[id] !== undefined),
(shape) => ({ point: deltaMap[shape.id].next }), (shape) => ({ point: deltaMap[shape.id]?.next }),
currentPageId currentPageId
) )

View file

@ -3,13 +3,15 @@ import type { TldrawApp } from '~state/TldrawApp'
import type { TldrawCommand } from '~types' import type { TldrawCommand } from '~types'
export function duplicatePage(app: TldrawApp, pageId: string): TldrawCommand { export function duplicatePage(app: TldrawApp, pageId: string): TldrawCommand {
const newId = Utils.uniqueId()
const { const {
currentPageId, currentPageId,
page,
pageState: { camera }, pageState: { camera },
} = app } = app
const page = app.document.pages[pageId]
const newId = Utils.uniqueId()
const nextPage = { const nextPage = {
...page, ...page,
id: newId, id: newId,
@ -20,7 +22,7 @@ export function duplicatePage(app: TldrawApp, pageId: string): TldrawCommand {
id, id,
{ {
...shape, ...shape,
parentId: shape.parentId === pageId ? newId : shape.parentId, parentId: shape.parentId === page.id ? newId : shape.parentId,
}, },
] ]
}) })

View file

@ -89,7 +89,7 @@ describe('Flip command', () => {
expect(app.getShape<RectangleShape>('rect1').point).toStrictEqual([100, 0]) expect(app.getShape<RectangleShape>('rect1').point).toStrictEqual([100, 0])
expect(app.getShape<RectangleShape>('rect2').point).toStrictEqual([0, 100]) expect(app.getShape<RectangleShape>('rect2').point).toStrictEqual([0, 100])
}) })
it('stays in the same point when the grouped shape is selected with other shape', () => { it('move the grouped shape when flipped with other shape', () => {
app.group( app.group(
[app.getShape<RectangleShape>('rect1').id, app.getShape<RectangleShape>('rect2').id], [app.getShape<RectangleShape>('rect1').id, app.getShape<RectangleShape>('rect2').id],
'groupId' 'groupId'

View file

@ -1,8 +1,8 @@
import { TLBoundsCorner, Utils } from '@tldraw/core'
import { TLDR } from '~state/TLDR' import { TLDR } from '~state/TLDR'
import type { TldrawApp } from '~state/TldrawApp' import type { TldrawApp } from '~state/TldrawApp'
import { FlipType } from '~types' import { FlipType } from '~types'
import type { TldrawCommand } from '~types' import type { TldrawCommand } from '~types'
import { TLBoundsCorner, Utils } from '@tldraw/core'
export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): TldrawCommand { export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): TldrawCommand {
const { const {
@ -13,6 +13,8 @@ export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): Tldra
const boundsForShapes = ids.map((id) => TLDR.getBounds(shapes[id])) const boundsForShapes = ids.map((id) => TLDR.getBounds(shapes[id]))
const isSinglySelectedGroup = ids.length === 1 && shapes[ids[0]].type === 'group'
const commonBounds = Utils.getCommonBounds(boundsForShapes) const commonBounds = Utils.getCommonBounds(boundsForShapes)
const { before, after } = TLDR.mutateShapes( const { before, after } = TLDR.mutateShapes(
@ -20,8 +22,33 @@ export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): Tldra
ids, ids,
(shape) => { (shape) => {
const shapeBounds = TLDR.getBounds(shape) const shapeBounds = TLDR.getBounds(shape)
const isChildOfGroup = shape.parentId !== currentPageId
switch (type) { switch (type) {
case FlipType.Horizontal: { case FlipType.Horizontal: {
if (isChildOfGroup && !isSinglySelectedGroup) {
// do translation of this child
const groupBounds = TLDR.getBounds(shapes[shape.parentId])
const newGroupBounds = Utils.getRelativeTransformedBoundingBox(
commonBounds,
commonBounds,
groupBounds,
true,
false
)
const dx = newGroupBounds.minX - groupBounds.minX
return TLDR.getShapeUtil(shape).transform(
shape,
{ ...shapeBounds, minX: shapeBounds.minX + dx, maxX: shapeBounds.maxX + dx },
{
type: TLBoundsCorner.TopLeft,
scaleX: 1,
scaleY: 1,
initialShape: shape,
transformOrigin: [0.5, 0.5],
}
)
}
const newShapeBounds = Utils.getRelativeTransformedBoundingBox( const newShapeBounds = Utils.getRelativeTransformedBoundingBox(
commonBounds, commonBounds,
commonBounds, commonBounds,
@ -39,6 +66,29 @@ export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): Tldra
}) })
} }
case FlipType.Vertical: { case FlipType.Vertical: {
if (isChildOfGroup && !isSinglySelectedGroup) {
// do translation of this child
const groupBounds = TLDR.getBounds(shapes[shape.parentId])
const newGroupBounds = Utils.getRelativeTransformedBoundingBox(
commonBounds,
commonBounds,
groupBounds,
false,
true
)
const dy = newGroupBounds.minY - groupBounds.minY
return TLDR.getShapeUtil(shape).transform(
shape,
{ ...shapeBounds, minY: shapeBounds.minY + dy, maxY: shapeBounds.maxY + dy },
{
type: TLBoundsCorner.TopLeft,
scaleX: 1,
scaleY: 1,
initialShape: shape,
transformOrigin: [0.5, 0.5],
}
)
}
const newShapeBounds = Utils.getRelativeTransformedBoundingBox( const newShapeBounds = Utils.getRelativeTransformedBoundingBox(
commonBounds, commonBounds,
commonBounds, commonBounds,
@ -57,7 +107,8 @@ export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): Tldra
} }
} }
}, },
currentPageId currentPageId,
true
) )
return { return {

View file

@ -122,4 +122,9 @@
"copy.current.page.link": "Copy Current Page Link", "copy.current.page.link": "Copy Current Page Link",
"copy.project.link": "Copy Project Link", "copy.project.link": "Copy Project Link",
"data.too.big.encoded": "Data is too big to be encoded into an URL" "data.too.big.encoded": "Data is too big to be encoded into an URL"
"dialog.save.firsttime": "Do you want to save your current project?",
"dialog.save.again": "Do you want to save changes to your current project?",
"dialog.cancel": "Cancel",
"dialog.no": "No",
"dialog.yes": "Yes"
} }

View file

@ -0,0 +1,87 @@
{
"style.menu.color": "Cor",
"style.menu.fill": "Preencher",
"style.menu.dash": "Traço",
"style.menu.size": "Tamanho",
"style.menu.keep.open": "Manter aberto",
"style.menu.font": "Fonte",
"style.menu.align": "Alinhamento",
"styles": "Estilos",
"zoom.in": "Aumentar zoom",
"zoom.out": "Diminuir zoom",
"to": "para",
"menu.file": "Ficheiro",
"menu.edit": "Editar",
"menu.view": "Visualizar",
"menu.preferences": "Preferências",
"menu.sign.in": "Entrar",
"menu.sign.out": "Sair",
"become.a.sponsor": "Torne-se um patrocinador",
"zoom.to.selection": "Zoom na seleção",
"zoom.to.fit": "Zoom para caber",
"zoom.to": "Zoom para",
"preferences.dark.mode": "Modo Escuro",
"preferences.focus.mode": "Modo Foco",
"preferences.debug.mode": "Modo Debug",
"preferences.show.grid": "Mostrar Grelha",
"preferences.use.cad.selection": "Usar seleção CAD",
"preferences.keep.stylemenu.open": "Manter Menu de Estilos Aberto",
"preferences.always.show.snaps": "Mostrar Pontos de Ajuste",
"preferences.rotate.handles": "Controlo de Rotação",
"preferences.binding.handles": "Controlo de Binds",
"preferences.clone.handles": "Controlo de Clone",
"undo": "Desfazer",
"redo": "Refazer",
"cut": "Cortar",
"copy": "Copiar",
"paste": "Colar",
"copy.as": "Copiar como",
"export.as": "Exportar como",
"select.all": "Selecionar todos",
"select.none": "Selecionar nenhum",
"delete": "Apagar",
"new.project": "Novo Projeto",
"open": "Abrir",
"save": "Salvar",
"save.as": "Salvar Como",
"upload.media": "Upload Média",
"create.page": "Criar Página",
"new.page": "Nova Página",
"page.name": "Nome da Página",
"duplicate": "Duplicar",
"cancel": "Cancelar",
"copy.invite.link": "Copiar Link de Convite",
"create.multiplayer.project": "Criar um Projeto Multi-Utilizador",
"copy.multiplayer.project": "Copiar num Projeto Multi-Utilizador",
"select": "Selecionar",
"eraser": "Borracha",
"draw": "Desenhar",
"arrow": "Seta",
"text": "Texto",
"sticky": "Post-it",
"rectangle": "Retângulo",
"ellipse": "Elipse",
"triangle": "Triângulo",
"line": "Linha",
"rotate": "Rodar",
"lock.aspect.ratio": "Trancar a Proporção",
"unlock.aspect.ratio": "Destrancar a Proporção",
"group": "Agrupar",
"ungroup": "Desagrupar",
"move.to.back": "Colocar no Fundo",
"move.backward": "Mover abaixo",
"move.forward": "Mover acima",
"move.to.front": "Colocar à Frente",
"reset.angle": "Reiniciar Ângulo",
"lock": "Trancar",
"unlock": "Destrancar",
"move.to.page": "Mover para Página",
"flip.horizontal": "Inverter Horizontalmente",
"flip.vertical": "Inverter Verticalmente",
"move": "Mover",
"to.front": "Para Frente",
"forward": "Avançar",
"backward": "Recuar",
"back": "Voltar",
"language": "Língua"
}

View file

@ -1,6 +1,6 @@
{ {
"style.menu.color": "Färg", "style.menu.color": "Färg",
"style.menu.fill": "Fyll", "style.menu.fill": "Ifylld",
"style.menu.dash": "Streck", "style.menu.dash": "Streck",
"style.menu.size": "Storlek", "style.menu.size": "Storlek",
"style.menu.keep.open": "Håll stilmenyn öppen", "style.menu.keep.open": "Håll stilmenyn öppen",

View file

@ -14,6 +14,7 @@ import ne from './ne.json'
import no from './no.json' import no from './no.json'
import pl from './pl.json' import pl from './pl.json'
import pt_br from './pt-br.json' import pt_br from './pt-br.json'
import pt_pt from './pt-pt.json'
import ru from './ru.json' import ru from './ru.json'
import sv from './sv.json' import sv from './sv.json'
import tr from './tr.json' import tr from './tr.json'
@ -41,12 +42,13 @@ export const TRANSLATIONS: TDTranslations = [
{ locale: 'ne', label: 'नेपाली', messages: ne }, { locale: 'ne', label: 'नेपाली', messages: ne },
{ locale: 'no', label: 'Norwegian', messages: no }, { locale: 'no', label: 'Norwegian', messages: no },
{ locale: 'pl', label: 'Polski', messages: pl }, { locale: 'pl', label: 'Polski', messages: pl },
{ locale: 'pt', label: 'Português - Europeu', messages: pt_pt },
{ locale: 'pt-br', label: 'Português - Brasil', messages: pt_br }, { locale: 'pt-br', label: 'Português - Brasil', messages: pt_br },
{ locale: 'ru', label: 'Russian', messages: ru }, { locale: 'ru', label: 'Russian', messages: ru },
{ locale: 'sv', label: 'Svenska', messages: sv }, { locale: 'sv', label: 'Svenska', messages: sv },
{ locale: 'tr', label: 'Türkçe', messages: tr }, { locale: 'tr', label: 'Türkçe', messages: tr },
{ locale: 'uk', label: 'Ukrainian', messages: uk }, { locale: 'uk', label: 'Ukrainian', messages: uk },
{ locale: 'zh-ch', label: 'Chinese - Simplified', messages: zh_cn }, { locale: 'zh-ch', label: '简体中文', messages: zh_cn },
{ locale: 'zh-tw', label: '繁體中文 (台灣)', messages: zh_tw }, { locale: 'zh-tw', label: '繁體中文 (台灣)', messages: zh_tw },
] ]

View file

@ -44,7 +44,7 @@
"select.all": "选中全部", "select.all": "选中全部",
"select.none": "取消选中", "select.none": "取消选中",
"delete": "删除", "delete": "删除",
"new.project": "新工程", "new.project": "新项目",
"open": "打开", "open": "打开",
"save": "保存", "save": "保存",
"save.as": "保存为", "save.as": "保存为",
@ -55,8 +55,8 @@
"duplicate": "复制", "duplicate": "复制",
"cancel": "取消", "cancel": "取消",
"copy.invite.link": "复制邀请链接", "copy.invite.link": "复制邀请链接",
"create.multiplayer.project": "创建多人工程", "create.multiplayer.project": "创建多人项目",
"copy.multiplayer.project": "复制到多人工程", "copy.multiplayer.project": "复制到多人项目",
"select": "选择", "select": "选择",
"eraser": "橡皮", "eraser": "橡皮",
"draw": "画笔", "draw": "画笔",
@ -103,5 +103,10 @@
"dark": "暗黑", "dark": "暗黑",
"copy.readonly.link": "复制只读链接", "copy.readonly.link": "复制只读链接",
"image": "图片", "image": "图片",
"align.distribute": "对齐 / 分散" "align.distribute": "对齐 / 分散",
"dialog.save.firsttime": "您是否想保存当前的项目?",
"dialog.save.again": "您是否想保存对当前项目的更改?",
"dialog.cancel": "取消",
"dialog.no": "否",
"dialog.yes": "是"
} }

View file

@ -460,7 +460,7 @@ export enum AlignStyle {
export enum FontStyle { export enum FontStyle {
Script = 'script', Script = 'script',
Sans = 'sans', Sans = 'sans',
Serif = 'erif', Serif = 'serif',
Mono = 'mono', Mono = 'mono',
} }

View file

@ -517,7 +517,7 @@ export class Vec {
} }
/** /**
* Get an array of points (with simulated pressure) between two points. * Get an array of points between two points.
* @param A The first point. * @param A The first point.
* @param B The second point. * @param B The second point.
* @param steps The number of points to return. * @param steps The number of points to return.