diff --git a/.gitignore b/.gitignore
index 4dc8d0e52..c10c31114 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ coverage
apps/www/public/worker-*
apps/www/public/sw.js
apps/www/public/sw.js.map
+.env
\ No newline at end of file
diff --git a/README.md b/README.md
index 0dcb937ad..743be0a25 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
Welcome to the [tldraw](https://tldraw.com) monorepo. Here you'll find the source code for [@tldraw/tldraw](https://www.npmjs.com/package/@tldraw/tldraw), [@tldraw/core](https://www.npmjs.com/package/@tldraw/core), and the tldraw.com website.
@@ -8,6 +8,14 @@ Welcome to the [tldraw](https://tldraw.com) monorepo. Here you'll find the sourc
💕 Love this project? Consider [becoming a sponsor](https://github.com/sponsors/steveruizok?frequency=recurring&sponsor=steveruizok).
+Thanks to our corporate sponsors:
+
+
+
+
+
+...and to our [individual sponsors](https://github.com/sponsors/steveruizok#sponsors)!
+
## Contents
This repository is a monorepo containing two packages:
@@ -27,18 +35,18 @@ This repository is a monorepo containing two packages:
- [**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
-See the [contributing guide](/CONTRIBUTING.md).
+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.
-## Discussion
-
-Want to connect with other devs? Visit the [Discord channel](https://discord.gg/SBBEVCA4PG).
-
## License
This project is licensed under MIT.
diff --git a/apps/www/.env b/apps/www/.env
deleted file mode 100644
index b298903cd..000000000
--- a/apps/www/.env
+++ /dev/null
@@ -1 +0,0 @@
-NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_API_KEY=pk_live_1LJGGaqBSNLjLT-4Jalkl-U9
\ No newline at end of file
diff --git a/apps/www/components/Editor.tsx b/apps/www/components/Editor.tsx
index 4962cd3f4..cd6ec81e3 100644
--- a/apps/www/components/Editor.tsx
+++ b/apps/www/components/Editor.tsx
@@ -11,7 +11,7 @@ interface EditorProps {
isSponsor?: boolean
}
-export default function Editor({ id = 'home', isSponsor = false }: EditorProps) {
+export default function Editor({ id = 'home', isUser = false, isSponsor = false }: EditorProps) {
const handleMount = React.useCallback((app: TldrawApp) => {
window.app = app
}, [])
@@ -39,7 +39,7 @@ export default function Editor({ id = 'home', isSponsor = false }: EditorProps)
onPersist={handlePersist}
showSponsorLink={!isSponsor}
onSignIn={isSponsor ? undefined : onSignIn}
- onSignOut={onSignOut}
+ onSignOut={isUser ? onSignOut : undefined}
{...fileSystemEvents}
/>
diff --git a/apps/www/components/MultiplayerEditor.tsx b/apps/www/components/MultiplayerEditor.tsx
index 3d8c45b46..e3d5fa006 100644
--- a/apps/www/components/MultiplayerEditor.tsx
+++ b/apps/www/components/MultiplayerEditor.tsx
@@ -38,7 +38,15 @@ export default function MultiplayerEditor({
// Inner Editor
-function Editor({ roomId, isSponsor }: { roomId: string; isUser; isSponsor: boolean }) {
+function Editor({
+ roomId,
+ isUser,
+ isSponsor,
+}: {
+ roomId: string
+ isUser: boolean
+ isSponsor: boolean
+}) {
const [docId] = React.useState(() => Utils.uniqueId())
const [app, setApp] = React.useState()
@@ -175,7 +183,7 @@ function Editor({ roomId, isSponsor }: { roomId: string; isUser; isSponsor: bool
showPages={false}
showSponsorLink={isSponsor}
onSignIn={isSponsor ? undefined : onSignIn}
- onSignOut={onSignOut}
+ onSignOut={isUser ? onSignOut : undefined}
{...fileSystemEvents}
/>
diff --git a/apps/www/pages/api/auth/[...nextauth].ts b/apps/www/pages/api/auth/[...nextauth].ts
index 949f09489..9583f9509 100644
--- a/apps/www/pages/api/auth/[...nextauth].ts
+++ b/apps/www/pages/api/auth/[...nextauth].ts
@@ -1,3 +1,4 @@
+import { isSponsoringMe } from '-utils/isSponsoringMe'
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
@@ -30,28 +31,3 @@ export default function Auth(
},
})
}
-
-const whitelist = ['steveruizok']
-
-async function isSponsoringMe(login: string) {
- if (whitelist.includes(login)) return true
-
- const res = await fetch('https://api.github.com/graphql', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'bearer ' + process.env.GITHUB_API_SECRET,
- },
- body: JSON.stringify({
- query: `
- query {
- user(login: "steveruizok") {
- isSponsoredBy(accountLogin: "${login}")
- }
- }
- `,
- }),
- }).then((res) => res.json())
-
- return res?.data?.user?.isSponsoredBy
-}
diff --git a/apps/www/pages/api/sponsors.ts b/apps/www/pages/api/sponsors.ts
new file mode 100644
index 000000000..1b22ea817
--- /dev/null
+++ b/apps/www/pages/api/sponsors.ts
@@ -0,0 +1,96 @@
+import { NextApiRequest, NextApiResponse } from 'next'
+
+const AV_SIZE = 32
+const PADDING = 4
+const COLS = 16
+
+type SponsorResult = { url: string; login: string }
+type QueryResult = {
+ node: { sponsorEntity: { avatarUrl: string; login: string } }
+}
+
+function getXY(i: number) {
+ return [(i % COLS) * (AV_SIZE + PADDING), Math.floor(i / COLS) * (AV_SIZE + PADDING)]
+}
+
+export default async function GetSponsors(req: NextApiRequest, res: NextApiResponse) {
+ const sponsorInfo = await fetch('https://api.github.com/graphql', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: 'bearer ' + process.env.GITHUB_API_SECRET,
+ },
+ body: JSON.stringify({
+ query: `{
+ viewer {
+ sponsors(first: 0) {
+ totalCount
+ }
+ sponsorshipsAsMaintainer(first: 100, orderBy: {
+ field:CREATED_AT,
+ direction:DESC
+ }) {
+ edges {
+ node {
+ sponsorEntity {
+ ...on User {
+ avatarUrl
+ login
+ }
+ }
+ }
+ }
+ }
+ }
+ }`,
+ }),
+ }).then((res) => res.json())
+
+ const totalCount: number = sponsorInfo.data.viewer.sponsors.totalCount
+
+ const results = (
+ sponsorInfo.data.viewer.sponsorshipsAsMaintainer.edges as QueryResult[]
+ ).map((edge) => ({
+ url: edge.node.sponsorEntity.avatarUrl?.replaceAll('&', '&') ?? '',
+ login: edge.node.sponsorEntity.login,
+ }))
+
+ if (results.length % COLS <= 2) {
+ results.pop()
+ results.pop()
+ results.pop()
+ }
+
+ // Avatars
+
+ const avatars = results
+ .map(({ url, login }, i) => {
+ const [x, y] = getXY(i)
+ return ``
+ })
+ .join('')
+
+ // More text
+
+ const [x, y] = getXY(results.length)
+ const width = (AV_SIZE + PADDING) * 3
+ const more = `
+ ...and ${totalCount - 100} more!`
+
+ const svgImage = `
+`
+
+ // const html = `
+ //
+ // ${images.join(`
+ // `)}
+ //
`
+
+ res
+ .status(200)
+ .setHeader('Cache-Control', 'max-age=604800')
+ .setHeader('Content-Type', 'image/svg+xml')
+ .send(svgImage)
+}
diff --git a/apps/www/pages/index.tsx b/apps/www/pages/index.tsx
index ae2483613..784bf855d 100644
--- a/apps/www/pages/index.tsx
+++ b/apps/www/pages/index.tsx
@@ -26,7 +26,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
return {
props: {
- isUser: false,
+ isUser: session?.user,
isSponsor: session?.user ? true : false,
},
}
diff --git a/apps/www/pages/r/[id].tsx b/apps/www/pages/r/[id].tsx
index a228b21e7..fc4b09af4 100644
--- a/apps/www/pages/r/[id].tsx
+++ b/apps/www/pages/r/[id].tsx
@@ -22,7 +22,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
return {
props: {
id,
- isUser: false,
+ isUser: session?.user,
isSponsor: session?.user ? true : false,
},
}
diff --git a/apps/www/utils/isSponsoringMe.ts b/apps/www/utils/isSponsoringMe.ts
new file mode 100644
index 000000000..0e21fc88e
--- /dev/null
+++ b/apps/www/utils/isSponsoringMe.ts
@@ -0,0 +1,24 @@
+const whitelist = ['steveruizok']
+
+export async function isSponsoringMe(login: string) {
+ if (whitelist.includes(login)) return true
+
+ const res = await fetch('https://api.github.com/graphql', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: 'bearer ' + process.env.GITHUB_API_SECRET,
+ },
+ body: JSON.stringify({
+ query: `
+ query {
+ user(login: "steveruizok") {
+ isSponsoredBy(accountLogin: "${login}")
+ }
+ }
+ `,
+ }),
+ }).then((res) => res.json())
+
+ return res?.data?.user?.isSponsoredBy
+}
diff --git a/assets/sentry.svg b/assets/sentry.svg
new file mode 100644
index 000000000..bfc892b42
--- /dev/null
+++ b/assets/sentry.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/card-repo.png b/assets/tldraw.png
similarity index 100%
rename from card-repo.png
rename to assets/tldraw.png
diff --git a/assets/vercel.svg b/assets/vercel.svg
new file mode 100644
index 000000000..c7dc242a9
--- /dev/null
+++ b/assets/vercel.svg
@@ -0,0 +1,6 @@
+
diff --git a/examples/tldraw-example/.env b/examples/tldraw-example/.env
deleted file mode 100644
index cd40368bd..000000000
--- a/examples/tldraw-example/.env
+++ /dev/null
@@ -1 +0,0 @@
-LIVEBLOCKS_PUBLIC_API_KEY=pk_live_1LJGGaqBSNLjLT-4Jalkl-U9
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 93f9db273..e8d1cf21a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9341,7 +9341,7 @@ jose@^1.27.2:
dependencies:
"@panva/asn1.js" "^1.0.0"
-jpeg-js@^0.4.2:
+jpeg-js@^0.4.1, jpeg-js@^0.4.2:
version "0.4.3"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b"
integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==
@@ -11183,6 +11183,11 @@ only@~0.0.2:
resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4"
integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=
+opentype.js@^0.4.3:
+ version "0.4.11"
+ resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-0.4.11.tgz#281a2390639cc15931c955d8d63c14a7c7772b41"
+ integrity sha1-KBojkGOcwVkxyVXY1jwUp8d3K0E=
+
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
@@ -11711,6 +11716,11 @@ plist@^3.0.1:
base64-js "^1.5.1"
xmlbuilder "^9.0.7"
+pngjs@^3.3.1:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
+ integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
+
pngjs@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
@@ -11975,6 +11985,15 @@ pupa@^2.1.1:
dependencies:
escape-goat "^2.0.0"
+pureimage@^0.3.6:
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/pureimage/-/pureimage-0.3.6.tgz#3ab6070e2779193a8767fc9b657d41c6894b070f"
+ integrity sha512-Wtk+QdlB1X7wnfaXads+5i6tI95dNMyR7Hq9Q0qmEOXiG38JvPRcAISSzuSVGXPlgi54VFzl26ic8UpT6GLs6g==
+ dependencies:
+ jpeg-js "^0.4.1"
+ opentype.js "^0.4.3"
+ pngjs "^3.3.1"
+
q@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"