Merge branch 'main' into feat/share-via-link
This commit is contained in:
commit
e1861122f6
20 changed files with 211 additions and 58 deletions
|
@ -75,6 +75,6 @@ When you're finished with the changes, create a pull request, also known as a PR
|
|||
|
||||
### 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).
|
||||
|
|
|
@ -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/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/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:
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ npm i @tldraw/core
|
|||
|
||||
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.
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ function addToShapeTree<T extends TLShape, M extends Record<string, unknown>>(
|
|||
|
||||
shape.children
|
||||
.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)
|
||||
.forEach((childShape) =>
|
||||
addToShapeTree(
|
||||
|
|
|
@ -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/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/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:
|
||||
|
||||
|
|
|
@ -103,6 +103,10 @@ export interface TldrawProps extends TDCallbacks {
|
|||
disableAssets?: boolean
|
||||
}
|
||||
|
||||
const isSystemDarkMode = window.matchMedia
|
||||
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
: false
|
||||
|
||||
export function Tldraw({
|
||||
id,
|
||||
document,
|
||||
|
@ -117,7 +121,7 @@ export function Tldraw({
|
|||
showUI = true,
|
||||
readOnly = false,
|
||||
disableAssets = false,
|
||||
darkMode = false,
|
||||
darkMode = isSystemDarkMode,
|
||||
onMount,
|
||||
onChange,
|
||||
onChangePresence,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
|
||||
import * as React from 'react'
|
||||
import { FormattedMessage, useIntl } from 'react-intl'
|
||||
import { DialogState, useDialog } from '~hooks'
|
||||
import { styled } from '~styles'
|
||||
|
||||
|
@ -42,13 +43,13 @@ export const AlertDialogDescription = StyledDescription
|
|||
export const AlertDialogAction = AlertDialogPrimitive.Action
|
||||
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 }) => {
|
||||
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 (
|
||||
<AlertDialogRoot open={dialogState !== null}>
|
||||
|
@ -73,7 +74,7 @@ export const AlertDialog = ({ container }: { container: any }) => {
|
|||
setDialogState(null)
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
<FormattedMessage id="dialog.cancel" />
|
||||
</Button>
|
||||
</AlertDialogCancel>
|
||||
)}
|
||||
|
@ -86,7 +87,7 @@ export const AlertDialog = ({ container }: { container: any }) => {
|
|||
setDialogState(null)
|
||||
}}
|
||||
>
|
||||
No
|
||||
<FormattedMessage id="dialog.no" />
|
||||
</Button>
|
||||
</AlertDialogAction>
|
||||
)}
|
||||
|
@ -99,7 +100,7 @@ export const AlertDialog = ({ container }: { container: any }) => {
|
|||
setDialogState(null)
|
||||
}}
|
||||
>
|
||||
Yes
|
||||
<FormattedMessage id="dialog.yes" />
|
||||
</Button>
|
||||
</AlertDialogAction>
|
||||
)}
|
||||
|
|
|
@ -383,7 +383,8 @@ export class TLDR {
|
|||
data: TDSnapshot,
|
||||
ids: string[],
|
||||
fn: (shape: T, i: number) => Partial<T> | void,
|
||||
pageId: string
|
||||
pageId: string,
|
||||
forceChildrenTraversal = false
|
||||
): {
|
||||
before: Record<string, Partial<T>>
|
||||
after: Record<string, Partial<T>>
|
||||
|
@ -392,21 +393,11 @@ export class TLDR {
|
|||
const beforeShapes: 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) => {
|
||||
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)
|
||||
if (shape.isLocked) return
|
||||
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, {
|
||||
document: {
|
||||
|
|
|
@ -48,7 +48,8 @@ export function alignShapes(app: TldrawApp, ids: string[], type: AlignType): Tld
|
|||
if (!deltaMap[shape.id]) return shape
|
||||
return { point: deltaMap[shape.id].next }
|
||||
},
|
||||
currentPageId
|
||||
currentPageId,
|
||||
false
|
||||
)
|
||||
|
||||
initialShapes.forEach((shape) => {
|
||||
|
|
|
@ -18,7 +18,7 @@ export function distributeShapes(
|
|||
const { before, after } = TLDR.mutateShapes(
|
||||
app.state,
|
||||
ids.filter((id) => deltaMap[id] !== undefined),
|
||||
(shape) => ({ point: deltaMap[shape.id].next }),
|
||||
(shape) => ({ point: deltaMap[shape.id]?.next }),
|
||||
currentPageId
|
||||
)
|
||||
|
||||
|
|
|
@ -3,13 +3,15 @@ import type { TldrawApp } from '~state/TldrawApp'
|
|||
import type { TldrawCommand } from '~types'
|
||||
|
||||
export function duplicatePage(app: TldrawApp, pageId: string): TldrawCommand {
|
||||
const newId = Utils.uniqueId()
|
||||
const {
|
||||
currentPageId,
|
||||
page,
|
||||
pageState: { camera },
|
||||
} = app
|
||||
|
||||
const page = app.document.pages[pageId]
|
||||
|
||||
const newId = Utils.uniqueId()
|
||||
|
||||
const nextPage = {
|
||||
...page,
|
||||
id: newId,
|
||||
|
@ -20,7 +22,7 @@ export function duplicatePage(app: TldrawApp, pageId: string): TldrawCommand {
|
|||
id,
|
||||
{
|
||||
...shape,
|
||||
parentId: shape.parentId === pageId ? newId : shape.parentId,
|
||||
parentId: shape.parentId === page.id ? newId : shape.parentId,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
|
|
@ -89,7 +89,7 @@ describe('Flip command', () => {
|
|||
expect(app.getShape<RectangleShape>('rect1').point).toStrictEqual([100, 0])
|
||||
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.getShape<RectangleShape>('rect1').id, app.getShape<RectangleShape>('rect2').id],
|
||||
'groupId'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { TLBoundsCorner, Utils } from '@tldraw/core'
|
||||
import { TLDR } from '~state/TLDR'
|
||||
import type { TldrawApp } from '~state/TldrawApp'
|
||||
import { FlipType } from '~types'
|
||||
import type { TldrawCommand } from '~types'
|
||||
import { TLBoundsCorner, Utils } from '@tldraw/core'
|
||||
|
||||
export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): TldrawCommand {
|
||||
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 isSinglySelectedGroup = ids.length === 1 && shapes[ids[0]].type === 'group'
|
||||
|
||||
const commonBounds = Utils.getCommonBounds(boundsForShapes)
|
||||
|
||||
const { before, after } = TLDR.mutateShapes(
|
||||
|
@ -20,8 +22,33 @@ export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): Tldra
|
|||
ids,
|
||||
(shape) => {
|
||||
const shapeBounds = TLDR.getBounds(shape)
|
||||
const isChildOfGroup = shape.parentId !== currentPageId
|
||||
switch (type) {
|
||||
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(
|
||||
commonBounds,
|
||||
commonBounds,
|
||||
|
@ -39,6 +66,29 @@ export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): Tldra
|
|||
})
|
||||
}
|
||||
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(
|
||||
commonBounds,
|
||||
commonBounds,
|
||||
|
@ -57,7 +107,8 @@ export function flipShapes(app: TldrawApp, ids: string[], type: FlipType): Tldra
|
|||
}
|
||||
}
|
||||
},
|
||||
currentPageId
|
||||
currentPageId,
|
||||
true
|
||||
)
|
||||
|
||||
return {
|
||||
|
|
|
@ -122,4 +122,9 @@
|
|||
"copy.current.page.link": "Copy Current Page Link",
|
||||
"copy.project.link": "Copy Project Link",
|
||||
"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"
|
||||
}
|
||||
|
|
87
packages/tldraw/src/translations/pt-pt.json
Normal file
87
packages/tldraw/src/translations/pt-pt.json
Normal 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"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"style.menu.color": "Färg",
|
||||
"style.menu.fill": "Fyll",
|
||||
"style.menu.fill": "Ifylld",
|
||||
"style.menu.dash": "Streck",
|
||||
"style.menu.size": "Storlek",
|
||||
"style.menu.keep.open": "Håll stilmenyn öppen",
|
||||
|
|
|
@ -14,6 +14,7 @@ import ne from './ne.json'
|
|||
import no from './no.json'
|
||||
import pl from './pl.json'
|
||||
import pt_br from './pt-br.json'
|
||||
import pt_pt from './pt-pt.json'
|
||||
import ru from './ru.json'
|
||||
import sv from './sv.json'
|
||||
import tr from './tr.json'
|
||||
|
@ -41,12 +42,13 @@ export const TRANSLATIONS: TDTranslations = [
|
|||
{ locale: 'ne', label: 'नेपाली', messages: ne },
|
||||
{ locale: 'no', label: 'Norwegian', messages: no },
|
||||
{ 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: 'ru', label: 'Russian', messages: ru },
|
||||
{ locale: 'sv', label: 'Svenska', messages: sv },
|
||||
{ locale: 'tr', label: 'Türkçe', messages: tr },
|
||||
{ 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 },
|
||||
]
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
"select.all": "选中全部",
|
||||
"select.none": "取消选中",
|
||||
"delete": "删除",
|
||||
"new.project": "新工程",
|
||||
"new.project": "新项目",
|
||||
"open": "打开",
|
||||
"save": "保存",
|
||||
"save.as": "保存为",
|
||||
|
@ -55,8 +55,8 @@
|
|||
"duplicate": "复制",
|
||||
"cancel": "取消",
|
||||
"copy.invite.link": "复制邀请链接",
|
||||
"create.multiplayer.project": "创建多人工程",
|
||||
"copy.multiplayer.project": "复制到多人工程",
|
||||
"create.multiplayer.project": "创建多人项目",
|
||||
"copy.multiplayer.project": "复制到多人项目",
|
||||
"select": "选择",
|
||||
"eraser": "橡皮",
|
||||
"draw": "画笔",
|
||||
|
@ -103,5 +103,10 @@
|
|||
"dark": "暗黑",
|
||||
"copy.readonly.link": "复制只读链接",
|
||||
"image": "图片",
|
||||
"align.distribute": "对齐 / 分散"
|
||||
"align.distribute": "对齐 / 分散",
|
||||
"dialog.save.firsttime": "您是否想保存当前的项目?",
|
||||
"dialog.save.again": "您是否想保存对当前项目的更改?",
|
||||
"dialog.cancel": "取消",
|
||||
"dialog.no": "否",
|
||||
"dialog.yes": "是"
|
||||
}
|
||||
|
|
|
@ -460,7 +460,7 @@ export enum AlignStyle {
|
|||
export enum FontStyle {
|
||||
Script = 'script',
|
||||
Sans = 'sans',
|
||||
Serif = 'erif',
|
||||
Serif = 'serif',
|
||||
Mono = 'mono',
|
||||
}
|
||||
|
||||
|
|
|
@ -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 B The second point.
|
||||
* @param steps The number of points to return.
|
||||
|
|
Loading…
Reference in a new issue