lite: delete all
|
@ -1,8 +0,0 @@
|
||||||
# Changesets
|
|
||||||
|
|
||||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
||||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
||||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
||||||
|
|
||||||
We have a quick list of common questions to get you started engaging with this project in
|
|
||||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://unpkg.com/@changesets/config@1.6.4/schema.json",
|
|
||||||
"changelog": "@changesets/cli/changelog",
|
|
||||||
"commit": false,
|
|
||||||
"linked": [],
|
|
||||||
"access": "public",
|
|
||||||
"baseBranch": "main",
|
|
||||||
"updateInternalDependencies": "patch",
|
|
||||||
"ignore": []
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
**/node_modules/*
|
|
||||||
**/out/*
|
|
||||||
**/.next/*
|
|
43
.eslintrc
|
@ -1,43 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"tsconfigRootDir": "__dirname",
|
|
||||||
"project": [
|
|
||||||
"./apps/*/tsconfig.json",
|
|
||||||
"./apps/vscode/*/tsconfig.json",
|
|
||||||
"./packages/*/tsconfig.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint", "jest"],
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"plugin:jest/recommended"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"jest/no-export": "warn",
|
|
||||||
"jest/no-identical-title": "warn"
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
// enable the rule specifically for TypeScript files
|
|
||||||
"files": ["*.ts", "*.tsx"],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": [0],
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"destructuredArrayIgnorePattern": "^_",
|
|
||||||
"caughtErrorsIgnorePattern": "^_"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
1
.github/FUNDING.yml
vendored
|
@ -1 +0,0 @@
|
||||||
github: [steveruizok]
|
|
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug Report
|
|
||||||
about: Writing and other documentation.
|
|
||||||
title: '[bug] Bug description'
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1,5 +0,0 @@
|
||||||
blank_issues_enabled: true
|
|
||||||
contact_links:
|
|
||||||
- name: tldraw Beta
|
|
||||||
url: https://github.com/tldraw/tldraw-beta/issues/new
|
|
||||||
about: Is your issue for the tldraw beta? Report it on the beta repo instead.
|
|
7
.github/ISSUE_TEMPLATE/documentation.md
vendored
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
name: Documentation
|
|
||||||
about: Writing and other documentation.
|
|
||||||
title: '[documentation] Content'
|
|
||||||
labels: documentation
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
7
.github/ISSUE_TEMPLATE/feature.md
vendored
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
name: Feature
|
|
||||||
about: Begin discussion of a new feature.
|
|
||||||
title: '[feature] Feature or improvement'
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
7
.github/ISSUE_TEMPLATE/testing.md
vendored
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
name: Testing
|
|
||||||
about: Tests that need to be written.
|
|
||||||
title: '[tests] Test'
|
|
||||||
labels: testing
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
33
.github/workflows/main.yml
vendored
|
@ -1,33 +0,0 @@
|
||||||
name: CI
|
|
||||||
on: push
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# turbo cache
|
|
||||||
- name: Turbo Cache
|
|
||||||
id: turbo-cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: node_modules/.cache/turbo
|
|
||||||
key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
turbo-${{ github.job }}-${{ github.ref_name }}-
|
|
||||||
|
|
||||||
# install modules
|
|
||||||
- name: Install modules
|
|
||||||
run: yarn
|
|
||||||
|
|
||||||
# build
|
|
||||||
- name: Build Packages
|
|
||||||
run: yarn build:packages --cache-dir=".turbo"
|
|
||||||
|
|
||||||
# lint
|
|
||||||
- name: Lint
|
|
||||||
run: yarn lint
|
|
||||||
|
|
||||||
# run unit tests
|
|
||||||
- name: Jest Annotations & Coverage
|
|
||||||
run: yarn test:ci
|
|
20
.gitignore
vendored
|
@ -1,20 +0,0 @@
|
||||||
node_modules/
|
|
||||||
build/
|
|
||||||
lib/
|
|
||||||
dist/
|
|
||||||
docs/
|
|
||||||
.idea/*
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
coverage
|
|
||||||
*.log
|
|
||||||
|
|
||||||
.vercel
|
|
||||||
.next
|
|
||||||
apps/www/public/workbox-*
|
|
||||||
apps/www/public/worker-*
|
|
||||||
apps/www/public/sw.js
|
|
||||||
apps/www/public/sw.js.map
|
|
||||||
.env
|
|
||||||
firebase.config.*.turbo
|
|
||||||
.turbo
|
|
|
@ -1,4 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
yarn run pre-commit
|
|
|
@ -1,4 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
yarn run pre-push
|
|
9
.ignore
|
@ -1,9 +0,0 @@
|
||||||
# Ignored Files for Search
|
|
||||||
|
|
||||||
dist
|
|
||||||
node_modules
|
|
||||||
*.d.ts
|
|
||||||
*.js
|
|
||||||
*.md
|
|
||||||
*.lock
|
|
||||||
*.tsbuildinfo
|
|
16
.npmignore
|
@ -1,16 +0,0 @@
|
||||||
/.github/
|
|
||||||
/.vscode/
|
|
||||||
/node_modules/
|
|
||||||
/build/
|
|
||||||
/tmp/
|
|
||||||
.idea/*
|
|
||||||
|
|
||||||
coverage
|
|
||||||
*.log
|
|
||||||
.gitlab-ci.yml
|
|
||||||
|
|
||||||
package-lock.json
|
|
||||||
/*.tgz
|
|
||||||
/tmp*
|
|
||||||
/mnt/
|
|
||||||
/package/
|
|
|
@ -1,18 +0,0 @@
|
||||||
assets
|
|
||||||
**/assets
|
|
||||||
**/coverage
|
|
||||||
**/dist
|
|
||||||
**/extension/editor
|
|
||||||
**/out
|
|
||||||
**/public
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
.next
|
|
||||||
.tldr
|
|
||||||
.turbo
|
|
||||||
.vercel
|
|
||||||
.vsix
|
|
||||||
|
|
||||||
*.lock
|
|
||||||
*.log
|
|
||||||
*.yaml
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"singleQuote": true,
|
|
||||||
"semi": false,
|
|
||||||
"printWidth": 100,
|
|
||||||
"importOrder": ["^[~]", "^[./]"],
|
|
||||||
"importOrderSortSpecifiers": true,
|
|
||||||
"importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy"]
|
|
||||||
}
|
|
3
.vscode/extensions.json
vendored
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"recommendations": ["esbenp.prettier-vscode", "tldraw-org.tldraw-vscode"]
|
|
||||||
}
|
|
3
.vscode/settings.json
vendored
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
|
||||||
}
|
|
17
.vscode/snippets.code-snippets
vendored
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"createComment": {
|
|
||||||
"scope": "typescript,typescriptreact",
|
|
||||||
"prefix": "ccc",
|
|
||||||
"body": [
|
|
||||||
"/**",
|
|
||||||
" * ${1:description}",
|
|
||||||
" *",
|
|
||||||
" * ### Example",
|
|
||||||
" *",
|
|
||||||
" *```ts",
|
|
||||||
" * ${2:example}",
|
|
||||||
" *```"
|
|
||||||
],
|
|
||||||
"description": "comment"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for our community include:
|
|
||||||
|
|
||||||
- Demonstrating empathy and kindness toward other people
|
|
||||||
- Being respectful of differing opinions, viewpoints, and experiences
|
|
||||||
- Giving and gracefully accepting constructive feedback
|
|
||||||
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
|
||||||
- Focusing on what is best not just for us as individuals, but for the overall community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
|
||||||
|
|
||||||
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
||||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
||||||
- Public or private harassment
|
|
||||||
- Publishing others' private information, such as a physical or email address, without their explicit permission
|
|
||||||
- Contacting individual members, contributors, or leaders privately, outside designated community mechanisms, without their explicit permission
|
|
||||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at opensource@github.com. All complaints will be reviewed and investigated promptly and fairly.
|
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
|
||||||
|
|
||||||
## Enforcement Guidelines
|
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
|
||||||
|
|
||||||
### 1. Correction
|
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
|
||||||
|
|
||||||
### 2. Warning
|
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series of actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at <https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
|
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at <https://www.contributor-covenant.org/faq>. Translations are available at <https://www.contributor-covenant.org/translations>.
|
|
|
@ -1,80 +0,0 @@
|
||||||
# Welcome to the tldraw contributing guide <!-- omit in toc -->
|
|
||||||
|
|
||||||
Thank you for investing your time in contributing to our project! Any contribution you make will be reflected in the @tldraw/tldraw package and at [tldraw.com](https://tldraw.com).
|
|
||||||
|
|
||||||
Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
|
|
||||||
|
|
||||||
In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
|
|
||||||
|
|
||||||
Use the table of contents icon on the top left corner of this document to get to a specific section of this guide quickly.
|
|
||||||
|
|
||||||
## New contributor guide
|
|
||||||
|
|
||||||
To get an overview of the project, read the [README](README.md). Here are some resources to help you get started with open source contributions:
|
|
||||||
|
|
||||||
- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github)
|
|
||||||
- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git)
|
|
||||||
- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow)
|
|
||||||
- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests)
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
Join the [Discord channel](https://discord.gg/SBBEVCA4PG). If you have questions or feedback, this is the best place to reach the team and other contributors directly.
|
|
||||||
|
|
||||||
### Issues
|
|
||||||
|
|
||||||
#### Create a new issue
|
|
||||||
|
|
||||||
If you spot a problem, [search if an issue already exists](https://github.com/tldraw/tldraw/issues). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/tldraw/tldraw/issues/new/choose).
|
|
||||||
|
|
||||||
#### Solve an issue
|
|
||||||
|
|
||||||
Scan through our [existing issues](https://github.com/tldraw/tldraw/issues) to find one that interests you. You can narrow down the search using `labels` as filters. See [Labels](/contributing/how-to-use-labels.md) for more information. If you find an issue to work on, you are welcome to open a PR with a fix.
|
|
||||||
|
|
||||||
### Make Changes
|
|
||||||
|
|
||||||
#### Make changes locally
|
|
||||||
|
|
||||||
1. [Install Git LFS](https://docs.github.com/en/github/managing-large-files/versioning-large-files/installing-git-large-file-storage).
|
|
||||||
|
|
||||||
2. Fork the repository.
|
|
||||||
|
|
||||||
- Using GitHub Desktop:
|
|
||||||
|
|
||||||
- [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) will guide you through setting up Desktop.
|
|
||||||
- Once Desktop is set up, you can use it to [fork the repo](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop)!
|
|
||||||
|
|
||||||
- Using the command line:
|
|
||||||
|
|
||||||
- [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them.
|
|
||||||
|
|
||||||
- GitHub Codespaces:
|
|
||||||
- [Fork, edit, and preview](https://docs.github.com/en/free-pro-team@latest/github/developing-online-with-codespaces/creating-a-codespace) using [GitHub Codespaces](https://github.com/features/codespaces) without having to install and run the project locally.
|
|
||||||
|
|
||||||
3. Install or update to **Node.js v16**.
|
|
||||||
|
|
||||||
4. Create a working branch and start with your changes!
|
|
||||||
|
|
||||||
5. Follow the [the development guide](guides/development.md).
|
|
||||||
|
|
||||||
### Commit your update
|
|
||||||
|
|
||||||
Commit the changes once you are happy with them.
|
|
||||||
|
|
||||||
### Pull Request
|
|
||||||
|
|
||||||
When you're finished with the changes, create a pull request, also known as a PR.
|
|
||||||
|
|
||||||
- Fill the "Ready for review" template so that we can review your PR. This template helps reviewers understand your changes as well as the purpose of your pull request.
|
|
||||||
- Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one.
|
|
||||||
- Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge.
|
|
||||||
Once you submit your PR, a team member will review your proposal. We may ask questions or request for additional information.
|
|
||||||
- We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
|
|
||||||
- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations).
|
|
||||||
- If you run into any merge issues, checkout this [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) to help you resolve merge conflicts and other issues.
|
|
||||||
|
|
||||||
### Your PR is merged!
|
|
||||||
|
|
||||||
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).
|
|
21
LICENSE.md
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 tldraw GB Ltd
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
74
README.md
|
@ -1,74 +0,0 @@
|
||||||
> **Note** We're between versions! The code in this repository is for the **original version** of tldraw, which is hosted at [old.tldraw.com](https://old.tldraw.com). The new version (hosted at [tldraw.com](https://tldraw.com)) is not yet open source. It is _distributed_ however, and you can read more about the new version on **the new docs site** at [docs.tldraw.dev](https://docs.tldraw.dev). This should all be cleaned up soon. 🚧
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<div style="text-align: center; transform: scale(.5);">
|
|
||||||
<img src="https://github.com/tldraw/tldraw/raw/main/assets/card-repo.png"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
![A screenshot of the tldraw web app](./assets/screenshot.png)
|
|
||||||
|
|
||||||
Welcome to the [tldraw](https://tldraw.com) monorepo.
|
|
||||||
|
|
||||||
🙌 Questions? Join the [Discord channel](https://discord.gg/SBBEVCA4PG) or start a [discussion](https://github.com/tldraw/tldraw/discussions/new).
|
|
||||||
|
|
||||||
💕 Love this project? Consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok).
|
|
||||||
|
|
||||||
Thanks to our corporate sponsors:
|
|
||||||
|
|
||||||
<div style="display: flex; flex-wrap: wrap; gap: 32px;">
|
|
||||||
<a href="https://sentry.io"><img src="./assets/sentry.svg"></img></a>
|
|
||||||
|
|
||||||
<a href="https://vercel.com/?utm_source=team-slug&utm_campaign=oss"><img src="./assets/vercel.svg"></img></a>
|
|
||||||
|
|
||||||
<a href="https://oppizi.com"><img src="./assets/oppizi.png" width="212"></img></a>
|
|
||||||
|
|
||||||
<a href="https://logseq.com"><img src="./assets/logseq.svg" width="80"></img></a>
|
|
||||||
|
|
||||||
<a href="https://blindsidenetworks.com/"><img src="./assets/blindside.png" width="212"></img></a>
|
|
||||||
|
|
||||||
<a href="https://www.100ms.live/"><img src="./assets/100ms.png" width="212"></img></a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
...and to our [individual sponsors](https://github.com/sponsors/steveruizok#sponsors)!
|
|
||||||
|
|
||||||
## Contents
|
|
||||||
|
|
||||||
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 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).
|
|
||||||
|
|
||||||
...and three examples:
|
|
||||||
|
|
||||||
- [**examples/core-example**](https://github.com/tldraw/tldraw/tree/main/examples/core-example) is a simple example for `@tldraw/core`.
|
|
||||||
- [**examples/core-example-advanced**](https://github.com/tldraw/tldraw/tree/main/examples/core-example-advanced) is a second example for `@tldraw/core`.
|
|
||||||
- [**examples/tldraw-example**](https://github.com/tldraw/tldraw/tree/main/examples/tldraw-example) is an example for `@tldraw/tldraw`.
|
|
||||||
|
|
||||||
## Discussion
|
|
||||||
|
|
||||||
Want to connect? Visit the [Discord channel](https://discord.gg/SBBEVCA4PG).
|
|
||||||
|
|
||||||
## Contribution
|
|
||||||
|
|
||||||
Interested in contributing? See the [contributing guide](/CONTRIBUTING.md).
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Need help? Please [open an issue](https://github.com/tldraw/tldraw/issues/new) for support.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under MIT.
|
|
||||||
|
|
||||||
If you're using the library in a commercial product, please consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok).
|
|
||||||
|
|
||||||
## Author
|
|
||||||
|
|
||||||
- [@steveruizok](https://twitter.com/steveruizok)
|
|
|
@ -1,55 +0,0 @@
|
||||||
# @tldraw/vscode
|
|
||||||
|
|
||||||
This folder contains the source for the tldraw VS Code extension.
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
## 1. Install dependencies
|
|
||||||
|
|
||||||
- Run `yarn` from the root folder
|
|
||||||
|
|
||||||
## 2. Build @tldraw/tldraw
|
|
||||||
|
|
||||||
- Run `yarn build:packages` from the root folder.
|
|
||||||
|
|
||||||
## 3. Start the editor
|
|
||||||
|
|
||||||
In the root folder:
|
|
||||||
|
|
||||||
- Run `yarn start:vscode`.
|
|
||||||
|
|
||||||
This will start the development server for the `vscode/editor` project and open the `vscode/extension` folder in a new window.
|
|
||||||
|
|
||||||
In the `vscode/extension` window, open the terminal and run:
|
|
||||||
|
|
||||||
- Install dependencies (`yarn`)
|
|
||||||
- Start the VS Code debugger (Menu > Run > Start Debugging)
|
|
||||||
|
|
||||||
Open a `.tldr` file or create a new `.tldr` file from the command palette.
|
|
||||||
|
|
||||||
## Publishing
|
|
||||||
|
|
||||||
To publish, chat with the team on the [Discord channel](https://discord.gg/SBBEVCA4PG).
|
|
||||||
|
|
||||||
- Install `vsce` globally
|
|
||||||
- Run `vsce login tldraw-org` and sign in
|
|
||||||
|
|
||||||
In the `vscode/extension` folder:
|
|
||||||
|
|
||||||
- Run `yarn vscode:publish`
|
|
||||||
|
|
||||||
#### References
|
|
||||||
|
|
||||||
- [VS Code Marketplace Manager](https://marketplace.visualstudio.com/manage/)
|
|
||||||
- [Web Extensions Guide](https://code.visualstudio.com/api/extension-guides/web-extensions)
|
|
||||||
- [Test Your Web Extension](https://code.visualstudio.com/api/extension-guides/web-extensions#test-your-web-extension)
|
|
||||||
- [Web Extension Testing](https://code.visualstudio.com/api/extension-guides/web-extensions#web-extension-tests)
|
|
||||||
- An example custom editor that does work as a Web Extension
|
|
||||||
- https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio
|
|
||||||
- https://github.com/hediet/vscode-drawio
|
|
||||||
- [VS Code Extension API/Landing Page](https://code.visualstudio.com/api)
|
|
||||||
- [Getting Started](https://code.visualstudio.com/api/get-started/your-first-extension)
|
|
||||||
- [Custom Editor API](https://code.visualstudio.com/api/extension-guides/custom-editors)
|
|
||||||
- [github.com/microsoft/vscode-extension-samples](https://github.com/microsoft/vscode-extension-samples)
|
|
||||||
- [Extensions Guide -> Webviews](https://code.visualstudio.com/api/extension-guides/webview)
|
|
||||||
- [Publishing Extensions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension)
|
|
|
@ -1,134 +0,0 @@
|
||||||
# @tldraw/vscode-editor
|
|
||||||
|
|
||||||
## 1.15.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Include clipboard fixes.
|
|
||||||
|
|
||||||
## 1.15.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix clipboard stealing focus.
|
|
||||||
|
|
||||||
## 1.15.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Fixes copy as PNG
|
|
||||||
- Fixes a bug with copy and paste
|
|
||||||
- Fixes a bug on iPad while using pencil
|
|
||||||
- Fixes a bug with the style menus
|
|
||||||
- Fixes a bug with image export
|
|
||||||
- Updates translations
|
|
||||||
|
|
||||||
## 1.14.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates German translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
|
|
||||||
## 1.13.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates German translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
- 12c0b2ac: - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
|
|
||||||
## 1.13.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Adds missing Arabic translations for dialogs. @abedshamia
|
|
||||||
- Updates core-example. @brydenfogelman
|
|
||||||
- Updates Polish translations. @adan2013
|
|
||||||
- Adds missing Aria-Labels. @KDSBrowne
|
|
||||||
- Improves Japanese translation. @yashkumarbarot
|
|
||||||
- Fixes height and width in app.viewport. @hiroshisuga
|
|
||||||
- Improves labels on StlyeMenu @proke03
|
|
||||||
- Adds missing tooltips to undo / redo buttons. @proke03
|
|
||||||
|
|
||||||
## 1.12.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Improve middle mouse panning
|
|
||||||
- Fix bug with assets in VS Code plugin
|
|
||||||
- Improve performance of draw-style shapes
|
|
||||||
- Fix bug with creating assets
|
|
||||||
- Fix bug with text align in labels when outputting images
|
|
||||||
- Fix bug with middle mouse panning on Linux
|
|
||||||
- Fix bug with zoom shortcuts on number pad
|
|
||||||
- Fix bug with draw and erase direction when holding shift
|
|
||||||
|
|
||||||
## 1.11.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- d919bd27: Bump dependencies, add international support.
|
|
||||||
|
|
||||||
## 1.11.0-next.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Bump dependencies, add international support.
|
|
||||||
|
|
||||||
## 1.10.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix tldraw assets for vscode extension.
|
|
||||||
|
|
||||||
## 1.10.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix build.
|
|
||||||
|
|
||||||
## 1.10.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Fix build error in extension.
|
|
||||||
|
|
||||||
## 1.9.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Bump underlying packages.
|
|
||||||
|
|
||||||
## 1.8.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- c09d6a3a: Adds text field for page rename, undo buttons on all screen sizes, arrow behavior with alt key.
|
|
||||||
|
|
||||||
## 1.7.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix bug with missing parents / children.
|
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 Stephen Ruiz Ltd
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,9 +0,0 @@
|
||||||
<div style="text-align: center; transform: scale(.5);">
|
|
||||||
<img src="https://github.com/tldraw/tldraw/raw/main/assets/card-repo.png"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
# @tldraw/vscode-editor
|
|
||||||
|
|
||||||
The app for the tldraw VS Code Extension.
|
|
||||||
|
|
||||||
See the README at `vscode` for more about this project.
|
|
|
@ -1,35 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@tldraw/vscode-editor",
|
|
||||||
"version": "1.15.2",
|
|
||||||
"private": true,
|
|
||||||
"description": "An an editor for the tldraw vscode extension.",
|
|
||||||
"author": "@steveruizok",
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"react",
|
|
||||||
"typescript",
|
|
||||||
"esbuild"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"dev": "node scripts/dev.mjs -w",
|
|
||||||
"build": "node scripts/build.mjs",
|
|
||||||
"lint": "TIMING=1 eslint src/ --ext .ts,.tsx",
|
|
||||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@tldraw/tldraw": "*",
|
|
||||||
"@types/node": "^17.0.14",
|
|
||||||
"@types/react": "^18.0.17",
|
|
||||||
"@types/react-dom": "^18.0.6",
|
|
||||||
"@types/react-router-dom": "^5.1.8",
|
|
||||||
"concurrently": "7.0.0",
|
|
||||||
"create-serve": "1.0.1",
|
|
||||||
"esbuild": "^0.14.54",
|
|
||||||
"esbuild-serve": "^1.0.1",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"tslib": "^2.4.0",
|
|
||||||
"typescript": "^4.7.3"
|
|
||||||
},
|
|
||||||
"gitHead": "a7dac0f83ad998e205c2aab58182cb4ba4e099a6"
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
import esbuild from 'esbuild'
|
|
||||||
import fs from 'fs'
|
|
||||||
import { createRequire } from 'module'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
const pkg = createRequire(import.meta.url)('../package.json')
|
|
||||||
|
|
||||||
const { log: jslog } = console
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
if (fs.existsSync('./dist')) {
|
|
||||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
|
||||||
if (e) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync('./dist')) {
|
|
||||||
fs.mkdirSync('./dist')
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.readdirSync('./src/public').forEach((file) =>
|
|
||||||
fs.copyFile(path.join('./src/public', file), path.join('./dist', file), (err) => {
|
|
||||||
if (err) throw err
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
esbuild.buildSync({
|
|
||||||
entryPoints: ['./src/index.tsx'],
|
|
||||||
outfile: 'dist/index.js',
|
|
||||||
minify: false,
|
|
||||||
bundle: true,
|
|
||||||
format: 'esm',
|
|
||||||
target: 'es6',
|
|
||||||
jsxFactory: 'React.createElement',
|
|
||||||
jsxFragment: 'React.Fragment',
|
|
||||||
tsconfig: './tsconfig.json',
|
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': '"production"',
|
|
||||||
},
|
|
||||||
loader: {
|
|
||||||
'.woff2': 'dataurl',
|
|
||||||
'.woff': 'dataurl',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
jslog(`✔ ${pkg.name}: Build completed.`)
|
|
||||||
} catch (e) {
|
|
||||||
jslog(`× ${pkg.name}: Build failed due to an error.`)
|
|
||||||
jslog(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
|
@ -1,65 +0,0 @@
|
||||||
/* eslint-disable no-undef */
|
|
||||||
import dotenv from 'dotenv'
|
|
||||||
import esbuildServe from 'esbuild-serve'
|
|
||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
dotenv.config()
|
|
||||||
|
|
||||||
const { log: jslog } = console
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
if (fs.existsSync('./dist')) {
|
|
||||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
|
||||||
if (e) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync('./dist')) {
|
|
||||||
fs.mkdirSync('./dist')
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.readdirSync('./src/public').forEach((file) =>
|
|
||||||
fs.copyFile(path.join('./src/public', file), path.join('./dist', file), (err) => {
|
|
||||||
if (err) throw err
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await esbuildServe(
|
|
||||||
{
|
|
||||||
entryPoints: ['src/index.tsx'],
|
|
||||||
outfile: 'dist/index.js',
|
|
||||||
minify: false,
|
|
||||||
bundle: true,
|
|
||||||
incremental: true,
|
|
||||||
target: 'es6',
|
|
||||||
jsxFactory: 'React.createElement',
|
|
||||||
jsxFragment: 'React.Fragment',
|
|
||||||
loader: {
|
|
||||||
'.woff2': 'dataurl',
|
|
||||||
'.woff': 'dataurl',
|
|
||||||
},
|
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': '"production"',
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
onRebuild(err) {
|
|
||||||
err ? error('❌ Failed') : jslog('✅ Updated')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
port: 5420,
|
|
||||||
root: './dist',
|
|
||||||
live: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} catch (err) {
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
|
@ -1,77 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
||||||
import { TDDocument, TDFile, Tldraw, TldrawApp } from '@tldraw/tldraw'
|
|
||||||
import * as React from 'react'
|
|
||||||
import type { MessageFromExtension, MessageFromWebview } from './types'
|
|
||||||
import { defaultDocument } from './utils/defaultDocument'
|
|
||||||
import { vscode } from './utils/vscode'
|
|
||||||
|
|
||||||
// Will be placed in global scope by extension
|
|
||||||
declare let currentFile: TDFile
|
|
||||||
declare let assetSrc: string
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const rLoaded = React.useRef(false)
|
|
||||||
const rTldrawApp = React.useRef<TldrawApp>()
|
|
||||||
const rInitialDocument = React.useRef<TDDocument>(
|
|
||||||
currentFile ? currentFile.document : defaultDocument
|
|
||||||
)
|
|
||||||
|
|
||||||
// When the editor mounts, save the state instance in a ref.
|
|
||||||
const handleMount = React.useCallback((app: TldrawApp) => {
|
|
||||||
TldrawApp.assetSrc = assetSrc ?? 'tldraw-assets.json'
|
|
||||||
rTldrawApp.current = app
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// When the editor's document changes, post the stringified document to the vscode extension.
|
|
||||||
const handlePersist = React.useCallback((app: TldrawApp) => {
|
|
||||||
vscode.postMessage({
|
|
||||||
type: 'editorUpdated',
|
|
||||||
text: JSON.stringify({
|
|
||||||
...currentFile,
|
|
||||||
document: app.document,
|
|
||||||
assets: {},
|
|
||||||
} as TDFile),
|
|
||||||
} as MessageFromWebview)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// When the file changes from VS Code's side, update the editor's document.
|
|
||||||
React.useEffect(() => {
|
|
||||||
function handleMessage({ data }: MessageEvent<MessageFromExtension>) {
|
|
||||||
if (data.type === 'openedFile') {
|
|
||||||
try {
|
|
||||||
const { document } = JSON.parse(data.text) as TDFile
|
|
||||||
const app = rTldrawApp.current!
|
|
||||||
if (rLoaded.current) {
|
|
||||||
app.updateDocument(document)
|
|
||||||
} else {
|
|
||||||
app.loadDocument(document)
|
|
||||||
rLoaded.current = true
|
|
||||||
}
|
|
||||||
app.zoomToFit()
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Failed to parse file:', data.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('message', handleMessage)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('message', handleMessage)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="tldraw">
|
|
||||||
<Tldraw
|
|
||||||
id={rInitialDocument.current.id}
|
|
||||||
document={rInitialDocument.current}
|
|
||||||
onMount={handleMount}
|
|
||||||
onPersist={handlePersist}
|
|
||||||
autofocus
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
|
@ -1,10 +0,0 @@
|
||||||
import * as React from 'react'
|
|
||||||
import * as ReactDOM from 'react-dom'
|
|
||||||
import App from './app'
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<App />
|
|
||||||
</React.StrictMode>,
|
|
||||||
document.getElementById('root')
|
|
||||||
)
|
|
|
@ -1,20 +0,0 @@
|
||||||
html,
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
overscroll-behavior: none;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tldraw {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="index.css" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>tldraw</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<script type="module" src="./index.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,14 +0,0 @@
|
||||||
export type MessageFromWebview = {
|
|
||||||
type: 'editorUpdated'
|
|
||||||
text: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MessageFromExtension =
|
|
||||||
| {
|
|
||||||
type: 'openedFile'
|
|
||||||
text: string
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'fileSaved'
|
|
||||||
text: string
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
import { TDDocument, TldrawApp } from '@tldraw/tldraw'
|
|
||||||
|
|
||||||
export const defaultDocument: TDDocument = {
|
|
||||||
id: 'doc',
|
|
||||||
name: 'New Document',
|
|
||||||
version: TldrawApp.version,
|
|
||||||
pages: {
|
|
||||||
page: {
|
|
||||||
id: 'page',
|
|
||||||
name: 'Page 1',
|
|
||||||
childIndex: 1,
|
|
||||||
shapes: {},
|
|
||||||
bindings: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
assets: {},
|
|
||||||
pageStates: {
|
|
||||||
page: {
|
|
||||||
id: 'page',
|
|
||||||
selectedIds: [],
|
|
||||||
camera: {
|
|
||||||
point: [0, 0],
|
|
||||||
zoom: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { TDExport } from '@tldraw/tldraw'
|
|
||||||
|
|
||||||
export const EXPORT_ENDPOINT =
|
|
||||||
process.env.NODE_ENV === 'development'
|
|
||||||
? 'http://localhost:3000/api/export'
|
|
||||||
: 'https://www.tldraw.com/api/export'
|
|
||||||
|
|
||||||
export async function exportToImage(info: TDExport) {
|
|
||||||
if (info.serialized) {
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(info.serialized)
|
|
||||||
link.download = info.name + '.' + info.type
|
|
||||||
link.click()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(EXPORT_ENDPOINT, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(info),
|
|
||||||
})
|
|
||||||
const blob = await response.blob()
|
|
||||||
const blobUrl = URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = blobUrl
|
|
||||||
link.download = info.name + '.' + info.type
|
|
||||||
link.click()
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import type { MessageFromWebview } from '../types'
|
|
||||||
|
|
||||||
// Will be placed in global scope by extension
|
|
||||||
declare function acquireVsCodeApi(): {
|
|
||||||
postMessage(options: MessageFromWebview): void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const vscode = acquireVsCodeApi()
|
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "../../../tsconfig.base.json",
|
|
||||||
"include": ["src"],
|
|
||||||
"exclude": ["node_modules", "dist"],
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./dist",
|
|
||||||
"rootDir": "src",
|
|
||||||
"baseUrl": "src",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"paths": {
|
|
||||||
"@tldraw/tldraw": ["../../../packages/tldraw"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "../../../packages/tldraw"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
6
apps/vscode/extension/.gitignore
vendored
|
@ -1,6 +0,0 @@
|
||||||
dist
|
|
||||||
editor
|
|
||||||
node_modules
|
|
||||||
.vscode-test-web/
|
|
||||||
*.vsix
|
|
||||||
.DS_Store
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
|
||||||
// for the documentation about the extensions.json format
|
|
||||||
"recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"]
|
|
||||||
}
|
|
24
apps/vscode/extension/.vscode/launch.json
vendored
|
@ -1,24 +0,0 @@
|
||||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Run Web Extension ",
|
|
||||||
"type": "pwa-extensionHost",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceFolder}/src/extension.ts",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"args": [
|
|
||||||
"--disable-extensions",
|
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
|
||||||
"${workspaceFolder}/src/extension.ts"
|
|
||||||
],
|
|
||||||
"outFiles": ["${workspaceFolder}/dist/web/**/*.js", "!**/node_modules/**"],
|
|
||||||
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
|
|
||||||
"sourceMaps": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
11
apps/vscode/extension/.vscode/settings.json
vendored
|
@ -1,11 +0,0 @@
|
||||||
// Place your settings in this file to overwrite default and user settings.
|
|
||||||
{
|
|
||||||
"files.exclude": {
|
|
||||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
|
||||||
},
|
|
||||||
"search.exclude": {
|
|
||||||
"out": true // set this to false to include "out" folder in search results
|
|
||||||
},
|
|
||||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
|
||||||
"typescript.tsc.autoDetect": "off"
|
|
||||||
}
|
|
|
@ -1,390 +0,0 @@
|
||||||
## 1.2.4
|
|
||||||
|
|
||||||
## 1.28.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Include clipboard fixes.
|
|
||||||
|
|
||||||
## 1.28.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix clipboard stealing focus.
|
|
||||||
|
|
||||||
## 1.28.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Fixes copy as PNG
|
|
||||||
- Fixes a bug with copy and paste
|
|
||||||
- Fixes a bug on iPad while using pencil
|
|
||||||
- Fixes a bug with the style menus
|
|
||||||
- Fixes a bug with image export
|
|
||||||
- Updates translations
|
|
||||||
|
|
||||||
## 1.27.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates German translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
|
|
||||||
## 1.26.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates German translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
- 12c0b2ac: - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
|
|
||||||
## 1.26.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Adds missing Arabic translations for dialogs. @abedshamia
|
|
||||||
- Updates core-example. @brydenfogelman
|
|
||||||
- Updates Polish translations. @adan2013
|
|
||||||
- Adds missing Aria-Labels. @KDSBrowne
|
|
||||||
- Improves Japanese translation. @yashkumarbarot
|
|
||||||
- Fixes height and width in app.viewport. @hiroshisuga
|
|
||||||
- Improves labels on StlyeMenu @proke03
|
|
||||||
- Adds missing tooltips to undo / redo buttons. @proke03
|
|
||||||
|
|
||||||
## 1.25.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix types in core.
|
|
||||||
|
|
||||||
## 1.25.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fixes blurring text inputs in multiplayer.
|
|
||||||
|
|
||||||
## 1.25.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Add metadata property to user.
|
|
||||||
|
|
||||||
## 1.24.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Adds `components` prop to Tldraw component (for custom Cursor, etc) @jamesbvaughan
|
|
||||||
- Adds Thai language @watchakorn-18k
|
|
||||||
- Fix event bug on `onRightPointCanvas`
|
|
||||||
- Fix bug with bad data in document with up-to-date version number
|
|
||||||
- Fix bug with arrow bindings
|
|
||||||
- Improves freehand line performance
|
|
||||||
- Improves performance of shape tree
|
|
||||||
- Improved .tldr file size (strip white space)
|
|
||||||
|
|
||||||
## 1.23.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix mouse events.
|
|
||||||
|
|
||||||
## 1.23.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix menu bug.
|
|
||||||
|
|
||||||
## 1.23.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Small bump.
|
|
||||||
|
|
||||||
## 1.23.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix bug with scrolling.
|
|
||||||
|
|
||||||
## 1.23.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- - Fix bug with mouse button state
|
|
||||||
|
|
||||||
## 1.23.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Remove `mobx` and `mobx-react-lite` as dependencies. This is a breaking change for libraries that expect data to be observable in `@tldraw/core`.
|
|
||||||
|
|
||||||
## 1.22.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Improve middle mouse panning
|
|
||||||
- Fix bug with assets in VS Code plugin
|
|
||||||
- Improve performance of draw-style shapes
|
|
||||||
- Fix bug with creating assets
|
|
||||||
- Fix bug with text align in labels when outputting images
|
|
||||||
- Fix bug with middle mouse panning on Linux
|
|
||||||
- Fix bug with zoom shortcuts on number pad
|
|
||||||
- Fix bug with draw and erase direction when holding shift
|
|
||||||
|
|
||||||
## 1.21.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Remove share by URL.
|
|
||||||
|
|
||||||
## 1.21.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Fix broken VS Code extension
|
|
||||||
- Add share by URL
|
|
||||||
|
|
||||||
## 1.20.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Improve text (and multiline text) in image exports
|
|
||||||
- Create European Portugese translation
|
|
||||||
- Create Swedish translation
|
|
||||||
- Use system default for theme default
|
|
||||||
- Update translation label for Chinese
|
|
||||||
- Fix bugs with flip command
|
|
||||||
- Fix bug with duplicate page command
|
|
||||||
- Improve dialogs
|
|
||||||
- Improve SVG pasting
|
|
||||||
|
|
||||||
## 1.19.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - restores the sponsor link
|
|
||||||
- removes sign in / sign out / authentication / next-auth
|
|
||||||
- removes sponsorware page
|
|
||||||
- removes unused translation keys
|
|
||||||
- fixes dark mode on help icon
|
|
||||||
- improves border radius on panels
|
|
||||||
- fixes dividers on panels
|
|
||||||
- removes animated cursors (replace with CSS transitions for performance when - many cursors are present)
|
|
||||||
- removes unused icons
|
|
||||||
- adds migration for export default background option
|
|
||||||
- correctly normalizes mouse wheel
|
|
||||||
|
|
||||||
## 1.18.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Adds Ukrainian translations
|
|
||||||
- Adds Farsi translation
|
|
||||||
- Adds Hebrew translation
|
|
||||||
- Adds option for dock position
|
|
||||||
- Improves page numbering
|
|
||||||
- Support dark mode in menus
|
|
||||||
- Make language menu scrollable
|
|
||||||
- Adds link to translation guide
|
|
||||||
|
|
||||||
## 1.17.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Improve page reordering, add german translation
|
|
||||||
|
|
||||||
## 1.17.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Add additional translations.
|
|
||||||
|
|
||||||
## 1.17.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 8ef86c19: - Updates multiplayer implementation.
|
|
||||||
- Adds translation guide.
|
|
||||||
- Fixes bug on text shape
|
|
||||||
- Updates undo redo for text shapes.
|
|
||||||
|
|
||||||
## 1.16.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Replace multiplayer icon.
|
|
||||||
|
|
||||||
## 1.16.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix clipboard bug in Firefox, add overwite option to `insertContent`.
|
|
||||||
|
|
||||||
## 1.16.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Add getContent / insertContent, improve copy and paste position logic
|
|
||||||
|
|
||||||
## 1.15.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- d919bd27: Bump dependencies, add international support.
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Add internationalization, improve readonly mode, bump dependencies for React 18
|
|
||||||
|
|
||||||
## 1.15.0-next.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Bump dependencies, add international support.
|
|
||||||
|
|
||||||
## 1.14.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Improve eraser scribble.
|
|
||||||
|
|
||||||
## 1.14.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Add erase line, bump dependencies.
|
|
||||||
|
|
||||||
## 1.13.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix keyboard events when style menu is open.
|
|
||||||
|
|
||||||
## 1.13.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Move style panel to right corner.
|
|
||||||
|
|
||||||
## 1.13.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Add option to keep style panel open.
|
|
||||||
|
|
||||||
## 1.13.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Fixes zooming and pinching bugs. Adds ErrorBoundary to Tldraw component. Cleans up sponosrship feature in menu.
|
|
||||||
|
|
||||||
## 1.12.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Improve image export for files that include scaled or rotated text.
|
|
||||||
|
|
||||||
## 1.12.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Improve clipboard, SVG text.
|
|
||||||
|
|
||||||
## 1.12.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix export on dark mode.
|
|
||||||
|
|
||||||
## 1.12.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix clipboard events in editing text in vscode extension, fix outline for editing text in vscode extension.
|
|
||||||
|
|
||||||
## 1.12.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Update to include 1.12.2.
|
|
||||||
|
|
||||||
## 1.12.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix tldraw assets for vscode extension.
|
|
||||||
|
|
||||||
## 1.12.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- This update changes how clipboard actions (cut, copy, paste) and exports work. Significantly, image exports are no longer handled via a server-side integration, and are instead handled locally on the client. This allows now for exports in the VS Code extension, as well as greatly simplifying exports for apps that embed the Tldraw React component.
|
|
||||||
|
|
||||||
## 1.11.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Add paste for assets.
|
|
||||||
|
|
||||||
## 1.11.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix build.
|
|
||||||
|
|
||||||
## 1.10.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Bump underlying packages.
|
|
||||||
|
|
||||||
## 1.9.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- c09d6a3a: Adds text field for page rename, undo buttons on all screen sizes, arrow behavior with alt key.
|
|
||||||
|
|
||||||
## 1.8.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix bug with missing parents / children.
|
|
||||||
|
|
||||||
- Fixed bug that prevented saving.
|
|
||||||
|
|
||||||
## 1.1.9
|
|
||||||
|
|
||||||
- Updates READMEs.
|
|
||||||
|
|
||||||
## 1.1.6
|
|
||||||
|
|
||||||
- Fixes bugs in VS Code extension.
|
|
||||||
|
|
||||||
## 0.1.23
|
|
||||||
|
|
||||||
- Fixing bugs related to saving files.
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
- Launched!
|
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 Stephen Ruiz Ltd
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,30 +0,0 @@
|
||||||
## Introduction
|
|
||||||
|
|
||||||
Create and edit diagrams using the [tldraw](https://tldraw.com/) editor, all inside of VS Code.
|
|
||||||
|
|
||||||
![A screenshot of tldraw in VS Code](./assets/screenshot.png)
|
|
||||||
|
|
||||||
[tldraw](https://tldraw.com) is a free drawing and diagramming tool with a hand-drawn style and convenient features such as smart arrows, snapping, and sticky notes. With the tldraw extension for VS Code, your tldraw files can be version controlled alongside your code.
|
|
||||||
|
|
||||||
![A recording of tldraw in VS Code](./assets/recording.gif)
|
|
||||||
|
|
||||||
> **Tip:** The files you create or edit here can also be opened in the tldraw [web app](https://tldraw.com).
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
1. View, edit and save tldraw files (`.tldr`)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
- To view an existing tldraw file, open a file with the `.tldr` extension in VS Code.
|
|
||||||
- To create a new tldraw file, use the provided command: "tldraw: New tldraw File".
|
|
||||||
|
|
||||||
## Community
|
|
||||||
|
|
||||||
### Support
|
|
||||||
|
|
||||||
Need help? Please [open an issue](https://github.com/tldraw/tldraw/issues/new/choose) for support.
|
|
||||||
|
|
||||||
### Discussion
|
|
||||||
|
|
||||||
Want to connect with other devs? Visit the [Discord channel](https://discord.gg/SBBEVCA4PG).
|
|
Before Width: | Height: | Size: 2.4 MiB |
Before Width: | Height: | Size: 80 KiB |
|
@ -1,168 +0,0 @@
|
||||||
{
|
|
||||||
"fileHandle": null,
|
|
||||||
"document": {
|
|
||||||
"id": "doc",
|
|
||||||
"name": "New Document",
|
|
||||||
"version": 15.3,
|
|
||||||
"pages": {
|
|
||||||
"page": {
|
|
||||||
"id": "page",
|
|
||||||
"name": "Page 1",
|
|
||||||
"childIndex": 1,
|
|
||||||
"shapes": {
|
|
||||||
"3805a6e3-b50a-4bb2-0782-e687526cafc8": {
|
|
||||||
"id": "3805a6e3-b50a-4bb2-0782-e687526cafc8",
|
|
||||||
"type": "rectangle",
|
|
||||||
"name": "Rectangle",
|
|
||||||
"parentId": "page",
|
|
||||||
"childIndex": 1,
|
|
||||||
"point": [
|
|
||||||
245.96,
|
|
||||||
277.88
|
|
||||||
],
|
|
||||||
"size": [
|
|
||||||
292.43,
|
|
||||||
125.76
|
|
||||||
],
|
|
||||||
"rotation": 0,
|
|
||||||
"style": {
|
|
||||||
"color": "white",
|
|
||||||
"size": "small",
|
|
||||||
"isFilled": false,
|
|
||||||
"dash": "draw"
|
|
||||||
},
|
|
||||||
"label": "Rectangles with labels"
|
|
||||||
},
|
|
||||||
"9a20ffa8-044b-4ca3-0448-632e2b2831d1": {
|
|
||||||
"id": "9a20ffa8-044b-4ca3-0448-632e2b2831d1",
|
|
||||||
"type": "ellipse",
|
|
||||||
"name": "Ellipse",
|
|
||||||
"parentId": "page",
|
|
||||||
"childIndex": 3,
|
|
||||||
"point": [
|
|
||||||
995.24,
|
|
||||||
235.11
|
|
||||||
],
|
|
||||||
"radius": [
|
|
||||||
112.92500000000001,
|
|
||||||
108.96000000000004
|
|
||||||
],
|
|
||||||
"rotation": 0,
|
|
||||||
"style": {
|
|
||||||
"color": "white",
|
|
||||||
"size": "small",
|
|
||||||
"isFilled": true,
|
|
||||||
"dash": "draw",
|
|
||||||
"scale": 1
|
|
||||||
},
|
|
||||||
"label": "Ovals with labels",
|
|
||||||
"labelPoint": [
|
|
||||||
0.5,
|
|
||||||
0.5
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"c2125c4e-e9ef-4eeb-2011-9e8aaeadd6ea": {
|
|
||||||
"id": "c2125c4e-e9ef-4eeb-2011-9e8aaeadd6ea",
|
|
||||||
"type": "arrow",
|
|
||||||
"name": "Arrow",
|
|
||||||
"parentId": "page",
|
|
||||||
"childIndex": 4,
|
|
||||||
"point": [
|
|
||||||
538.39,
|
|
||||||
341.56
|
|
||||||
],
|
|
||||||
"rotation": 0,
|
|
||||||
"bend": -0.000013369615345367926,
|
|
||||||
"handles": {
|
|
||||||
"start": {
|
|
||||||
"id": "start",
|
|
||||||
"index": 0,
|
|
||||||
"point": [
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"canBind": true,
|
|
||||||
"bindingId": "31007b26-4667-4932-00aa-7f389642b187"
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"id": "end",
|
|
||||||
"index": 1,
|
|
||||||
"point": [
|
|
||||||
448.85,
|
|
||||||
2.44
|
|
||||||
],
|
|
||||||
"canBind": true,
|
|
||||||
"bindingId": "7b099540-f8b1-4bf3-0d7d-2b74df370067"
|
|
||||||
},
|
|
||||||
"bend": {
|
|
||||||
"id": "bend",
|
|
||||||
"index": 2,
|
|
||||||
"point": [
|
|
||||||
224.43,
|
|
||||||
1.22
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"decorations": {
|
|
||||||
"end": "arrow"
|
|
||||||
},
|
|
||||||
"style": {
|
|
||||||
"color": "white",
|
|
||||||
"size": "small",
|
|
||||||
"isFilled": false,
|
|
||||||
"dash": "draw",
|
|
||||||
"scale": 1
|
|
||||||
},
|
|
||||||
"label": "Arrows with labels",
|
|
||||||
"labelPoint": [
|
|
||||||
0.5,
|
|
||||||
0.5
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bindings": {
|
|
||||||
"7b099540-f8b1-4bf3-0d7d-2b74df370067": {
|
|
||||||
"id": "7b099540-f8b1-4bf3-0d7d-2b74df370067",
|
|
||||||
"type": "arrow",
|
|
||||||
"fromId": "c2125c4e-e9ef-4eeb-2011-9e8aaeadd6ea",
|
|
||||||
"toId": "9a20ffa8-044b-4ca3-0448-632e2b2831d1",
|
|
||||||
"handleId": "end",
|
|
||||||
"point": [
|
|
||||||
0.08,
|
|
||||||
0.5
|
|
||||||
],
|
|
||||||
"distance": 8
|
|
||||||
},
|
|
||||||
"31007b26-4667-4932-00aa-7f389642b187": {
|
|
||||||
"id": "31007b26-4667-4932-00aa-7f389642b187",
|
|
||||||
"type": "arrow",
|
|
||||||
"fromId": "c2125c4e-e9ef-4eeb-2011-9e8aaeadd6ea",
|
|
||||||
"toId": "3805a6e3-b50a-4bb2-0782-e687526cafc8",
|
|
||||||
"handleId": "start",
|
|
||||||
"point": [
|
|
||||||
0.5,
|
|
||||||
0.5
|
|
||||||
],
|
|
||||||
"distance": 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pageStates": {
|
|
||||||
"page": {
|
|
||||||
"id": "page",
|
|
||||||
"selectedIds": [],
|
|
||||||
"camera": {
|
|
||||||
"point": [
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"zoom": 1
|
|
||||||
},
|
|
||||||
"editingId": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"assets": {}
|
|
||||||
},
|
|
||||||
"assets": {}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 6.4 KiB |
|
@ -1,136 +0,0 @@
|
||||||
{
|
|
||||||
"name": "tldraw-vscode",
|
|
||||||
"displayName": "tldraw",
|
|
||||||
"description": "The tldraw Extension for VS Code.",
|
|
||||||
"version": "1.28.4",
|
|
||||||
"license": "MIT",
|
|
||||||
"publisher": "tldraw-org",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/tldraw/tldraw"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"vscode": "^1.59.0"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"diagram",
|
|
||||||
"draw",
|
|
||||||
"drawing",
|
|
||||||
"sketch",
|
|
||||||
"design",
|
|
||||||
"documentation",
|
|
||||||
"tldraw"
|
|
||||||
],
|
|
||||||
"icon": "icon.png",
|
|
||||||
"galleryBanner": {
|
|
||||||
"color": "#1d1d1d",
|
|
||||||
"theme": "dark"
|
|
||||||
},
|
|
||||||
"categories": [
|
|
||||||
"Visualization"
|
|
||||||
],
|
|
||||||
"activationEvents": [
|
|
||||||
"onCustomEditor:tldraw.tldr",
|
|
||||||
"onCommand:tldraw.tldr.new"
|
|
||||||
],
|
|
||||||
"browser": "./dist/web/extension.js",
|
|
||||||
"main": "./dist/web/extension.js",
|
|
||||||
"extensionKind": [
|
|
||||||
"workspace"
|
|
||||||
],
|
|
||||||
"contributes": {
|
|
||||||
"customEditors": [
|
|
||||||
{
|
|
||||||
"viewType": "tldraw.tldr",
|
|
||||||
"displayName": "tldraw",
|
|
||||||
"selector": [
|
|
||||||
{
|
|
||||||
"filenamePattern": "*.tldr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filenamePattern": "*.tldr.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"keybindings": [
|
|
||||||
{
|
|
||||||
"key": "ctrl+shift+d",
|
|
||||||
"mac": "cmd+shift+d",
|
|
||||||
"title": "Toggle Dark Mode",
|
|
||||||
"command": "tldraw.tldr.toggleDarkMode",
|
|
||||||
"category": "tldraw",
|
|
||||||
"when": "resourceExtname == .tldr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "ctrl+numpad_add",
|
|
||||||
"mac": "cmd+numpad_add",
|
|
||||||
"title": "Zoom In",
|
|
||||||
"command": "tldraw.tldr.zoomIn",
|
|
||||||
"category": "tldraw",
|
|
||||||
"when": "resourceExtname == .tldr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "ctrl+=",
|
|
||||||
"mac": "cmd+=",
|
|
||||||
"title": "Zoom In",
|
|
||||||
"command": "tldraw.tldr.zoomIn",
|
|
||||||
"category": "tldraw",
|
|
||||||
"when": "resourceExtname == .tldr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "ctrl+numpad_subtract",
|
|
||||||
"mac": "cmd+numpad_subtract",
|
|
||||||
"title": "Zoom Out",
|
|
||||||
"command": "tldraw.tldr.zoomOut",
|
|
||||||
"category": "tldraw",
|
|
||||||
"when": "resourceExtname == .tldr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "ctrl+-",
|
|
||||||
"mac": "cmd+-",
|
|
||||||
"title": "Zoom Out",
|
|
||||||
"command": "tldraw.tldr.zoomOut",
|
|
||||||
"category": "tldraw",
|
|
||||||
"when": "resourceExtname == .tldr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "ctrl+numpad0",
|
|
||||||
"mac": "cmd+numpad0",
|
|
||||||
"title": "Reset Zoom",
|
|
||||||
"command": "tldraw.tldr.resetZoom",
|
|
||||||
"category": "tldraw",
|
|
||||||
"when": "resourceExtname == .tldr"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"commands": [
|
|
||||||
{
|
|
||||||
"command": "tldraw.tldr.new",
|
|
||||||
"title": "New File",
|
|
||||||
"category": "tldraw"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "node scripts/dev",
|
|
||||||
"build": "node scripts/build",
|
|
||||||
"web": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=.",
|
|
||||||
"package": "node scripts/package",
|
|
||||||
"publish": "vsce publish",
|
|
||||||
"lint": "TIMING=1 eslint src/ --ext ts",
|
|
||||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist && rm -rf editor && rm -rf temp"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@tldraw/tldraw": "*",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
|
||||||
"@typescript-eslint/parser": "^5.10.2",
|
|
||||||
"assert": "^2.0.0",
|
|
||||||
"mocha": "^9.1.1",
|
|
||||||
"process": "^0.11.10",
|
|
||||||
"ts-loader": "^9.2.5",
|
|
||||||
"tslib": "^2.4.0",
|
|
||||||
"typescript": "^4.7.3",
|
|
||||||
"vsce": "^2.2.0"
|
|
||||||
},
|
|
||||||
"gitHead": "4b1137849ad07da36fc8f0f19cb64e7535a79296"
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
const fs = require('fs')
|
|
||||||
const pkg = require('../package.json')
|
|
||||||
const esbuild = require('esbuild')
|
|
||||||
const { exec } = require('child_process')
|
|
||||||
|
|
||||||
const { log: jslog } = console
|
|
||||||
|
|
||||||
async function copyEditor() {
|
|
||||||
if (fs.existsSync('./editor')) {
|
|
||||||
fs.rmSync('./editor', { recursive: true }, (e) => {
|
|
||||||
if (e) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
exec(`cp -r ../editor/dist editor;`, (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message)
|
|
||||||
}
|
|
||||||
if (stderr && stderr.search('warning') !== 0) {
|
|
||||||
throw new Error(stderr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
jslog(`× ${pkg.name}: Build failed due to an error.`)
|
|
||||||
jslog(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
await copyEditor()
|
|
||||||
|
|
||||||
if (fs.existsSync('./dist')) {
|
|
||||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
|
||||||
if (e) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
esbuild.buildSync({
|
|
||||||
entryPoints: ['./src/extension.ts'],
|
|
||||||
outdir: 'dist/web',
|
|
||||||
minify: false,
|
|
||||||
bundle: true,
|
|
||||||
format: 'cjs',
|
|
||||||
target: 'es6',
|
|
||||||
platform: 'node',
|
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': '"production"',
|
|
||||||
},
|
|
||||||
tsconfig: './tsconfig.json',
|
|
||||||
external: ['vscode'],
|
|
||||||
loader: {
|
|
||||||
'.woff2': 'dataurl',
|
|
||||||
'.woff': 'dataurl',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
jslog(`Built package.`)
|
|
||||||
} catch (e) {
|
|
||||||
jslog(`× Build failed due to an error.`)
|
|
||||||
jslog(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
|
@ -1,46 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
const fs = require('fs')
|
|
||||||
const esbuild = require('esbuild')
|
|
||||||
|
|
||||||
const { log: jslog } = console
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
if (fs.existsSync('./dist')) {
|
|
||||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
|
||||||
if (e) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await esbuild.build({
|
|
||||||
entryPoints: ['./src/extension.ts'],
|
|
||||||
outdir: 'dist/web',
|
|
||||||
minify: false,
|
|
||||||
bundle: true,
|
|
||||||
format: 'cjs',
|
|
||||||
target: 'es6',
|
|
||||||
sourcemap: 'inline',
|
|
||||||
platform: 'node',
|
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': '"development"',
|
|
||||||
},
|
|
||||||
tsconfig: './tsconfig.json',
|
|
||||||
external: ['vscode'],
|
|
||||||
incremental: true,
|
|
||||||
watch: {
|
|
||||||
onRebuild(err) {
|
|
||||||
err ? console.error('❌ Failed') : jslog('✅ Updated')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
jslog(`Built package.`)
|
|
||||||
} catch (e) {
|
|
||||||
jslog(`× Build failed due to an error.`)
|
|
||||||
jslog(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
|
@ -1,45 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
//const version = require('../../../lerna.json').version
|
|
||||||
const fs = require('fs')
|
|
||||||
const pkg = require('../package.json')
|
|
||||||
const { exec } = require('child_process')
|
|
||||||
|
|
||||||
const { log: jslog } = console
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
if (fs.existsSync('./editor')) {
|
|
||||||
fs.rmSync('./editor', { recursive: true }, (e) => {
|
|
||||||
if (e) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (fs.existsSync('./temp')) {
|
|
||||||
fs.rmSync('./temp', { recursive: true }, (e) => {
|
|
||||||
if (e) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.mkdirSync('./temp')
|
|
||||||
|
|
||||||
try {
|
|
||||||
exec(
|
|
||||||
`cp -r ../editor/dist editor; vsce package; mv ${pkg.name}-${pkg.version}.vsix ${'./temp'}`,
|
|
||||||
(error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
throw new Error(error.message)
|
|
||||||
}
|
|
||||||
if (stderr && stderr.search('warning') !== 0) {
|
|
||||||
throw new Error(stderr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} catch (e) {
|
|
||||||
jslog(`× ${pkg.name}: Build failed due to an error.`)
|
|
||||||
jslog(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
|
@ -1,62 +0,0 @@
|
||||||
import * as vscode from 'vscode'
|
|
||||||
import { TldrawWebviewManager } from './TldrawWebviewManager'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Tldraw extension's editor uses CustomTextEditorProvider, which means
|
|
||||||
* it's underlying model from VS Code's perspective is a text file. We likely
|
|
||||||
* will switch to CustomEditorProvider which gives us more control but will require
|
|
||||||
* more book keeping on our part.
|
|
||||||
*/
|
|
||||||
export class TldrawEditorProvider implements vscode.CustomTextEditorProvider {
|
|
||||||
constructor(private readonly context: vscode.ExtensionContext) {}
|
|
||||||
|
|
||||||
private static newTDFileId = 1
|
|
||||||
|
|
||||||
private static readonly viewType = 'tldraw.tldr'
|
|
||||||
|
|
||||||
public static register = (context: vscode.ExtensionContext): vscode.Disposable => {
|
|
||||||
// Several commands exist only to prevent the default keyboard shortcuts
|
|
||||||
const noopCmds = ['zoomIn', 'zoomOut', 'resetZoom', 'toggleDarkMode']
|
|
||||||
noopCmds.forEach((name) =>
|
|
||||||
vscode.commands.registerCommand(`${this.viewType}.${name}`, () => null)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register the 'Create New File' command, which creates a temporary
|
|
||||||
// .tldr file and opens it in the editor.
|
|
||||||
vscode.commands.registerCommand(`${this.viewType}.new`, () => {
|
|
||||||
const id = this.newTDFileId++
|
|
||||||
const name = id > 1 ? `New Document ${id}.tldr` : `New Document.tldr`
|
|
||||||
|
|
||||||
const workspaceFolders = vscode.workspace.workspaceFolders
|
|
||||||
const path = workspaceFolders ? workspaceFolders[0].uri : vscode.Uri.parse('')
|
|
||||||
|
|
||||||
vscode.commands.executeCommand(
|
|
||||||
'vscode.openWith',
|
|
||||||
vscode.Uri.joinPath(path, name).with({ scheme: 'untitled' }),
|
|
||||||
this.viewType
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Register our editor provider, indicating to VS Code that we can
|
|
||||||
// handle files with the .tldr extension.
|
|
||||||
return vscode.window.registerCustomEditorProvider(
|
|
||||||
this.viewType,
|
|
||||||
new TldrawEditorProvider(context),
|
|
||||||
{
|
|
||||||
webviewOptions: {
|
|
||||||
retainContextWhenHidden: true,
|
|
||||||
},
|
|
||||||
supportsMultipleEditorsPerDocument: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// When our custom editor is opened, create a TldrawWebviewManager to
|
|
||||||
// configure the webview and set event listeners to handle events.
|
|
||||||
public async resolveCustomTextEditor(
|
|
||||||
document: vscode.TextDocument,
|
|
||||||
webviewPanel: vscode.WebviewPanel
|
|
||||||
): Promise<void> {
|
|
||||||
new TldrawWebviewManager(this.context, document, webviewPanel)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
import { TDFile } from '@tldraw/tldraw'
|
|
||||||
import * as vscode from 'vscode'
|
|
||||||
import { MessageFromExtension, MessageFromWebview } from './types'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When a new editor is opened, an instance of this class will
|
|
||||||
* be created to configure the webview and handle its events.
|
|
||||||
*/
|
|
||||||
export class TldrawWebviewManager {
|
|
||||||
private disposables: vscode.Disposable[] = []
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private context: vscode.ExtensionContext,
|
|
||||||
private document: vscode.TextDocument,
|
|
||||||
private webviewPanel: vscode.WebviewPanel
|
|
||||||
) {
|
|
||||||
// Configure the webview. For now all we do is enable scripts and also
|
|
||||||
// provide the initial webview's html content.
|
|
||||||
Object.assign(webviewPanel.webview, {
|
|
||||||
options: { enableScripts: true },
|
|
||||||
html: this.getHtmlForWebview(),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen for changes to the document when saved from VS Code.m
|
|
||||||
vscode.workspace.onDidSaveTextDocument(
|
|
||||||
this.handleDidSaveTextDocument,
|
|
||||||
undefined,
|
|
||||||
this.disposables
|
|
||||||
)
|
|
||||||
|
|
||||||
// Listen for changes made to the text document by VS Code or by some other app.
|
|
||||||
vscode.workspace.onDidChangeTextDocument(
|
|
||||||
this.handleDidChangeTextDocument,
|
|
||||||
undefined,
|
|
||||||
this.disposables
|
|
||||||
)
|
|
||||||
|
|
||||||
// Listen for messages sent from the extensions webview.
|
|
||||||
webviewPanel.webview.onDidReceiveMessage(
|
|
||||||
this.handleMessageFromWebview,
|
|
||||||
undefined,
|
|
||||||
this.disposables
|
|
||||||
)
|
|
||||||
|
|
||||||
// Send the initial document content to bootstrap the Tldraw/Tldraw component.
|
|
||||||
webviewPanel.webview.postMessage({
|
|
||||||
type: 'openedFile',
|
|
||||||
text: document.getText(),
|
|
||||||
} as MessageFromExtension)
|
|
||||||
|
|
||||||
// Clean up disposables when the editor is closed.
|
|
||||||
webviewPanel.onDidDispose(this.handleDidDispose)
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleDidDispose = () => {
|
|
||||||
this.disposables.forEach(({ dispose }) => dispose())
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleDidSaveTextDocument = () => {
|
|
||||||
const { webviewPanel, document } = this
|
|
||||||
if (!(webviewPanel && document)) return
|
|
||||||
|
|
||||||
webviewPanel.webview.postMessage({
|
|
||||||
type: 'fileSaved',
|
|
||||||
text: document.getText(),
|
|
||||||
} as MessageFromExtension)
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleDidChangeTextDocument = (event: vscode.TextDocumentChangeEvent) => {
|
|
||||||
// TODO
|
|
||||||
// If we can figure out whether the change came from inside of the
|
|
||||||
// editor vs. from some other app, we can update the document to
|
|
||||||
// show that external change.
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleMessageFromWebview = (e: MessageFromWebview) => {
|
|
||||||
const { document } = this
|
|
||||||
if (!document) return
|
|
||||||
|
|
||||||
switch (e.type) {
|
|
||||||
case 'editorUpdated': {
|
|
||||||
// The event will contain the new TDFile as JSON.
|
|
||||||
const nextFile = JSON.parse(e.text) as TDFile
|
|
||||||
|
|
||||||
if (document.getText()) {
|
|
||||||
try {
|
|
||||||
// Parse the contents of the current document.
|
|
||||||
const currentFile = JSON.parse(document.getText()) as TDFile
|
|
||||||
|
|
||||||
// Ensure that the current file's pageStates are preserved
|
|
||||||
// in the next file, unless the associated pages have been deleted.
|
|
||||||
Object.values(currentFile.document.pageStates).forEach((pageState) => {
|
|
||||||
if (nextFile.document.pages[pageState.id] !== undefined) {
|
|
||||||
nextFile.document.pageStates[pageState.id] = pageState
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Could not parse the current file to JSON.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an edit that replaces the document's current text
|
|
||||||
// content (a serialized TDFile) with the next file.
|
|
||||||
const edit = new vscode.WorkspaceEdit()
|
|
||||||
|
|
||||||
edit.replace(
|
|
||||||
document.uri,
|
|
||||||
new vscode.Range(0, 0, document.lineCount, 0),
|
|
||||||
JSON.stringify(nextFile, null, 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
vscode.workspace.applyEdit(edit)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getHtmlForWebview = (): string => {
|
|
||||||
const { document, context, webviewPanel } = this
|
|
||||||
|
|
||||||
let documentContent: string
|
|
||||||
|
|
||||||
let cssSrc: string | vscode.Uri
|
|
||||||
let jsSrc: string | vscode.Uri
|
|
||||||
const assetSrc = webviewPanel.webview.asWebviewUri(
|
|
||||||
vscode.Uri.joinPath(context.extensionUri, 'editor/', 'tldraw-assets.json')
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
JSON.parse(document.getText())
|
|
||||||
documentContent = document.getText()
|
|
||||||
} catch (error) {
|
|
||||||
// For now we're going to tread badly formed .tldr files as freshly created files.
|
|
||||||
// This will happen if say a user creates a new .tldr file using New File or if they
|
|
||||||
// have a bad auto-merge that messes up the json of an existing .tldr file
|
|
||||||
// We pass null as the initialDocument value if we can't parse as json.
|
|
||||||
documentContent = 'null'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
cssSrc = webviewPanel.webview.asWebviewUri(
|
|
||||||
vscode.Uri.joinPath(context.extensionUri, 'editor', 'index.css')
|
|
||||||
)
|
|
||||||
jsSrc = webviewPanel.webview.asWebviewUri(
|
|
||||||
vscode.Uri.joinPath(context.extensionUri, 'editor', 'index.js')
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
const localhost = 'http://localhost:5420/'
|
|
||||||
cssSrc = `${localhost}index.css`
|
|
||||||
jsSrc = `${localhost}index.js`
|
|
||||||
}
|
|
||||||
|
|
||||||
return `
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="stylesheet" href="${cssSrc}" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>tldraw</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<script>
|
|
||||||
var currentFile = ${documentContent};
|
|
||||||
var assetSrc = "${assetSrc}";
|
|
||||||
</script>
|
|
||||||
<script src="${jsSrc}"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import * as vscode from 'vscode'
|
|
||||||
import { TldrawEditorProvider } from './TldrawEditorProvider'
|
|
||||||
|
|
||||||
// When a .tldr is first opened or created, activate the extension.
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
|
||||||
try {
|
|
||||||
context.subscriptions.push(TldrawEditorProvider.register(context))
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
export type MessageFromWebview = {
|
|
||||||
type: 'editorUpdated'
|
|
||||||
text: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MessageFromExtension =
|
|
||||||
| {
|
|
||||||
type: 'openedFile'
|
|
||||||
text: string
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'fileSaved'
|
|
||||||
text: string
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
export function getNonce() {
|
|
||||||
let text = ''
|
|
||||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
||||||
for (let i = 0; i < 32; i++) {
|
|
||||||
text += possible.charAt(Math.floor(Math.random() * possible.length))
|
|
||||||
}
|
|
||||||
return text
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es6",
|
|
||||||
"outDir": "dist",
|
|
||||||
"lib": ["es6", "WebWorker"],
|
|
||||||
"sourceMap": true,
|
|
||||||
"rootDir": "src",
|
|
||||||
"strict": true,
|
|
||||||
"paths": {
|
|
||||||
"@tldraw/tldraw": ["../../../packages/tldraw"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "../../../packages/tldraw"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"exclude": ["node_modules", ".vscode-test-web"]
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"extends": "next/core-web-vitals",
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-explicit-any": "off"
|
|
||||||
}
|
|
||||||
}
|
|
34
apps/www/.gitignore
vendored
|
@ -1,34 +0,0 @@
|
||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# local env files
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
# vercel
|
|
||||||
.vercel
|
|
|
@ -1,342 +0,0 @@
|
||||||
# @tldraw/www
|
|
||||||
|
|
||||||
## 1.8.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Include clipboard fixes.
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.23.2
|
|
||||||
- @tldraw/tldraw@1.29.2
|
|
||||||
|
|
||||||
## 1.8.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix clipboard stealing focus.
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.23.1
|
|
||||||
- @tldraw/tldraw@1.29.1
|
|
||||||
|
|
||||||
## 1.8.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.23.0
|
|
||||||
- @tldraw/tldraw@1.29.0
|
|
||||||
|
|
||||||
## 1.8.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates German translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.22.0
|
|
||||||
- @tldraw/tldraw@1.28.0
|
|
||||||
|
|
||||||
## 1.7.27
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates German translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
- 12c0b2ac: - Adds Galacian language translation
|
|
||||||
- Adds Farsi (Persian) language translation
|
|
||||||
- Updates Norwegian translation
|
|
||||||
- Updates Japanese translation
|
|
||||||
- Updates Spanish translation
|
|
||||||
- Improves aria-labels
|
|
||||||
- Fixes a bug with multiplayer menu
|
|
||||||
- Fixes a bug with image exports
|
|
||||||
- Updated dependencies
|
|
||||||
- Updated dependencies [12c0b2ac]
|
|
||||||
- @tldraw/core@1.21.1
|
|
||||||
- @tldraw/tldraw@1.27.1
|
|
||||||
|
|
||||||
## 1.7.26
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.21.0
|
|
||||||
- @tldraw/tldraw@1.27.0
|
|
||||||
|
|
||||||
## 1.7.25
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.20.3
|
|
||||||
- @tldraw/tldraw@1.26.4
|
|
||||||
|
|
||||||
## 1.7.24
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.20.2
|
|
||||||
- @tldraw/tldraw@1.26.3
|
|
||||||
|
|
||||||
## 1.7.23
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.20.1
|
|
||||||
- @tldraw/tldraw@1.26.2
|
|
||||||
|
|
||||||
## 1.7.22
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.26.1
|
|
||||||
|
|
||||||
## 1.7.21
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.20.0
|
|
||||||
- @tldraw/tldraw@1.26.0
|
|
||||||
|
|
||||||
## 1.7.20
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.19.0
|
|
||||||
- @tldraw/tldraw@1.25.0
|
|
||||||
|
|
||||||
## 1.7.19
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.18.4
|
|
||||||
- @tldraw/tldraw@1.24.5
|
|
||||||
|
|
||||||
## 1.7.18
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.24.4
|
|
||||||
|
|
||||||
## 1.7.17
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Small bump.
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.18.3
|
|
||||||
- @tldraw/tldraw@1.24.3
|
|
||||||
|
|
||||||
## 1.7.16
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.18.2
|
|
||||||
- @tldraw/tldraw@1.24.2
|
|
||||||
|
|
||||||
## 1.7.15
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.18.1
|
|
||||||
- @tldraw/tldraw@1.24.1
|
|
||||||
|
|
||||||
## 1.7.14
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.18.0
|
|
||||||
- @tldraw/tldraw@1.24.0
|
|
||||||
|
|
||||||
## 1.7.13
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.17.0
|
|
||||||
- @tldraw/tldraw@1.23.0
|
|
||||||
|
|
||||||
## 1.7.12
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.22.1
|
|
||||||
|
|
||||||
## 1.7.11
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.22.0
|
|
||||||
|
|
||||||
## 1.7.10
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.16.0
|
|
||||||
- @tldraw/tldraw@1.21.0
|
|
||||||
|
|
||||||
## 1.7.9
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.15.0
|
|
||||||
- @tldraw/tldraw@1.20.0
|
|
||||||
|
|
||||||
## 1.7.8
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.19.0
|
|
||||||
|
|
||||||
## 1.7.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.18.3
|
|
||||||
|
|
||||||
## 1.7.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.14.1
|
|
||||||
- @tldraw/tldraw@1.18.2
|
|
||||||
|
|
||||||
## 1.7.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.18.1
|
|
||||||
|
|
||||||
## 1.7.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [8ef86c19]
|
|
||||||
- @tldraw/tldraw@1.18.0
|
|
||||||
|
|
||||||
## 1.7.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.17.2
|
|
||||||
|
|
||||||
## 1.7.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.17.1
|
|
||||||
|
|
||||||
## 1.7.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.17.0
|
|
||||||
|
|
||||||
## 1.7.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- d919bd27: Bump dependencies, add international support.
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [d919bd27]
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.14.0
|
|
||||||
- @tldraw/tldraw@1.16.0
|
|
||||||
|
|
||||||
## 1.7.0-next.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- Bump dependencies, add international support.
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.14.0-next.0
|
|
||||||
- @tldraw/tldraw@1.16.0-next.0
|
|
||||||
|
|
||||||
## 1.6.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.13.1
|
|
||||||
- @tldraw/tldraw@1.15.1
|
|
||||||
|
|
||||||
## 1.6.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.13.0
|
|
||||||
- @tldraw/tldraw@1.15.0
|
|
||||||
|
|
||||||
## 1.6.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.14.2
|
|
||||||
|
|
||||||
## 1.6.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.14.1
|
|
||||||
|
|
||||||
## 1.6.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/tldraw@1.14.0
|
|
||||||
|
|
||||||
## 1.6.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Fix publish version.
|
|
||||||
- Updated dependencies
|
|
||||||
- @tldraw/core@1.9.1
|
|
||||||
- @tldraw/tldraw@1.9.1
|
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 Stephen Ruiz Ltd
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,7 +0,0 @@
|
||||||
<div style="text-align: center; transform: scale(.5);">
|
|
||||||
<img src="https://github.com/tldraw/tldraw/raw/main/assets/card-repo.png"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
# @tldraw/www
|
|
||||||
|
|
||||||
The [tldraw](https://tldraw.com) website.
|
|
|
@ -1,93 +0,0 @@
|
||||||
import { ExternalLinkIcon } from '@radix-ui/react-icons'
|
|
||||||
import React, { useLayoutEffect } from 'react'
|
|
||||||
import { css, styled } from '~styles'
|
|
||||||
|
|
||||||
const STORAGE_KEY = 'tldraw_dismiss_beta_notification_3'
|
|
||||||
|
|
||||||
export function BetaNotification() {
|
|
||||||
const [isDismissed, setIsDismissed] = React.useState(true)
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
try {
|
|
||||||
const storageIsDismissed = localStorage.getItem(STORAGE_KEY)
|
|
||||||
|
|
||||||
if (storageIsDismissed !== null) {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
setIsDismissed(false)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setIsDismissed(false)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const handleDismiss = React.useCallback(() => {
|
|
||||||
setIsDismissed(true)
|
|
||||||
localStorage.setItem(STORAGE_KEY, 'true')
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (isDismissed) return null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Panel>
|
|
||||||
<div>
|
|
||||||
On April 4th 2023 at 9:30AM UTC, tldraw will be offline as we upgrade to our new version.
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
marginRight: -14,
|
|
||||||
marginLeft: -14,
|
|
||||||
marginBottom: -14,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button onClick={handleDismiss}>Dismiss</Button>
|
|
||||||
<Link href="https://tldraw.substack.com/p/tldraws-upcoming-re-launch" target="_blank">
|
|
||||||
Learn more <ExternalLinkIcon style={{ marginLeft: 4, marginTop: 1, height: 14 }} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</Panel>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Panel = styled('div', {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 42,
|
|
||||||
left: 0,
|
|
||||||
margin: 16,
|
|
||||||
zIndex: 999,
|
|
||||||
backgroundColor: '$panel',
|
|
||||||
flexDirection: 'column',
|
|
||||||
boxShadow: '$panel',
|
|
||||||
padding: 16,
|
|
||||||
fontSize: 'var(--fontSizes-2)',
|
|
||||||
fontFamily: 'var(--fonts-ui)',
|
|
||||||
width: 300,
|
|
||||||
display: 'flex',
|
|
||||||
gap: 8,
|
|
||||||
border: '1px solid $panelContrast',
|
|
||||||
overflow: 'hidden',
|
|
||||||
borderRadius: 9,
|
|
||||||
})
|
|
||||||
|
|
||||||
const buttonStyles = css({
|
|
||||||
borderRadius: '$2',
|
|
||||||
backgroundColor: '$panel',
|
|
||||||
padding: '0 14px',
|
|
||||||
height: 40,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
color: 'inherit',
|
|
||||||
font: 'inherit',
|
|
||||||
cursor: 'pointer',
|
|
||||||
border: 'none',
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: '$hover',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const Button = styled('button', buttonStyles)
|
|
||||||
const Link = styled('a', buttonStyles)
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { Tldraw, TldrawApp, TldrawProps, useFileSystem } from '@tldraw/tldraw'
|
|
||||||
import * as React from 'react'
|
|
||||||
import { useUploadAssets } from '~hooks/useUploadAssets'
|
|
||||||
import * as gtag from '~utils/gtag'
|
|
||||||
import { BetaNotification } from './BetaNotification'
|
|
||||||
|
|
||||||
declare const window: Window & { app: TldrawApp }
|
|
||||||
|
|
||||||
interface EditorProps {
|
|
||||||
id?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const Editor = ({ id = 'home', ...rest }: EditorProps & Partial<TldrawProps>) => {
|
|
||||||
const handleMount = React.useCallback((app: TldrawApp) => {
|
|
||||||
window.app = app
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Send events to gtag as actions.
|
|
||||||
const handlePersist = React.useCallback((_app: TldrawApp, reason?: string) => {
|
|
||||||
gtag.event({
|
|
||||||
action: reason ?? '',
|
|
||||||
category: 'editor',
|
|
||||||
label: reason ?? 'persist',
|
|
||||||
value: 0,
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const fileSystemEvents = useFileSystem()
|
|
||||||
|
|
||||||
const { onAssetUpload } = useUploadAssets()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="tldraw">
|
|
||||||
<Tldraw
|
|
||||||
id={id}
|
|
||||||
autofocus
|
|
||||||
onMount={handleMount}
|
|
||||||
onPersist={handlePersist}
|
|
||||||
onAssetUpload={onAssetUpload}
|
|
||||||
{...fileSystemEvents}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
<BetaNotification />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Editor
|
|
|
@ -1,126 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import { styled } from '~styles'
|
|
||||||
|
|
||||||
export default function IFrameWarning({ url = 'https://tldraw.com' }: { url?: string }) {
|
|
||||||
const [copied, setCopied] = React.useState(false)
|
|
||||||
const rTimeout = React.useRef<any>(0)
|
|
||||||
|
|
||||||
const handleCopy = React.useCallback(() => {
|
|
||||||
setCopied(true)
|
|
||||||
clearTimeout(rTimeout.current)
|
|
||||||
rTimeout.current = setTimeout(() => {
|
|
||||||
setCopied(false)
|
|
||||||
}, 1200)
|
|
||||||
|
|
||||||
const textarea = document.createElement('textarea')
|
|
||||||
textarea.setAttribute('position', 'fixed')
|
|
||||||
textarea.setAttribute('top', '0')
|
|
||||||
textarea.setAttribute('readonly', 'true')
|
|
||||||
textarea.setAttribute('contenteditable', 'true')
|
|
||||||
textarea.style.position = 'fixed'
|
|
||||||
textarea.value = url
|
|
||||||
document.body.appendChild(textarea)
|
|
||||||
textarea.focus()
|
|
||||||
textarea.select()
|
|
||||||
try {
|
|
||||||
const range = document.createRange()
|
|
||||||
range.selectNodeContents(textarea)
|
|
||||||
const sel = window.getSelection()
|
|
||||||
if (sel) {
|
|
||||||
sel.removeAllRanges()
|
|
||||||
sel.addRange(range)
|
|
||||||
textarea.setSelectionRange(0, textarea.value.length)
|
|
||||||
}
|
|
||||||
document.execCommand('copy')
|
|
||||||
} catch (err) {
|
|
||||||
null // Could not copy to clipboard
|
|
||||||
} finally {
|
|
||||||
document.body.removeChild(textarea)
|
|
||||||
}
|
|
||||||
}, [url])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<StyledUrlLink href={url} target="_parent">
|
|
||||||
Visit this page on tldraw.com{' '}
|
|
||||||
<svg
|
|
||||||
width="15"
|
|
||||||
height="15"
|
|
||||||
viewBox="0 0 15 15"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M3 2C2.44772 2 2 2.44772 2 3V12C2 12.5523 2.44772 13 3 13H12C12.5523 13 13 12.5523 13 12V8.5C13 8.22386 12.7761 8 12.5 8C12.2239 8 12 8.22386 12 8.5V12H3V3L6.5 3C6.77614 3 7 2.77614 7 2.5C7 2.22386 6.77614 2 6.5 2H3ZM12.8536 2.14645C12.9015 2.19439 12.9377 2.24964 12.9621 2.30861C12.9861 2.36669 12.9996 2.4303 13 2.497L13 2.5V2.50049V5.5C13 5.77614 12.7761 6 12.5 6C12.2239 6 12 5.77614 12 5.5V3.70711L6.85355 8.85355C6.65829 9.04882 6.34171 9.04882 6.14645 8.85355C5.95118 8.65829 5.95118 8.34171 6.14645 8.14645L11.2929 3H9.5C9.22386 3 9 2.77614 9 2.5C9 2.22386 9.22386 2 9.5 2H12.4999H12.5C12.5678 2 12.6324 2.01349 12.6914 2.03794C12.7504 2.06234 12.8056 2.09851 12.8536 2.14645Z"
|
|
||||||
fill="currentColor"
|
|
||||||
stroke="black"
|
|
||||||
strokeWidth=".5"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</StyledUrlLink>
|
|
||||||
<StyledUrlContainer onClick={handleCopy}>
|
|
||||||
<span>{url}</span>
|
|
||||||
<svg
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 15 15"
|
|
||||||
fill="currentColor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
{copied ? (
|
|
||||||
<path d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z" />
|
|
||||||
) : (
|
|
||||||
<path d="M5 2V1H10V2H5ZM4.75 0C4.33579 0 4 0.335786 4 0.75V1H3.5C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H7V13H3.5C3.22386 13 3 12.7761 3 12.5V2.5C3 2.22386 3.22386 2 3.5 2H4V2.25C4 2.66421 4.33579 3 4.75 3H10.25C10.6642 3 11 2.66421 11 2.25V2H11.5C11.7761 2 12 2.22386 12 2.5V7H13V2.5C13 1.67157 12.3284 1 11.5 1H11V0.75C11 0.335786 10.6642 0 10.25 0H4.75ZM9 8.5C9 8.77614 8.77614 9 8.5 9C8.22386 9 8 8.77614 8 8.5C8 8.22386 8.22386 8 8.5 8C8.77614 8 9 8.22386 9 8.5ZM10.5 9C10.7761 9 11 8.77614 11 8.5C11 8.22386 10.7761 8 10.5 8C10.2239 8 10 8.22386 10 8.5C10 8.77614 10.2239 9 10.5 9ZM13 8.5C13 8.77614 12.7761 9 12.5 9C12.2239 9 12 8.77614 12 8.5C12 8.22386 12.2239 8 12.5 8C12.7761 8 13 8.22386 13 8.5ZM14.5 9C14.7761 9 15 8.77614 15 8.5C15 8.22386 14.7761 8 14.5 8C14.2239 8 14 8.22386 14 8.5C14 8.77614 14.2239 9 14.5 9ZM15 10.5C15 10.7761 14.7761 11 14.5 11C14.2239 11 14 10.7761 14 10.5C14 10.2239 14.2239 10 14.5 10C14.7761 10 15 10.2239 15 10.5ZM14.5 13C14.7761 13 15 12.7761 15 12.5C15 12.2239 14.7761 12 14.5 12C14.2239 12 14 12.2239 14 12.5C14 12.7761 14.2239 13 14.5 13ZM14.5 15C14.7761 15 15 14.7761 15 14.5C15 14.2239 14.7761 14 14.5 14C14.2239 14 14 14.2239 14 14.5C14 14.7761 14.2239 15 14.5 15ZM8.5 11C8.77614 11 9 10.7761 9 10.5C9 10.2239 8.77614 10 8.5 10C8.22386 10 8 10.2239 8 10.5C8 10.7761 8.22386 11 8.5 11ZM9 12.5C9 12.7761 8.77614 13 8.5 13C8.22386 13 8 12.7761 8 12.5C8 12.2239 8.22386 12 8.5 12C8.77614 12 9 12.2239 9 12.5ZM8.5 15C8.77614 15 9 14.7761 9 14.5C9 14.2239 8.77614 14 8.5 14C8.22386 14 8 14.2239 8 14.5C8 14.7761 8.22386 15 8.5 15ZM11 14.5C11 14.7761 10.7761 15 10.5 15C10.2239 15 10 14.7761 10 14.5C10 14.2239 10.2239 14 10.5 14C10.7761 14 11 14.2239 11 14.5ZM12.5 15C12.7761 15 13 14.7761 13 14.5C13 14.2239 12.7761 14 12.5 14C12.2239 14 12 14.2239 12 14.5C12 14.7761 12.2239 15 12.5 15Z" />
|
|
||||||
)}
|
|
||||||
</svg>
|
|
||||||
</StyledUrlContainer>
|
|
||||||
</StyledContainer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledContainer = styled('div', {
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
fontFamily: '$body',
|
|
||||||
fontWeight: 'normal',
|
|
||||||
fontSize: '$3',
|
|
||||||
gap: '$5',
|
|
||||||
flexDirection: 'column',
|
|
||||||
border: '1px solid $hover',
|
|
||||||
borderRadius: '$2',
|
|
||||||
})
|
|
||||||
|
|
||||||
const StyledUrlLink = styled('a', {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '$2',
|
|
||||||
padding: '$5',
|
|
||||||
})
|
|
||||||
|
|
||||||
const StyledUrlContainer = styled('a', {
|
|
||||||
backgroundColor: '$hover',
|
|
||||||
borderRadius: '$2',
|
|
||||||
maxWidth: '62%',
|
|
||||||
minWidth: 320,
|
|
||||||
gap: '$4',
|
|
||||||
padding: '$4 $5',
|
|
||||||
fontSize: '$2',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
cursor: 'pointer',
|
|
||||||
|
|
||||||
'& span': {
|
|
||||||
flexGrow: 2,
|
|
||||||
overflow: 'auto',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
'-ms-overflow-style': 'none' /* for Internet Explorer, Edge */,
|
|
||||||
scrollbarWidth: 'none',
|
|
||||||
'&::webkit-scrollbar': {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -1,108 +0,0 @@
|
||||||
import { ReactNode, useEffect, useRef, useState } from 'react'
|
|
||||||
import { light, styled } from '~styles'
|
|
||||||
|
|
||||||
const CONTROL_SERVER = process.env.NEXT_PUBLIC_CONTROL_SERVER
|
|
||||||
? process.env.NEXT_PUBLIC_CONTROL_SERVER
|
|
||||||
: process.env.NEXT_PUBLIC_ENABLE_DEV_CONTROL_SERVER
|
|
||||||
? 'http://localhost:3001'
|
|
||||||
: null
|
|
||||||
|
|
||||||
type MaintenanceModeConfig =
|
|
||||||
| {
|
|
||||||
enabled: false
|
|
||||||
ttlSeconds: number
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
enabled: true
|
|
||||||
ttlSeconds: number
|
|
||||||
titleMessage: string
|
|
||||||
bodyMessageHtml: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ControlResponse = {
|
|
||||||
maintenance: MaintenanceModeConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MaintenanceMode({ children }: { children: ReactNode }) {
|
|
||||||
const [maintenanceMode, setMaintenanceMode] = useState<MaintenanceModeConfig | null>(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!CONTROL_SERVER) return
|
|
||||||
let isCancelled = false
|
|
||||||
const updateMaintenanceMode = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${CONTROL_SERVER}/control.json`)
|
|
||||||
const data: ControlResponse = await response.json()
|
|
||||||
if (isCancelled) return
|
|
||||||
setMaintenanceMode(data.maintenance)
|
|
||||||
} catch {
|
|
||||||
setMaintenanceMode({
|
|
||||||
enabled: false,
|
|
||||||
ttlSeconds: 60,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maintenanceMode) {
|
|
||||||
const refreshTimeout = setTimeout(updateMaintenanceMode, maintenanceMode.ttlSeconds * 1000)
|
|
||||||
return () => {
|
|
||||||
isCancelled = true
|
|
||||||
clearTimeout(refreshTimeout)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateMaintenanceMode()
|
|
||||||
return () => {
|
|
||||||
isCancelled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [maintenanceMode])
|
|
||||||
|
|
||||||
const isMaintenanceModeEnabled = maintenanceMode?.enabled ?? false
|
|
||||||
const wasInMaintenanceModeRef = useRef(isMaintenanceModeEnabled)
|
|
||||||
useEffect(() => {
|
|
||||||
// If we were in maintenance mode and now we're not, reload the page
|
|
||||||
if (wasInMaintenanceModeRef.current && !isMaintenanceModeEnabled) {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
wasInMaintenanceModeRef.current = isMaintenanceModeEnabled
|
|
||||||
}, [isMaintenanceModeEnabled])
|
|
||||||
|
|
||||||
if (maintenanceMode?.enabled) {
|
|
||||||
return (
|
|
||||||
<div className={`tldraw ${light}`}>
|
|
||||||
<Container>
|
|
||||||
<Modal>
|
|
||||||
<Heading>{maintenanceMode.titleMessage}</Heading>
|
|
||||||
<div dangerouslySetInnerHTML={{ __html: maintenanceMode.bodyMessageHtml }} />
|
|
||||||
</Modal>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{children}</>
|
|
||||||
}
|
|
||||||
|
|
||||||
const Container = styled('div', {
|
|
||||||
backgroundColor: '$canvas',
|
|
||||||
position: 'absolute',
|
|
||||||
inset: 0,
|
|
||||||
padding: '1rem',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
})
|
|
||||||
|
|
||||||
const Modal = styled('div', {
|
|
||||||
backgroundColor: '$panel',
|
|
||||||
padding: '1rem 1.5rem',
|
|
||||||
borderRadius: '$2',
|
|
||||||
boxShadow: '$8',
|
|
||||||
maxWidth: '400px',
|
|
||||||
width: '100%',
|
|
||||||
})
|
|
||||||
|
|
||||||
const Heading = styled('h2', {
|
|
||||||
margin: 0,
|
|
||||||
marginTop: '1rem',
|
|
||||||
})
|
|
|
@ -1,73 +0,0 @@
|
||||||
import { TDUserStatus, Tldraw, TldrawProps, useFileSystem } from '@tldraw/tldraw'
|
|
||||||
import * as React from 'react'
|
|
||||||
import { useMultiplayerAssets } from '~hooks/useMultiplayerAssets'
|
|
||||||
import { useMultiplayerState } from '~hooks/useMultiplayerState'
|
|
||||||
import { useUploadAssets } from '~hooks/useUploadAssets'
|
|
||||||
import { styled } from '~styles'
|
|
||||||
import { RoomProvider } from '~utils/liveblocks'
|
|
||||||
import { BetaNotification } from './BetaNotification'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
roomId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const MultiplayerEditor = ({ roomId }: Props) => {
|
|
||||||
return (
|
|
||||||
<RoomProvider
|
|
||||||
id={roomId}
|
|
||||||
initialPresence={{
|
|
||||||
id: 'DEFAULT_ID',
|
|
||||||
user: {
|
|
||||||
id: 'DEFAULT_ID',
|
|
||||||
status: TDUserStatus.Connecting,
|
|
||||||
activeShapes: [],
|
|
||||||
color: 'black',
|
|
||||||
point: [0, 0],
|
|
||||||
selectedIds: [],
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Editor roomId={roomId} />
|
|
||||||
</RoomProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inner Editor
|
|
||||||
|
|
||||||
function Editor({ roomId }: Props) {
|
|
||||||
const fileSystemEvents = useFileSystem()
|
|
||||||
const { error, ...events } = useMultiplayerState(roomId)
|
|
||||||
const { onAssetCreate, onAssetDelete } = useMultiplayerAssets()
|
|
||||||
const { onAssetUpload } = useUploadAssets()
|
|
||||||
|
|
||||||
if (error) return <LoadingScreen>Error: {error.message}</LoadingScreen>
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="tldraw">
|
|
||||||
<Tldraw
|
|
||||||
autofocus
|
|
||||||
disableAssets={false}
|
|
||||||
showPages={false}
|
|
||||||
onAssetCreate={onAssetCreate}
|
|
||||||
onAssetDelete={onAssetDelete}
|
|
||||||
onAssetUpload={onAssetUpload}
|
|
||||||
{...fileSystemEvents}
|
|
||||||
{...events}
|
|
||||||
/>
|
|
||||||
<BetaNotification />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MultiplayerEditor
|
|
||||||
|
|
||||||
const LoadingScreen = styled('div', {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
})
|
|
|
@ -1,66 +0,0 @@
|
||||||
import { TDUserStatus, Tldraw, useFileSystem } from '@tldraw/tldraw'
|
|
||||||
import * as React from 'react'
|
|
||||||
import { useReadOnlyMultiplayerState } from '~hooks/useReadOnlyMultiplayerState'
|
|
||||||
import { styled } from '~styles'
|
|
||||||
import { RoomProvider } from '~utils/liveblocks'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
roomId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const ReadOnlyMultiplayerEditor = ({ roomId }: Props) => {
|
|
||||||
return (
|
|
||||||
<RoomProvider
|
|
||||||
id={roomId}
|
|
||||||
initialPresence={{
|
|
||||||
id: 'DEFAULT_ID',
|
|
||||||
user: {
|
|
||||||
id: 'DEFAULT_ID',
|
|
||||||
status: TDUserStatus.Connecting,
|
|
||||||
activeShapes: [],
|
|
||||||
color: 'black',
|
|
||||||
point: [0, 0],
|
|
||||||
selectedIds: [],
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ReadOnlyEditor roomId={roomId} />
|
|
||||||
</RoomProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inner Editor
|
|
||||||
|
|
||||||
function ReadOnlyEditor({ roomId }: Props) {
|
|
||||||
const { onSaveProjectAs, onSaveProject } = useFileSystem()
|
|
||||||
const { error, ...events } = useReadOnlyMultiplayerState(roomId)
|
|
||||||
|
|
||||||
if (error) return <LoadingScreen>Error: {error.message}</LoadingScreen>
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="tldraw">
|
|
||||||
<Tldraw
|
|
||||||
autofocus
|
|
||||||
disableAssets={false}
|
|
||||||
showPages={false}
|
|
||||||
onSaveProjectAs={onSaveProjectAs}
|
|
||||||
onSaveProject={onSaveProject}
|
|
||||||
readOnly
|
|
||||||
{...events}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ReadOnlyMultiplayerEditor
|
|
||||||
|
|
||||||
const LoadingScreen = styled('div', {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
})
|
|
|
@ -1,42 +0,0 @@
|
||||||
import { Utils } from '@tldraw/core'
|
|
||||||
import { TDAsset, TldrawApp } from '@tldraw/tldraw'
|
|
||||||
import { useCallback } from 'react'
|
|
||||||
|
|
||||||
export function useMultiplayerAssets() {
|
|
||||||
const onAssetCreate = useCallback(
|
|
||||||
// Send the asset to our upload endpoint, which in turn will send it to AWS and
|
|
||||||
// respond with the URL of the uploaded file.
|
|
||||||
async (app: TldrawApp, file: File, id: string): Promise<string | false> => {
|
|
||||||
const filename = encodeURIComponent((id ?? Utils.uniqueId()) + file.name)
|
|
||||||
|
|
||||||
const fileType = encodeURIComponent(file.type)
|
|
||||||
|
|
||||||
const res = await fetch(`/api/upload?file=${filename}&fileType=${fileType}`)
|
|
||||||
|
|
||||||
const { url, fields } = await res.json()
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
|
|
||||||
Object.entries({ ...fields, file }).forEach(([key, value]) => {
|
|
||||||
formData.append(key, value as any)
|
|
||||||
})
|
|
||||||
|
|
||||||
const upload = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!upload.ok) return false
|
|
||||||
|
|
||||||
return url + '/' + filename
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const onAssetDelete = useCallback(async (app: TldrawApp, id: string): Promise<boolean> => {
|
|
||||||
// noop
|
|
||||||
return true
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { onAssetCreate, onAssetDelete }
|
|
||||||
}
|
|
|
@ -1,260 +0,0 @@
|
||||||
import { LiveMap } from '@liveblocks/client'
|
|
||||||
import type { TDAsset, TDBinding, TDShape, TDUser, TldrawApp } from '@tldraw/tldraw'
|
|
||||||
import React, { useCallback, useRef, useState } from 'react'
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
|
||||||
import { Storage, useRedo, useRoom, useUndo, useUpdateMyPresence } from '~utils/liveblocks'
|
|
||||||
|
|
||||||
declare const window: Window & { app: TldrawApp }
|
|
||||||
|
|
||||||
export function useMultiplayerState(roomId: string) {
|
|
||||||
const [app, setApp] = useState<TldrawApp>()
|
|
||||||
const [error, setError] = useState<Error>()
|
|
||||||
const [loading, setLoading] = useState(true)
|
|
||||||
|
|
||||||
const room = useRoom()
|
|
||||||
const onUndo = useUndo()
|
|
||||||
const onRedo = useRedo()
|
|
||||||
const updateMyPresence = useUpdateMyPresence()
|
|
||||||
|
|
||||||
const rIsPaused = useRef(false)
|
|
||||||
|
|
||||||
const rLiveShapes = useRef<Storage['shapes']>()
|
|
||||||
const rLiveBindings = useRef<Storage['bindings']>()
|
|
||||||
const rLiveAssets = useRef<Storage['assets']>()
|
|
||||||
|
|
||||||
// Callbacks --------------
|
|
||||||
|
|
||||||
// Put the state into the window, for debugging.
|
|
||||||
const onMount = useCallback(
|
|
||||||
(app: TldrawApp) => {
|
|
||||||
app.loadRoom(roomId)
|
|
||||||
app.pause() // Turn off the app's own undo / redo stack
|
|
||||||
window.app = app
|
|
||||||
setApp(app)
|
|
||||||
},
|
|
||||||
[roomId]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Update the live shapes when the app's shapes change.
|
|
||||||
const onChangePage = useCallback(
|
|
||||||
(
|
|
||||||
app: TldrawApp,
|
|
||||||
shapes: Record<string, TDShape | undefined>,
|
|
||||||
bindings: Record<string, TDBinding | undefined>,
|
|
||||||
assets: Record<string, TDAsset | undefined>
|
|
||||||
) => {
|
|
||||||
room.batch(() => {
|
|
||||||
const lShapes = rLiveShapes.current
|
|
||||||
const lBindings = rLiveBindings.current
|
|
||||||
const lAssets = rLiveAssets.current
|
|
||||||
|
|
||||||
if (!(lShapes && lBindings && lAssets)) return
|
|
||||||
|
|
||||||
Object.entries(shapes).forEach(([id, shape]) => {
|
|
||||||
if (!shape) {
|
|
||||||
lShapes.delete(id)
|
|
||||||
} else {
|
|
||||||
lShapes.set(shape.id, shape)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Object.entries(bindings).forEach(([id, binding]) => {
|
|
||||||
if (!binding) {
|
|
||||||
lBindings.delete(id)
|
|
||||||
} else {
|
|
||||||
lBindings.set(binding.id, binding)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Object.entries(assets).forEach(([id, asset]) => {
|
|
||||||
if (!asset) {
|
|
||||||
lAssets.delete(id)
|
|
||||||
} else {
|
|
||||||
lAssets.set(asset.id, asset)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[room]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle presence updates when the user's pointer / selection changes
|
|
||||||
const onChangePresence = useCallback(
|
|
||||||
(app: TldrawApp, user: TDUser) => {
|
|
||||||
updateMyPresence({ id: app.room?.userId, user })
|
|
||||||
},
|
|
||||||
[updateMyPresence]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Document Changes --------
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const unsubs: (() => void)[] = []
|
|
||||||
if (!(app && room)) return
|
|
||||||
// Handle errors
|
|
||||||
unsubs.push(room.subscribe('error', (error) => setError(error)))
|
|
||||||
|
|
||||||
// Handle changes to other users' presence
|
|
||||||
unsubs.push(
|
|
||||||
room.subscribe('others', (others, event) => {
|
|
||||||
if (event.type === 'leave') {
|
|
||||||
if (event.user.presence) {
|
|
||||||
app?.removeUser(event.user.presence.id)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
app.updateUsers(
|
|
||||||
others
|
|
||||||
.toArray()
|
|
||||||
.filter((other) => other.presence)
|
|
||||||
.map((other) => other.presence!.user)
|
|
||||||
.filter(Boolean)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
let stillAlive = true
|
|
||||||
|
|
||||||
// Setup the document's storage and subscriptions
|
|
||||||
async function setupDocument() {
|
|
||||||
const storage = await room.getStorage()
|
|
||||||
|
|
||||||
// Migrate previous versions
|
|
||||||
const version = storage.root.get('version')
|
|
||||||
|
|
||||||
// Initialize (get or create) maps for shapes/bindings/assets
|
|
||||||
|
|
||||||
let lShapes = storage.root.get('shapes')
|
|
||||||
if (!lShapes || !('_serialize' in lShapes)) {
|
|
||||||
storage.root.set('shapes', new LiveMap())
|
|
||||||
lShapes = storage.root.get('shapes')
|
|
||||||
}
|
|
||||||
rLiveShapes.current = lShapes
|
|
||||||
|
|
||||||
let lBindings = storage.root.get('bindings')
|
|
||||||
if (!lBindings || !('_serialize' in lBindings)) {
|
|
||||||
storage.root.set('bindings', new LiveMap())
|
|
||||||
lBindings = storage.root.get('bindings')
|
|
||||||
}
|
|
||||||
rLiveBindings.current = lBindings
|
|
||||||
|
|
||||||
let lAssets = storage.root.get('assets')
|
|
||||||
if (!lAssets || !('_serialize' in lAssets)) {
|
|
||||||
storage.root.set('assets', new LiveMap())
|
|
||||||
lAssets = storage.root.get('assets')
|
|
||||||
}
|
|
||||||
rLiveAssets.current = lAssets
|
|
||||||
|
|
||||||
if (!version) {
|
|
||||||
// The doc object will only be present if the document was created
|
|
||||||
// prior to the current multiplayer implementation. At this time, the
|
|
||||||
// document was a single LiveObject named 'doc'. If we find a doc,
|
|
||||||
// then we need to move the shapes and bindings over to the new structures
|
|
||||||
// and then mark the doc as migrated.
|
|
||||||
const doc = storage.root.get('doc')
|
|
||||||
|
|
||||||
// No doc? No problem. This was likely a newer document
|
|
||||||
if (doc) {
|
|
||||||
const {
|
|
||||||
document: {
|
|
||||||
pages: {
|
|
||||||
page: { shapes, bindings },
|
|
||||||
},
|
|
||||||
assets,
|
|
||||||
},
|
|
||||||
} = doc.toObject()
|
|
||||||
|
|
||||||
Object.values(shapes).forEach((shape) => lShapes.set(shape.id, shape))
|
|
||||||
Object.values(bindings).forEach((binding) => lBindings.set(binding.id, binding))
|
|
||||||
Object.values(assets).forEach((asset) => lAssets.set(asset.id, asset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the version number for future migrations
|
|
||||||
storage.root.set('version', 2.1)
|
|
||||||
|
|
||||||
// Subscribe to changes
|
|
||||||
const handleChanges = () => {
|
|
||||||
app?.replacePageContent(
|
|
||||||
Object.fromEntries(lShapes.entries()),
|
|
||||||
Object.fromEntries(lBindings.entries()),
|
|
||||||
Object.fromEntries(lAssets.entries())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stillAlive) {
|
|
||||||
unsubs.push(room.subscribe(lShapes, handleChanges))
|
|
||||||
|
|
||||||
// Update the document with initial content
|
|
||||||
handleChanges()
|
|
||||||
|
|
||||||
// Zoom to fit the content
|
|
||||||
app.zoomToFit()
|
|
||||||
if (app.zoom > 1) {
|
|
||||||
app.resetZoom()
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setupDocument()
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
stillAlive = false
|
|
||||||
unsubs.forEach((unsub) => unsub())
|
|
||||||
}
|
|
||||||
}, [room, app])
|
|
||||||
|
|
||||||
const onSessionStart = React.useCallback(() => {
|
|
||||||
if (!room) return
|
|
||||||
room.history.pause()
|
|
||||||
rIsPaused.current = true
|
|
||||||
}, [room])
|
|
||||||
|
|
||||||
const onSessionEnd = React.useCallback(() => {
|
|
||||||
if (!room) return
|
|
||||||
room.history.resume()
|
|
||||||
rIsPaused.current = false
|
|
||||||
}, [room])
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
'ctrl+shift+l;,⌘+shift+l',
|
|
||||||
() => {
|
|
||||||
if (window.confirm('Reset the document?')) {
|
|
||||||
room.batch(() => {
|
|
||||||
const lShapes = rLiveShapes.current
|
|
||||||
const lBindings = rLiveBindings.current
|
|
||||||
const lAssets = rLiveAssets.current
|
|
||||||
|
|
||||||
if (!(lShapes && lBindings && lAssets)) return
|
|
||||||
|
|
||||||
lShapes.forEach((shape) => {
|
|
||||||
lShapes.delete(shape.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
lBindings.forEach((shape) => {
|
|
||||||
lBindings.delete(shape.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
lAssets.forEach((shape) => {
|
|
||||||
lAssets.delete(shape.id)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
onUndo,
|
|
||||||
onRedo,
|
|
||||||
onMount,
|
|
||||||
onSessionStart,
|
|
||||||
onSessionEnd,
|
|
||||||
onChangePage,
|
|
||||||
onChangePresence,
|
|
||||||
error,
|
|
||||||
loading,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
import { LiveMap } from '@liveblocks/client'
|
|
||||||
import type { TDUser, TldrawApp } from '@tldraw/tldraw'
|
|
||||||
import React, { useCallback, useRef, useState } from 'react'
|
|
||||||
import { Storage, useRedo, useRoom, useUndo, useUpdateMyPresence } from '~utils/liveblocks'
|
|
||||||
|
|
||||||
declare const window: Window & { app: TldrawApp }
|
|
||||||
|
|
||||||
export function useReadOnlyMultiplayerState(roomId: string) {
|
|
||||||
const [app, setApp] = useState<TldrawApp>()
|
|
||||||
const [error, setError] = useState<Error>()
|
|
||||||
const [loading, setLoading] = useState(true)
|
|
||||||
|
|
||||||
const room = useRoom()
|
|
||||||
const onUndo = useUndo()
|
|
||||||
const onRedo = useRedo()
|
|
||||||
const updateMyPresence = useUpdateMyPresence()
|
|
||||||
|
|
||||||
const rIsPaused = useRef(false)
|
|
||||||
|
|
||||||
const rLiveShapes = useRef<Storage['shapes']>()
|
|
||||||
const rLiveBindings = useRef<Storage['bindings']>()
|
|
||||||
const rLiveAssets = useRef<Storage['assets']>()
|
|
||||||
|
|
||||||
// Callbacks --------------
|
|
||||||
|
|
||||||
// Put the state into the window, for debugging.
|
|
||||||
const onMount = useCallback(
|
|
||||||
(app: TldrawApp) => {
|
|
||||||
app.loadRoom(roomId)
|
|
||||||
app.pause() // Turn off the app's own undo / redo stack
|
|
||||||
window.app = app
|
|
||||||
setApp(app)
|
|
||||||
},
|
|
||||||
[roomId]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle presence updates when the user's pointer / selection changes
|
|
||||||
const onChangePresence = useCallback(
|
|
||||||
(app: TldrawApp, user: TDUser) => {
|
|
||||||
updateMyPresence({ id: app.room?.userId, user })
|
|
||||||
},
|
|
||||||
[updateMyPresence]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Document Changes --------
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const unsubs: (() => void)[] = []
|
|
||||||
if (!(app && room)) return
|
|
||||||
// Handle errors
|
|
||||||
unsubs.push(room.subscribe('error', (error) => setError(error)))
|
|
||||||
|
|
||||||
// Handle changes to other users' presence
|
|
||||||
unsubs.push(
|
|
||||||
room.subscribe('others', (others, event) => {
|
|
||||||
if (event.type === 'leave') {
|
|
||||||
if (event.user.presence) {
|
|
||||||
app?.removeUser(event.user.presence.id)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
app.updateUsers(
|
|
||||||
others
|
|
||||||
.toArray()
|
|
||||||
.filter((other) => other.presence)
|
|
||||||
.map((other) => other.presence!.user)
|
|
||||||
.filter(Boolean)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
let stillAlive = true
|
|
||||||
|
|
||||||
// Setup the document's storage and subscriptions
|
|
||||||
async function setupDocument() {
|
|
||||||
const storage = await room.getStorage()
|
|
||||||
|
|
||||||
// Migrate previous versions
|
|
||||||
const version = storage.root.get('version')
|
|
||||||
|
|
||||||
// Initialize (get or create) maps for shapes/bindings/assets
|
|
||||||
|
|
||||||
let lShapes = storage.root.get('shapes')
|
|
||||||
if (!lShapes || !('_serialize' in lShapes)) {
|
|
||||||
storage.root.set('shapes', new LiveMap())
|
|
||||||
lShapes = storage.root.get('shapes')
|
|
||||||
}
|
|
||||||
rLiveShapes.current = lShapes
|
|
||||||
|
|
||||||
let lBindings = storage.root.get('bindings')
|
|
||||||
if (!lBindings || !('_serialize' in lBindings)) {
|
|
||||||
storage.root.set('bindings', new LiveMap())
|
|
||||||
lBindings = storage.root.get('bindings')
|
|
||||||
}
|
|
||||||
rLiveBindings.current = lBindings
|
|
||||||
|
|
||||||
let lAssets = storage.root.get('assets')
|
|
||||||
if (!lAssets || !('_serialize' in lAssets)) {
|
|
||||||
storage.root.set('assets', new LiveMap())
|
|
||||||
lAssets = storage.root.get('assets')
|
|
||||||
}
|
|
||||||
rLiveAssets.current = lAssets
|
|
||||||
|
|
||||||
// Save the version number for future migrations
|
|
||||||
storage.root.set('version', 2.1)
|
|
||||||
|
|
||||||
// Subscribe to changes
|
|
||||||
const handleChanges = () => {
|
|
||||||
app?.replacePageContent(
|
|
||||||
Object.fromEntries(lShapes.entries()),
|
|
||||||
Object.fromEntries(lBindings.entries()),
|
|
||||||
Object.fromEntries(lAssets.entries())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stillAlive) {
|
|
||||||
unsubs.push(room.subscribe(lShapes, handleChanges))
|
|
||||||
|
|
||||||
// Update the document with initial content
|
|
||||||
handleChanges()
|
|
||||||
|
|
||||||
// Zoom to fit the content
|
|
||||||
app.zoomToFit()
|
|
||||||
if (app.zoom > 1) {
|
|
||||||
app.resetZoom()
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setupDocument()
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
stillAlive = false
|
|
||||||
unsubs.forEach((unsub) => unsub())
|
|
||||||
}
|
|
||||||
}, [room, app])
|
|
||||||
|
|
||||||
return {
|
|
||||||
onUndo,
|
|
||||||
onRedo,
|
|
||||||
onMount,
|
|
||||||
onChangePresence,
|
|
||||||
error,
|
|
||||||
loading,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { Utils } from '@tldraw/core'
|
|
||||||
import { TldrawApp } from '@tldraw/tldraw'
|
|
||||||
import { useCallback } from 'react'
|
|
||||||
|
|
||||||
export function useUploadAssets() {
|
|
||||||
const onAssetUpload = useCallback(
|
|
||||||
// Send the asset to our upload endpoint, which in turn will send it to AWS and
|
|
||||||
// respond with the URL of the uploaded file.
|
|
||||||
|
|
||||||
async (app: TldrawApp, file: File, id: string): Promise<string | false> => {
|
|
||||||
const filename = encodeURIComponent((id ?? Utils.uniqueId()) + file.name)
|
|
||||||
|
|
||||||
const fileType = encodeURIComponent(file.type)
|
|
||||||
|
|
||||||
const res = await fetch(`/api/upload?file=${filename}&fileType=${fileType}`)
|
|
||||||
|
|
||||||
const { url, fields } = await res.json()
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
|
|
||||||
Object.entries({ ...fields, file }).forEach(([key, value]) => {
|
|
||||||
formData.append(key, value as any)
|
|
||||||
})
|
|
||||||
|
|
||||||
const upload = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!upload.ok) return false
|
|
||||||
|
|
||||||
return url + '/' + filename
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
return { onAssetUpload }
|
|
||||||
}
|
|
5
apps/www/next-env.d.ts
vendored
|
@ -1,5 +0,0 @@
|
||||||
/// <reference types="next" />
|
|
||||||
/// <reference types="next/image-types/global" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
|
@ -1,24 +0,0 @@
|
||||||
const withPWA = require('next-pwa')
|
|
||||||
const withTM = require('next-transpile-modules')
|
|
||||||
|
|
||||||
const { GITHUB_ID, GITHUB_API_SECRET, NODE_ENV, VERCEL_GIT_COMMIT_SHA, GA_MEASUREMENT_ID } =
|
|
||||||
process.env
|
|
||||||
|
|
||||||
const isProduction = NODE_ENV === 'production'
|
|
||||||
|
|
||||||
module.exports = withTM(['@tldraw/tldraw', '@tldraw/core'])(
|
|
||||||
withPWA({
|
|
||||||
reactStrictMode: true,
|
|
||||||
pwa: {
|
|
||||||
disable: !isProduction,
|
|
||||||
dest: 'public',
|
|
||||||
},
|
|
||||||
productionBrowserSourceMaps: true,
|
|
||||||
env: {
|
|
||||||
NEXT_PUBLIC_COMMIT_SHA: VERCEL_GIT_COMMIT_SHA,
|
|
||||||
GA_MEASUREMENT_ID,
|
|
||||||
GITHUB_ID,
|
|
||||||
GITHUB_API_SECRET,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
|
@ -1,44 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@tldraw/www",
|
|
||||||
"version": "1.8.3",
|
|
||||||
"private": true,
|
|
||||||
"description": "A tiny little drawing app (site).",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/tldraw/tldraw.git"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"author": "@steveruizok",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "next dev",
|
|
||||||
"build": "next build",
|
|
||||||
"start": "next start",
|
|
||||||
"lint": "TIMING=1 next lint",
|
|
||||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@liveblocks/client": "^0.18.3",
|
|
||||||
"@liveblocks/react": "^0.18.3",
|
|
||||||
"@stitches/react": "^1.2.8",
|
|
||||||
"@tldraw/core": "*",
|
|
||||||
"@tldraw/tldraw": "*",
|
|
||||||
"@types/react": "^18.0.17",
|
|
||||||
"@types/react-dom": "^18.0.6",
|
|
||||||
"aws-sdk": "^2.1053.0",
|
|
||||||
"eslint": "^8.8.0",
|
|
||||||
"eslint-config-next": "^12.0.10",
|
|
||||||
"lz-string": "^1.4.4",
|
|
||||||
"nanoid": "^3.3.4",
|
|
||||||
"next": "^12.1.6",
|
|
||||||
"next-pwa": "^5.5.4",
|
|
||||||
"next-themes": "^0.0.15",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-hotkeys-hook": "^3.4.6",
|
|
||||||
"typescript": "^4.7.3"
|
|
||||||
},
|
|
||||||
"gitHead": "838fabdbff1a66d4d7ee8aa5c5d117bc55acbff2",
|
|
||||||
"devDependencies": {
|
|
||||||
"next-transpile-modules": "^9.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
import '@fontsource/recursive'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import type React from 'react'
|
|
||||||
import { MaintenanceMode } from '~components/MaintenanceMode'
|
|
||||||
import '~styles/globals.css'
|
|
||||||
import useGtag from '~utils/useGtag'
|
|
||||||
|
|
||||||
const APP_NAME = 'tldraw'
|
|
||||||
const APP_DESCRIPTION = 'A tiny little drawing app.'
|
|
||||||
const APP_URL = 'https://tldraw.com'
|
|
||||||
const IMAGE = 'https://tldraw.com/social-image.png'
|
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: any) {
|
|
||||||
useGtag()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<meta name="application-name" content={APP_NAME} />
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
|
||||||
<meta name="apple-mobile-web-app-title" content={APP_NAME} />
|
|
||||||
<meta name="description" content={APP_DESCRIPTION} />
|
|
||||||
<meta name="format-detection" content="telephone=no" />
|
|
||||||
<meta name="mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="theme-color" content="#fafafa" />
|
|
||||||
|
|
||||||
<meta name="twitter:url" content={APP_URL} />
|
|
||||||
<meta name="twitter:title" content={APP_NAME} />
|
|
||||||
<meta name="twitter:description" content={APP_DESCRIPTION} />
|
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
|
||||||
<meta name="twitter:creator" content="@tldraw" />
|
|
||||||
<meta name="twitter:site" content="@tldraw" />
|
|
||||||
<meta name="twitter:image" content={IMAGE} />
|
|
||||||
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:title" content={APP_NAME} />
|
|
||||||
<meta property="og:description" content={APP_DESCRIPTION} />
|
|
||||||
<meta property="og:site_name" content={APP_NAME} />
|
|
||||||
<meta property="og:url" content={APP_URL} />
|
|
||||||
<meta property="og:image" content={IMAGE} />
|
|
||||||
|
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
|
||||||
<link rel="shortcut icon" href="/favicon.ico" />
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
|
|
||||||
|
|
||||||
<title>tldraw</title>
|
|
||||||
</Head>
|
|
||||||
<MaintenanceMode>
|
|
||||||
<Component {...pageProps} />
|
|
||||||
</MaintenanceMode>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MyApp
|
|
|
@ -1,47 +0,0 @@
|
||||||
import NextDocument, { DocumentContext, Head, Html, Main, NextScript } from 'next/document'
|
|
||||||
import { getCssText } from '~styles'
|
|
||||||
import { GA_TRACKING_ID } from '~utils/gtag'
|
|
||||||
|
|
||||||
class MyDocument extends NextDocument {
|
|
||||||
static async getInitialProps(ctx: DocumentContext): Promise<any> {
|
|
||||||
const initialProps = await NextDocument.getInitialProps(ctx)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...initialProps,
|
|
||||||
styles: (
|
|
||||||
<>
|
|
||||||
{initialProps.styles}
|
|
||||||
<style id="stitches" dangerouslySetInnerHTML={{ __html: getCssText() }} />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Html lang="en">
|
|
||||||
<Head>
|
|
||||||
<script async src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`} />
|
|
||||||
<script
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: `
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
gtag('config', '${GA_TRACKING_ID}', {
|
|
||||||
page_path: window.location.pathname,
|
|
||||||
});
|
|
||||||
`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
<body>
|
|
||||||
<Main />
|
|
||||||
<NextScript />
|
|
||||||
</body>
|
|
||||||
</Html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MyDocument
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { Utils } from '@tldraw/core'
|
|
||||||
import { TDDocument } from '@tldraw/tldraw'
|
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
|
|
||||||
type RequestBody = {
|
|
||||||
pageId: string
|
|
||||||
document: TDDocument
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function CreateMultiplayerRoom(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
// 1. Get an authentication token from Liveblocks
|
|
||||||
|
|
||||||
const { token } = await fetch('https://liveblocks.io/api/authorize', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.LIVEBLOCKS_SECRET_KEY}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
}).then((d) => d.json())
|
|
||||||
|
|
||||||
// 2. Create the Liveblocks storage JSON
|
|
||||||
|
|
||||||
const { pageId, document } = JSON.parse(req.body) as RequestBody
|
|
||||||
|
|
||||||
const storageJson = {
|
|
||||||
liveblocksType: 'LiveObject',
|
|
||||||
data: {
|
|
||||||
version: 2.1,
|
|
||||||
shapes: {
|
|
||||||
liveblocksType: 'LiveMap',
|
|
||||||
data: {},
|
|
||||||
},
|
|
||||||
bindings: {
|
|
||||||
liveblocksType: 'LiveMap',
|
|
||||||
data: {},
|
|
||||||
},
|
|
||||||
assets: {
|
|
||||||
liveblocksType: 'LiveMap',
|
|
||||||
data: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const page = document.pages[pageId]
|
|
||||||
|
|
||||||
storageJson.data.shapes.data = page.shapes ?? {}
|
|
||||||
storageJson.data.bindings.data = page.bindings ?? {}
|
|
||||||
storageJson.data.assets.data = document.assets ?? {}
|
|
||||||
|
|
||||||
// 3. Post the JSON and token to Liveblocks
|
|
||||||
|
|
||||||
const roomId = Utils.uniqueId()
|
|
||||||
|
|
||||||
const result = await fetch(`https://liveblocks.net/api/v1/room/${roomId}/storage`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(storageJson),
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (result.status === 200) {
|
|
||||||
// If success, send back the url for the new multiplayer project
|
|
||||||
res.send({ status: 'success', message: result.statusText, url: '/r/' + roomId })
|
|
||||||
} else {
|
|
||||||
throw Error(result.statusText)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
res.send({ status: 'error', message: e.message })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import aws from 'aws-sdk'
|
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
|
||||||
aws.config.update({
|
|
||||||
accessKeyId: process.env.TL_AWS_ACCESS_KEY,
|
|
||||||
secretAccessKey: process.env.TL_AWS_SECRET_KEY,
|
|
||||||
region: process.env.TL_AWS_REGION,
|
|
||||||
signatureVersion: 'v4',
|
|
||||||
})
|
|
||||||
|
|
||||||
const s3 = new aws.S3()
|
|
||||||
|
|
||||||
const post = s3.createPresignedPost({
|
|
||||||
Bucket: process.env.TL_AWS_BUCKET_NAME,
|
|
||||||
Fields: {
|
|
||||||
key: req.query.file,
|
|
||||||
'Content-Type': req.query.fileType,
|
|
||||||
},
|
|
||||||
Expires: 60, // seconds
|
|
||||||
Conditions: [
|
|
||||||
['content-length-range', 0, 5242880], // up to 5 MB
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(200).json(post)
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { useMemo } from 'react'
|
|
||||||
|
|
||||||
const Editor = dynamic(() => import('~components/Editor'), { ssr: false }) as any
|
|
||||||
|
|
||||||
const Home = () => {
|
|
||||||
const { query } = useRouter()
|
|
||||||
const isExportMode = useMemo(() => 'exportMode' in query, [query])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>tldraw</title>
|
|
||||||
</Head>
|
|
||||||
<Editor id="home" showUI={!isExportMode} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Home
|
|
|
@ -1,41 +0,0 @@
|
||||||
import type { GetServerSideProps } from 'next'
|
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
const IFrameWarning = dynamic(() => import('~components/IFrameWarning'), {
|
|
||||||
ssr: false,
|
|
||||||
}) as any
|
|
||||||
|
|
||||||
const MultiplayerEditor = dynamic(() => import('~components/MultiplayerEditor'), {
|
|
||||||
ssr: false,
|
|
||||||
}) as any
|
|
||||||
|
|
||||||
interface RoomProps {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Room({ id }: RoomProps) {
|
|
||||||
if (typeof window !== 'undefined' && window.self !== window.top) {
|
|
||||||
return <IFrameWarning url={`${window.location.origin}/r/${id}`} />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>tldraw - {id}</title>
|
|
||||||
</Head>
|
|
||||||
<MultiplayerEditor roomId={id} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
|
||||||
const id = context.query.id?.toString()
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
import type { GetServerSideProps } from 'next'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
export default function RandomRoomPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>tldraw</title>
|
|
||||||
</Head>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
|
||||||
// Generate random id
|
|
||||||
const id = Date.now().toString()
|
|
||||||
|
|
||||||
// Route to a room with that id
|
|
||||||
context.res.setHeader('Location', `/r/${id}`)
|
|
||||||
context.res.statusCode = 307
|
|
||||||
|
|
||||||
// Return id (though it shouldn't matter)
|
|
||||||
return {
|
|
||||||
props: {},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
import { Utils } from '@tldraw/core'
|
|
||||||
import type { GetServerSideProps } from 'next'
|
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
import Head from 'next/head'
|
|
||||||
import * as React from 'react'
|
|
||||||
|
|
||||||
const IFrameWarning = dynamic(() => import('~components/IFrameWarning'), {
|
|
||||||
ssr: false,
|
|
||||||
}) as any
|
|
||||||
|
|
||||||
const ReadOnlyMultiplayerEditor = dynamic(() => import('~components/ReadOnlyMultiplayerEditor'), {
|
|
||||||
ssr: false,
|
|
||||||
}) as any
|
|
||||||
|
|
||||||
interface RoomProps {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Room({ id }: RoomProps) {
|
|
||||||
if (typeof window !== 'undefined' && window.self !== window.top) {
|
|
||||||
return <IFrameWarning url={`https://tldraw.com/v/${id}`} />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>tldraw - {id} (read only)</title>
|
|
||||||
</Head>
|
|
||||||
<ReadOnlyMultiplayerEditor roomId={id} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
|
||||||
const id = context.query.id?.toString()
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
id: Utils.lns(id),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.2 KiB |