From d6085e4ea6e62f1816c7188537f2b6b27dd8317c Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 1 Jun 2023 16:34:59 +0100 Subject: [PATCH] [3/3] Highlighter styling (#1490) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- assets/icons/icon/tool-highlight.svg | 4 +- assets/icons/icon/tool-highlighter.svg | 3 - packages/assets/imports.d.ts | 1 - packages/assets/imports.js | 2 - packages/assets/urls.d.ts | 1 - packages/assets/urls.js | 4 - packages/editor/api-report.md | 1 + packages/editor/editor.css | 70 +++++++++ packages/editor/src/lib/app/App.ts | 10 +- .../app/shapeutils/TLDrawUtil/TLDrawUtil.tsx | 16 +- .../lib/app/shapeutils/TLDrawUtil/getPath.ts | 15 ++ .../TLHighlightUtil/TLHighlightUtil.tsx | 138 ++++++++++-------- .../app/shapeutils/shared/TLExportColors.ts | 1 + .../src/lib/app/statechart/RootState.ts | 2 +- .../TLHighlightTool.ts | 5 +- packages/editor/src/lib/components/Shape.tsx | 4 +- packages/editor/src/lib/hooks/useDarkMode.ts | 9 +- packages/editor/src/lib/utils/debug-flags.ts | 3 +- packages/ui/api-report.md | 4 +- packages/ui/src/lib/components/DebugPanel.tsx | 1 + .../ui/src/lib/hooks/useToolbarSchema.tsx | 20 ++- packages/ui/src/lib/hooks/useTools.tsx | 2 +- packages/ui/src/lib/icon-types.ts | 2 - 23 files changed, 211 insertions(+), 107 deletions(-) delete mode 100644 assets/icons/icon/tool-highlighter.svg rename packages/editor/src/lib/app/statechart/{TLDrawTool => TLHighlightTool}/TLHighlightTool.ts (76%) diff --git a/assets/icons/icon/tool-highlight.svg b/assets/icons/icon/tool-highlight.svg index 6fd96fc1a..768600a3e 100644 --- a/assets/icons/icon/tool-highlight.svg +++ b/assets/icons/icon/tool-highlight.svg @@ -1,4 +1,4 @@ - - + + diff --git a/assets/icons/icon/tool-highlighter.svg b/assets/icons/icon/tool-highlighter.svg deleted file mode 100644 index 62588c091..000000000 --- a/assets/icons/icon/tool-highlighter.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/assets/imports.d.ts b/packages/assets/imports.d.ts index a36c6aa32..2ef54d37a 100644 --- a/packages/assets/imports.d.ts +++ b/packages/assets/imports.d.ts @@ -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 diff --git a/packages/assets/imports.js b/packages/assets/imports.js index e7f7fef4f..c6559d360 100644 --- a/packages/assets/imports.js +++ b/packages/assets/imports.js @@ -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), diff --git a/packages/assets/urls.d.ts b/packages/assets/urls.d.ts index 0faabc890..cf13263bf 100644 --- a/packages/assets/urls.d.ts +++ b/packages/assets/urls.d.ts @@ -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 diff --git a/packages/assets/urls.js b/packages/assets/urls.js index 7d6781a48..214fcef76 100644 --- a/packages/assets/urls.js +++ b/packages/assets/urls.js @@ -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 diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md index 8ceef7dcf..51fc7df58 100644 --- a/packages/editor/api-report.md +++ b/packages/editor/api-report.md @@ -634,6 +634,7 @@ export const debugFlags: { logMessages: DebugFlag; resetConnectionEveryPing: DebugFlag; debugCursors: DebugFlag; + forceSrgb: DebugFlag; }; // @internal (undocumented) diff --git a/packages/editor/editor.css b/packages/editor/editor.css index af6ff87d2..2cb69e3ce 100644 --- a/packages/editor/editor.css +++ b/packages/editor/editor.css @@ -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; diff --git a/packages/editor/src/lib/app/App.ts b/packages/editor/src/lib/app/App.ts index fb117db91..aa887f144 100644 --- a/packages/editor/src/lib/app/App.ts +++ b/packages/editor/src/lib/app/App.ts @@ -5617,7 +5617,9 @@ export class App extends EventEmitter { // 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 { containerStyle.getPropertyValue(`--palette-${color.id}-semi`), ]) ) as Record, + highlight: Object.fromEntries( + STYLES.color.map((color) => [ + color.id, + containerStyle.getPropertyValue(`--palette-${color.id}-highlight`), + ]) + ) as Record, text: containerStyle.getPropertyValue(`--color-text`), background: containerStyle.getPropertyValue(`--color-background`), solid: containerStyle.getPropertyValue(`--palette-solid`), diff --git a/packages/editor/src/lib/app/shapeutils/TLDrawUtil/TLDrawUtil.tsx b/packages/editor/src/lib/app/shapeutils/TLDrawUtil/TLDrawUtil.tsx index fe0a7284a..277ba675c 100644 --- a/packages/editor/src/lib/app/shapeutils/TLDrawUtil/TLDrawUtil.tsx +++ b/packages/editor/src/lib/app/shapeutils/TLDrawUtil/TLDrawUtil.tsx @@ -23,10 +23,10 @@ import { getDrawShapeStrokeDashArray, getFreehandOptions, getPointsFromSegments export class TLDrawUtil extends TLShapeUtil { 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 { 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 +} diff --git a/packages/editor/src/lib/app/shapeutils/TLDrawUtil/getPath.ts b/packages/editor/src/lib/app/shapeutils/TLDrawUtil/getPath.ts index f2ac228e4..537e408a7 100644 --- a/packages/editor/src/lib/app/shapeutils/TLDrawUtil/getPath.ts +++ b/packages/editor/src/lib/app/shapeutils/TLDrawUtil/getPath.ts @@ -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, diff --git a/packages/editor/src/lib/app/shapeutils/TLHighlightUtil/TLHighlightUtil.tsx b/packages/editor/src/lib/app/shapeutils/TLHighlightUtil/TLHighlightUtil.tsx index d627d22bd..48f416714 100644 --- a/packages/editor/src/lib/app/shapeutils/TLHighlightUtil/TLHighlightUtil.tsx +++ b/packages/editor/src/lib/app/shapeutils/TLHighlightUtil/TLHighlightUtil.tsx @@ -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 { 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 { } } - 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 { 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 { 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 { } render(shape: TLHighlightShape) { - return + return ( + + ) } renderBackground(shape: TLHighlightShape) { - return + return ( + + ) } 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 { } 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 + let strokePath + if (strokePoints.length < 2) { + strokePath = getIndicatorDot(allPointsFromSegments[0], sw) + } else { + strokePath = getSvgPathFromStrokePoints(strokePoints, false) + } + + return } - 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 = (shape, info) => { @@ -171,30 +186,32 @@ export class TLHighlightUtil extends TLShapeUtil { }, } } - - 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 ( - ) @@ -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 +} diff --git a/packages/editor/src/lib/app/shapeutils/shared/TLExportColors.ts b/packages/editor/src/lib/app/shapeutils/shared/TLExportColors.ts index e6137f17d..af613d59c 100644 --- a/packages/editor/src/lib/app/shapeutils/shared/TLExportColors.ts +++ b/packages/editor/src/lib/app/shapeutils/shared/TLExportColors.ts @@ -4,6 +4,7 @@ export type TLExportColors = { fill: Record pattern: Record semi: Record + highlight: Record solid: string text: string background: string diff --git a/packages/editor/src/lib/app/statechart/RootState.ts b/packages/editor/src/lib/app/statechart/RootState.ts index 45a825efd..01bfee06d 100644 --- a/packages/editor/src/lib/app/statechart/RootState.ts +++ b/packages/editor/src/lib/app/statechart/RootState.ts @@ -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' diff --git a/packages/editor/src/lib/app/statechart/TLDrawTool/TLHighlightTool.ts b/packages/editor/src/lib/app/statechart/TLHighlightTool/TLHighlightTool.ts similarity index 76% rename from packages/editor/src/lib/app/statechart/TLDrawTool/TLHighlightTool.ts rename to packages/editor/src/lib/app/statechart/TLHighlightTool/TLHighlightTool.ts index 53d6a53e3..320a9a38b 100644 --- a/packages/editor/src/lib/app/statechart/TLDrawTool/TLHighlightTool.ts +++ b/packages/editor/src/lib/app/statechart/TLHighlightTool/TLHighlightTool.ts @@ -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' diff --git a/packages/editor/src/lib/components/Shape.tsx b/packages/editor/src/lib/components/Shape.tsx index c383b59d8..bc300fe2a 100644 --- a/packages/editor/src/lib/components/Shape.tsx +++ b/packages/editor/src/lib/components/Shape.tsx @@ -116,9 +116,7 @@ export const Shape = track(function Shape({ data-shape-type={shape.type} draggable={false} > - {isCulled ? ( - - ) : ( + {!isCulled && ( : null} onError={(error) => diff --git a/packages/editor/src/lib/hooks/useDarkMode.ts b/packages/editor/src/lib/hooks/useDarkMode.ts index 878c51ec9..5d09ac721 100644 --- a/packages/editor/src/lib/hooks/useDarkMode.ts +++ b/packages/editor/src/lib/hooks/useDarkMode.ts @@ -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]) } diff --git a/packages/editor/src/lib/utils/debug-flags.ts b/packages/editor/src/lib/utils/debug-flags.ts index bf22bd424..02012654c 100644 --- a/packages/editor/src/lib/utils/debug-flags.ts +++ b/packages/editor/src/lib/utils/debug-flags.ts @@ -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> /** @internal */ @@ -51,6 +51,7 @@ export const debugFlags = { debugCursors: createDebugValue('debugCursors', { defaults: { all: false }, }), + forceSrgb: createDebugValue('forceSrgbColors', { defaults: { all: false } }), } declare global { diff --git a/packages/ui/api-report.md b/packages/ui/api-report.md index 65ddb1d6f..098d2bada 100644 --- a/packages/ui/api-report.md +++ b/packages/ui/api-report.md @@ -732,10 +732,10 @@ export type TLUiEventHandler 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; diff --git a/packages/ui/src/lib/components/DebugPanel.tsx b/packages/ui/src/lib/components/DebugPanel.tsx index a6134f6ba..5dea3a2ce 100644 --- a/packages/ui/src/lib/components/DebugPanel.tsx +++ b/packages/ui/src/lib/components/DebugPanel.tsx @@ -181,6 +181,7 @@ const DebugMenuContent = track(function DebugMenuContent({ app.setReadOnly(r)} /> + { diff --git a/packages/ui/src/lib/hooks/useToolbarSchema.tsx b/packages/ui/src/lib/hooks/useToolbarSchema.tsx index 32c43cee1..df7b2ed92 100644 --- a/packages/ui/src/lib/hooks/useToolbarSchema.tsx +++ b/packages/ui/src/lib/hooks/useToolbarSchema.tsx @@ -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(() => { - 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 }) diff --git a/packages/ui/src/lib/hooks/useTools.tsx b/packages/ui/src/lib/hooks/useTools.tsx index 095414591..6c249ebce 100644 --- a/packages/ui/src/lib/hooks/useTools.tsx +++ b/packages/ui/src/lib/hooks/useTools.tsx @@ -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' }) diff --git a/packages/ui/src/lib/icon-types.ts b/packages/ui/src/lib/icon-types.ts index 4a7374544..591145520 100644 --- a/packages/ui/src/lib/icon-types.ts +++ b/packages/ui/src/lib/icon-types.ts @@ -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',