snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
30
apps/docs/components/common/autorefresh.tsx
Normal file
30
apps/docs/components/common/autorefresh.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { ReactNode, useEffect } from 'react'
|
||||
|
||||
let AutoRefresh = ({ children }: { children: ReactNode }) => {
|
||||
return children
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
AutoRefresh = function AutoRefresh({ children }) {
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
const ws = new WebSocket('ws://localhost:3201')
|
||||
ws.onmessage = (event) => {
|
||||
if (event.data === 'refresh') {
|
||||
router.refresh()
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
ws.close()
|
||||
}
|
||||
}, [router])
|
||||
|
||||
return children
|
||||
}
|
||||
}
|
||||
|
||||
export default AutoRefresh
|
||||
16
apps/docs/components/common/back-to-top-button.tsx
Normal file
16
apps/docs/components/common/back-to-top-button.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
'use client'
|
||||
|
||||
import { ArrowLongUpIcon } from '@heroicons/react/20/solid'
|
||||
|
||||
export function BackToTopButton() {
|
||||
return (
|
||||
<button
|
||||
id="back-to-top"
|
||||
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||
className="flex items-center gap-1.5 text-blue-500 hover:text-blue-600 dark:hover:bg-blue-400"
|
||||
>
|
||||
<ArrowLongUpIcon className="h-3.5 -mr-px" />
|
||||
<span>Scroll to top</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
88
apps/docs/components/common/button.tsx
Normal file
88
apps/docs/components/common/button.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
'use client'
|
||||
|
||||
import { ArrowLongLeftIcon, ArrowLongRightIcon } from '@heroicons/react/20/solid'
|
||||
import Link from 'next/link'
|
||||
import { MouseEventHandler } from 'react'
|
||||
import { track } from '@/app/analytics'
|
||||
import { Icon, IconName } from '@/components/common/icon'
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function Button({
|
||||
id,
|
||||
href,
|
||||
newTab,
|
||||
onClick,
|
||||
caption,
|
||||
icon,
|
||||
arrow,
|
||||
className,
|
||||
size = 'base',
|
||||
type = 'primary',
|
||||
darkRingOffset,
|
||||
}: {
|
||||
id?: string
|
||||
href?: string
|
||||
newTab?: boolean
|
||||
onClick?: MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>
|
||||
caption: string
|
||||
icon?: IconName
|
||||
arrow?: 'left' | 'right'
|
||||
className?: string
|
||||
size?: 'xs' | 'sm' | 'base' | 'lg'
|
||||
type?: 'primary' | 'secondary' | 'tertiary' | 'black'
|
||||
darkRingOffset?: boolean
|
||||
}) {
|
||||
const iconSizes = { xs: 'h-3', sm: 'h-3.5', base: 'h-4', lg: 'h-5' }
|
||||
className = cn(
|
||||
'relative overflow-hidden flex items-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-offset-zinc-900',
|
||||
darkRingOffset ? 'focus:ring-offset-zinc-900' : 'focus:ring-offset-zinc-50',
|
||||
size === 'xs' && 'h-6 px-2 gap-1.5 rounded-md text-xs',
|
||||
size === 'sm' && 'h-7 px-3 gap-2 rounded-md text-sm',
|
||||
size === 'base' && 'h-9 px-4 gap-2.5 rounded-lg text-base',
|
||||
size === 'lg' && 'h-11 px-5 gap-3 rounded-xl text-lg',
|
||||
type === 'primary' && 'bg-blue-500 text-white hover:bg-blue-600 dark:hover:bg-blue-400',
|
||||
type === 'black' &&
|
||||
'bg-black text-white dark:text-black hover:bg-zinc-800 dark:bg-white dark:hover:bg-zinc-100',
|
||||
type === 'secondary' &&
|
||||
'bg-zinc-100 text-zinc-800 dark:text-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700',
|
||||
type === 'tertiary' &&
|
||||
'text-black dark:text-white dark:hover:text-zinc-200 hover:text-zinc-700',
|
||||
className
|
||||
)
|
||||
// no word wrapping
|
||||
const spanClassName = cn('whitespace-nowrap')
|
||||
if (href)
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
target={newTab ? '_blank' : undefined}
|
||||
rel={newTab ? 'noopener noreferrer' : undefined}
|
||||
className={className}
|
||||
onClick={(e) => {
|
||||
onClick?.(e)
|
||||
if (id) track('button', { id })
|
||||
}}
|
||||
>
|
||||
{arrow === 'left' && <ArrowLongLeftIcon className={cn(iconSizes[size])} />}
|
||||
{icon && <Icon icon={icon} className={cn(iconSizes[size])} />}
|
||||
<span className={spanClassName}>{caption}</span>
|
||||
{arrow === 'right' && <ArrowLongRightIcon className={cn(iconSizes[size])} />}
|
||||
</Link>
|
||||
)
|
||||
if (onClick)
|
||||
return (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
if (id) track('button', { id })
|
||||
onClick?.(e)
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
{arrow === 'left' && <ArrowLongLeftIcon className={cn(iconSizes[size])} />}
|
||||
{icon && <Icon icon={icon} className={cn(iconSizes[size])} />}
|
||||
<span className={spanClassName}>{caption}</span>
|
||||
{arrow === 'right' && <ArrowLongRightIcon className={cn(iconSizes[size])} />}
|
||||
</button>
|
||||
)
|
||||
return null
|
||||
}
|
||||
13
apps/docs/components/common/icon/discord.tsx
Normal file
13
apps/docs/components/common/icon/discord.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function DiscordIcon({ className }: { className: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 640 512"
|
||||
className={cn('fill-current', className)}
|
||||
>
|
||||
<path d="M524.5 69.8a1.5 1.5 0 0 0 -.8-.7A485.1 485.1 0 0 0 404.1 32a1.8 1.8 0 0 0 -1.9 .9 337.5 337.5 0 0 0 -14.9 30.6 447.8 447.8 0 0 0 -134.4 0 309.5 309.5 0 0 0 -15.1-30.6 1.9 1.9 0 0 0 -1.9-.9A483.7 483.7 0 0 0 116.1 69.1a1.7 1.7 0 0 0 -.8 .7C39.1 183.7 18.2 294.7 28.4 404.4a2 2 0 0 0 .8 1.4A487.7 487.7 0 0 0 176 479.9a1.9 1.9 0 0 0 2.1-.7A348.2 348.2 0 0 0 208.1 430.4a1.9 1.9 0 0 0 -1-2.6 321.2 321.2 0 0 1 -45.9-21.9 1.9 1.9 0 0 1 -.2-3.1c3.1-2.3 6.2-4.7 9.1-7.1a1.8 1.8 0 0 1 1.9-.3c96.2 43.9 200.4 43.9 295.5 0a1.8 1.8 0 0 1 1.9 .2c2.9 2.4 6 4.9 9.1 7.2a1.9 1.9 0 0 1 -.2 3.1 301.4 301.4 0 0 1 -45.9 21.8 1.9 1.9 0 0 0 -1 2.6 391.1 391.1 0 0 0 30 48.8 1.9 1.9 0 0 0 2.1 .7A486 486 0 0 0 610.7 405.7a1.9 1.9 0 0 0 .8-1.4C623.7 277.6 590.9 167.5 524.5 69.8zM222.5 337.6c-29 0-52.8-26.6-52.8-59.2S193.1 219.1 222.5 219.1c29.7 0 53.3 26.8 52.8 59.2C275.3 311 251.9 337.6 222.5 337.6zm195.4 0c-29 0-52.8-26.6-52.8-59.2S388.4 219.1 417.9 219.1c29.7 0 53.3 26.8 52.8 59.2C470.7 311 447.5 337.6 417.9 337.6z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
13
apps/docs/components/common/icon/github.tsx
Normal file
13
apps/docs/components/common/icon/github.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function GithubIcon({ className }: { className: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 496 512"
|
||||
className={cn('fill-current', className)}
|
||||
>
|
||||
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
30
apps/docs/components/common/icon/index.tsx
Normal file
30
apps/docs/components/common/icon/index.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import * as HeroiconsSolid from '@heroicons/react/20/solid'
|
||||
import * as HeroiconsOutline from '@heroicons/react/24/outline'
|
||||
import { DiscordIcon } from './discord'
|
||||
import { GithubIcon } from './github'
|
||||
import { LinkedinIcon } from './linkedin'
|
||||
import { TldrawIcon } from './tldraw'
|
||||
import { TwitterIcon } from './twitter'
|
||||
|
||||
const icons = {
|
||||
check: HeroiconsOutline.CheckIcon,
|
||||
copy: HeroiconsOutline.ClipboardIcon,
|
||||
discord: DiscordIcon,
|
||||
github: GithubIcon,
|
||||
linkedin: LinkedinIcon,
|
||||
paperclip: HeroiconsOutline.PaperClipIcon,
|
||||
play: HeroiconsSolid.PlayIcon,
|
||||
tldraw: TldrawIcon,
|
||||
twitter: TwitterIcon,
|
||||
bolt: HeroiconsOutline.BoltIcon,
|
||||
cube: HeroiconsOutline.CubeIcon,
|
||||
wrench: HeroiconsOutline.WrenchScrewdriverIcon,
|
||||
shield: HeroiconsOutline.ShieldCheckIcon,
|
||||
}
|
||||
|
||||
export type IconName = keyof typeof icons
|
||||
|
||||
export function Icon({ icon, className }: { icon: IconName; className: string }) {
|
||||
const IconComponent = icons[icon]
|
||||
return <IconComponent className={className} />
|
||||
}
|
||||
13
apps/docs/components/common/icon/linkedin.tsx
Normal file
13
apps/docs/components/common/icon/linkedin.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function LinkedinIcon({ className }: { className: string }) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn('fill-current', className)}
|
||||
>
|
||||
<path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
16
apps/docs/components/common/icon/tldraw.tsx
Normal file
16
apps/docs/components/common/icon/tldraw.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function TldrawIcon({ className }: { className: string }) {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 15 14"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn('fill-current', className)}
|
||||
>
|
||||
<path d="M8.78578 3.4513C9.15358 3.0593 9.33748 2.58208 9.33748 2.01964C9.33748 1.45722 9.15358 0.980001 8.78578 0.587998C8.41806 0.195995 7.97035 0 7.44271 0C6.89906 0 6.44337 0.195995 6.07562 0.587998C5.70786 0.980001 5.52398 1.45722 5.52398 2.01964C5.52398 2.58208 5.70786 3.0593 6.07562 3.4513C6.44337 3.84331 6.89906 4.0393 7.44271 4.0393C7.97035 4.0393 8.41806 3.84331 8.78578 3.4513Z" />
|
||||
<path d="M6.05162 7.666C5.68387 8.05801 5.5 8.53521 5.5 9.09764C5.5 9.62606 5.66788 10.0691 6.00366 10.427C6.33943 10.7849 6.66722 10.9724 6.987 10.9894C7.11492 10.9894 7.17089 11.0577 7.1549 11.194C7.10693 11.5008 7.00299 11.7991 6.8431 12.0888C6.6832 12.3786 6.48334 12.6171 6.2435 12.8046C5.81179 13.1795 5.73185 13.5034 6.00366 13.7761C6.29147 14.0658 6.65922 14.0744 7.10693 13.8017C7.92238 13.3245 8.54597 12.6342 8.97772 11.7308C9.42541 10.8276 9.58532 9.9158 9.45732 8.99537C9.39338 8.50108 9.16953 8.05801 8.78578 7.666C8.40207 7.25696 7.95436 7.05243 7.44271 7.05243C6.89906 7.05243 6.43538 7.25696 6.05162 7.666Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
13
apps/docs/components/common/icon/twitter.tsx
Normal file
13
apps/docs/components/common/icon/twitter.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function TwitterIcon({ className }: { className: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className={cn('fill-current', className)}
|
||||
>
|
||||
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
46
apps/docs/components/common/logo.tsx
Normal file
46
apps/docs/components/common/logo.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function Logo({ className, forceWhite }: { className: string; forceWhite?: boolean }) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 4081 1000"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn(
|
||||
'fill-current',
|
||||
forceWhite ? 'text-white' : 'text-black dark:text-white',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<path
|
||||
d="M1303.69 270.972C1303.69 240.816 1328.13 216.369 1358.29 216.369H1428.49C1458.65 216.369 1483.09 240.816 1483.09 270.972V315.954C1483.09 327.011 1492.06 335.975 1503.12 335.975H1541C1561.11 335.975 1577.4 352.272 1577.4 372.376V429.579C1577.4 449.683 1561.11 465.98 1541 465.98H1503.12C1492.06 465.98 1483.09 474.944 1483.09 486.001V674.64C1483.09 681.573 1484.29 687.424 1486.67 692.19C1489.05 696.741 1492.74 700.207 1497.72 702.591C1502.7 704.758 1522.56 705.841 1530.37 705.841C1549.5 705.841 1560.72 712.765 1564.67 731.492L1577.4 791.954C1581.54 811.61 1569.85 830.984 1550.28 835.522C1535.55 838.989 1511.26 841.264 1490.9 842.347C1449.73 844.514 1415.17 840.289 1387.22 829.672C1359.26 818.838 1338.25 801.829 1324.16 778.644C1310.08 755.46 1303.25 726.425 1303.69 691.54V486.001C1303.69 474.944 1294.72 465.98 1283.67 465.98H1266.96C1246.85 465.98 1230.56 449.683 1230.56 429.579V372.376C1230.56 352.272 1246.85 335.975 1266.96 335.975H1283.67C1294.72 335.975 1303.69 327.011 1303.69 315.954V270.972Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M1974.54 814.96C1944.85 796.326 1921.02 768.049 1903.03 730.131C1885.27 692.213 1876.38 644.436 1876.38 586.8C1876.38 501.729 1898.19 403.692 1977.14 357.015C2038.76 320.844 2119.52 320.677 2175.17 368.834C2185.85 378.077 2214.4 369.799 2214.4 355.676V225.384C2214.4 195.228 2238.84 170.781 2269 170.781H2339.2C2369.36 170.781 2393.81 195.228 2393.81 225.384V781.808C2393.81 811.964 2369.36 836.411 2339.2 836.411H2249.9C2231.01 836.411 2215.7 821.099 2215.7 802.211C2215.7 791.902 2195.36 785.728 2188.33 793.263C2186.18 795.558 2184.03 797.807 2181.9 800.009C2128.34 855.383 2037.25 854.041 1974.54 814.96ZM2218.3 586.8C2218.3 537.286 2200.48 468.495 2138.99 468.495C2077.29 468.495 2060.99 537.551 2060.99 586.8C2060.99 623.113 2066.97 666.411 2096.74 691.13C2165.89 746.195 2218.3 647.886 2218.3 586.8Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2513.66 836.412C2483.51 836.412 2459.06 811.965 2459.06 781.809V391.792C2459.06 361.636 2483.51 337.19 2513.66 337.19H2603.2C2619.81 337.19 2633.27 350.651 2633.27 367.256C2633.27 382.997 2638.97 387.954 2649.77 387.954C2668.35 387.954 2672.24 328.627 2758.31 330.689C2813.1 330.689 2820.44 385.406 2820.44 412.863C2820.44 475.557 2800.52 481.496 2737.51 481.496C2681.18 481.496 2638.47 519.992 2638.47 576.4V781.809C2638.47 811.965 2614.02 836.412 2583.87 836.412H2513.66Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2970.68 842.913C2916.63 842.913 2859.79 826.876 2828.98 779.21C2815.11 757.759 2808.18 730.458 2808.18 697.306C2808.18 651.927 2823.66 607.404 2861.15 579.651C2898.44 552.053 2946.07 542.134 2991.49 538.699C3020.52 536.448 3107.19 538.281 3107.19 494.497C3107.19 448.71 3041.54 443.568 3011.96 464.271C2970.82 492.82 2973.83 498.397 2910.23 498.397C2834.58 498.397 2857.58 387.482 2932.33 352.791C2965.48 337.19 3006.87 329.39 3056.49 329.39C3114.93 329.39 3177.44 341.047 3225.17 376.842C3245.1 391.576 3260.27 408.91 3270.67 428.844C3281.29 448.562 3286.6 470.013 3286.6 493.197V780.51C3286.6 810.666 3262.15 835.112 3232 835.112H3152.37C3133.88 835.112 3118.89 820.125 3118.89 801.638C3118.89 792.06 3103.8 787.48 3097.15 794.364C3063.59 829.054 3017.92 842.913 2970.68 842.913ZM3097.11 696.981C3108 681.743 3109.4 662.66 3109.14 643.805C3108.97 630.868 3095.3 621.942 3082.84 625.429C3067.81 629.638 3052.39 632.57 3036.99 634.904C3017.05 638.018 2994.44 645.215 2983.36 663.505C2973.07 680.83 2975.13 706.154 2991.81 718.757C3023.43 742.651 3075.6 727.106 3097.11 696.981Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M1761.82 163C1792.33 163 1817.06 187.732 1817.06 218.24V781.158C1817.06 811.666 1792.33 836.398 1761.82 836.398H1690.8C1660.29 836.398 1635.56 811.666 1635.56 781.158V218.24C1635.56 187.732 1660.29 163 1690.8 163H1761.82Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M3480.44 836.409C3455.39 836.409 3433.55 819.357 3427.47 795.049L3329.98 405.032C3321.37 370.57 3347.43 337.187 3382.95 337.187H3447.12C3473.38 337.187 3495.92 355.886 3500.78 381.699L3534.84 562.897C3538.14 580.407 3563.06 580.816 3566.93 563.423L3607.7 379.944C3613.25 354.961 3635.4 337.187 3660.99 337.187H3748.03C3773.42 337.187 3795.45 354.684 3801.21 379.411L3843.62 561.638C3847.65 578.962 3872.52 578.345 3875.69 560.842L3908.05 382.062C3912.75 356.08 3935.37 337.187 3961.77 337.187H4025.63C4061.15 337.187 4087.21 370.57 4078.6 405.032L3981.11 795.049C3975.03 819.357 3953.19 836.409 3928.14 836.409H3818.64C3794.09 836.409 3772.56 820.015 3766.02 796.343L3719.13 626.567C3714.69 610.471 3691.83 610.567 3687.52 626.7L3642.32 795.901C3635.94 819.79 3614.3 836.409 3589.57 836.409H3480.44Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M0.557373 130C0.557373 58 58.5574 0 130.557 0H870.557C942.557 0 1000.56 58 1000.56 130V870C1000.56 942 942.557 1000 870.557 1000H130.557C58.5574 1000 0.557373 942 0.557373 870V130ZM590.557 300C590.557 350 550.557 390 500.557 390C450.557 390 410.557 350 410.557 300C410.557 250 450.557 210 500.557 210C550.557 210 590.557 250 590.557 300ZM470.557 810C513.557 810 554.557 751 569.557 719C588.557 678 599.557 605 580.557 562C567.557 532 537.557 510 497.557 510C449.557 510 410.557 549 410.557 597C410.557 640 441.557 674 481.557 681C483.557 681 485.557 684 485.557 686C481.557 711 470.557 743 453.557 759C432.557 779 438.557 810 470.557 810Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
22
apps/docs/components/common/navigation.tsx
Normal file
22
apps/docs/components/common/navigation.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function Navigation({
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
className?: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<nav
|
||||
className={cn('w-52 lg:w-60 shrink-0 flex flex-col sticky top-24 overflow-hidden', className)}
|
||||
style={{
|
||||
height: 'calc(100vh - 6rem)',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
20
apps/docs/components/common/page-title.tsx
Normal file
20
apps/docs/components/common/page-title.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
export function PageTitle({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}) {
|
||||
return (
|
||||
<h1
|
||||
className={cn(
|
||||
'font-black text-black dark:text-white text-3xl sm:text-4xl break-words max-w-full',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</h1>
|
||||
)
|
||||
}
|
||||
43
apps/docs/components/common/theme-switch.tsx
Normal file
43
apps/docs/components/common/theme-switch.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
'use client'
|
||||
|
||||
import { MoonIcon, SunIcon } from '@heroicons/react/20/solid'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { cn } from '@/utils/cn'
|
||||
|
||||
const themes = [
|
||||
{ id: 'light', name: 'Light Mode', icon: SunIcon },
|
||||
{ id: 'dark', name: 'Dark Mode', icon: MoonIcon },
|
||||
// { id: 'system', name: 'System Preference', icon: Cog6ToothIcon },
|
||||
]
|
||||
|
||||
export function ThemeSwitch() {
|
||||
const { theme: initialTheme, setTheme: persistTheme } = useTheme()
|
||||
const [theme, setTheme] = useState<string>()
|
||||
const Icon = themes.find((t) => t.id === theme)?.icon ?? SunIcon
|
||||
|
||||
useEffect(() => {
|
||||
setTheme(initialTheme)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const setThemeAndPersist = () => {
|
||||
const nextTheme = theme === 'light' ? 'dark' : 'light'
|
||||
persistTheme(nextTheme)
|
||||
setTheme(nextTheme)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-end sm:flex-row sm:items-center gap-4 ml-2 md:ml-0">
|
||||
<div className="hidden md:block h-px sm:h-5 w-full sm:w-px bg-zinc-100 sm:bg-zinc-200 dark:bg-zinc-800 dark:sm:bg-zinc-700" />
|
||||
<button
|
||||
onClick={() => setThemeAndPersist()}
|
||||
className="hover:text-zinc-600 dark:hover:text-zinc-100"
|
||||
>
|
||||
<div className={cn('transition-opacity duration-300', theme ? 'opacity-100' : 'opacity-0')}>
|
||||
<Icon className="size-5" />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
69
apps/docs/components/common/tldraw-link.tsx
Normal file
69
apps/docs/components/common/tldraw-link.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export function TldrawLink(props: React.ComponentProps<'a'>) {
|
||||
const { href, children } = props
|
||||
const windowObject = typeof window !== 'undefined' ? window : null
|
||||
// Start empty so the first client render matches the server HTML — reading
|
||||
// posthog during render appends `#session_id` to hrefs before hydration and
|
||||
// causes a hydration mismatch whenever posthog loads first.
|
||||
const [sessionId, setSessionId] = useState<string | undefined>(undefined)
|
||||
const [distinctId, setDistinctId] = useState<string | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
// Note this href check is intentionally loose, just to avoid
|
||||
// doing this setInterval for every link. We do the real check below.
|
||||
if (sessionId || !href?.includes('tldraw.com')) return
|
||||
|
||||
if (windowObject?.posthog) {
|
||||
setSessionId(windowObject.posthog.get_session_id())
|
||||
setDistinctId(windowObject.posthog.get_distinct_id())
|
||||
return
|
||||
}
|
||||
|
||||
// XXX: have to wait a bit for posthog to be ready.
|
||||
// there's unfortunately no event callback for this.
|
||||
const timeout = setInterval(() => {
|
||||
setSessionId(windowObject?.posthog?.get_session_id())
|
||||
setDistinctId(windowObject?.posthog?.get_distinct_id())
|
||||
clearTimeout(timeout)
|
||||
}, 1000)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}, [sessionId, windowObject, href])
|
||||
|
||||
const isLocalUrl = href?.startsWith('/') || href?.startsWith('#')
|
||||
let maybeParsedUrl
|
||||
try {
|
||||
maybeParsedUrl = isLocalUrl ? null : href ? new URL(href) : null
|
||||
} catch {
|
||||
console.error(`Invalid URL: ${href}`)
|
||||
}
|
||||
const derivedTarget =
|
||||
isLocalUrl ||
|
||||
maybeParsedUrl?.host.includes('tldraw.com') ||
|
||||
maybeParsedUrl?.host.includes('localhost')
|
||||
? undefined
|
||||
: '_blank'
|
||||
const target = props.target ?? derivedTarget
|
||||
|
||||
let newHref = href
|
||||
if (
|
||||
maybeParsedUrl?.host.includes('tldraw.com') &&
|
||||
sessionId &&
|
||||
distinctId &&
|
||||
!href?.includes('#')
|
||||
) {
|
||||
newHref = `${href}#session_id=${sessionId}&distinct_id=${distinctId}`
|
||||
}
|
||||
|
||||
return (
|
||||
<Link {...props} href={newHref ?? ''} target={target}>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user