Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/dotcom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
You'll need a clerk publishable and secret key.

In `sync-worker/.dev.vars`, set `CLERK_PUBLISHABLE_KEY` & `CLERK_SECRET_KEY`. In `client/.env.local`, set `VITE_CLERK_PUBLISHABLE_KEY`.

Having trouble getting started? Maybe an old build on your machine? Run `cd apps/dotcom/zero-cache && yarn clean && cd ../../../`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
.anonDotDevLink {
position: absolute;
bottom: max(var(--tl-space-4), env(safe-area-inset-bottom));
right: max(var(--tl-space-2), env(safe-area-inset-right));
z-index: calc(var(--tl-layer-watermark) + 1);
height: 36px;
padding: 2px;
}

.anonDotDevLink > div {
font-size: 12px;
color: var(--tl-color-text);
pointer-events: all;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;
background-color: var(--tl-color-background);
border: 1px solid var(--tl-color-text-1);
border-radius: 2px;
}

.anonDotDevLink::after {
content: '';
position: absolute;
top: 4px;
left: -4px;
right: 4px;
bottom: -4px;
border-radius: 2px;
border: 1px dashed var(--tl-color-text-1);
z-index: -1;
pointer-events: none;
}

.anonDotDevLink a {
position: relative;
display: flex;
align-items: center;
gap: 4px;
height: 100%;
padding: 0 8px;
color: inherit;
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.anonDotDevLink a div {
position: relative;
flex-shrink: 0;
transform: rotate(180deg);
transition: transform 3s ease-in-out 0s;
}

.anonDotDevDismissButton {
position: relative;
height: 28px;
width: 28px;
min-width: 0;
color: var(--tl-color-text-3);
transition: opacity 0.2s ease-in-out;
}

.anonDotDevDismissButton div {
width: 12px;
height: 12px;
}

@media (hover: hover) {
.anonDotDevLink:hover a div {
transform: translate(80px, 0px) rotate(180deg);
transition: transform 6s ease-in-out 1s;
}

.anonDotDevLink:has(a:hover) .anonDotDevDismissButton {
opacity: 0;
}

.anonDotDevLink:has(a:hover)::after {
background-color: var(--tl-color-hover);
}
}

/* Hide on small viewports to match the navigation panel breakpoint */
@media (max-width: 580px) {
.anonDotDevLink {
display: none;
}
}

/* Match the watermark's debug-mode offset so we don't overlap the debug panel */
.anonDotDevLink[data-debug='true'] {
bottom: max(46px, env(safe-area-inset-bottom));
}

/* RTL: mirror to bottom-left like the watermark */
:global(.tl-container[dir='rtl']) .anonDotDevLink {
right: auto;
left: max(var(--tl-space-2), env(safe-area-inset-left));
}

:global(.tl-container[dir='rtl']) .anonDotDevLink::after {
left: 4px;
right: -4px;
}

/* Hide the watermark when this button is visible (they share the same corner) */
:global(.tl-container):has(.anonDotDevLink) :global(.tl-watermark_SEE-LICENSE) {
display: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useAuth } from '@clerk/clerk-react'
import {
PORTRAIT_BREAKPOINT,
TldrawUiButton,
TldrawUiIcon,
useBreakpoint,
useEditor,
useLocalStorageState,
useValue,
} from 'tldraw'
import { trackEvent } from '../../../utils/analytics'
import { defineMessages, F, useMsg } from '../../utils/i18n'
import { ExternalLink } from '../ExternalLink/ExternalLink'
import styles from './TlaAnonDotDevLink.module.css'

const messages = defineMessages({
dismiss: { defaultMessage: 'Dismiss' },
})

const STORAGE_KEY = 'tldraw-dotcom:anon-dotdev-link:dismissed'

export function TlaAnonDotDevLink() {
const { isSignedIn, isLoaded } = useAuth()
const [isDismissed, setIsDismissed] = useLocalStorageState(STORAGE_KEY, false)
const dismissLbl = useMsg(messages.dismiss)
const editor = useEditor()
const isDebugMode = useValue('debug mode', () => editor.getInstanceState().isDebugMode, [editor])
const breakpoint = useBreakpoint()

if (!isLoaded || isSignedIn !== false) return null
if (isDismissed) return null
if (breakpoint < PORTRAIT_BREAKPOINT.MOBILE) return null

return (
<div className={styles.anonDotDevLink} data-debug={isDebugMode}>
<div>
<ExternalLink
to="https://tldraw.dev?utm_source=dotcom&utm_medium=organic&utm_campaign=anon-overlay-link"
data-testid="tla-anon-dotdev-link"
eventName="anon-dotdev-link-clicked"
>
<F defaultMessage="Build with the tldraw SDK" />
<TldrawUiIcon icon="arrow-left" label="Build with the tldraw SDK" small />
</ExternalLink>
<TldrawUiButton
type="icon"
title={dismissLbl}
aria-label={dismissLbl}
data-testid="tla-anon-dotdev-dismiss-button"
className={styles.anonDotDevDismissButton}
onClick={() => {
trackEvent('anon-dotdev-link-dismissed')
setIsDismissed(true)
}}
>
<TldrawUiIcon icon="cross-2" label={dismissLbl} small />
</TldrawUiButton>
</div>
</div>
)
}
2 changes: 2 additions & 0 deletions apps/dotcom/client/src/tla/components/TlaEditor/TlaEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { ReadyWrapper, useSetIsReady } from '../../hooks/useIsReady'
import { useNewRoomCreationTracking } from '../../hooks/useNewRoomCreationTracking'
import { useTldrawCurrentUser } from '../../hooks/useUser'
import { maybeSlurp } from '../../utils/slurping'
import { TlaAnonDotDevLink } from '../TlaAnonDotDevLink/TlaAnonDotDevLink'
import { TlaEditorErrorFallback } from './editor-components/TlaEditorErrorFallback'
import { TlaEditorMenuPanel } from './editor-components/TlaEditorMenuPanel'
import { TlaEditorSharePanel } from './editor-components/TlaEditorSharePanel'
Expand Down Expand Up @@ -301,6 +302,7 @@ function TlaEditorInner({ fileSlug, deepLinks }: TlaEditorProps) {
{app && <SneakyTldrawFileDropHandler />}
<SneakyLargeFileHander />
<SneakyDebugModeToast />
<TlaAnonDotDevLink />
</Tldraw>
</TlaEditorWrapper>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export function UIThemeSubmenu() {
if (!editor || themeIds.length === 0) return null

return (
<TldrawUiMenuSubmenu id="ui-theme" label="Color theme">
<TldrawUiMenuSubmenu id="ui-theme" label="menu.color-theme">
<div
className="tlui-menu__group"
onPointerCancel={clearThemePreview}
Expand Down
2 changes: 2 additions & 0 deletions apps/dotcom/client/src/tla/pages/local.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { assert, getFromSessionStorage, omit, react } from 'tldraw'
import { LocalEditor } from '../../components/LocalEditor'
import { routes } from '../../routeDefs'
import { globalEditor } from '../../utils/globalEditor'
import { TlaAnonDotDevLink } from '../components/TlaAnonDotDevLink/TlaAnonDotDevLink'
import { SneakyDarkModeSync } from '../components/TlaEditor/sneaky/SneakyDarkModeSync'
import { SneakyDebugModeToast } from '../components/TlaEditor/sneaky/SneakyDebugModeToast'
import { components } from '../components/TlaEditor/TlaEditor'
Expand Down Expand Up @@ -130,6 +131,7 @@ function LocalTldraw() {
>
<SneakyDarkModeSync />
<SneakyDebugModeToast />
<TlaAnonDotDevLink />
</LocalEditor>
</TlaAnonLayout>
)
Expand Down
1 change: 0 additions & 1 deletion apps/dotcom/client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"jsxImportSource": "react",
"incremental": true,
"downlevelIteration": true,
"plugins": [{ "name": "next" }],
"experimentalDecorators": true,
"types": ["vitest/globals"]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'tldraw/tldraw.css'

// [1]
const CustomGeoShapeUtil = GeoShapeUtil.configure({
customGeoStyles: {
customGeoTypes: {
'rounded-rect': {
getPath(w, h, shape) {
// `isFilled` is used by the path builder to determine whether the
Expand Down Expand Up @@ -158,34 +158,34 @@ export default function CustomGeoTypesExample() {

/*
[1]
Use GeoShapeUtil.configure() with a customGeoStyles map. Each entry
defines a new geo style with:
Use GeoShapeUtil.configure() with a customGeoTypes map. Each entry
defines a new geo type with:
- getPath: returns a PathBuilder describing the shape outline
- snapType: 'polygon' (snap to vertices + center) or 'blobby' (center only)
- icon: icon name for the style panel picker
- defaultSize: optional creation size when clicking (not dragging)

[2]
Pass the configured shape util in an array. It replaces the default
GeoShapeUtil but keeps all built-in geo styles alongside your custom ones.
GeoShapeUtil but keeps all built-in geo types alongside your custom ones.

[3]
Provide custom icon SVGs for your geo styles via assetUrls. The icon key
must be 'geo-' followed by the geo style name (e.g., 'geo-rounded-rect').
Provide custom icon SVGs for your geo types via assetUrls. The icon key
must be 'geo-' followed by the geo type name (e.g., 'geo-rounded-rect').

[4]
Provide translations for the tool labels so that the tooltips in the
toolbar show the right name. The translation keys are 'tool.' followed
by the geo style name (e.g., 'tool.rounded-rect').
by the geo type name (e.g., 'tool.rounded-rect').

[5]
Override the Toolbar component to add ToolbarItems for your custom geo
styles. The tool ID matches the key in your customGeoStyles map. Custom
geo styles are automatically registered as tools, so you just need to
types. The tool ID matches the key in your customGeoTypes map. Custom
geo types are automatically registered as tools, so you just need to
reference them by name.

[6]
Custom geo styles appear in the geo style panel picker. They support all
Custom geo types appear in the geo style panel picker. They support all
standard geo features: labels, fill/dash/color styles, resizing, SVG
export, and snap points.
*/
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ Add custom geo types that plug into the existing geo shape system.

---

Use `GeoShapeUtil.configure()` with a `customGeoStyles` map to register new geometric shape types. Each custom style provides its own path geometry, snap behavior, default size, and style panel icon, while inheriting all standard geo shape features like labels, resizing, fill styles, and SVG export.
Use `GeoShapeUtil.configure()` with a `customGeoTypes` map to register new geometric shape types. Each custom type provides its own path geometry, snap behavior, default size, and style panel icon, while inheriting all standard geo shape features like labels, resizing, fill styles, and SVG export.
1 change: 1 addition & 0 deletions assets/translations/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@
"a11y.open-keyboard-shortcuts": "Keyboard shortcuts…",
"menu.title": "Menu",
"menu.theme": "Theme",
"menu.color-theme": "Color theme",
"menu.accessibility": "Accessibility",
"menu.copy-as": "Copy as",
"menu.edit": "Edit",
Expand Down
2 changes: 1 addition & 1 deletion internal/scripts/deploy-dotcom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ const zeroVmSizes = {
rm: { cpus: 2, memory: '4gb', cpuKind: 'performance' },
vs: { cpus: 4, memory: '8gb', cpuKind: 'performance' },
volumeSize: '8gb',
vsMinMachines: 5,
vsMinMachines: 7,
killTimeout: '5m',
},
preview: { single: { cpus: 2, memory: '2gb' } },
Expand Down
Loading
Loading