2023-04-25 11:01:25 +00:00
|
|
|
import {
|
|
|
|
App,
|
[refactor] User-facing APIs (#1478)
This PR updates our user-facing APIs for the Tldraw and TldrawEditor
components, as well as the Editor (App). It mainly incorporates surface
changes from #1450 without any changes to validators or migrators,
incorporating feedback / discussion with @SomeHats and @ds300.
Here we:
- remove the TldrawEditorConfig
- bring back a loose version of shape definitions
- make a separation between "core" shapes and "default" shapes
- do not allow custom shapes, migrators or validators to overwrite core
shapes
- but _do_ allow new shapes
## `<Tldraw>` component
In this PR, the `Tldraw` component wraps both the `TldrawEditor`
component and our `TldrawUi` component. It accepts a union of props for
both components. Previously, this component also added local syncing via
a `useLocalSyncClient` hook call, however that has been pushed down to
the `TldrawEditor` component.
## `<TldrawEditor>` component
The `TldrawEditor` component now more neatly wraps up the different ways
that the editor can be configured.
## The store prop (`TldrawEditorProps.store`)
There are three main ways for the `TldrawEditor` component to be run:
1. with an externally defined store
2. with an externally defined syncing store (local or remote)
3. with an internally defined store
4. with an internally defined locally syncing store
The `store` prop allows for these configurations.
If the `store` prop is defined, it may be defined either as a `TLStore`
or as a `SyncedStore`. If the store is a `TLStore`, then the Editor will
assume that the store is ready to go; if it is defined as a SyncedStore,
then the component will display the loading / error screens as needed,
or the final editor once the store's status is "synced".
When the store is left undefined, then the `TldrawEditor` will create
its own internal store using the optional `instanceId`, `initialData`,
or `shapes` props to define the store / store schema.
If the `persistenceKey` prop is left undefined, then the store will not
be synced. If the `persistenceKey` is defined, then the store will be
synced locally. In the future, we may also here accept the API key /
roomId / etc for creating a remotely synced store.
The `SyncedStore` type has been expanded to also include types used for
remote syncing, e.g. with `ConnectionStatus`.
## Tools
By default, the App has two "baked-in" tools: the select tool and the
zoom tool. These cannot (for now) be replaced or removed. The default
tools are used by default, but may be replaced by other tools if
provided.
## Shapes
By default, the App has a set of "core" shapes:
- group
- embed
- bookmark
- image
- video
- text
That cannot by overwritten because they're created by the app at
different moments, such as when double clicking on the canvas or via a
copy and paste event. In follow up PRs, we'll split these out so that
users can replace parts of the code where these shapes are created.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
2023-06-01 15:47:34 +00:00
|
|
|
createTLStore,
|
2023-04-25 11:01:25 +00:00
|
|
|
fileToBase64,
|
|
|
|
TLAsset,
|
|
|
|
TLInstanceId,
|
|
|
|
TLRecord,
|
|
|
|
TLStore,
|
|
|
|
} from '@tldraw/editor'
|
|
|
|
import {
|
|
|
|
ID,
|
|
|
|
MigrationFailureReason,
|
|
|
|
MigrationResult,
|
|
|
|
SerializedSchema,
|
|
|
|
StoreSnapshot,
|
2023-05-24 11:25:41 +00:00
|
|
|
UnknownRecord,
|
2023-04-25 11:01:25 +00:00
|
|
|
} from '@tldraw/tlstore'
|
|
|
|
import { T } from '@tldraw/tlvalidate'
|
|
|
|
import { TLTranslationKey, ToastsContextType } from '@tldraw/ui'
|
|
|
|
import { exhaustiveSwitchError, Result } from '@tldraw/utils'
|
2023-06-02 09:38:13 +00:00
|
|
|
import { buildFromV1Document } from './buildFromV1Document'
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export const TLDRAW_FILE_MIMETYPE = 'application/vnd.tldraw+json' as const
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export const TLDRAW_FILE_EXTENSION = '.tldr' as const
|
|
|
|
|
|
|
|
// When incrementing this, you'll need to update parseTldrawJsonFile to handle
|
|
|
|
// both your new changes and the old file format
|
|
|
|
const LATEST_TLDRAW_FILE_FORMAT_VERSION = 1
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export interface TldrawFile {
|
|
|
|
tldrawFileFormatVersion: number
|
|
|
|
schema: SerializedSchema
|
2023-05-24 11:25:41 +00:00
|
|
|
records: UnknownRecord[]
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const tldrawFileValidator: T.Validator<TldrawFile> = T.object({
|
|
|
|
tldrawFileFormatVersion: T.nonZeroInteger,
|
|
|
|
schema: T.object({
|
|
|
|
schemaVersion: T.positiveInteger,
|
|
|
|
storeVersion: T.positiveInteger,
|
|
|
|
recordVersions: T.dict(
|
|
|
|
T.string,
|
|
|
|
T.object({
|
|
|
|
version: T.positiveInteger,
|
|
|
|
subTypeVersions: T.dict(T.string, T.positiveInteger).optional(),
|
|
|
|
subTypeKey: T.string.optional(),
|
|
|
|
})
|
|
|
|
),
|
|
|
|
}),
|
|
|
|
records: T.arrayOf(
|
|
|
|
T.object({
|
|
|
|
id: T.string as T.Validator<ID<any>>,
|
|
|
|
typeName: T.string,
|
|
|
|
}).allowUnknownProperties()
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export function isV1File(data: any) {
|
|
|
|
try {
|
|
|
|
if (data.document?.version) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
} catch (e) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export type TldrawFileParseError =
|
|
|
|
| { type: 'v1File'; data: any }
|
|
|
|
| { type: 'notATldrawFile'; cause: unknown }
|
|
|
|
| { type: 'fileFormatVersionTooNew'; version: number }
|
|
|
|
| { type: 'migrationFailed'; reason: MigrationFailureReason }
|
|
|
|
| { type: 'invalidRecords'; cause: unknown }
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export function parseTldrawJsonFile({
|
|
|
|
json,
|
|
|
|
instanceId,
|
[refactor] User-facing APIs (#1478)
This PR updates our user-facing APIs for the Tldraw and TldrawEditor
components, as well as the Editor (App). It mainly incorporates surface
changes from #1450 without any changes to validators or migrators,
incorporating feedback / discussion with @SomeHats and @ds300.
Here we:
- remove the TldrawEditorConfig
- bring back a loose version of shape definitions
- make a separation between "core" shapes and "default" shapes
- do not allow custom shapes, migrators or validators to overwrite core
shapes
- but _do_ allow new shapes
## `<Tldraw>` component
In this PR, the `Tldraw` component wraps both the `TldrawEditor`
component and our `TldrawUi` component. It accepts a union of props for
both components. Previously, this component also added local syncing via
a `useLocalSyncClient` hook call, however that has been pushed down to
the `TldrawEditor` component.
## `<TldrawEditor>` component
The `TldrawEditor` component now more neatly wraps up the different ways
that the editor can be configured.
## The store prop (`TldrawEditorProps.store`)
There are three main ways for the `TldrawEditor` component to be run:
1. with an externally defined store
2. with an externally defined syncing store (local or remote)
3. with an internally defined store
4. with an internally defined locally syncing store
The `store` prop allows for these configurations.
If the `store` prop is defined, it may be defined either as a `TLStore`
or as a `SyncedStore`. If the store is a `TLStore`, then the Editor will
assume that the store is ready to go; if it is defined as a SyncedStore,
then the component will display the loading / error screens as needed,
or the final editor once the store's status is "synced".
When the store is left undefined, then the `TldrawEditor` will create
its own internal store using the optional `instanceId`, `initialData`,
or `shapes` props to define the store / store schema.
If the `persistenceKey` prop is left undefined, then the store will not
be synced. If the `persistenceKey` is defined, then the store will be
synced locally. In the future, we may also here accept the API key /
roomId / etc for creating a remotely synced store.
The `SyncedStore` type has been expanded to also include types used for
remote syncing, e.g. with `ConnectionStatus`.
## Tools
By default, the App has two "baked-in" tools: the select tool and the
zoom tool. These cannot (for now) be replaced or removed. The default
tools are used by default, but may be replaced by other tools if
provided.
## Shapes
By default, the App has a set of "core" shapes:
- group
- embed
- bookmark
- image
- video
- text
That cannot by overwritten because they're created by the app at
different moments, such as when double clicking on the canvas or via a
copy and paste event. In follow up PRs, we'll split these out so that
users can replace parts of the code where these shapes are created.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
2023-06-01 15:47:34 +00:00
|
|
|
store,
|
2023-04-25 11:01:25 +00:00
|
|
|
}: {
|
[refactor] User-facing APIs (#1478)
This PR updates our user-facing APIs for the Tldraw and TldrawEditor
components, as well as the Editor (App). It mainly incorporates surface
changes from #1450 without any changes to validators or migrators,
incorporating feedback / discussion with @SomeHats and @ds300.
Here we:
- remove the TldrawEditorConfig
- bring back a loose version of shape definitions
- make a separation between "core" shapes and "default" shapes
- do not allow custom shapes, migrators or validators to overwrite core
shapes
- but _do_ allow new shapes
## `<Tldraw>` component
In this PR, the `Tldraw` component wraps both the `TldrawEditor`
component and our `TldrawUi` component. It accepts a union of props for
both components. Previously, this component also added local syncing via
a `useLocalSyncClient` hook call, however that has been pushed down to
the `TldrawEditor` component.
## `<TldrawEditor>` component
The `TldrawEditor` component now more neatly wraps up the different ways
that the editor can be configured.
## The store prop (`TldrawEditorProps.store`)
There are three main ways for the `TldrawEditor` component to be run:
1. with an externally defined store
2. with an externally defined syncing store (local or remote)
3. with an internally defined store
4. with an internally defined locally syncing store
The `store` prop allows for these configurations.
If the `store` prop is defined, it may be defined either as a `TLStore`
or as a `SyncedStore`. If the store is a `TLStore`, then the Editor will
assume that the store is ready to go; if it is defined as a SyncedStore,
then the component will display the loading / error screens as needed,
or the final editor once the store's status is "synced".
When the store is left undefined, then the `TldrawEditor` will create
its own internal store using the optional `instanceId`, `initialData`,
or `shapes` props to define the store / store schema.
If the `persistenceKey` prop is left undefined, then the store will not
be synced. If the `persistenceKey` is defined, then the store will be
synced locally. In the future, we may also here accept the API key /
roomId / etc for creating a remotely synced store.
The `SyncedStore` type has been expanded to also include types used for
remote syncing, e.g. with `ConnectionStatus`.
## Tools
By default, the App has two "baked-in" tools: the select tool and the
zoom tool. These cannot (for now) be replaced or removed. The default
tools are used by default, but may be replaced by other tools if
provided.
## Shapes
By default, the App has a set of "core" shapes:
- group
- embed
- bookmark
- image
- video
- text
That cannot by overwritten because they're created by the app at
different moments, such as when double clicking on the canvas or via a
copy and paste event. In follow up PRs, we'll split these out so that
users can replace parts of the code where these shapes are created.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
2023-06-01 15:47:34 +00:00
|
|
|
store: TLStore
|
2023-04-25 11:01:25 +00:00
|
|
|
json: string
|
|
|
|
instanceId: TLInstanceId
|
|
|
|
}): Result<TLStore, TldrawFileParseError> {
|
|
|
|
// first off, we parse .json file and check it matches the general shape of
|
|
|
|
// a tldraw file
|
|
|
|
let data
|
|
|
|
try {
|
|
|
|
data = tldrawFileValidator.validate(JSON.parse(json))
|
|
|
|
} catch (e) {
|
|
|
|
// could be a v1 file!
|
|
|
|
try {
|
|
|
|
data = JSON.parse(json)
|
|
|
|
if (isV1File(data)) {
|
|
|
|
return Result.err({ type: 'v1File', data })
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
// noop
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result.err({ type: 'notATldrawFile', cause: e })
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the file format version isn't supported, we can't open it - it's
|
|
|
|
// probably from a newer version of tldraw
|
|
|
|
if (data.tldrawFileFormatVersion > LATEST_TLDRAW_FILE_FORMAT_VERSION) {
|
|
|
|
return Result.err({
|
|
|
|
type: 'fileFormatVersionTooNew',
|
|
|
|
version: data.tldrawFileFormatVersion,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// even if the file version is up to date, it might contain old-format
|
|
|
|
// records. lets create a store with the records and migrate it to the
|
|
|
|
// latest version
|
|
|
|
let migrationResult: MigrationResult<StoreSnapshot<TLRecord>>
|
|
|
|
try {
|
|
|
|
const storeSnapshot = Object.fromEntries(data.records.map((r) => [r.id, r as TLRecord]))
|
[refactor] User-facing APIs (#1478)
This PR updates our user-facing APIs for the Tldraw and TldrawEditor
components, as well as the Editor (App). It mainly incorporates surface
changes from #1450 without any changes to validators or migrators,
incorporating feedback / discussion with @SomeHats and @ds300.
Here we:
- remove the TldrawEditorConfig
- bring back a loose version of shape definitions
- make a separation between "core" shapes and "default" shapes
- do not allow custom shapes, migrators or validators to overwrite core
shapes
- but _do_ allow new shapes
## `<Tldraw>` component
In this PR, the `Tldraw` component wraps both the `TldrawEditor`
component and our `TldrawUi` component. It accepts a union of props for
both components. Previously, this component also added local syncing via
a `useLocalSyncClient` hook call, however that has been pushed down to
the `TldrawEditor` component.
## `<TldrawEditor>` component
The `TldrawEditor` component now more neatly wraps up the different ways
that the editor can be configured.
## The store prop (`TldrawEditorProps.store`)
There are three main ways for the `TldrawEditor` component to be run:
1. with an externally defined store
2. with an externally defined syncing store (local or remote)
3. with an internally defined store
4. with an internally defined locally syncing store
The `store` prop allows for these configurations.
If the `store` prop is defined, it may be defined either as a `TLStore`
or as a `SyncedStore`. If the store is a `TLStore`, then the Editor will
assume that the store is ready to go; if it is defined as a SyncedStore,
then the component will display the loading / error screens as needed,
or the final editor once the store's status is "synced".
When the store is left undefined, then the `TldrawEditor` will create
its own internal store using the optional `instanceId`, `initialData`,
or `shapes` props to define the store / store schema.
If the `persistenceKey` prop is left undefined, then the store will not
be synced. If the `persistenceKey` is defined, then the store will be
synced locally. In the future, we may also here accept the API key /
roomId / etc for creating a remotely synced store.
The `SyncedStore` type has been expanded to also include types used for
remote syncing, e.g. with `ConnectionStatus`.
## Tools
By default, the App has two "baked-in" tools: the select tool and the
zoom tool. These cannot (for now) be replaced or removed. The default
tools are used by default, but may be replaced by other tools if
provided.
## Shapes
By default, the App has a set of "core" shapes:
- group
- embed
- bookmark
- image
- video
- text
That cannot by overwritten because they're created by the app at
different moments, such as when double clicking on the canvas or via a
copy and paste event. In follow up PRs, we'll split these out so that
users can replace parts of the code where these shapes are created.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
2023-06-01 15:47:34 +00:00
|
|
|
migrationResult = store.schema.migrateStoreSnapshot(storeSnapshot, data.schema)
|
2023-04-25 11:01:25 +00:00
|
|
|
} catch (e) {
|
|
|
|
// junk data in the migration
|
|
|
|
return Result.err({ type: 'invalidRecords', cause: e })
|
|
|
|
}
|
|
|
|
// if the migration failed, we can't open the file
|
|
|
|
if (migrationResult.type === 'error') {
|
|
|
|
return Result.err({ type: 'migrationFailed', reason: migrationResult.reason })
|
|
|
|
}
|
|
|
|
|
|
|
|
// at this stage, the store should have records at the latest versions, so
|
|
|
|
// we should be able to validate them. if any of the records at this stage
|
|
|
|
// are invalid, we don't open the file
|
|
|
|
try {
|
[refactor] User-facing APIs (#1478)
This PR updates our user-facing APIs for the Tldraw and TldrawEditor
components, as well as the Editor (App). It mainly incorporates surface
changes from #1450 without any changes to validators or migrators,
incorporating feedback / discussion with @SomeHats and @ds300.
Here we:
- remove the TldrawEditorConfig
- bring back a loose version of shape definitions
- make a separation between "core" shapes and "default" shapes
- do not allow custom shapes, migrators or validators to overwrite core
shapes
- but _do_ allow new shapes
## `<Tldraw>` component
In this PR, the `Tldraw` component wraps both the `TldrawEditor`
component and our `TldrawUi` component. It accepts a union of props for
both components. Previously, this component also added local syncing via
a `useLocalSyncClient` hook call, however that has been pushed down to
the `TldrawEditor` component.
## `<TldrawEditor>` component
The `TldrawEditor` component now more neatly wraps up the different ways
that the editor can be configured.
## The store prop (`TldrawEditorProps.store`)
There are three main ways for the `TldrawEditor` component to be run:
1. with an externally defined store
2. with an externally defined syncing store (local or remote)
3. with an internally defined store
4. with an internally defined locally syncing store
The `store` prop allows for these configurations.
If the `store` prop is defined, it may be defined either as a `TLStore`
or as a `SyncedStore`. If the store is a `TLStore`, then the Editor will
assume that the store is ready to go; if it is defined as a SyncedStore,
then the component will display the loading / error screens as needed,
or the final editor once the store's status is "synced".
When the store is left undefined, then the `TldrawEditor` will create
its own internal store using the optional `instanceId`, `initialData`,
or `shapes` props to define the store / store schema.
If the `persistenceKey` prop is left undefined, then the store will not
be synced. If the `persistenceKey` is defined, then the store will be
synced locally. In the future, we may also here accept the API key /
roomId / etc for creating a remotely synced store.
The `SyncedStore` type has been expanded to also include types used for
remote syncing, e.g. with `ConnectionStatus`.
## Tools
By default, the App has two "baked-in" tools: the select tool and the
zoom tool. These cannot (for now) be replaced or removed. The default
tools are used by default, but may be replaced by other tools if
provided.
## Shapes
By default, the App has a set of "core" shapes:
- group
- embed
- bookmark
- image
- video
- text
That cannot by overwritten because they're created by the app at
different moments, such as when double clicking on the canvas or via a
copy and paste event. In follow up PRs, we'll split these out so that
users can replace parts of the code where these shapes are created.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
2023-06-01 15:47:34 +00:00
|
|
|
return Result.ok(
|
|
|
|
createTLStore({
|
|
|
|
initialData: migrationResult.value,
|
|
|
|
instanceId,
|
|
|
|
})
|
|
|
|
)
|
2023-04-25 11:01:25 +00:00
|
|
|
} catch (e) {
|
|
|
|
// junk data in the records (they're not validated yet!) could cause the
|
|
|
|
// migrations to crash. We treat any throw from a migration as an
|
|
|
|
// invalid record
|
|
|
|
return Result.err({ type: 'invalidRecords', cause: e })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export async function serializeTldrawJson(store: TLStore): Promise<string> {
|
|
|
|
const recordsToSave: TLRecord[] = []
|
|
|
|
for (const record of store.allRecords()) {
|
|
|
|
switch (record.typeName) {
|
|
|
|
case 'asset':
|
|
|
|
if (
|
|
|
|
record.type !== 'bookmark' &&
|
|
|
|
record.props.src &&
|
|
|
|
!record.props.src.startsWith('data:')
|
|
|
|
) {
|
|
|
|
let assetSrcToSave
|
|
|
|
try {
|
|
|
|
// try to save the asset as a base64 string
|
|
|
|
assetSrcToSave = await fileToBase64(await (await fetch(record.props.src)).blob())
|
|
|
|
} catch {
|
|
|
|
// if that fails, just save the original src
|
|
|
|
assetSrcToSave = record.props.src
|
|
|
|
}
|
|
|
|
|
|
|
|
recordsToSave.push({
|
|
|
|
...record,
|
|
|
|
props: {
|
|
|
|
...record.props,
|
|
|
|
src: assetSrcToSave,
|
|
|
|
},
|
|
|
|
} as TLAsset)
|
|
|
|
} else {
|
|
|
|
recordsToSave.push(record)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
recordsToSave.push(record)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return JSON.stringify({
|
|
|
|
tldrawFileFormatVersion: LATEST_TLDRAW_FILE_FORMAT_VERSION,
|
|
|
|
schema: store.schema.serialize(),
|
|
|
|
records: recordsToSave,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export async function serializeTldrawJsonBlob(store: TLStore): Promise<Blob> {
|
|
|
|
return new Blob([await serializeTldrawJson(store)], { type: TLDRAW_FILE_MIMETYPE })
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
export async function parseAndLoadDocument(
|
|
|
|
app: App,
|
|
|
|
document: string,
|
|
|
|
msg: (id: TLTranslationKey) => string,
|
|
|
|
addToast: ToastsContextType['addToast'],
|
|
|
|
onV1FileLoad?: () => void,
|
|
|
|
forceDarkMode?: boolean
|
|
|
|
) {
|
|
|
|
const parseFileResult = parseTldrawJsonFile({
|
[refactor] User-facing APIs (#1478)
This PR updates our user-facing APIs for the Tldraw and TldrawEditor
components, as well as the Editor (App). It mainly incorporates surface
changes from #1450 without any changes to validators or migrators,
incorporating feedback / discussion with @SomeHats and @ds300.
Here we:
- remove the TldrawEditorConfig
- bring back a loose version of shape definitions
- make a separation between "core" shapes and "default" shapes
- do not allow custom shapes, migrators or validators to overwrite core
shapes
- but _do_ allow new shapes
## `<Tldraw>` component
In this PR, the `Tldraw` component wraps both the `TldrawEditor`
component and our `TldrawUi` component. It accepts a union of props for
both components. Previously, this component also added local syncing via
a `useLocalSyncClient` hook call, however that has been pushed down to
the `TldrawEditor` component.
## `<TldrawEditor>` component
The `TldrawEditor` component now more neatly wraps up the different ways
that the editor can be configured.
## The store prop (`TldrawEditorProps.store`)
There are three main ways for the `TldrawEditor` component to be run:
1. with an externally defined store
2. with an externally defined syncing store (local or remote)
3. with an internally defined store
4. with an internally defined locally syncing store
The `store` prop allows for these configurations.
If the `store` prop is defined, it may be defined either as a `TLStore`
or as a `SyncedStore`. If the store is a `TLStore`, then the Editor will
assume that the store is ready to go; if it is defined as a SyncedStore,
then the component will display the loading / error screens as needed,
or the final editor once the store's status is "synced".
When the store is left undefined, then the `TldrawEditor` will create
its own internal store using the optional `instanceId`, `initialData`,
or `shapes` props to define the store / store schema.
If the `persistenceKey` prop is left undefined, then the store will not
be synced. If the `persistenceKey` is defined, then the store will be
synced locally. In the future, we may also here accept the API key /
roomId / etc for creating a remotely synced store.
The `SyncedStore` type has been expanded to also include types used for
remote syncing, e.g. with `ConnectionStatus`.
## Tools
By default, the App has two "baked-in" tools: the select tool and the
zoom tool. These cannot (for now) be replaced or removed. The default
tools are used by default, but may be replaced by other tools if
provided.
## Shapes
By default, the App has a set of "core" shapes:
- group
- embed
- bookmark
- image
- video
- text
That cannot by overwritten because they're created by the app at
different moments, such as when double clicking on the canvas or via a
copy and paste event. In follow up PRs, we'll split these out so that
users can replace parts of the code where these shapes are created.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
2023-06-01 15:47:34 +00:00
|
|
|
store: createTLStore(),
|
2023-04-25 11:01:25 +00:00
|
|
|
json: document,
|
|
|
|
instanceId: app.instanceId,
|
|
|
|
})
|
|
|
|
if (!parseFileResult.ok) {
|
|
|
|
let description
|
|
|
|
switch (parseFileResult.error.type) {
|
|
|
|
case 'notATldrawFile':
|
|
|
|
app.annotateError(parseFileResult.error.cause, {
|
|
|
|
origin: 'file-system.open.parse',
|
|
|
|
willCrashApp: false,
|
|
|
|
tags: { parseErrorType: parseFileResult.error.type },
|
|
|
|
})
|
|
|
|
reportError(parseFileResult.error.cause)
|
|
|
|
description = msg('file-system.file-open-error.not-a-tldraw-file')
|
|
|
|
break
|
|
|
|
case 'fileFormatVersionTooNew':
|
|
|
|
description = msg('file-system.file-open-error.file-format-version-too-new')
|
|
|
|
break
|
|
|
|
case 'migrationFailed':
|
|
|
|
if (parseFileResult.error.reason === MigrationFailureReason.TargetVersionTooNew) {
|
|
|
|
description = msg('file-system.file-open-error.file-format-version-too-new')
|
|
|
|
} else {
|
|
|
|
description = msg('file-system.file-open-error.generic-corrupted-file')
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case 'invalidRecords':
|
|
|
|
app.annotateError(parseFileResult.error.cause, {
|
|
|
|
origin: 'file-system.open.parse',
|
|
|
|
willCrashApp: false,
|
|
|
|
tags: { parseErrorType: parseFileResult.error.type },
|
|
|
|
})
|
|
|
|
reportError(parseFileResult.error.cause)
|
|
|
|
description = msg('file-system.file-open-error.generic-corrupted-file')
|
|
|
|
break
|
|
|
|
case 'v1File': {
|
|
|
|
buildFromV1Document(app, parseFileResult.error.data.document)
|
|
|
|
onV1FileLoad?.()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
exhaustiveSwitchError(parseFileResult.error, 'type')
|
|
|
|
}
|
|
|
|
addToast({
|
|
|
|
title: msg('file-system.file-open-error.title'),
|
|
|
|
description,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// tldraw file contain the full state of the app,
|
|
|
|
// including ephemeral data. it up to the opener to
|
|
|
|
// decide what to restore and what to retain. Here, we
|
|
|
|
// just restore everything, so if the user has opened
|
|
|
|
// this file before they'll get their camera etc.
|
|
|
|
// restored. we could change this in the future.
|
|
|
|
app.replaceStoreContentsWithRecordsForOtherDocument(parseFileResult.value.allRecords())
|
|
|
|
|
|
|
|
if (forceDarkMode) app.setDarkMode(true)
|
|
|
|
}
|