Conversation
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 -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 : )