Skip to content

[pull] main from tldraw:main#259

Merged
pull[bot] merged 3 commits intocode:mainfrom
tldraw:main
Nov 10, 2025
Merged

[pull] main from tldraw:main#259
pull[bot] merged 3 commits intocode:mainfrom
tldraw:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Nov 10, 2025

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

TodePond and others added 3 commits November 10, 2025 10:12
This PR adds a new "lined fill" style that's similar to the secret fill
style but with visible outlines. We use it within Teach and the agent
starter already because it makes AI generations look better. After this
PR, it will be available to fairies too.

Users can select the lined fill style by pressing `Option+Shift+F`,
similar to the existing `Option+F` shortcut for the secret fill style.

<img width="822" height="609" alt="image"
src="https://github.com/user-attachments/assets/a39ca4ed-8ae9-4ff6-adc2-c8f56eaa0eab"
/>

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [x] `feature`
- [ ] `api`
- [ ] `other`

### Test plan

1. Create a fillable shape (eg: rectangle) and select it.
2. Press Option+Shift+F.
3. Make sure the shape becomes filled with a lined outline.

- [ ] Unit tests
- [ ] End to end tests

### Release notes

- Added the secret "lined fill" style, used by the agent starter. Users
can select it by pressing `Option+Shift+F`.

### API changes

- Added the secret "lined fill" style, used by the agent starter. Users
can select it by pressing `Option+Shift+F`.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a new `lined-fill` fill style across schema, theme, rendering,
and UI (Alt+Shift+F), updates translations/API, and removes the
agent-specific hack.
> 
> - **Fill style**:
> - Introduces `lined-fill` to `DefaultFillStyle` and public types/APIs
(`tlschema`, `tldraw` API report).
> - Updates shared mappings (`FocusFill`, `SimpleFill`) to map
solid→`lined-fill` and back.
> - **Theme/Colors**:
> - Adds `linedFill` variant to `TLDefaultColorThemeColor` and populates
values across light/dark palettes.
>   - `getColorValue` consumers can resolve `linedFill`.
> - **Rendering**:
>   - `ShapeFill` supports `lined-fill` using theme `linedFill` color.
> - **UI/Actions**:
> - New action `select-fill-lined-fill` with shortcut `alt+shift+f` to
apply `lined-fill` to selected/next shapes.
> - **Translations**:
> - Adds `fill-style.lined-fill` key to translation types and default
strings/assets.
> - **Templates/Agent**:
> - Removes `enableLinedFillStyle` override; agent starter now relies on
built-in `lined-fill`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e9c4543. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
GTM Integration:
- New GTMAnalyticsService loads GTM script/iframe, tracks events via
dataLayer
- Consent banner tracking: `display_consent_banner` and
`select_consent_banner` events
- Public consent API: `getConsentState()` and `onConsentUpdate()` on
`window.tlanalytics`
- Structured event tracking: `trackCopyCode()` and
`trackFormSubmission()` methods
  - Wired up code copy tracking in docs site
  - Config: `TL_GTM_CONTAINER_ID` env var support

Dotcom Analytics:
- Added cross-domain tracking linker for tldraw.dev
- Push watermark click tracking to GTM
- Added watermark event to editor emit types

Things to do:
- [ ] Set NEXT_PUBLIC_GTM_CONTAINER_ID in Vercel
- [ ] Remove NEXT_PUBLIC_GA4_MEASUREMENT_ID variable in Vercel
- [ ] Make similar changes on the Framer side of things. Code change
below.

Add  this to head:
```typescript
<script>
    window.TL_GTM_CONTAINER_ID = 'GTM-PJDV58LL';
  </script>
  <script
      id="tldraw-analytics"
      type="text/javascript"
      async
      defer
      src="https://analytics.tldraw.com/tl-analytics.js"
  ></script>
  <script>
    // Send form submission events to GTM
    document.addEventListener("framer:formsubmit", async (event) => {
      if (window.tlanalytics?.trackFormSubmission) {
        const formData = event.detail.data || {};

        // Build payload with only fields that have values
        const payload = {
          enquiry_type: event.detail.trackingId || 'form_submission',
        };

        if (formData.email) {
          payload.user_email = formData.email;
          payload.user_email_sha256 = await sha256(formData.email.toLowerCase());
        }
        if (formData.first_name) {
          payload.user_first_name = formData.first_name;
        }
        if (formData.last_name) {
          payload.user_last_name = formData.last_name;
        }
        if (formData.linkedin_company_page) {
          payload.company_linkedin = formData.linkedin_company_page;
        }
        if (formData.hs_employee_range) {
          payload.company_size = formData.hs_employee_range;
        }
        if (formData.website) {
          payload.company_website = formData.website;
        }
        if (formData.country) {
          payload.user_country = formData.country;
        }

        // Only call if we have required fields
        if (payload.user_email) {
          window.tlanalytics.trackFormSubmission(payload);
        }
      }
    });

    // Track code copy events
    document.addEventListener('copy', (copyEvent) => {
      const isWithinCodeBlock = copyEvent.target?.closest('pre, code');
      if (isWithinCodeBlock) {
        const copiedText = window.getSelection()?.toString() || '';

        // Only track if we have actual text
        if (copiedText && window.tlanalytics?.trackCopyCode) {
          window.tlanalytics.trackCopyCode({
            page_category: 'framer',
            text_snippet: copiedText,
          });
        }
      }
    });

    // Simple SHA-256 hash function for email
    async function sha256(message) {
      const msgBuffer = new TextEncoder().encode(message);
      const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }
  </script>
``` 

Add this to body:
```typescript
  <noscript>
    <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PJDV58LL"
      height="0" width="0" style="display:none;visibility:hidden"></iframe>
  </noscript>
```


### API changes
- Add click `click-watermark` emit type, so that you can listen to it
via the editor events.

### Change type

- [x] `improvement`





<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Integrates GTM with consent handling and new tracking APIs, wires
code-copy/form events, adds watermark click tracking end-to-end, and
updates allowed origins and docs embed.
> 
> - **Analytics Core**:
> - **GTM Integration**: New `GTMAnalyticsService` loads GTM, manages
consent via `dataLayer`, and tracks `page_view`/custom events.
> - **Consent**: Adds `getConsentState()`/`onConsentUpdate()` and tracks
consent banner display/selection with opt-in type.
> - **Structured Events**: Adds `trackCopyCode()` and
`trackFormSubmission()` and forwards to services.
> - **Service Wiring**: Includes `gtmService` alongside existing
services.
> - **Docs Site (`apps/docs`)**:
> - Sets `window.TL_GTM_CONTAINER_ID`; loads analytics from local in
dev; adds GTM `<noscript>` iframe.
>   - Wires code-copy to `tlanalytics.trackCopyCode`.
> - **Dotcom Analytics**:
>   - GA4 linker for cross-domain tracking with `tldraw.dev`.
>   - Tracks `click-watermark` in GA4.
> - **Editor SDK**:
> - Adds `click-watermark` event to `TLEventMap`/UI events; watermark
now emits it; `Tldraw` forwards to UI event system.
> - **Worker**:
>   - Allows CORS for `http://localhost:3001`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
853c2d3. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
ayyy I forgot to add the groups_backend flag for new users in #7058

which means that people who have been signing up since then have still
been using the old data model. it's no biggie, just requires slight
adjustment to the migration script.

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [ ] `feature`
- [ ] `api`
- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Ensure new users get `groups_backend` flag and update the migration to
use `ON CONFLICT DO NOTHING` for safe re-runs.
> 
> - **Backend**
> - Set `user.flags` to `groups_backend` on user creation in
`apps/dotcom/sync-worker/src/TLUserDurableObject.ts`.
> - **Database Migration**
(`apps/dotcom/zero-cache/migrations/023_groups.sql`)
> - Add `ON CONFLICT DO NOTHING` to inserts into `group`, `group_user`,
and `group_file` in `migrate_user_to_groups` to make it idempotent.
> - Standardize flag to `groups_backend` in migration flag updates and
comments.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cdf92fd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@pull pull Bot locked and limited conversation to collaborators Nov 10, 2025
@pull pull Bot added the ⤵️ pull label Nov 10, 2025
@pull pull Bot merged commit 46ed6db into code:main Nov 10, 2025
1 of 7 checks passed
@pull pull Bot had a problem deploying to deploy-staging November 10, 2025 15:13 Failure
@pull pull Bot had a problem deploying to deploy-production November 10, 2025 15:13 Failure
@pull pull Bot had a problem deploying to deploy-staging November 10, 2025 15:13 Failure
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants