[3/3] Highlighter styling (#1490)

This PR finalises the highlighter shape with new colors, sizing, and
perfect freehand options.

The colors are based on our existing colour palette, but take advantage
of wide-gamut displays to make the highlighter highlightier. I used my
[oklch color palette tool to pick the
palette](https://alex.dytry.ch/toys/palette/?palette=%7B%22families%22:%5B%22black%22,%22grey%22,%22white%22,%22green%22,%22light-green%22,%22blue%22,%22light-blue%22,%22violet%22,%22light-violet%22,%22red%22,%22light-red%22,%22orange%22,%22yellow%22%5D,%22shades%22:%5B%22light-mode%22,%22dark-mode%22,%22hl-light%22,%22hl-dark%22%5D,%22colors%22:%5B%5B%5B0.2308,0,null%5D,%5B0.9097,0,null%5D,%5B0.2308,0,null%5D,%5B0.2308,0,null%5D%5D,%5B%5B0.7692,0.0145,248.02%5D,%5B0.6778,0.0118,256.72%5D,%5B0.7692,0.0145,248.02%5D,%5B0.7692,0.0145,248.02%5D%5D,%5B%5B1,0,null%5D,%5B0.2308,0,null%5D,%5B1,0,null%5D,%5B1,0,null%5D%5D,%5B%5B0.5851,0.1227,164.1%5D,%5B0.5319,0.0811,162.23%5D,%5B0.8729,0.2083,173.3%5D,%5B0.5851,0.152,173.3%5D%5D,%5B%5B0.7146,0.1835,146.44%5D,%5B0.6384,0.1262,143.36%5D,%5B0.8603,0.2438,140.11%5D,%5B0.6082,0.2286,140.11%5D%5D,%5B%5B0.5566,0.2082,268.35%5D,%5B0.4961,0.1644,270.65%5D,%5B0.7158,0.173,243.85%5D,%5B0.5573,0.178,243.85%5D%5D,%5B%5B0.718,0.1422,246.06%5D,%5B0.6366,0.1055,250.98%5D,%5B0.8615,0.1896,200.03%5D,%5B0.707,0.161,200.03%5D%5D,%5B%5B0.5783,0.2186,319.15%5D,%5B0.5043,0.1647,315.37%5D,%5B0.728,0.2001,307.45%5D,%5B0.5433,0.2927,307.45%5D%5D,%5B%5B0.7904,0.1516,319.77%5D,%5B0.6841,0.1139,315.99%5D,%5B0.812,0.21,327.8%5D,%5B0.5668,0.281,327.8%5D%5D,%5B%5B0.5928,0.2106,26.53%5D,%5B0.5112,0.1455,26.18%5D,%5B0.7326,0.21,20.59%5D,%5B0.554,0.2461,20.59%5D%5D,%5B%5B0.7563,0.146,21.1%5D,%5B0.6561,0.0982,20.86%5D,%5B0.7749,0.178,6.8%5D,%5B0.5565,0.2454,6.8%5D%5D,%5B%5B0.6851,0.1954,44.57%5D,%5B0.5958,0.1366,46.6%5D,%5B0.8207,0.175,68.62%5D,%5B0.6567,0.164,68.61%5D%5D,%5B%5B0.8503,0.1149,68.95%5D,%5B0.7404,0.0813,72.25%5D,%5B0.8939,0.2137,100.36%5D,%5B0.7776,0.186,100.36%5D%5D%5D%7D&selected=3).
I'm not sure happy about these colors as they are right now - in
particular, i think dark mode looks a bit rubbish and there are a few
colors where the highlight and original version are much too similar
(light-violet & light-red). Black uses yellow (like note shape) and grey
uses light-blue. Exports are forced into srgb color space rather than P3
for maximum compatibility.


![image](https://github.com/tldraw/tldraw/assets/1489520/e3de762b-6ef7-4d17-87db-3e2b71dd8de1)


![image](https://github.com/tldraw/tldraw/assets/1489520/3bd90aa9-bdbc-4a2b-9e56-e3a83a2a877b)



The size of a highlighter stroke is now based on the text size which
works nicely for making the highlighter play well with text:


![image](https://github.com/tldraw/tldraw/assets/1489520/dd3184fc-decd-4db5-90ce-e9cc75edd3d6)


Perfect freehands settings are very similar to the draw tool, but with
the thinning turned way down. There is still some, but it's pretty
minimal.

### The plan
1. initial highlighter shape/tool #1401 
2. sandwich rendering for highlighter shapes #1418
3. shape styling - new colours and sizes, lightweight perfect freehand
changes #1490 **>you are here<**

### Change Type
- [x] `minor` — New Feature

### Test Plan

1. You can find the highlighter tool in the extended toolbar
2. You can activate the highlighter tool by pressing shift-D
3. Highlighter draws nice and vibrantly when over the page background or
frame background
4. Highlighter is less vibrant but still visible when drawn over images
/ other fills
5. Highlighter size should nicely match the corresponding unscaled text
size
6. Exports with highlighter look as expected

### Release Notes

Highlighter pen is here! 🎉🎉🎉

---------

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
alex 2023-06-01 16:34:59 +01:00 committed by GitHub
parent a11a741de4
commit d6085e4ea6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 211 additions and 107 deletions

View file

@ -1,4 +1,4 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.5 24.5C8 24 9.3 23 10.5 23C11.3229 23 12.1927 23.1567 12.8195 23.309C13.2406 23.4113 13.6936 23.3064 14 23V23M7.5 24.5L6.5 23.5L5.5 22.5M7.5 24.5L6.5 25.5L3.5 24.5L5.5 22.5M7.5 24.5L5.5 22.5M5.5 22.5C6 22 7 20.7 7 19.5C7 18.6771 6.84326 17.8073 6.69101 17.1805C6.5887 16.7594 6.69357 16.3064 7 16V16M7 16L18.5858 4.41421C19.3668 3.63317 20.6332 3.63317 21.4142 4.41421L25.5858 8.58579C26.3668 9.36684 26.3668 10.6332 25.5858 11.4142L14 23M7 16L14 23" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.5 24.5L5.5 22.5L7.5 24.5L6.5 25.5L3.5 24.5Z" fill="black"/>
<path d="M13.1024 25.123L26.6953 10.412C27.424 9.62334 27.3999 8.39987 26.6405 7.64054L23.3649 4.36488C22.6034 3.60337 21.3756 3.58158 20.5875 4.31559L5.98401 17.9175M13.1024 25.123L5.98401 17.9175M13.1024 25.123C12.6024 24.9563 11.3025 24.5374 10.1025 24.5374C8.90254 24.5374 7.60254 25.5374 7.10254 26.0374M5.98401 17.9175C6.15068 18.4175 6.60254 19.8374 6.60254 21.0374C6.60254 22.2374 5.60254 23.5374 5.10254 24.0374M7.10254 26.0374L6.10254 25.0374L5.10254 24.0374M7.10254 26.0374L6.10254 27.0374L2 27.1399L5.10254 24.0374" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 26L4 24L6 26L5 27L2 26Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 719 B

After

Width:  |  Height:  |  Size: 764 B

View file

@ -1,3 +0,0 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6041 25.123L27.1506 9.55414L20.0322 2.34865L4.48573 17.9175M11.6041 25.123L7.78596 26.7594L2.84937 21.7357L4.48573 17.9175M11.6041 25.123L4.48573 17.9175" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 352 B

View file

@ -151,7 +151,6 @@ export function getAssetUrlsByImport(opts?: AssetUrlOptions): {
'tool-frame': string
'tool-hand': string
'tool-highlight': string
'tool-highlighter': string
'tool-laser': string
'tool-line': string
'tool-media': string

View file

@ -162,7 +162,6 @@ import iconsToolEraser from './icons/icon/tool-eraser.svg'
import iconsToolFrame from './icons/icon/tool-frame.svg'
import iconsToolHand from './icons/icon/tool-hand.svg'
import iconsToolHighlight from './icons/icon/tool-highlight.svg'
import iconsToolHighlighter from './icons/icon/tool-highlighter.svg'
import iconsToolLaser from './icons/icon/tool-laser.svg'
import iconsToolLine from './icons/icon/tool-line.svg'
import iconsToolMedia from './icons/icon/tool-media.svg'
@ -394,7 +393,6 @@ export function getAssetUrlsByImport(opts) {
'tool-frame': formatAssetUrl(iconsToolFrame, opts),
'tool-hand': formatAssetUrl(iconsToolHand, opts),
'tool-highlight': formatAssetUrl(iconsToolHighlight, opts),
'tool-highlighter': formatAssetUrl(iconsToolHighlighter, opts),
'tool-laser': formatAssetUrl(iconsToolLaser, opts),
'tool-line': formatAssetUrl(iconsToolLine, opts),
'tool-media': formatAssetUrl(iconsToolMedia, opts),

View file

@ -151,7 +151,6 @@ export function getAssetUrlsByMetaUrl(opts?: AssetUrlOptions): {
'tool-frame': string
'tool-hand': string
'tool-highlight': string
'tool-highlighter': string
'tool-laser': string
'tool-line': string
'tool-media': string

View file

@ -498,10 +498,6 @@ export function getAssetUrlsByMetaUrl(opts) {
new URL('./icons/icon/tool-highlight.svg', import.meta.url).href,
opts
),
'tool-highlighter': formatAssetUrl(
new URL('./icons/icon/tool-highlighter.svg', import.meta.url).href,
opts
),
'tool-laser': formatAssetUrl(
new URL('./icons/icon/tool-laser.svg', import.meta.url).href,
opts

View file

@ -634,6 +634,7 @@ export const debugFlags: {
logMessages: DebugFlag<never[]>;
resetConnectionEveryPing: DebugFlag<boolean>;
debugCursors: DebugFlag<boolean>;
forceSrgb: DebugFlag<boolean>;
};
// @internal (undocumented)

View file

@ -1,3 +1,7 @@
/*
https://alex.dytry.ch/toys/palette/?palette=%7B%22families%22:%5B%22black%22,%22grey%22,%22white%22,%22green%22,%22light-green%22,%22blue%22,%22light-blue%22,%22violet%22,%22light-violet%22,%22red%22,%22light-red%22,%22orange%22,%22yellow%22%5D,%22shades%22:%5B%22light-mode%22,%22dark-mode%22,%22hl-light%22,%22hl-dark%22%5D,%22colors%22:%5B%5B%5B0.2308,0,null%5D,%5B0.9097,0,null%5D,%5B0.2308,0,null%5D,%5B0.2308,0,null%5D%5D,%5B%5B0.7692,0.0145,248.02%5D,%5B0.6778,0.0118,256.72%5D,%5B0.7692,0.0145,248.02%5D,%5B0.7692,0.0145,248.02%5D%5D,%5B%5B1,0,null%5D,%5B0.2308,0,null%5D,%5B1,0,null%5D,%5B1,0,null%5D%5D,%5B%5B0.5851,0.1227,164.1%5D,%5B0.5319,0.0811,162.23%5D,%5B0.8729,0.2083,173.3%5D,%5B0.5851,0.152,173.3%5D%5D,%5B%5B0.7146,0.1835,146.44%5D,%5B0.6384,0.1262,143.36%5D,%5B0.8603,0.2438,140.11%5D,%5B0.6082,0.2286,140.11%5D%5D,%5B%5B0.5566,0.2082,268.35%5D,%5B0.4961,0.1644,270.65%5D,%5B0.7158,0.173,243.85%5D,%5B0.5573,0.178,243.85%5D%5D,%5B%5B0.718,0.1422,246.06%5D,%5B0.6366,0.1055,250.98%5D,%5B0.8615,0.1896,200.03%5D,%5B0.707,0.161,200.03%5D%5D,%5B%5B0.5783,0.2186,319.15%5D,%5B0.5043,0.1647,315.37%5D,%5B0.728,0.2001,307.45%5D,%5B0.5433,0.2927,307.45%5D%5D,%5B%5B0.7904,0.1516,319.77%5D,%5B0.6841,0.1139,315.99%5D,%5B0.812,0.21,327.8%5D,%5B0.5668,0.281,327.8%5D%5D,%5B%5B0.5928,0.2106,26.53%5D,%5B0.5112,0.1455,26.18%5D,%5B0.7326,0.21,20.59%5D,%5B0.554,0.2461,20.59%5D%5D,%5B%5B0.7563,0.146,21.1%5D,%5B0.6561,0.0982,20.86%5D,%5B0.7749,0.178,6.8%5D,%5B0.5565,0.2454,6.8%5D%5D,%5B%5B0.6851,0.1954,44.57%5D,%5B0.5958,0.1366,46.6%5D,%5B0.8207,0.175,68.62%5D,%5B0.6567,0.164,68.61%5D%5D,%5B%5B0.8503,0.1149,68.95%5D,%5B0.7404,0.0813,72.25%5D,%5B0.8939,0.2137,100.36%5D,%5B0.7776,0.186,100.36%5D%5D%5D%7D&selected=3
*/
.tl-container {
width: 100%;
height: 100%;
@ -142,6 +146,21 @@
--palette-violet-pattern: #bd63d3;
--palette-white-pattern: #ffffff;
--palette-yellow-pattern: #fecb92;
/* for highlighter pen */
--palette-black-highlight: #fddd00;
--palette-grey-highlight: #cbe7f1;
--palette-green-highlight: #00ffc8;
--palette-light-green-highlight: #65f641;
--palette-blue-highlight: #10acff;
--palette-light-blue-highlight: #00f4ff;
--palette-violet-highlight: #c77cff;
--palette-light-violet-highlight: #ff88ff;
--palette-red-highlight: #ff636e;
--palette-light-red-highlight: #ff7fa3;
--palette-orange-highlight: #ffa500;
--palette-yellow-highlight: #fddd00;
--shadow-1: 0px 1px 2px rgba(0, 0, 0, 0.22), 0px 1px 3px rgba(0, 0, 0, 0.09);
--shadow-2: 0px 0px 2px rgba(0, 0, 0, 0.12), 0px 2px 3px rgba(0, 0, 0, 0.24),
0px 2px 6px rgba(0, 0, 0, 0.1), inset 0px 0px 0px 1px var(--color-panel-contrast);
@ -210,6 +229,7 @@
--palette-violet-semi: #31293c;
--palette-white-semi: #ffffff;
--palette-yellow-semi: #3c3934;
/* for fill style 'pattern' */
--palette-black-pattern: #989898;
--palette-blue-pattern: #3a4b9e;
@ -224,6 +244,21 @@
--palette-violet-pattern: #763a8b;
--palette-white-pattern: #ffffff;
--palette-yellow-pattern: #fecb92;
/* for highlighter pen */
--palette-black-highlight: #d2b700;
--palette-grey-highlight: #9cb4cb;
--palette-green-highlight: #009774;
--palette-light-green-highlight: #00a000;
--palette-blue-highlight: #0079d2;
--palette-light-blue-highlight: #00bdc8;
--palette-violet-highlight: #9e00ee;
--palette-light-violet-highlight: #c400c7;
--palette-red-highlight: #de002c;
--palette-light-red-highlight: #db005b;
--palette-orange-highlight: #d07a00;
--palette-yellow-highlight: #d2b700;
--shadow-1: 0px 1px 2px #00000029, 0px 1px 3px #00000038,
inset 0px 0px 0px 1px var(--color-panel-contrast);
--shadow-2: 0px 1px 3px #00000077, 0px 2px 6px #00000055,
@ -232,6 +267,41 @@
inset 0px 0px 0px 1px var(--color-panel-contrast);
}
/** p3 colors */
@media (color-gamut: p3) {
.tl-theme__light:not(.tl-theme__force-sRGB) {
/* for highlighter pen */
--palette-black-highlight: color(display-p3 0.972 0.8705 0.05);
--palette-grey-highlight: color(display-p3 0.8163 0.9023 0.9416);
--palette-green-highlight: color(display-p3 0.2536 0.984 0.7981);
--palette-light-green-highlight: color(display-p3 0.563 0.9495 0.3857);
--palette-blue-highlight: color(display-p3 0.308 0.6632 0.9996);
--palette-light-blue-highlight: color(display-p3 0.1512 0.9414 0.9996);
--palette-violet-highlight: color(display-p3 0.7469 0.5089 0.9995);
--palette-light-violet-highlight: color(display-p3 0.9676 0.5652 0.9999);
--palette-red-highlight: color(display-p3 0.9992 0.4376 0.45);
--palette-light-red-highlight: color(display-p3 0.9988 0.5301 0.6397);
--palette-orange-highlight: color(display-p3 0.9988 0.6905 0.266);
--palette-yellow-highlight: color(display-p3 0.972 0.8705 0.05);
}
.tl-theme__dark:not(.tl-theme__force-sRGB) {
/* for highlighter pen */
--palette-black-highlight: color(display-p3 0.8078 0.7225 0.0312);
--palette-grey-highlight: color(display-p3 0.6299 0.7012 0.7856);
--palette-green-highlight: color(display-p3 0.0085 0.582 0.4604);
--palette-light-green-highlight: color(display-p3 0.2711 0.6172 0.0195);
--palette-blue-highlight: color(display-p3 0.0032 0.4655 0.7991);
--palette-light-blue-highlight: color(display-p3 0.0023 0.7259 0.7735);
--palette-violet-highlight: color(display-p3 0.5651 0.0079 0.8986);
--palette-light-violet-highlight: color(display-p3 0.7024 0.0403 0.753);
--palette-red-highlight: color(display-p3 0.7978 0.0509 0.2035);
--palette-light-red-highlight: color(display-p3 0.7849 0.0585 0.3589);
--palette-orange-highlight: color(display-p3 0.7699 0.4937 0.0085);
--palette-yellow-highlight: color(display-p3 0.8078 0.7225 0.0312);
}
}
.tl-counter-scaled {
transform: scale(var(--tl-scale));
transform-origin: top left;

View file

@ -5617,7 +5617,9 @@ export class App extends EventEmitter<TLEventMap> {
// Get the styles from the container. We'll use these to pull out colors etc.
// NOTE: We can force force a light theme here becasue we don't want export
const fakeContainerEl = document.createElement('div')
fakeContainerEl.className = `tl-container tl-theme__${darkMode ? 'dark' : 'light'}`
fakeContainerEl.className = `tl-container tl-theme__${
darkMode ? 'dark' : 'light'
} tl-theme__force-sRGB`
document.body.appendChild(fakeContainerEl)
const containerStyle = getComputedStyle(fakeContainerEl)
@ -5642,6 +5644,12 @@ export class App extends EventEmitter<TLEventMap> {
containerStyle.getPropertyValue(`--palette-${color.id}-semi`),
])
) as Record<TLColorType, string>,
highlight: Object.fromEntries(
STYLES.color.map((color) => [
color.id,
containerStyle.getPropertyValue(`--palette-${color.id}-highlight`),
])
) as Record<TLColorType, string>,
text: containerStyle.getPropertyValue(`--color-text`),
background: containerStyle.getPropertyValue(`--color-background`),
solid: containerStyle.getPropertyValue(`--palette-solid`),

View file

@ -23,10 +23,10 @@ import { getDrawShapeStrokeDashArray, getFreehandOptions, getPointsFromSegments
export class TLDrawUtil extends TLShapeUtil<TLDrawShape> {
static override type = 'draw'
hideResizeHandles = (shape: TLDrawShape) => this.getIsDot(shape)
hideRotateHandle = (shape: TLDrawShape) => this.getIsDot(shape)
hideSelectionBoundsBg = (shape: TLDrawShape) => this.getIsDot(shape)
hideSelectionBoundsFg = (shape: TLDrawShape) => this.getIsDot(shape)
hideResizeHandles = (shape: TLDrawShape) => getIsDot(shape)
hideRotateHandle = (shape: TLDrawShape) => getIsDot(shape)
hideSelectionBoundsBg = (shape: TLDrawShape) => getIsDot(shape)
hideSelectionBoundsFg = (shape: TLDrawShape) => getIsDot(shape)
override defaultProps(): TLDrawShape['props'] {
return {
@ -44,10 +44,6 @@ export class TLDrawUtil extends TLShapeUtil<TLDrawShape> {
isClosed = (shape: TLDrawShape) => shape.props.isClosed
private getIsDot(shape: TLDrawShape) {
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
}
getBounds(shape: TLDrawShape) {
return Box2d.FromPoints(this.outline(shape))
}
@ -310,3 +306,7 @@ function getDot(point: VecLike, sw: number) {
r * 2
},0`
}
function getIsDot(shape: TLDrawShape) {
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
}

View file

@ -36,6 +36,21 @@ const solidSettings = (strokeWidth: number): StrokeOptions => {
}
}
export function getHighlightFreehandSettings(
strokeWidth: number,
showAsComplete: boolean
): StrokeOptions {
return {
size: 1 + strokeWidth,
thinning: 0.1,
streamline: 0.1, // 0.62 + ((1 + strokeWidth) / 8) * 0.06,
smoothing: 0.5,
simulatePressure: true,
easing: EASINGS.easeOutSine,
last: showAsComplete,
}
}
export function getFreehandOptions(
shapeProps: { dash: TLDashType; isPen: boolean; isComplete: boolean },
strokeWidth: number,

View file

@ -11,24 +11,25 @@ import {
import { TLDrawShapeSegment, TLHighlightShape } from '@tldraw/tlschema'
import { last, rng } from '@tldraw/utils'
import { SVGContainer } from '../../../components/SVGContainer'
import { FONT_SIZES } from '../../../constants'
import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg'
import { App } from '../../App'
import { ShapeFill } from '../shared/ShapeFill'
import { TLExportColors } from '../shared/TLExportColors'
import { useForceSolid } from '../shared/useForceSolid'
import { getFreehandOptions, getPointsFromSegments } from '../TLDrawUtil/getPath'
import { getHighlightFreehandSettings, getPointsFromSegments } from '../TLDrawUtil/getPath'
import { OnResizeHandler, TLShapeUtil } from '../TLShapeUtil'
const OVERLAY_OPACITY = 0.4
const OVERLAY_OPACITY = 0.35
const UNDERLAY_OPACITY = 0.82
/** @public */
export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
static type = 'highlight'
hideResizeHandles = (shape: TLHighlightShape) => this.getIsDot(shape)
hideRotateHandle = (shape: TLHighlightShape) => this.getIsDot(shape)
hideSelectionBoundsBg = (shape: TLHighlightShape) => this.getIsDot(shape)
hideSelectionBoundsFg = (shape: TLHighlightShape) => this.getIsDot(shape)
hideResizeHandles = (shape: TLHighlightShape) => getIsDot(shape)
hideRotateHandle = (shape: TLHighlightShape) => getIsDot(shape)
hideSelectionBoundsBg = (shape: TLHighlightShape) => getIsDot(shape)
hideSelectionBoundsFg = (shape: TLHighlightShape) => getIsDot(shape)
override defaultProps(): TLHighlightShape['props'] {
return {
@ -41,10 +42,6 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
}
}
private getIsDot(shape: TLHighlightShape) {
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
}
getBounds(shape: TLHighlightShape) {
return Box2d.FromPoints(this.outline(shape))
}
@ -60,7 +57,7 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
hitTestPoint(shape: TLHighlightShape, point: VecLike): boolean {
const outline = this.outline(shape)
const zoomLevel = this.app.zoomLevel
const offsetDist = this.app.getStrokeWidth(shape.props.size) / zoomLevel
const offsetDist = getStrokeWidth(shape) / zoomLevel
if (shape.props.segments.length === 1 && shape.props.segments[0].points.length < 4) {
if (shape.props.segments[0].points.some((pt) => Vec2d.Dist(point, pt) < offsetDist * 1.5)) {
@ -85,7 +82,7 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
if (shape.props.segments.length === 1 && shape.props.segments[0].points.length < 4) {
const zoomLevel = this.app.zoomLevel
const offsetDist = this.app.getStrokeWidth(shape.props.size) / zoomLevel
const offsetDist = getStrokeWidth(shape) / zoomLevel
if (
shape.props.segments[0].points.some(
@ -106,16 +103,28 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
}
render(shape: TLHighlightShape) {
return <HighlightRenderer app={this.app} shape={shape} opacity={OVERLAY_OPACITY} />
return (
<HighlightRenderer
strokeWidth={getStrokeWidth(shape)}
shape={shape}
opacity={OVERLAY_OPACITY}
/>
)
}
renderBackground(shape: TLHighlightShape) {
return <HighlightRenderer app={this.app} shape={shape} />
return (
<HighlightRenderer
strokeWidth={getStrokeWidth(shape)}
shape={shape}
opacity={UNDERLAY_OPACITY}
/>
)
}
indicator(shape: TLHighlightShape) {
const forceSolid = useForceSolid()
const strokeWidth = this.app.getStrokeWidth(shape.props.size)
const strokeWidth = getStrokeWidth(shape)
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
let sw = strokeWidth
@ -124,27 +133,33 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
}
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
const options = getFreehandOptions(
{ dash: 'draw', isComplete: shape.props.isComplete, isPen: shape.props.isPen },
sw,
showAsComplete,
true
)
const options = getHighlightFreehandSettings(strokeWidth, showAsComplete)
const strokePoints = getStrokePoints(allPointsFromSegments, options)
const solidStrokePath =
strokePoints.length > 1
? getSvgPathFromStrokePoints(strokePoints, false)
: getDot(allPointsFromSegments[0], sw)
return <path d={solidStrokePath} />
let strokePath
if (strokePoints.length < 2) {
strokePath = getIndicatorDot(allPointsFromSegments[0], sw)
} else {
strokePath = getSvgPathFromStrokePoints(strokePoints, false)
}
return <path d={strokePath} />
}
toSvg(shape: TLHighlightShape, _font: string | undefined, colors: TLExportColors) {
return highlighterToSvg(this.app, shape, OVERLAY_OPACITY, colors)
override expandSelectionOutlinePx(shape: TLHighlightShape): number {
return getStrokeWidth(shape) / 2
}
toBackgroundSvg(shape: TLHighlightShape, font: string | undefined, colors: TLExportColors) {
return highlighterToSvg(this.app, shape, 1, colors)
override toSvg(shape: TLHighlightShape, _font: string | undefined, colors: TLExportColors) {
return highlighterToSvg(getStrokeWidth(shape), shape, OVERLAY_OPACITY, colors)
}
override toBackgroundSvg(
shape: TLHighlightShape,
font: string | undefined,
colors: TLExportColors
) {
return highlighterToSvg(getStrokeWidth(shape), shape, UNDERLAY_OPACITY, colors)
}
override onResize: OnResizeHandler<TLHighlightShape> = (shape, info) => {
@ -171,30 +186,32 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
},
}
}
expandSelectionOutlinePx(shape: TLHighlightShape): number {
return (this.app.getStrokeWidth(shape.props.size) * 1.6) / 2
}
}
function getDot(point: VecLike, sw: number) {
const r = (sw + 1) * 0.5
function getShapeDot(point: VecLike) {
const r = 0.1
return `M ${point.x} ${point.y} m -${r}, 0 a ${r},${r} 0 1,0 ${r * 2},0 a ${r},${r} 0 1,0 -${
r * 2
},0`
}
function getIndicatorDot(point: VecLike, sw: number) {
const r = sw / 2
return `M ${point.x} ${point.y} m -${r}, 0 a ${r},${r} 0 1,0 ${r * 2},0 a ${r},${r} 0 1,0 -${
r * 2
},0`
}
function HighlightRenderer({
app,
strokeWidth,
shape,
opacity,
}: {
app: App
strokeWidth: number
shape: TLHighlightShape
opacity?: number
}) {
const forceSolid = useForceSolid()
const strokeWidth = app.getStrokeWidth(shape.props.size)
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
@ -204,30 +221,27 @@ function HighlightRenderer({
sw += rng(shape.id)() * (strokeWidth / 6)
}
const options = getFreehandOptions(
{ isComplete: shape.props.isComplete, isPen: shape.props.isPen, dash: 'draw' },
sw,
showAsComplete,
forceSolid
)
const options = getHighlightFreehandSettings(sw, showAsComplete)
const strokePoints = getStrokePoints(allPointsFromSegments, options)
const solidStrokePath =
strokePoints.length > 1
? getSvgPathFromStrokePoints(strokePoints, false)
: getDot(allPointsFromSegments[0], sw)
: getShapeDot(allPointsFromSegments[0])
if (!forceSolid || strokePoints.length < 2) {
setStrokePointRadii(strokePoints, options)
const strokeOutlinePoints = getStrokeOutlinePoints(strokePoints, options)
return (
<SVGContainer id={shape.id} style={{ opacity }}>
<ShapeFill fill="none" color={shape.props.color} d={solidStrokePath} />
<path
d={getSvgPathFromStroke(strokeOutlinePoints, true)}
d={solidStrokePath}
strokeLinecap="round"
fill="currentColor"
fill="none"
pointerEvents="all"
stroke={`var(--palette-${shape.props.color}-highlight)`}
strokeWidth={sw}
/>
</SVGContainer>
)
@ -240,7 +254,7 @@ function HighlightRenderer({
d={solidStrokePath}
strokeLinecap="round"
fill="none"
stroke="currentColor"
stroke={`var(--palette-${shape.props.color}-highlight)`}
strokeWidth={strokeWidth}
strokeDashoffset="0"
/>
@ -249,14 +263,13 @@ function HighlightRenderer({
}
function highlighterToSvg(
app: App,
strokeWidth: number,
shape: TLHighlightShape,
opacity: number,
colors: TLExportColors
) {
const { color } = shape.props
const strokeWidth = app.getStrokeWidth(shape.props.size)
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
@ -266,12 +279,7 @@ function highlighterToSvg(
sw += rng(shape.id)() * (strokeWidth / 6)
}
const options = getFreehandOptions(
{ dash: 'draw', isComplete: shape.props.isComplete, isPen: shape.props.isPen },
sw,
showAsComplete,
false
)
const options = getHighlightFreehandSettings(sw, showAsComplete)
const strokePoints = getStrokePoints(allPointsFromSegments, options)
setStrokePointRadii(strokePoints, options)
@ -279,9 +287,17 @@ function highlighterToSvg(
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
path.setAttribute('d', getSvgPathFromStroke(strokeOutlinePoints, true))
path.setAttribute('fill', colors.fill[color])
path.setAttribute('fill', colors.highlight[color])
path.setAttribute('stroke-linecap', 'round')
path.setAttribute('opacity', opacity.toString())
return path
}
function getStrokeWidth(shape: TLHighlightShape) {
return FONT_SIZES[shape.props.size] * 1.12
}
function getIsDot(shape: TLHighlightShape) {
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
}

View file

@ -4,6 +4,7 @@ export type TLExportColors = {
fill: Record<TLColorType, string>
pattern: Record<TLColorType, string>
semi: Record<TLColorType, string>
highlight: Record<TLColorType, string>
solid: string
text: string
background: string

View file

@ -2,11 +2,11 @@ import { TLEventHandlers } from '../types/event-types'
import { StateNode } from './StateNode'
import { TLArrowTool } from './TLArrowTool/TLArrowTool'
import { TLDrawTool } from './TLDrawTool/TLDrawTool'
import { TLHighlightTool } from './TLDrawTool/TLHighlightTool'
import { TLEraserTool } from './TLEraserTool/TLEraserTool'
import { TLFrameTool } from './TLFrameTool/TLFrameTool'
import { TLGeoTool } from './TLGeoTool/TLGeoTool'
import { TLHandTool } from './TLHandTool/TLHandTool'
import { TLHighlightTool } from './TLHighlightTool/TLHighlightTool'
import { TLLaserTool } from './TLLaserTool/TLLaserTool'
import { TLLineTool } from './TLLineTool/TLLineTool'
import { TLNoteTool } from './TLNoteTool/TLNoteTool'

View file

@ -1,8 +1,9 @@
import { TLStyleType } from '@tldraw/tlschema'
import { StateNode } from '../StateNode'
import { Drawing } from './children/Drawing'
import { Idle } from './children/Idle'
// shared custody
import { Drawing } from '../TLDrawTool/children/Drawing'
import { Idle } from '../TLDrawTool/children/Idle'
export class TLHighlightTool extends StateNode {
static override id = 'highlight'

View file

@ -116,9 +116,7 @@ export const Shape = track(function Shape({
data-shape-type={shape.type}
draggable={false}
>
{isCulled ? (
<CulledShape shape={shape} util={util} />
) : (
{!isCulled && (
<OptionalErrorBoundary
fallback={ShapeErrorFallback ? (error) => <ShapeErrorFallback error={error} /> : null}
onError={(error) =>

View file

@ -1,5 +1,6 @@
import React from 'react'
import { useValue } from 'signia-react'
import { debugFlags } from '../utils/debug-flags'
import { useApp } from './useApp'
import { useContainer } from './useContainer'
@ -7,6 +8,7 @@ export function useDarkMode() {
const app = useApp()
const container = useContainer()
const isDarkMode = useValue('isDarkMode', () => app.isDarkMode, [app])
const forceSrgb = useValue(debugFlags.forceSrgb)
React.useEffect(() => {
if (isDarkMode) {
@ -24,5 +26,10 @@ export function useDarkMode() {
color: 'black',
})
}
}, [app, container, isDarkMode])
if (forceSrgb) {
container.classList.add('tl-theme__force-sRGB')
} else {
container.classList.remove('tl-theme__force-sRGB')
}
}, [app, container, forceSrgb, isDarkMode])
}

View file

@ -11,7 +11,7 @@ export const featureFlags = {
// todo: remove this. it's not used, but we only have one feature flag and i
// wanted an example :(
peopleMenu: createFeatureFlag('peopleMenu'),
highlighterTool: createFeatureFlag('highlighterTool'),
highlighterTool: createFeatureFlag('highlighterTool', { all: true }),
} satisfies Record<string, DebugFlag<boolean>>
/** @internal */
@ -51,6 +51,7 @@ export const debugFlags = {
debugCursors: createDebugValue('debugCursors', {
defaults: { all: false },
}),
forceSrgb: createDebugValue('forceSrgbColors', { defaults: { all: false } }),
}
declare global {

View file

@ -732,10 +732,10 @@ export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap>
export type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'toolbar' | 'unknown' | 'zoom-menu';
// @public (undocumented)
export type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copied' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlight' | 'tool-highlighter' | 'tool-laser' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'vertical-align-center' | 'vertical-align-end' | 'vertical-align-start' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out';
export type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copied' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlight' | 'tool-laser' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'vertical-align-center' | 'vertical-align-end' | 'vertical-align-start' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out';
// @public (undocumented)
export const TLUiIconTypes: readonly ["align-bottom-center", "align-bottom-left", "align-bottom-right", "align-bottom", "align-center-center", "align-center-horizontal", "align-center-left", "align-center-right", "align-center-vertical", "align-left", "align-right", "align-top-center", "align-top-left", "align-top-right", "align-top", "arrow-left", "arrowhead-arrow", "arrowhead-bar", "arrowhead-diamond", "arrowhead-dot", "arrowhead-none", "arrowhead-square", "arrowhead-triangle-inverted", "arrowhead-triangle", "aspect-ratio", "avatar", "blob", "bring-forward", "bring-to-front", "check", "checkbox-checked", "checkbox-empty", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-ne", "chevrons-sw", "clipboard-copied", "clipboard-copy", "code", "collab", "color", "comment", "cross-2", "cross", "dash-dashed", "dash-dotted", "dash-draw", "dash-solid", "discord", "distribute-horizontal", "distribute-vertical", "dot", "dots-horizontal", "dots-vertical", "drag-handle-dots", "duplicate", "edit", "external-link", "file", "fill-none", "fill-pattern", "fill-semi", "fill-solid", "follow", "following", "font-draw", "font-mono", "font-sans", "font-serif", "geo-arrow-down", "geo-arrow-left", "geo-arrow-right", "geo-arrow-up", "geo-check-box", "geo-diamond", "geo-ellipse", "geo-hexagon", "geo-octagon", "geo-oval", "geo-pentagon", "geo-rectangle", "geo-rhombus-2", "geo-rhombus", "geo-star", "geo-trapezoid", "geo-triangle", "geo-x-box", "github", "group", "hidden", "image", "info-circle", "leading", "link", "lock-small", "lock", "menu", "minus", "mixed", "pack", "page", "plus", "question-mark-circle", "question-mark", "redo", "reset-zoom", "rotate-ccw", "rotate-cw", "ruler", "search", "send-backward", "send-to-back", "settings-horizontal", "settings-vertical-1", "settings-vertical", "share-1", "share-2", "size-extra-large", "size-large", "size-medium", "size-small", "spline-cubic", "spline-line", "stack-horizontal", "stack-vertical", "stretch-horizontal", "stretch-vertical", "text-align-center", "text-align-justify", "text-align-left", "text-align-right", "tool-arrow", "tool-embed", "tool-eraser", "tool-frame", "tool-hand", "tool-highlight", "tool-highlighter", "tool-laser", "tool-line", "tool-media", "tool-note", "tool-pencil", "tool-pointer", "tool-text", "trash", "triangle-down", "triangle-up", "twitter", "undo", "ungroup", "unlock-small", "unlock", "vertical-align-center", "vertical-align-end", "vertical-align-start", "visible", "warning-triangle", "zoom-in", "zoom-out"];
export const TLUiIconTypes: readonly ["align-bottom-center", "align-bottom-left", "align-bottom-right", "align-bottom", "align-center-center", "align-center-horizontal", "align-center-left", "align-center-right", "align-center-vertical", "align-left", "align-right", "align-top-center", "align-top-left", "align-top-right", "align-top", "arrow-left", "arrowhead-arrow", "arrowhead-bar", "arrowhead-diamond", "arrowhead-dot", "arrowhead-none", "arrowhead-square", "arrowhead-triangle-inverted", "arrowhead-triangle", "aspect-ratio", "avatar", "blob", "bring-forward", "bring-to-front", "check", "checkbox-checked", "checkbox-empty", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-ne", "chevrons-sw", "clipboard-copied", "clipboard-copy", "code", "collab", "color", "comment", "cross-2", "cross", "dash-dashed", "dash-dotted", "dash-draw", "dash-solid", "discord", "distribute-horizontal", "distribute-vertical", "dot", "dots-horizontal", "dots-vertical", "drag-handle-dots", "duplicate", "edit", "external-link", "file", "fill-none", "fill-pattern", "fill-semi", "fill-solid", "follow", "following", "font-draw", "font-mono", "font-sans", "font-serif", "geo-arrow-down", "geo-arrow-left", "geo-arrow-right", "geo-arrow-up", "geo-check-box", "geo-diamond", "geo-ellipse", "geo-hexagon", "geo-octagon", "geo-oval", "geo-pentagon", "geo-rectangle", "geo-rhombus-2", "geo-rhombus", "geo-star", "geo-trapezoid", "geo-triangle", "geo-x-box", "github", "group", "hidden", "image", "info-circle", "leading", "link", "lock-small", "lock", "menu", "minus", "mixed", "pack", "page", "plus", "question-mark-circle", "question-mark", "redo", "reset-zoom", "rotate-ccw", "rotate-cw", "ruler", "search", "send-backward", "send-to-back", "settings-horizontal", "settings-vertical-1", "settings-vertical", "share-1", "share-2", "size-extra-large", "size-large", "size-medium", "size-small", "spline-cubic", "spline-line", "stack-horizontal", "stack-vertical", "stretch-horizontal", "stretch-vertical", "text-align-center", "text-align-justify", "text-align-left", "text-align-right", "tool-arrow", "tool-embed", "tool-eraser", "tool-frame", "tool-hand", "tool-highlight", "tool-laser", "tool-line", "tool-media", "tool-note", "tool-pencil", "tool-pointer", "tool-text", "trash", "triangle-down", "triangle-up", "twitter", "undo", "ungroup", "unlock-small", "unlock", "vertical-align-center", "vertical-align-end", "vertical-align-start", "visible", "warning-triangle", "zoom-in", "zoom-out"];
// @public (undocumented)
export const ToastsContext: Context<ToastsContextType>;

View file

@ -181,6 +181,7 @@ const DebugMenuContent = track(function DebugMenuContent({
<DropdownMenu.Group>
<Toggle label="Read-only" value={app.isReadOnly} onChange={(r) => app.setReadOnly(r)} />
<DebugFlagToggle flag={debugFlags.debugSvg} />
<DebugFlagToggle flag={debugFlags.forceSrgb} />
<DebugFlagToggle
flag={debugFlags.debugCursors}
onChange={(enabled) => {

View file

@ -1,4 +1,5 @@
import { App, featureFlags, useApp } from '@tldraw/editor'
import { compact } from '@tldraw/utils'
import React from 'react'
import { useValue } from 'signia-react'
import { ToolItem, ToolsContextType, useTools } from './useTools'
@ -45,7 +46,7 @@ export function ToolbarSchemaProvider({ overrides, children }: ToolbarSchemaProv
const highlighterEnabled = useValue(featureFlags.highlighterTool)
const toolbarSchema = React.useMemo<ToolbarSchemaContextType>(() => {
const schema: ToolbarSchemaContextType = [
const schema: ToolbarSchemaContextType = compact([
toolbarItem(tools.select),
toolbarItem(tools.hand),
toolbarItem(tools.draw),
@ -62,23 +63,20 @@ export function ToolbarSchemaProvider({ overrides, children }: ToolbarSchemaProv
toolbarItem(tools['rhombus']),
toolbarItem(tools['pentagon']),
toolbarItem(tools['hexagon']),
toolbarItem(tools['octagon']),
// toolbarItem(tools['octagon']),
toolbarItem(tools['star']),
toolbarItem(tools['oval']),
toolbarItem(tools.line),
toolbarItem(tools['arrow-right']),
toolbarItem(tools['x-box']),
toolbarItem(tools['check-box']),
toolbarItem(tools['arrow-left']),
toolbarItem(tools['arrow-up']),
toolbarItem(tools['arrow-down']),
toolbarItem(tools['x-box']),
toolbarItem(tools['check-box']),
toolbarItem(tools['arrow-right']),
toolbarItem(tools.frame),
toolbarItem(tools.line),
highlighterEnabled ? toolbarItem(tools.highlight) : null,
toolbarItem(tools.laser),
]
if (highlighterEnabled) {
schema.push(toolbarItem(tools.highlight))
}
])
if (overrides) {
return overrides(app, schema, { tools })

View file

@ -210,7 +210,7 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true,
icon: 'tool-highlight',
// TODO: pick a better shortcut
kbd: 'i',
kbd: '!d',
onSelect(source) {
app.setSelectedTool('highlight')
trackEvent('select-tool', { source, id: 'highlight' })

View file

@ -142,7 +142,6 @@ export type TLUiIconType =
| 'tool-frame'
| 'tool-hand'
| 'tool-highlight'
| 'tool-highlighter'
| 'tool-laser'
| 'tool-line'
| 'tool-media'
@ -307,7 +306,6 @@ export const TLUiIconTypes = [
'tool-frame',
'tool-hand',
'tool-highlight',
'tool-highlighter',
'tool-laser',
'tool-line',
'tool-media',