dsh-image-gen 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +381 -355
- package/README.md +381 -355
- package/README.zh-CN.md +3 -3
- package/docs/assets/readme/canvas-edit.webp +0 -0
- package/docs/assets/readme/canvas-generate.webp +0 -0
- package/docs/assets/readme/hero-en.webp +0 -0
- package/docs/assets/readme/hero-zh.webp +0 -0
- package/docs/assets/readme/workflow-overview-en.webp +0 -0
- package/docs/assets/readme/workflow-overview.webp +0 -0
- package/lib/client.js +499 -354
- package/lib/client.js.map +1 -1
- package/lib/types/client/inspiration-api.d.ts +10 -0
- package/lib/types/client/inspiration-detail.d.ts +14 -0
- package/lib/types/client/tl/canvas-sync.d.ts +3 -3
- package/lib/types/client/tl/conversation-landings.d.ts +33 -0
- package/lib/types/client/tl/studio-tl-canvas.d.ts +2 -0
- package/lib/types/client/tl/tl-canvas-bridge.d.ts +12 -32
- package/lib/types/client/tldraw/generated/tldraw-assets.d.ts +12 -0
- package/lib/types/client/tldraw/generated/tldraw-css.d.ts +1 -0
- package/lib/types/client/tldraw/tldraw-style.d.ts +1 -0
- package/lib/types/client/tldraw/tldraw-workspace.d.ts +1 -0
- package/lib/types/inspiration/cache.d.ts +20 -0
- package/lib/types/inspiration/downloader.d.ts +27 -0
- package/lib/types/inspiration/routes.d.ts +13 -0
- package/lib/types/inspiration/sources.d.ts +33 -0
- package/lib/types/inspiration/types.d.ts +65 -0
- package/lib/types/inspiration/updater.d.ts +7 -0
- package/package.json +8 -10
- package/skills/install-dsh-image-gen/SKILL.md +22 -22
- package/lib/types/client/studio-chat-panel.d.ts +0 -23
- package/lib/types/client/tl/diagram-landing.d.ts +0 -75
- package/lib/types/client/tl/diagram-layout.d.ts +0 -76
- package/lib/types/client/tl/diagram-measure.d.ts +0 -40
- package/lib/types/diagram.d.ts +0 -42
- package/lib/types/studio-chat.d.ts +0 -48
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TLDRAW_STYLE = "/* THIS CSS FILE IS GENERATED! DO NOT EDIT. OR EDIT. I'M A COMMENT NOT A COP */ \n/* This file is created by the copy-css-files.mjs script in packages/tldraw. */\n/* It combines @tldraw/editor's editor.css and tldraw's ui.css */\n\n/* @tldraw/editor */\n\n.tl-container {\n\twidth: 100%;\n\theight: 100%;\n\tfont-size: 12px;\n\t/* Spacing */\n\t--tl-space-1: 2px;\n\t--tl-space-2: 4px;\n\t--tl-space-3: 8px;\n\t--tl-space-4: 12px;\n\t--tl-space-5: 16px;\n\t--tl-space-6: 20px;\n\t--tl-space-7: 28px;\n\t--tl-space-8: 32px;\n\t--tl-space-9: 64px;\n\t--tl-space-10: 72px;\n\t/* Radius */\n\t--tl-radius-0: 2px;\n\t--tl-radius-1: 4px;\n\t--tl-radius-2: 6px;\n\t--tl-radius-3: 9px;\n\t--tl-radius-4: 11px;\n\n\t/*\n\t * Canvas z-index\n\t *\n\t * .tl-canvas has `contain: strict` which creates its own stacking context.\n\t * background, grid, shapes, overlays, and blocker are all children of .tl-canvas,\n\t * so their z-indices are relative to the canvas, not the container.\n\t *\n\t * collaborators, watermark, and canvas-in-front are siblings of .tl-canvas\n\t * (outside it), so they compete with the UI layer (.tlui-layout at z-index 300).\n\t * canvas-in-front must be below the UI layer (300) to sit between\n\t * the canvas and the UI; collaborator cursors sit above canvas content\n\t * (and layers that anchor to it, like comment pins) but below the\n\t * in-front content and the UI panels. The watermark (a licensing element)\n\t * sits above that content so it can't be occluded, but still below the\n\t * in-front content and the UI.\n\t */\n\t--tl-layer-canvas-hidden: -999999;\n\t--tl-layer-canvas-background: 100;\n\t--tl-layer-canvas-grid: 150;\n\t--tl-layer-collaborators: 245;\n\t--tl-layer-watermark: 248;\n\t--tl-layer-canvas-in-front: 250;\n\t--tl-layer-canvas-shapes: 300;\n\t--tl-layer-canvas-overlays: 500;\n\t--tl-layer-canvas-blocker: 10000;\n\n\t/* Canvas overlays z-index */\n\t--tl-layer-overlays-collaborator-scribble: 10;\n\t--tl-layer-overlays-collaborator-brush: 20;\n\t--tl-layer-overlays-collaborator-shape-indicator: 30;\n\t--tl-layer-overlays-user-scribble: 40;\n\t--tl-layer-overlays-user-brush: 50;\n\t--tl-layer-overlays-user-snapline: 90;\n\t--tl-layer-overlays-selection-fg: 100;\n\t/* User handles need to be above selection edges / corners, matters for sticky note clone handles */\n\t--tl-layer-overlays-user-handles: 105;\n\t--tl-layer-overlays-user-indicator-hint: 110;\n\t--tl-layer-overlays-collaborator-cursor-hint: 120;\n\t--tl-layer-overlays-collaborator-cursor: 130;\n\n\t/* Text editor z-index */\n\t--tl-layer-text-container: 1;\n\t--tl-layer-text-content: 3;\n\t--tl-layer-text-editor: 4;\n\n\t/* Error fallback z-index */\n\t--tl-layer-error-overlay: 1;\n\t--tl-layer-error-canvas: 2;\n\t--tl-layer-error-canvas-after: 3;\n\t--tl-layer-error-content: 4;\n\n\t/* Misc */\n\t--tl-zoom: 1;\n\t--tl-tab-size: 2;\n\n\t/* Cursor SVGs */\n\t--tl-cursor-none: none;\n\t--tl-cursor-default:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m12 24.4219v-16.015l11.591 11.619h-6.781l-.411.124z' fill='white'/><path d='m21.0845 25.0962-3.605 1.535-4.682-11.089 3.686-1.553z' fill='white'/><path d='m19.751 24.4155-1.844.774-3.1-7.374 1.841-.775z' fill='black'/><path d='m13 10.814v11.188l2.969-2.866.428-.139h4.768z' fill='black'/></g></svg>\")\n\t\t\t12 8,\n\t\tdefault;\n\t--tl-cursor-pointer:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m13.3315 21.3799c-.284-.359-.629-1.093-1.243-1.984-.348-.504-1.211-1.453-1.468-1.935-.223-.426-.199-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.307.302.459.214.121-.071-.496-.187-1.343-.355-2.092-.128-.568-.159-.657-.281-1.093-.129-.464-.195-.789-.316-1.281-.084-.348-.235-1.059-.276-1.459-.057-.547-.087-1.439.264-1.849.275-.321.906-.418 1.297-.22.512.259.803 1.003.936 1.3.239.534.387 1.151.516 1.961.164 1.031.466 2.462.476 2.763.024-.369-.068-1.146-.004-1.5.058-.321.328-.694.666-.795.286-.085.621-.116.916-.055.313.064.643.288.766.499.362.624.369 1.899.384 1.831.086-.376.071-1.229.284-1.584.14-.234.497-.445.687-.479.294-.052.655-.068.964-.008.249.049.586.345.677.487.218.344.342 1.317.379 1.658.015.141.074-.392.293-.736.406-.639 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.012.828-.04 1.202-.031.4-.117 1.304-.242 1.742-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.191 1.813-.118.562-.079.566-.102.965-.023.398.121.922.121.922s-.802.104-1.234.035c-.391-.063-.875-.841-1-1.079-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.031-3.139.02 0 0 .185-1.011-.227-1.358-.305-.259-.83-.784-1.144-1.06z' fill='white'/><g stroke='black' stroke-linecap='round' stroke-width='.75'><path d='m13.3315 21.3799c-.284-.359-.629-1.093-1.243-1.984-.348-.504-1.211-1.453-1.468-1.935-.223-.426-.199-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.307.302.459.214.121-.071-.496-.187-1.343-.355-2.092-.128-.568-.159-.657-.281-1.093-.129-.464-.195-.789-.316-1.281-.084-.348-.235-1.059-.276-1.459-.057-.547-.087-1.439.264-1.849.275-.321.906-.418 1.297-.22.512.259.803 1.003.936 1.3.239.534.387 1.151.516 1.961.164 1.031.466 2.462.476 2.763.024-.369-.068-1.146-.004-1.5.058-.321.328-.694.666-.795.286-.085.621-.116.916-.055.313.064.643.288.766.499.362.624.369 1.899.384 1.831.086-.376.071-1.229.284-1.584.14-.234.497-.445.687-.479.294-.052.655-.068.964-.008.249.049.586.345.677.487.218.344.342 1.317.379 1.658.015.141.074-.392.293-.736.406-.639 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.012.828-.04 1.202-.031.4-.117 1.304-.242 1.742-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.191 1.813-.118.562-.079.566-.102.965-.023.398.121.922.121.922s-.802.104-1.234.035c-.391-.063-.875-.841-1-1.079-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.031-3.139.02 0 0 .185-1.011-.227-1.358-.305-.259-.83-.784-1.144-1.06z' stroke-linejoin='round'/><path d='m21.5664 21.7344v-3.459'/><path d='m19.5508 21.7461-.016-3.473'/><path d='m17.5547 18.3047.021 3.426'/></g></g></svg>\")\n\t\t\t14 10,\n\t\tpointer;\n\t--tl-cursor-cross:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m25 16h-6.01v-6h-2.98v6h-6.01v3h6.01v6h2.98v-6h6.01z' fill='white'/><path d='m23.9902 17.0103h-6v-6.01h-.98v6.01h-6v.98h6v6.01h.98v-6.01h6z' fill='%23231f1f'/></g></svg>\")\n\t\t\t16 16,\n\t\tcrosshair;\n\t--tl-cursor-comment:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g filter='url(%23shadow)'><path d='M5 19.5 L5 11 A8 8 0 0 1 13 3 A8 8 0 0 1 21 11 A8 8 0 0 1 13 19 Z' fill='white' stroke='black' stroke-width='1' stroke-linejoin='round'/></g></svg>\")\n\t\t\t5 19,\n\t\tcrosshair;\n\t--tl-cursor-move:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m19 14h1v1h-1zm1 6h-1v-1h1zm-5-5h-1v-1h1zm0 5h-1v-1h1zm2-10.987-7.985 7.988 5.222 5.221 2.763 2.763 7.984-7.985z' fill='white'/><g fill='black'><path d='m23.5664 16.9971-2.557-2.809v1.829h-4.009-4.001v-1.829l-2.571 2.809 2.572 2.808-.001-1.808h4.001 4.009l-.001 1.808z'/><path d='m17.9873 17h.013v-4.001l1.807.001-2.807-2.571-2.809 2.57h1.809v4.001h.008v4.002l-1.828-.001 2.807 2.577 2.805-2.576h-1.805z'/></g></g></svg>\")\n\t\t\t16 16,\n\t\tmove;\n\t--tl-cursor-grab:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m13.5557 17.5742c-.098-.375-.196-.847-.406-1.552-.167-.557-.342-.859-.47-1.233-.155-.455-.303-.721-.496-1.181-.139-.329-.364-1.048-.457-1.44-.119-.509.033-.924.244-1.206.253-.339.962-.49 1.357-.351.371.13.744.512.916.788.288.46.357.632.717 1.542.393.992.564 1.918.611 2.231l.085.452c-.001-.04-.043-1.122-.044-1.162-.035-1.029-.06-1.823-.038-2.939.002-.126.064-.587.084-.715.078-.5.305-.8.673-.979.412-.201.926-.215 1.401-.017.423.173.626.55.687 1.022.014.109.094.987.093 1.107-.013 1.025.006 1.641.015 2.174.004.231.003 1.625.017 1.469.061-.656.094-3.189.344-3.942.144-.433.405-.746.794-.929.431-.203 1.113-.07 1.404.243.285.305.446.692.482 1.153.032.405-.019.897-.02 1.245 0 .867-.021 1.324-.037 2.121-.001.038-.015.298.023.182.094-.28.188-.542.266-.745.049-.125.241-.614.359-.859.114-.234.211-.369.415-.688.2-.313.415-.448.668-.561.54-.235 1.109.112 1.301.591.086.215.009.713-.028 1.105-.061.647-.254 1.306-.352 1.648-.128.447-.274 1.235-.34 1.601-.072.394-.234 1.382-.359 1.82-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.192 1.812-.117.563-.078.567-.101.965-.024.399.121.923.121.923s-.802.104-1.234.034c-.391-.062-.875-.841-1-1.078-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.03-3.139.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.284-.36-.629-1.093-1.243-1.985-.348-.504-1.027-1.085-1.284-1.579-.223-.425-.331-.954-.19-1.325.225-.594.675-.897 1.362-.832.519.05.848.206 1.238.537.225.19.573.534.75.748.163.195.203.276.377.509.23.307.302.459.214.121' fill='white'/><g stroke='black' stroke-linecap='round' stroke-width='.75'><path d='m13.5557 17.5742c-.098-.375-.196-.847-.406-1.552-.167-.557-.342-.859-.47-1.233-.155-.455-.303-.721-.496-1.181-.139-.329-.364-1.048-.457-1.44-.119-.509.033-.924.244-1.206.253-.339.962-.49 1.357-.351.371.13.744.512.916.788.288.46.357.632.717 1.542.393.992.564 1.918.611 2.231l.085.452c-.001-.04-.043-1.122-.044-1.162-.035-1.029-.06-1.823-.038-2.939.002-.126.064-.587.084-.715.078-.5.305-.8.673-.979.412-.201.926-.215 1.401-.017.423.173.626.55.687 1.022.014.109.094.987.093 1.107-.013 1.025.006 1.641.015 2.174.004.231.003 1.625.017 1.469.061-.656.094-3.189.344-3.942.144-.433.405-.746.794-.929.431-.203 1.113-.07 1.404.243.285.305.446.692.482 1.153.032.405-.019.897-.02 1.245 0 .867-.021 1.324-.037 2.121-.001.038-.015.298.023.182.094-.28.188-.542.266-.745.049-.125.241-.614.359-.859.114-.234.211-.369.415-.688.2-.313.415-.448.668-.561.54-.235 1.109.112 1.301.591.086.215.009.713-.028 1.105-.061.647-.254 1.306-.352 1.648-.128.447-.274 1.235-.34 1.601-.072.394-.234 1.382-.359 1.82-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.192 1.812-.117.563-.078.567-.101.965-.024.399.121.923.121.923s-.802.104-1.234.034c-.391-.062-.875-.841-1-1.078-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.03-3.139.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.284-.36-.629-1.093-1.243-1.985-.348-.504-1.027-1.085-1.284-1.579-.223-.425-.331-.954-.19-1.325.225-.594.675-.897 1.362-.832.519.05.848.206 1.238.537.225.19.573.534.75.748.163.195.203.276.377.509.23.307.302.459.214.121' stroke-linejoin='round'/><path d='m20.5664 21.7344v-3.459'/><path d='m18.5508 21.7461-.016-3.473'/><path d='m16.5547 18.3047.021 3.426'/></g></g></svg>\")\n\t\t\t16 16,\n\t\tgrab;\n\t--tl-cursor-grabbing:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m13.5732 12.0361c.48-.178 1.427-.069 1.677.473.213.462.396 1.241.406 1.075.024-.369-.024-1.167.137-1.584.117-.304.347-.59.686-.691.285-.086.62-.116.916-.055.313.064.642.287.765.499.362.623.368 1.899.385 1.831.064-.272.07-1.229.283-1.584.141-.235.497-.445.687-.479.294-.052.656-.068.964-.008.249.049.586.344.677.487.219.344.342 1.316.379 1.658.016.141.074-.393.293-.736.406-.639 1.844-.763 1.898.639.026.654.02.624.02 1.064 0 .516-.012.828-.04 1.202-.03.399-.116 1.304-.241 1.742-.086.301-.371.978-.653 1.384 0 0-1.074 1.25-1.191 1.812-.117.563-.078.567-.102.965-.023.399.121.923.121.923s-.801.104-1.234.034c-.391-.062-.875-.84-1-1.078-.172-.328-.539-.265-.682-.023-.224.383-.709 1.07-1.05 1.113-.669.084-2.055.03-3.14.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.283-.36-1.002-.929-1.243-1.985-.213-.936-.192-1.395.037-1.77.232-.381.67-.589.854-.625.208-.042.692-.039.875.062.223.123.313.159.488.391.23.307.312.456.213.121-.076-.262-.322-.595-.434-.97-.109-.361-.401-.943-.38-1.526.008-.221.103-.771.832-1.042' fill='white'/><g stroke='black' stroke-width='.75'><path d='m13.5732 12.0361c.48-.178 1.427-.069 1.677.473.213.462.396 1.241.406 1.075.024-.369-.024-1.167.137-1.584.117-.304.347-.59.686-.691.285-.086.62-.116.916-.055.313.064.642.287.765.499.362.623.368 1.899.385 1.831.064-.272.07-1.229.283-1.584.141-.235.497-.445.687-.479.294-.052.656-.068.964-.008.249.049.586.344.677.487.219.344.342 1.316.379 1.658.016.141.074-.393.293-.736.406-.639 1.844-.763 1.898.639.026.654.02.624.02 1.064 0 .516-.012.828-.04 1.202-.03.399-.116 1.304-.241 1.742-.086.301-.371.978-.653 1.384 0 0-1.074 1.25-1.191 1.812-.117.563-.078.567-.102.965-.023.399.121.923.121.923s-.801.104-1.234.034c-.391-.062-.875-.84-1-1.078-.172-.328-.539-.265-.682-.023-.224.383-.709 1.07-1.05 1.113-.669.084-2.055.03-3.14.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.283-.36-1.002-.929-1.243-1.985-.213-.936-.192-1.395.037-1.77.232-.381.67-.589.854-.625.208-.042.692-.039.875.062.223.123.313.159.488.391.23.307.312.456.213.121-.076-.262-.322-.595-.434-.97-.109-.361-.401-.943-.38-1.526.008-.221.103-.771.832-1.042z' stroke-linejoin='round'/><path d='m20.5664 19.7344v-3.459' stroke-linecap='round'/><path d='m18.5508 19.7461-.016-3.473' stroke-linecap='round'/><path d='m16.5547 16.3047.021 3.426' stroke-linecap='round'/></g></g></svg>\")\n\t\t\t16 16,\n\t\tgrabbing;\n\t--tl-cursor-text:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path fill='white' d='M7.94 0a5.25 5.25 0 0 0-3.47 1.17A5.27 5.27 0 0 0 1 0H0v3h1c1.41 0 1.85.7 2 1v3.94H2v3h1v3c-.13.3-.57 1-2 1H0v3h1a5.27 5.27 0 0 0 3.47-1.17c.98.8 2.21 1.21 3.47 1.17h1v-3h-1c-1.41 0-1.85-.7-2-1v-3H7v-3H6V4c.13-.3.57-1 2-1h1V0H7.94z'/><path fill='black' d='M7.94 2V1a4 4 0 0 0-3.47 1.64A4 4 0 0 0 1 1v1c1.3-.17 2.56.6 3 1.84v5.1H3v1h1v4.16c-.45 1.24-1.7 2-3 1.84v1a4.05 4.05 0 0 0 3.47-1.63 4.05 4.05 0 0 0 3.47 1.63v-1A2.82 2.82 0 0 1 5 14.1V9.93h1v-1H5V3.85A2.81 2.81 0 0 1 7.94 2z'/></g></svg>\")\n\t\t\t4 10,\n\t\ttext;\n\t--tl-cursor-zoom-in:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5' fill='white'/><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5z' stroke='black'/><g fill='black'><path d='m18 14h-2v-2h-2v2h-2v1.98h2v2.02h2v-2.02h2z'/><path d='m23.5859 25 1.414-1.414-5.449-5.449-1.414 1.414z'/></g></g></svg>\")\n\t\t\t16 16,\n\t\tzoom-in;\n\t--tl-cursor-zoom-out:\n\t\turl(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5' fill='white'/><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5z' stroke='black'/><g fill='black'><path d='m18 16h-5.98v-1.98h5.98z'/><path d='m23.5859 25 1.414-1.414-5.449-5.449-1.414 1.414z'/></g></g></svg>\")\n\t\t\t16 16,\n\t\tzoom-out;\n\n\t/* These cursor values get programmatically overridden */\n\t/* They're just here to help your editor autocomplete */\n\t--tl-cursor: var(--tl-cursor-default);\n\t--tl-cursor-resize-edge: ew-resize;\n\t--tl-cursor-resize-corner: nesw-resize;\n\t--tl-cursor-ew-resize: ew-resize;\n\t--tl-cursor-ns-resize: ns-resize;\n\t--tl-cursor-nesw-resize: nesw-resize;\n\t--tl-cursor-nwse-resize: nwse-resize;\n\t--tl-cursor-rotate: pointer;\n\t--tl-cursor-nwse-rotate: pointer;\n\t--tl-cursor-nesw-rotate: pointer;\n\t--tl-cursor-senw-rotate: pointer;\n\t--tl-cursor-swne-rotate: pointer;\n\t--tl-scale: calc(1 / var(--tl-zoom));\n\t/* fonts */\n\t--tl-font-draw: 'tldraw_draw', sans-serif;\n\t--tl-font-sans: 'tldraw_sans', sans-serif;\n\t--tl-font-serif: 'tldraw_serif', serif;\n\t--tl-font-mono: 'tldraw_mono', monospace;\n\t/* text outline */\n\t--tl-text-outline-a: calc(min(0.5, 1 / var(--tl-zoom)) * 2px);\n\t--tl-text-outline-b: calc(min(0.5, 1 / var(--tl-zoom)) * -2px);\n\t--tl-text-outline-reference:\n\t\t0 var(--tl-text-outline-b) 0 var(--tl-color-background),\n\t\t0 var(--tl-text-outline-a) 0 var(--tl-color-background),\n\t\tvar(--tl-text-outline-b) var(--tl-text-outline-b) 0 var(--tl-color-background),\n\t\tvar(--tl-text-outline-a) var(--tl-text-outline-b) 0 var(--tl-color-background),\n\t\tvar(--tl-text-outline-a) var(--tl-text-outline-a) 0 var(--tl-color-background),\n\t\tvar(--tl-text-outline-b) var(--tl-text-outline-a) 0 var(--tl-color-background);\n\t--tl-text-outline: var(--tl-text-outline-reference);\n\t/* own properties */\n\tposition: relative;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\toverflow: clip;\n\tcolor: var(--tl-color-text);\n}\n\n.tl-theme__light {\n\t/* Canvas */\n\t--tl-color-selection-fill: hsl(210, 100%, 56%, 24%);\n\t--tl-color-selection-stroke: hsl(214, 84%, 56%);\n\t--tl-color-background: hsl(210, 20%, 98%);\n\t/* if you ever update --tl-color-background, update the hardcoded values in theme-init.js and globals.css\n\tthey're there to make sure the background color matches the user's preference before the actual app loads*/\n\t--tl-color-grid: hsl(0, 0%, 43%);\n\t/* UI */\n\t--tl-color-low: hsl(204, 16%, 94%);\n\t--tl-color-low-border: hsl(204, 16%, 92%);\n\t--tl-color-muted-none: hsl(0, 0%, 0%, 0%);\n\t--tl-color-muted-0: hsl(0, 0%, 0%, 2%);\n\t--tl-color-muted-1: hsl(0, 0%, 0%, 10%);\n\t--tl-color-muted-2: hsl(0, 0%, 0%, 4.3%);\n\t--tl-color-hint: hsl(0, 0%, 0%, 5.5%);\n\t--tl-color-overlay: hsl(0, 0%, 0%, 20%);\n\t--tl-color-divider: hsl(0, 0%, 91%);\n\t--tl-color-panel: hsl(0, 0%, 99%);\n\t--tl-color-panel-contrast: hsl(0, 0%, 100%);\n\t--tl-color-panel-overlay: hsl(0, 0%, 100%, 82%);\n\t--tl-color-panel-transparent: hsla(0, 0%, 99%, 0%);\n\t--tl-color-selected: hsl(214, 84%, 56%);\n\t--tl-color-selected-contrast: hsl(0, 0%, 100%);\n\t--tl-color-focus: hsl(219, 65%, 50%);\n\t--tl-color-tooltip: hsla(200, 14%, 4%, 1);\n\t/* Text */\n\t--tl-color-text: hsl(0, 0%, 0%);\n\t--tl-color-text-0: hsl(0, 0%, 11%);\n\t--tl-color-text-1: hsl(0, 0%, 18%);\n\t--tl-color-text-3: hsl(204, 4%, 45%);\n\t--tl-color-text-disabled: hsl(204, 4%, 55%);\n\t--tl-color-text-shadow: hsl(0, 0%, 100%);\n\t--tl-color-text-highlight: hsl(52, 100%, 50%);\n\t--tl-color-text-highlight-p3: color(display-p3 0.972 0.8205 0.05);\n\t/* Named */\n\t--tl-color-primary: hsl(214, 84%, 56%);\n\t--tl-color-success: hsl(123, 46%, 34%);\n\t--tl-color-info: hsl(201, 98%, 41%);\n\t--tl-color-warning: hsl(27, 98%, 47%);\n\t--tl-color-danger: hsl(0, 90%, 43%);\n\n\t/* Shadows */\n\t--tl-shadow-1: 0px 1px 2px hsl(0, 0%, 0%, 25%), 0px 1px 3px hsl(0, 0%, 0%, 9%);\n\t--tl-shadow-2:\n\t\t0px 0px 2px hsl(0, 0%, 0%, 16%), 0px 2px 3px hsl(0, 0%, 0%, 24%),\n\t\t0px 2px 6px hsl(0, 0%, 0%, 0.1), inset 0px 0px 0px 1px var(--tl-color-panel-contrast);\n\t--tl-shadow-3:\n\t\t0px 1px 2px hsl(0, 0%, 0%, 28%), 0px 2px 6px hsl(0, 0%, 0%, 14%),\n\t\tinset 0px 0px 0px 1px var(--tl-color-panel-contrast);\n\t--tl-shadow-4:\n\t\t0px 0px 3px hsl(0, 0%, 0%, 19%), 0px 5px 4px hsl(0, 0%, 0%, 16%),\n\t\t0px 2px 16px hsl(0, 0%, 0%, 6%), inset 0px 0px 0px 1px var(--tl-color-panel-contrast);\n}\n\n.tl-theme__dark {\n\t/* Canvas */\n\t--tl-color-selection-fill: hsl(209, 100%, 57%, 20%);\n\t--tl-color-selection-stroke: hsl(214, 84%, 56%);\n\t--tl-color-background: hsl(240, 5%, 6.5%);\n\t/* if you ever update --tl-color-background, update the hardcoded values in theme-init.js and globals.css\n\tthey're there to make sure the background color matches the user's preference before the actual app loads*/\n\t--tl-color-grid: hsl(0, 0%, 40%);\n\t/* UI */\n\t--tl-color-low: hsl(260, 4.5%, 10.5%);\n\t--tl-color-low-border: hsl(207, 10%, 10%);\n\t--tl-color-muted-none: hsl(0, 0%, 100%, 0%);\n\t--tl-color-muted-0: hsl(0, 0%, 100%, 2%);\n\t--tl-color-muted-1: hsl(0, 0%, 100%, 10%);\n\t--tl-color-muted-2: hsl(0, 0%, 100%, 5%);\n\t--tl-color-hint: hsl(0, 0%, 100%, 7%);\n\t--tl-color-overlay: hsl(0, 0%, 0%, 50%);\n\t--tl-color-divider: hsl(240, 9%, 22%);\n\t--tl-color-panel: hsl(235, 6.8%, 13.5%);\n\t--tl-color-panel-contrast: hsl(245, 12%, 23%);\n\t--tl-color-panel-overlay: hsl(210, 10%, 24%, 82%);\n\t--tl-color-panel-transparent: hsla(235, 6.8%, 13.5%, 0%);\n\t--tl-color-selected: hsl(217, 89%, 61%);\n\t--tl-color-selected-contrast: hsl(0, 0%, 100%);\n\t--tl-color-focus: hsl(217, 76%, 80%);\n\t--tl-color-tooltip: hsla(0, 0%, 100%, 1);\n\t/* Text */\n\t--tl-color-text: hsl(210, 17%, 98%);\n\t--tl-color-text-0: hsl(0, 9%, 94%);\n\t--tl-color-text-1: hsl(0, 0%, 85%);\n\t--tl-color-text-3: hsl(204, 4%, 75%);\n\t--tl-color-text-disabled: hsl(204, 4%, 50%);\n\t--tl-color-text-shadow: hsl(210, 13%, 18%);\n\t--tl-color-text-highlight: hsl(52, 100%, 41%);\n\t--tl-color-text-highlight-p3: color(display-p3 0.8078 0.6225 0.0312);\n\t/* Named */\n\t--tl-color-primary: hsl(214, 84%, 56%);\n\t--tl-color-success: hsl(123, 38%, 57%);\n\t--tl-color-info: hsl(199, 92%, 56%);\n\t--tl-color-warning: hsl(36, 100%, 57%);\n\t--tl-color-danger: hsl(0, 82%, 66%);\n\n\t/* Shadows */\n\t--tl-shadow-1:\n\t\t0px 1px 2px hsl(0, 0%, 0%, 16.1%), 0px 1px 3px hsl(0, 0%, 0%, 22%),\n\t\tinset 0px 0px 0px 1px var(--tl-color-panel-contrast);\n\t--tl-shadow-2:\n\t\t0px 1px 3px hsl(0, 0%, 0%, 66.6%), 0px 2px 6px hsl(0, 0%, 0%, 33%),\n\t\tinset 0px 0px 0px 1px var(--tl-color-panel-contrast);\n\t--tl-shadow-3:\n\t\t0px 1px 3px hsl(0, 0%, 0%, 50%), 0px 2px 12px hsl(0, 0%, 0%, 50%),\n\t\tinset 0px 0px 0px 1px var(--tl-color-panel-contrast);\n}\n\n.tl-counter-scaled {\n\ttransform: scale(var(--tl-scale));\n\ttransform-origin: top left;\n\twidth: calc(100% * var(--tl-zoom));\n\theight: calc(100% * var(--tl-zoom));\n}\n\n.tl-container,\n.tl-container * {\n\t-webkit-touch-callout: none;\n\t-webkit-tap-highlight-color: transparent;\n\tscrollbar-highlight-color: transparent;\n\t-webkit-user-select: none;\n\tuser-select: none;\n\tbox-sizing: border-box;\n\toutline: none;\n}\n\n.tl-container a {\n\t-webkit-touch-callout: initial;\n}\n\n.tl-container__focused {\n\toutline: 1px solid var(--tl-color-low);\n}\n\n/* The host for <EditorPortal>, last among the container's children. It's a position in the DOM\n rather than a box: `display: contents` keeps what it holds positioned against the container. */\n.tl-portal-host {\n\tdisplay: contents;\n}\n\ninput,\n*[contenteditable],\n*[contenteditable] * {\n\tuser-select: text;\n}\n\n/* --------------------- Canvas --------------------- */\n\n.tl-canvas {\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tcolor: var(--tl-color-text);\n\tcursor: var(--tl-cursor);\n\toverflow: clip;\n\tcontent-visibility: auto;\n\ttouch-action: none;\n\tcontain: strict;\n}\n\n.tl-canvas__in-front {\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n\tz-index: var(--tl-layer-canvas-in-front);\n}\n\n.tl-shapes {\n\tposition: relative;\n\tz-index: var(--tl-layer-canvas-shapes);\n}\n\n.tl-svg-context {\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\twidth: 100%;\n\theight: 100%;\n\tpointer-events: none;\n}\n\n/* ------------------- Background ------------------- */\n\n.tl-background__wrapper {\n\tz-index: var(--tl-layer-canvas-background);\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n}\n\n.tl-background {\n\tbackground-color: var(--tl-color-background);\n\twidth: 100%;\n\theight: 100%;\n}\n\n/* --------------------- Grid Layer --------------------- */\n\n.tl-grid {\n\tposition: absolute;\n\tinset: 0px;\n\twidth: 100%;\n\theight: 100%;\n\ttouch-action: none;\n\tpointer-events: none;\n\tz-index: var(--tl-layer-canvas-grid);\n\tcontain: strict;\n}\n\n.tl-grid-dot {\n\tfill: var(--tl-color-grid);\n}\n\n/* --------------------- Layers --------------------- */\n\n.tl-html-layer {\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\twidth: 1px;\n\theight: 1px;\n\tcontain: layout style size;\n}\n\n/* The collaborator cursor layer: a sibling of .tl-canvas, above all canvas content, below the\n in-front layer (250) and the UI panels (300). Display-only \u2014 pointer events pass through. */\n.tl-collaborators {\n\tposition: absolute;\n\tinset: 0px;\n\tz-index: var(--tl-layer-collaborators);\n\tpointer-events: none;\n\toverflow: clip;\n\tcontain: layout style size;\n}\n\n.tl-collaborators__item {\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\toverflow: visible;\n\tpointer-events: none;\n\ttransform-origin: top left;\n}\n\n.tl-cursor {\n\toverflow: visible;\n}\n\n/* --------------- Overlay Stack --------------- */\n\n/* -------------- Selection foreground -------------- */\n\n.tl-selection__bg {\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\ttransform-origin: top left;\n\tbackground-color: transparent;\n\tpointer-events: all;\n}\n\n.tl-transparent {\n\tfill: transparent;\n\tstroke: transparent;\n}\n\n.tl-hidden {\n\topacity: 0;\n\tpointer-events: none;\n}\n\n/* -------------- Nametag / cursor chat ------------- */\n\n.tl-nametag {\n\tposition: absolute;\n\ttop: 16px;\n\tleft: 13px;\n\twidth: fit-content;\n\theight: fit-content;\n\tmax-width: 120px;\n\tpadding: 3px 6px;\n\twhite-space: nowrap;\n\tposition: absolute;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tfont-size: 12px;\n\tborder-radius: var(--tl-radius-2);\n\tcolor: var(--tl-color-selected-contrast);\n}\n\n.tl-nametag-title {\n\tposition: absolute;\n\ttop: -2px;\n\tleft: 13px;\n\twidth: fit-content;\n\theight: fit-content;\n\tpadding: 0px 6px;\n\tmax-width: 120px;\n\twhite-space: nowrap;\n\tposition: absolute;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tfont-size: 12px;\n\ttext-shadow: var(--tl-text-outline);\n\tcolor: var(--tl-color-selected-contrast);\n}\n\n.tl-nametag-chat {\n\tposition: absolute;\n\ttop: 16px;\n\tleft: 13px;\n\twidth: fit-content;\n\theight: fit-content;\n\tcolor: var(--tl-color-selected-contrast);\n\twhite-space: nowrap;\n\tposition: absolute;\n\tpadding: 3px 6px;\n\tfont-size: 12px;\n\topacity: 1;\n\tborder-radius: var(--tl-radius-2);\n}\n\n.tl-cursor-chat {\n\tposition: absolute;\n\tcolor: var(--tl-color-selected-contrast);\n\twhite-space: nowrap;\n\tpadding: 3px 6px;\n\tfont-size: 12px;\n\tpointer-events: none;\n\tz-index: var(--tl-layer-cursor);\n\tmargin-top: 16px;\n\tmargin-left: 13px;\n\topacity: 1;\n\tborder: none;\n\tuser-select: text;\n\tborder-radius: var(--tl-radius-2);\n}\n\n.tl-cursor-chat .tl-cursor-chat__bubble {\n\tpadding-right: 12px;\n}\n\n.tl-cursor-chat::selection {\n\tbackground: var(--tl-color-selected);\n\tcolor: var(--tl-color-selected-contrast);\n\ttext-shadow: none;\n}\n\n.tl-cursor-chat::placeholder {\n\tcolor: var(--tl-color-selected-contrast);\n\topacity: 0.7;\n}\n\n/* ---------------------- Text ---------------------- */\n\n.tl-text-shape-label {\n\tposition: relative;\n\tfont-weight: normal;\n\tmin-width: 1px;\n\tpadding: 0px;\n\tmargin: 0px;\n\tborder: none;\n\twidth: fit-content;\n\theight: fit-content;\n\tfont-variant: normal;\n\tfont-style: normal;\n\tpointer-events: all;\n\twhite-space: pre-wrap;\n\toverflow-wrap: break-word;\n}\n\n.tl-plain-text-wrapper[data-isediting='true'] .tl-text-content {\n\topacity: 0;\n}\n\n.tl-rich-text-wrapper[data-isediting='true'] .tl-text-content {\n\tdisplay: none;\n}\n\n.tl-text {\n\t/* remove overflow from textarea on windows */\n\tmargin: 0px;\n\tpadding: 0px;\n\n\tappearance: auto;\n\tbackground: none;\n\tborder-image: none;\n\tborder: 0px;\n\tcaret-color: var(--tl-color-text);\n\tcolor: inherit;\n\tcolumn-count: initial !important;\n\tdisplay: inline-block;\n\tfont-family: inherit;\n\tfont-feature-settings: normal;\n\tfont-kerning: auto;\n\tfont-optical-sizing: auto;\n\tfont-size: inherit;\n\tfont-stretch: 100%;\n\tfont-style: inherit;\n\tfont-variant: inherit;\n\tfont-variation-settings: normal;\n\tfont-weight: inherit;\n\tletter-spacing: inherit;\n\tline-height: inherit;\n\toutline: none;\n\toverflow-wrap: break-word;\n\ttext-align: inherit;\n\ttext-indent: 0px;\n\ttext-rendering: auto;\n\ttext-shadow: inherit;\n\ttext-transform: none;\n\twhite-space: pre-wrap;\n\tline-break: normal;\n\tword-spacing: 0px;\n\tword-wrap: break-word;\n\twriting-mode: horizontal-tb !important;\n}\n\n.tl-text-measure {\n\tz-index: var(--tl-layer-canvas-hidden);\n\topacity: 0;\n\tvisibility: hidden;\n\n\t/* pointer-events: all; */\n\t/* opacity: 1; */\n\t/* z-index: 99999; */\n\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\twidth: max-content;\n\tbox-sizing: border-box;\n\tpointer-events: none;\n\twhite-space: pre-wrap;\n\tword-wrap: break-word;\n\toverflow-wrap: break-word;\n\tresize: none;\n\tborder: none;\n\tuser-select: none;\n\tcontain: layout style paint;\n\t/* N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\") is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs. */\n\tunicode-bidi: plaintext;\n\t-webkit-user-select: none;\n}\n\n.tl-text-input,\n.tl-text-content {\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tmin-width: 1px;\n\tmin-height: 1px;\n\toutline: none;\n}\n\n.tl-text-content__wrapper {\n\tposition: relative;\n\twidth: fit-content;\n\theight: fit-content;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpointer-events: none;\n\tmin-height: auto;\n}\n\n.tl-text-content {\n\toverflow: visible;\n\tpointer-events: none;\n}\n\n.tl-text-input {\n\tresize: none;\n\tuser-select: all;\n\t-webkit-user-select: text;\n\tcursor: var(--tl-cursor-text);\n}\n\n.tl-text-input:not(.tl-rich-text) {\n\t/*\n\t * Note: this `overflow: hidden` is key for scrollbars to not show up\n\t * plaintext/<textarea> editors.\n\t */\n\toverflow: hidden;\n}\n\n.tl-text-input::selection {\n\tbackground: var(--tl-color-selected);\n\tcolor: var(--tl-color-selected-contrast);\n\ttext-shadow: none;\n}\n\n/* Text label */\n\n.tl-text-label {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tcolor: var(--tl-color-text);\n\tline-height: inherit;\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n}\n\n.tl-text-label[data-hastext='false'][data-isediting='false'] > .tl-text-label__inner {\n\twidth: 40px;\n\theight: 40px;\n}\n\n.tl-text-label[data-hastext='true'][data-isediting='false'] .tl-text-content {\n\tpointer-events: all;\n}\n\n.tl-text-label__inner > .tl-text-input.tl-rich-text {\n\tdisplay: none;\n\tposition: static;\n}\n\n.tl-text-wrapper[data-isediting='false'] .tl-text-input {\n\topacity: 0;\n\tcursor: var(--tl-cursor-default);\n}\n\n.tl-rich-text[data-is-ready-for-editing='true'],\n.tl-text-wrapper[data-is-ready-for-editing='true'] .tl-text-input {\n\tcursor: var(--tl-cursor-text);\n}\n\n.tl-text-label[data-textwrap='true'] > .tl-text-label__inner {\n\tmax-width: 100%;\n}\n\n.tl-text-label[data-isediting='true'] {\n\tbackground-color: transparent;\n\tmin-height: auto;\n}\n\n.tl-text-wrapper .tl-text-content {\n\tpointer-events: all;\n\tz-index: var(--tl-layer-text-content);\n}\n\n.tl-text-label__inner > .tl-text-content {\n\tposition: relative;\n\ttop: 0px;\n\tleft: 0px;\n\tpadding: inherit;\n\theight: fit-content;\n\twidth: fit-content;\n\tborder-radius: var(--tl-radius-1);\n\tmax-width: 100%;\n}\n\n.tl-text-label__inner > .tl-text-input {\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tpadding: inherit;\n}\n\n.tl-text-wrapper[data-isselected='true'] .tl-text-input {\n\tz-index: var(--tl-layer-text-editor);\n\tpointer-events: all;\n}\n\n/* This part of the rule helps preserve the occlusion rules for the shapes so we\n * don't click on shapes that are behind other shapes.\n * One extra nuance is we don't use this behavior for:\n * - arrows which have weird geometry and just gets in the way.\n * - draw/line shapes, because it feels restrictive to have them be 'in the way' of clicking on a textfield\n * - shapes that are not filled\n */\n.tl-canvas:is([data-iseditinganything='true'], [data-isselectinganything='true'])\n\t.tl-shape:not(\n\t\t[data-shape-type='arrow'],\n\t\t[data-shape-type='draw'],\n\t\t[data-shape-type='line'],\n\t\t[data-shape-type='highlight'],\n\t\t[data-shape-is-filled='false']\n\t) {\n\tpointer-events: all;\n}\n\n.tl-rich-text .ProseMirror {\n\tword-wrap: break-word;\n\toverflow-wrap: break-word;\n\twhite-space: pre-wrap;\n\n\t/**\n\t * Note: ProseMirror disables this in https://github.com/ProseMirror/prosemirror-view/commit/6b3b2205e2f3029cb8e8e86c55a190a22491df31\n\t * However, that was from 8 years ago and the browser caret issue\n\t * it mentions seems to be fixed. So, we're re-enabling it.\n\t * We'll tell ProseMirror maybe to get rid of this on their end.\n\t *\n\t */\n\t-webkit-font-variant-ligatures: inherit;\n\tfont-variant-ligatures: inherit;\n\tfont-feature-settings: inherit;\n\n\t/**\n\t * N.B. This following CSS Rule comes standard with the tiptap editor.\n\t * Combined with the above rule that it supersedes, it allows for\n\t * the auto-linking to work in text. Say, when typing example.com\n\t * this helps it automatically turn that bit of text into a link.\n\t *\n\t * However, specifically, the break-spaces features seems to cause\n\t * rendering differences when going in-and-out of edit mode. For example,\n\t * the statically rendered text 'the rain in spain falls mainly on the plain'\n\t * in a note shape will render differently when going in-and-out of edit mode.\n\t *\n\t * So, this is commented out to help make both the autolinking work (which now\n\t * relies on the white-space: pre-wrap to work and to make the static/edit-mode\n\t * rendering consistent.\n\t * In the future, we might consider just making the static rendering just use\n\t * white-space: break-spaces to make it consistent with the edit mode if need be.\n\t *\n\t * Also, the amount of ink I've spilt in my career writing comments explaining\n\t * white-space in contenteditable is incredible.\n\t *\n\t/* white-space: break-spaces; */\n}\n\n.tl-rich-text {\n\ttab-size: var(--tl-tab-size, 2);\n}\n\n.tl-rich-text p {\n\tmargin: 0;\n\t/* Depending on the extensions, <p> tags can be empty, without a <br />. */\n\tmin-height: 1lh;\n}\n\n.tl-rich-text ul,\n.tl-rich-text ol {\n\ttext-align: left;\n\tmargin: 0;\n\tpadding-left: 1.625ch;\n\t/* Some resets, like Tailwind, nix the list styling. */\n\tlist-style: revert;\n}\n\n.tl-rich-text ol:has(> li:nth-child(10)) {\n\tpadding-left: 2.625ch;\n}\n\n.tl-rich-text ol:has(> li:nth-child(100)) {\n\tpadding-left: 3.625ch;\n}\n\n.tl-rich-text h1,\n.tl-rich-text h2,\n.tl-rich-text h3,\n.tl-rich-text h4,\n.tl-rich-text h5,\n.tl-rich-text h6 {\n\tmargin-top: 5px;\n\tmargin-bottom: 10px;\n\t/**\n\t * Headings render at a larger font-size than the body text (via the browser's\n\t * default h1\u2013h6 sizing), so they can't inherit the container's resolved\n\t * whole-pixel line-height \u2014 that value is derived from the base font size and\n\t * would cram the taller lines together. Instead, scale line-height with each\n\t * heading's own font-size using tldraw's unitless line-height multiplier, which\n\t * the rich-text render/measure paths expose here. The 1.35 fallback mirrors the\n\t * default theme line-height (see defaultThemes.ts).\n\t */\n\tline-height: var(--tl-rich-text-heading-line-height, 1.35);\n}\n\n.tl-rich-text a {\n\tcolor: var(--tl-color-primary);\n\ttext-decoration: underline;\n}\n\n.tl-rich-text[data-is-select-tool-active='false'] a {\n\tcursor: inherit;\n}\n\n.tl-rich-text code {\n\tfont-family: var(--tl-font-mono);\n}\n\n.tl-rich-text mark {\n\tbackground-color: #fddd00;\n\tcolor: currentColor;\n\tborder-radius: 2px;\n}\n\n.tl-theme__light .tl-rich-text mark {\n\ttext-shadow: none;\n}\n\n.tl-theme__dark .tl-rich-text mark {\n\tbackground-color: var(--tl-color-text-highlight);\n\tcolor: currentColor;\n\ttext-shadow: none;\n}\n\n@supports (color: color(display-p3 1 1 1)) {\n\t@media (color-gamut: p3) {\n\t\t.tl-container:not(.tl-theme__force-sRGB) .tl-rich-text mark {\n\t\t\tbackground-color: var(--tl-color-text-highlight-p3);\n\t\t}\n\t}\n}\n\n.tl-text-wrapper[data-isediting='true'] .tl-rich-text {\n\tdisplay: block;\n}\n\n.tl-text__outline {\n\ttext-shadow: var(--tl-text-outline);\n}\n\n.tl-text__no-outline {\n\ttext-shadow: none;\n}\n\n/* --------------------- Loading -------------------- */\n\n.tl-loading {\n\tbackground-color: var(--tl-color-background);\n\tcolor: var(--tl-color-text-1);\n\theight: 100%;\n\twidth: 100%;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: center;\n\talign-items: center;\n\tgap: var(--tl-space-2);\n\tfont-size: 14px;\n\tfont-weight: 500;\n\topacity: 0;\n\tanimation: tl-fade-in 0.2s ease-in-out forwards;\n\tanimation-delay: 0.2s;\n\tposition: absolute;\n\tinset: 0px;\n\tz-index: var(--tl-layer-canvas-blocker);\n}\n\n@keyframes tl-fade-in {\n\t0% {\n\t\topacity: 0;\n\t}\n\t100% {\n\t\topacity: 1;\n\t}\n}\n\n.tl-spinner {\n\tanimation: tl-spin 1s linear infinite;\n}\n\n@keyframes tl-spin {\n\t0% {\n\t\ttransform: rotate(0deg);\n\t}\n\t100% {\n\t\ttransform: rotate(360deg);\n\t}\n}\n\n/* ---------------- Hyperlink Button ---------------- */\n\n.tl-hyperlink-button {\n\tbackground: none;\n\tmargin: 0px;\n\tposition: absolute;\n\ttop: 0px;\n\tright: 0px;\n\theight: 44px;\n\twidth: 44px;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-size: 12px;\n\tfont-weight: 400;\n\tcolor: var(--tl-color-text-1);\n\tpadding: 13px;\n\tcursor: var(--tl-cursor-pointer);\n\tborder: none;\n\toutline: none;\n\tpointer-events: all;\n\tz-index: 1;\n}\n\n.tl-hyperlink-button::after {\n\tcontent: '';\n\tz-index: -1;\n\tposition: absolute;\n\tright: 6px;\n\tbottom: 6px;\n\tdisplay: block;\n\twidth: calc(100% - 12px);\n\theight: calc(100% - 12px);\n\tborder-radius: var(--tl-radius-1);\n\tbackground-color: var(--tl-color-background);\n\tpointer-events: none;\n}\n\n.tl-hyperlink-button:focus-visible {\n\tcolor: var(--tl-color-selected);\n}\n\n.tl-hyperlink__icon {\n\twidth: 15px;\n\theight: 15px;\n\tbackground-color: currentColor;\n\tpointer-events: none;\n}\n\n.tl-hyperlink-button__hidden {\n\tdisplay: none;\n}\n\n/* ----------------- Shape indicator ---------------- */\n\n.tl-shape-indicator {\n\ttransform-origin: top left;\n\tfill: none;\n\tstroke-width: calc(1.5px * var(--tl-scale));\n\tcontain: size layout;\n}\n\n.tl-canvas-overlays {\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n\tz-index: var(--tl-layer-canvas-overlays);\n}\n\n/* ---------------------- Shape --------------------- */\n\n.tl-shape {\n\tposition: absolute;\n\tpointer-events: none;\n\toverflow: visible;\n\ttransform-origin: top left;\n\tcontain: size layout;\n}\n\n/* ---------------- Shape Containers ---------------- */\n\n.tl-svg-container {\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tpointer-events: none;\n\tstroke-linecap: round;\n\tstroke-linejoin: round;\n\ttransform-origin: top left;\n\toverflow: visible;\n}\n\n.tl-html-container {\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tpointer-events: none;\n\tstroke-linecap: round;\n\tstroke-linejoin: round;\n\t/* content-visibility: auto; */\n\ttransform-origin: top left;\n\tcolor: var(--tl-color-text-1);\n}\n\n.tl-content-slot {\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tpointer-events: none;\n\ttransform-origin: top left;\n}\n\n/* -------------------- Group shape ------------------ */\n\n.tl-group {\n\tstroke: var(--tl-color-text);\n\tstroke-width: calc(1px * var(--tl-scale));\n\topacity: 0.5;\n}\n\n/* --------------------- Arrow shape -------------------- */\n\n.tl-shape[data-shape-type='arrow'] .tl-text-label {\n\tposition: absolute;\n\ttop: -1px;\n\tleft: -1px;\n\twidth: 2px;\n\theight: 2px;\n\tpadding: 0px;\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\ttext-align: center;\n\tcolor: var(--tl-color-text);\n}\n\n.tl-shape[data-shape-type='arrow'] .tl-text-label__inner {\n\tborder-radius: var(--tl-radius-1);\n\tbox-sizing: content-box;\n\theight: max-content;\n\twidth: max-content;\n}\n\n.tl-shape[data-shape-type='arrow'] .tl-text {\n\theight: max-content;\n}\n\n/* ------------------- Bookmark shape ------------------- */\n\n.tl-bookmark__container {\n\twidth: 100%;\n\theight: 100%;\n\tposition: relative;\n\tborder: 1px solid var(--tl-color-panel-contrast);\n\tbackground-color: var(--tl-color-panel);\n\tborder-radius: var(--tl-radius-2);\n\tdisplay: flex;\n\tflex-direction: column;\n\toverflow: hidden;\n}\n\n.tl-bookmark__container--safariExport {\n\tborder: 1px solid var(--tl-color-divider);\n}\n\n.tl-bookmark__image_container {\n\tflex: 1 1 100%;\n\toverflow: hidden;\n\tborder-top-left-radius: var(--tl-radius-1);\n\tborder-top-right-radius: var(--tl-radius-1);\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\talign-items: flex-start;\n\tbox-shadow: inset 0px 0px 0px 1px var(--tl-color-divider);\n}\n\n.tl-bookmark__image_container > .tl-hyperlink-button::after {\n\tbackground-color: var(--tl-color-panel);\n}\n\n.tl-bookmark__placeholder {\n\twidth: 100%;\n\theight: 100%;\n\tbackground-color: var(--tl-color-muted-2);\n\tborder-bottom: 1px solid var(--tl-color-muted-2);\n}\n\n.tl-bookmark__image {\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center;\n\tborder-bottom: 1px solid var(--tl-color-muted-2);\n}\n\n.tl-bookmark__copy_container {\n\tbackground-color: var(--tl-color-muted-0);\n\tpadding: var(--tl-space-4);\n\tpointer-events: all;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: space-between;\n\tflex: 1;\n}\n\n.tl-bookmark__copy_container:has(.tl-bookmark__link:only-child) {\n\tjustify-content: center;\n}\n\n.tl-bookmark__heading,\n.tl-bookmark__description,\n.tl-bookmark__link {\n\tmargin: 0px;\n\twidth: 100%;\n\tfont-family: inherit;\n}\n\n.tl-bookmark__heading {\n\tfont-size: 16px;\n\tline-height: 1.6;\n\tfont-weight: bold;\n\tpadding-bottom: var(--tl-space-2);\n\toverflow: hidden;\n\tmax-height: calc((16px * 1.6) * 2);\n\t-webkit-box-orient: vertical;\n\t-webkit-line-clamp: 2;\n\tline-clamp: 2;\n\ttext-overflow: ellipsis;\n\tdisplay: -webkit-box;\n}\n\n.tl-bookmark__description {\n\tfont-size: 12px;\n\tline-height: 1.5;\n\toverflow: hidden;\n\tmax-height: calc((12px * 1.5) * 3);\n\t-webkit-box-orient: vertical;\n\t-webkit-line-clamp: 3;\n\tline-clamp: 3;\n\ttext-overflow: ellipsis;\n\tdisplay: -webkit-box;\n\tcolor: var(--tl-color-text-1);\n\tmargin: var(--tl-space-2) 0px;\n}\n\n.tl-bookmark__heading + .tl-bookmark__link,\n.tl-bookmark__description + .tl-bookmark__link {\n\tmargin-top: var(--tl-space-3);\n}\n.tl-bookmark__link {\n\tfont-size: 12px;\n\tpointer-events: all;\n\tdisplay: flex;\n\tcolor: var(--tl-color-text-1);\n\talign-items: center;\n\tcursor: var(--tl-cursor-pointer);\n\twidth: fit-content;\n\tmax-width: 100%;\n\ttext-decoration: none;\n}\n\n.tl-bookmark__link > span {\n\tflex-shrink: 0px;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.tl-bookmark__link > .tl-hyperlink__icon {\n\tmargin-right: 8px;\n\tflex-shrink: 0;\n}\n\n.tl-bookmark__link > .tl-bookmark__favicon {\n\tmargin-right: 8px;\n\twidth: 16px;\n\theight: 16px;\n\tflex-shrink: 0;\n}\n\n/* -------------- Image and video shape ------------- */\n\n.tl-image,\n.tl-video {\n\tobject-fit: cover;\n\tbackground-size: cover;\n\twidth: 100%;\n\theight: 100%;\n}\n\n.tl-video-container,\n.tl-image-container,\n.tl-embed-container {\n\twidth: 100%;\n\theight: 100%;\n\tpointer-events: all;\n\t/* background-color: var(--tl-color-background); */\n\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n}\n\n.tl-image-container {\n\tposition: relative;\n}\n\n.tl-image {\n\tposition: absolute;\n\tinset: 0;\n}\n\n.tl-video.tl-video-is-fullscreen {\n\tobject-fit: contain;\n\tbackground-size: contain;\n}\n\n/* -------------------- Note shape ------------------- */\n\n.tl-note__container {\n\tposition: relative;\n\twidth: 100%;\n\theight: 100%;\n\tpointer-events: all;\n\topacity: 1;\n\tz-index: var(--tl-layer-text-container);\n\tborder-radius: 1px;\n}\n\n.tl-note__container > .tl-text-label {\n\tcolor: currentColor;\n}\n\n.tl-note__attribution {\n\tposition: absolute;\n\tbottom: calc(4px * var(--note-attribution-scale, 1));\n\tright: calc(8px * var(--note-attribution-scale, 1));\n\tfont-family: var(--tl-font-sans);\n\tpointer-events: auto;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tmax-width: 60%;\n\tz-index: 1;\n}\n\n/* ------------------- Frame shape ------------------- */\n\n.tl-frame__body {\n\tstroke-width: calc(1px * var(--tl-scale));\n}\n\n.tl-frame__creating {\n\tstroke: var(--tl-color-selected);\n\tfill: none;\n}\n\n.tl-frame-heading {\n\t--tl-frame-padding-x: 6px;\n\t--tl-frame-height: 24px;\n\t--tl-frame-minimum-width: 32px;\n\t--tl-frame-offset-width: 16px;\n\tdisplay: flex;\n\talign-items: center;\n\tposition: absolute;\n\ttransform-origin: 0% 100%;\n\toverflow: hidden;\n\tmax-width: 100%;\n\tmin-width: var(--tl-frame-minimum-width);\n\theight: auto;\n\tfont-size: 12px;\n\tpadding-bottom: 4px;\n\tpointer-events: all;\n}\n\n.tl-frame-heading-hit-area {\n\tpointer-events: all;\n\t/* scale from bottom left corner so we can pin it to the top left corner of the frame */\n\ttransform-origin: 0% 100%;\n\tdisplay: flex;\n\theight: var(--tl-frame-height);\n\twidth: 100%;\n\talign-items: center;\n\tborder-radius: var(--tl-radius-1);\n}\n\n.tl-frame-label {\n\tpointer-events: all;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tpadding: 0px var(--tl-frame-padding-x);\n\tborder-radius: var(--tl-radius-1);\n\tposition: relative;\n\tfont-size: inherit;\n\twhite-space: pre;\n}\n\n.tl-frame-label__editing {\n\tcolor: transparent;\n\twhite-space: pre;\n\twidth: auto;\n\tmin-width: var(--tl-frame-minimum-width);\n\theight: 100%;\n\toverflow: visible;\n\tbackground-color: var(--tl-color-panel);\n\tborder-color: var(--tl-color-selected);\n\tbox-shadow: inset 0px 0px 0px 1.5px var(--tl-color-selected);\n}\n\n.tl-frame-name-input {\n\tposition: absolute;\n\tborder: none;\n\tbackground: none;\n\toutline: none;\n\tpadding: 0px var(--tl-frame-padding-x);\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tfont-size: inherit;\n\tfont-family: inherit;\n\tfont-weight: inherit;\n\twidth: 100%;\n\tcolor: var(--tl-color-text-1);\n\tborder-radius: var(--tl-radius-1);\n\tuser-select: all;\n\t-webkit-user-select: text;\n\twhite-space: pre;\n\tcursor: var(--tl-cursor-text);\n}\n\n/* ------------------- Embed Shape ------------------ */\n\n.tl-embed {\n\tborder: none;\n\tborder-radius: var(--tl-radius-2);\n}\n\n/* -------------- Shape error boundary -------------- */\n\n.tl-shape-error-boundary {\n\twidth: 100%;\n\theight: 100%;\n\tbackground-color: var(--tl-color-muted-1);\n\tborder-width: calc(1px * var(--tl-scale));\n\tborder-color: var(--tl-color-muted-1);\n\tborder-style: solid;\n\tborder-radius: calc(var(--tl-radius-1) * var(--tl-scale));\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\ttext-align: left;\n\tposition: relative;\n\tpointer-events: all;\n\toverflow: hidden;\n\tpadding: var(--tl-space-2);\n}\n\n.tl-shape-error-boundary::before {\n\ttransform: scale(var(--tl-scale));\n\tcontent: 'Error';\n\tfont-size: 12px;\n\tfont-family: inherit;\n\tcolor: var(--tl-color-text-0);\n}\n\n/* ----------------- Error boundary ----------------- */\n\n.tl-error-boundary {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: var(--tl-space-4);\n\tbackground-color: var(--tl-color-background);\n\tcolor: var(--tl-color-text-1);\n\tposition: absolute;\n}\n\n.tl-error-boundary__overlay {\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tz-index: var(--tl-layer-error-overlay);\n\tbackground-color: var(--tl-color-overlay);\n}\n\n.tl-error-boundary__content * {\n\tuser-select: all;\n\t-webkit-user-select: text;\n\tpointer-events: all;\n}\n\n.tl-error-boundary__canvas {\n\tpointer-events: none;\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tz-index: var(--tl-layer-error-canvas);\n}\n\n/* some browsers seem to have some weird interactions between stacking contexts\nand pointer-events. this ::after pseudo element covers the canvas and prevents\nit from receiving any pointer events or affecting the cursor. */\n.tl-error-boundary__canvas::after {\n\tcontent: ' ';\n\tdisplay: block;\n\tposition: absolute;\n\tinset: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tz-index: var(--tl-layer-error-canvas-after);\n\tpointer-events: all;\n}\n\n.tl-error-boundary__content {\n\twidth: fit-content;\n\theight: fit-content;\n\tmax-width: 100%;\n\twidth: 400px;\n\tmax-height: 100%;\n\tbackground-color: var(--tl-color-panel);\n\tpadding: 16px;\n\tborder-radius: 16px;\n\tbox-shadow: var(--tl-shadow-2);\n\tfont-size: 14px;\n\tfont-weight: 400;\n\tdisplay: flex;\n\tflex-direction: column;\n\toverflow: auto;\n\tz-index: var(--tl-layer-error-content);\n\tgap: 12px;\n}\n\n.tl-error-boundary__content__expanded {\n\twidth: 600px;\n}\n\n.tl-error-boundary__content h2 {\n\tfont-size: 16px;\n\tmargin: 0px;\n\tfont-weight: 500;\n}\n\n.tl-error-boundary__content h4 {\n\tborder: 1px solid var(--tl-color-low-border);\n\tmargin: -6px 0 0 0;\n\tpadding: var(--tl-space-5);\n\tborder-radius: var(--tl-radius-2);\n\tfont-weight: normal;\n}\n\n.tl-error-boundary__content p {\n\tline-height: 1.5;\n\tmargin: 0px;\n}\n\n.tl-error-boundary__content pre {\n\tbackground-color: var(--tl-color-muted-2);\n\tmargin-top: 0;\n\tpadding: var(--tl-space-5);\n\tborder-radius: var(--tl-radius-2);\n\toverflow: auto;\n\tfont-size: 12px;\n\tmax-height: 320px;\n}\n\n.tl-error-boundary__content button {\n\tbackground: none;\n\tborder: none;\n\tfont-family: inherit;\n\tfont-size: 14px;\n\tfont-weight: 500;\n\tpadding: var(--tl-space-4);\n\tborder-radius: var(--tl-radius-3);\n\tcursor: var(--tl-cursor-pointer);\n\tcolor: inherit;\n\tbackground-color: transparent;\n}\n\n.tl-error-boundary__content a {\n\tcolor: var(--tl-color-selected);\n\tfont-weight: 500;\n\ttext-decoration: none;\n}\n\n.tl-error-boundary__content__error {\n\tposition: relative;\n\tmargin: -6px 0 0 0;\n}\n\n.tl-error-boundary__content__error button {\n\tposition: absolute;\n\ttop: var(--tl-space-2);\n\tright: var(--tl-space-2);\n\tfont-size: 12px;\n\tpadding: var(--tl-space-2) var(--tl-space-3);\n\tbackground-color: var(--tl-color-panel);\n\tborder-radius: var(--tl-radius-1);\n}\n\n.tl-error-boundary__content__actions {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: var(--tl-space-4);\n\tmargin: 0px;\n\tmargin-left: -4px;\n}\n.tl-error-boundary__content__actions__group {\n\tdisplay: flex;\n\tgap: var(--tl-space-4);\n}\n.tl-error-boundary__content .tl-error-boundary__reset {\n\tcolor: var(--tl-color-danger);\n}\n.tl-error-boundary__content .tl-error-boundary__refresh {\n\tbackground-color: var(--tl-color-primary);\n\tcolor: var(--tl-color-selected-contrast);\n}\n.tl-container__focused:not(.tl-container__no-focus-ring)\n\t.tlui-button.tl-error-boundary__refresh:focus-visible {\n\tborder-radius: 8px;\n\toutline-offset: 0;\n}\n\n/* ---------------- Hit test blocker ---------------- */\n\n.tl-hit-test-blocker {\n\tposition: absolute;\n\tz-index: var(--tl-layer-canvas-blocker);\n\tinset: 0px;\n\twidth: 100%;\n\theight: 100%;\n\tpointer-events: all;\n}\n\n.tl-hit-test-blocker__hidden {\n\tdisplay: none;\n}\n\n/* --------------------- Hovers --------------------- */\n\n@media (hover: hover) {\n\t.tl-handle__create:hover {\n\t\topacity: 1;\n\t}\n\n\t.tl-handle__bg:hover {\n\t\tcursor: var(--tl-cursor-grab);\n\t\tfill: var(--tl-color-selection-fill);\n\t}\n\n\t.tl-bookmark__link:hover {\n\t\tcolor: var(--tl-color-selected);\n\t}\n\n\t.tl-hyperlink-button:hover {\n\t\tcolor: var(--tl-color-selected);\n\t}\n\n\t.tl-error-boundary__content button:hover {\n\t\tbackground-color: var(--tl-color-low);\n\t}\n\t.tl-error-boundary__content a:hover {\n\t\tcolor: var(--tl-color-text-1);\n\t}\n\t.tl-error-boundary__content .tl-error-boundary__refresh:hover {\n\t\tbackground-color: var(--tl-color-primary);\n\t\topacity: 0.9;\n\t}\n\n\t/* These three rules help preserve clicking into specific points in text areas *while*\n * already in edit mode when jumping from shape to shape. */\n\t.tl-canvas[data-iseditinganything='true'] .tl-text-wrapper:hover .tl-text-input {\n\t\tz-index: var(--tl-layer-text-editor);\n\t\tpointer-events: all;\n\t}\n}\n\n/* @tldraw/ui */\n\n.tl-container {\n\t/*\n\t * UI z-index layers\n\t *\n\t * menu-click-capture and panels (.tlui-layout) participate in the\n\t * container-level stacking context alongside the canvas layers:\n\t * watermark (200) < canvas-in-front (250) < menu-click-capture (250) < panels (300)\n\t *\n\t * menus, toasts, header-footer, etc. are children of .tlui-layout\n\t * (which creates its own stacking context at z-index 300), so their\n\t * z-indices are relative to the layout, not the container.\n\t *\n\t * cursor is rendered inside canvas-in-front (stacking context at\n\t * z-index 250), so their z-indices are relative to that context.\n\t */\n\t--tl-layer-above: 1;\n\t--tl-layer-focused-input: 10;\n\t--tl-layer-menu-click-capture: 250;\n\t--tl-layer-panels: 300;\n\t--tl-layer-menus: 400;\n\t--tl-layer-toasts: 650;\n\t--tl-layer-cursor: 700;\n\t--tl-layer-header-footer: 999;\n}\n\n/* Button */\n\n.tlui-button {\n\tposition: relative;\n\theight: 40px;\n\tmin-width: 40px;\n\tpadding: 0px 12px;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbackground-color: transparent;\n\tborder: transparent;\n\tcolor: currentColor;\n\tcursor: pointer;\n\tpointer-events: all;\n\tfont-weight: inherit;\n\tfont-family: inherit;\n\tline-height: inherit;\n\ttext-rendering: optimizeLegibility;\n\tfont-size: 12px;\n\tgap: 0px;\n\tcolor: var(--tl-color-text-1);\n\tz-index: 0;\n}\n\n.tlui-button:disabled,\n.tlui-button[data-disabled] {\n\tcolor: var(--tl-color-text-disabled);\n\ttext-shadow: none;\n\tcursor: default;\n}\n\n.tlui-button:disabled .tlui-kbd,\n.tlui-button[data-disabled] .tlui-kbd {\n\tcolor: var(--tl-color-text-disabled);\n}\n\n.tlui-button > * {\n\tposition: relative;\n\tz-index: var(--tl-layer-above);\n}\n\n.tlui-button__label {\n\tfont-size: 12px;\n\tflex-grow: 2;\n\ttext-align: start;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/*\n * We only want to show the focus ring if:\n * - the container is focused\n * - we're not using the mouse to interact (which is the .tl-container__no-focus-ring)\n */\n.tl-container__focused:not(.tl-container__no-focus-ring) .tlui-button:focus-visible {\n\tborder-radius: 10px;\n\toutline: 2px solid var(--tl-color-focus);\n\toutline-offset: -5px;\n}\n.tl-container__focused:not(.tl-container__no-focus-ring) .tlui-button__tool:focus-visible {\n\tborder-radius: 12px;\n}\n.tlui-slider__container:has(.tlui-slider__thumb:focus-visible) {\n\tborder-radius: 10px;\n\toutline: 2px solid var(--tl-color-focus);\n\toutline-offset: -5px;\n}\n\n.tlui-button::after {\n\tdisplay: block;\n\tcontent: '';\n\tposition: absolute;\n\tinset: 4px;\n\tborder-radius: var(--tl-radius-2);\n\tbackground: var(--tl-color-muted-2);\n\topacity: 0;\n}\n\n.tlui-button__menu[data-highlighted]::after {\n\topacity: 1;\n}\n\n.tlui-button[data-isactive='true']::after,\n.tlui-button[data-isactive='true']:not(:disabled, [data-disabled], :focus-visible):active:after {\n\tbackground: var(--tl-color-hint);\n\topacity: 1;\n}\n\n.tlui-button[aria-expanded='true'][data-direction='left'] {\n\tbackground: linear-gradient(270deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\topacity: 1;\n}\n\n@media (hover: hover) {\n\t.tlui-button[aria-expanded='true'][data-direction='left']:not(:hover)::after {\n\t\tbackground: linear-gradient(270deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\t\topacity: 1;\n\t}\n\n\t.tlui-button:not(:disabled, [data-disabled]):hover {\n\t\tz-index: 1;\n\t}\n\n\t.tlui-button:not(:disabled, [data-disabled]):hover::after {\n\t\topacity: 1;\n\t}\n}\n\n.tlui-button__icon + .tlui-button__label {\n\tmargin-inline-start: var(--tl-space-2);\n}\n\n/* Low button */\n\n.tlui-button__low {\n\tborder-radius: var(--tl-radius-3);\n\tbackground-color: var(--tl-color-low);\n}\n\n.tlui-button__low::after {\n\tbackground-color: var(--tl-color-muted-2);\n\topacity: 0;\n}\n\n@media (hover: hover) {\n\t.tlui-button__low:hover::after {\n\t\topacity: 1;\n\t}\n}\n\n/* Primary / danger buttons */\n\n.tlui-button__primary {\n\tcolor: var(--tl-color-primary);\n}\n\n.tlui-button__danger {\n\tcolor: var(--tl-color-danger);\n\ttext-shadow: none;\n}\n\n@media (hover: hover) {\n\t.tlui-button__primary:not(:disabled, [data-disabled], :focus-visible):hover {\n\t\tcolor: var(--tl-color-primary);\n\t}\n\n\t.tlui-button__danger:not(:disabled, [data-disabled], :focus-visible):hover {\n\t\tcolor: var(--tl-color-danger);\n\t\ttext-shadow: none;\n\t}\n}\n\n/* Menu button */\n\n.tlui-button__menu {\n\theight: 40px;\n\tmin-height: 40px;\n\twidth: 100%;\n\tgap: 8px;\n\tmargin-top: -4px;\n}\n\n.tlui-button__menu::after {\n\tinset: 4px;\n\tborder-radius: var(--tl-radius-2);\n}\n\n.tlui-button__menu > .tlui-icon + .tlui-button__label {\n\tmargin-inline-start: 0px;\n}\n\n.tlui-button__menu:nth-child(1) {\n\tmargin-top: 0px;\n}\n\n.tlui-button__menu:nth-last-child(1) {\n\tmargin-bottom: 0px;\n}\n\n/* Menu checkbox button */\n\n.tlui-button__checkbox {\n\tpadding-left: 8px;\n}\n\n.tlui-button__checkbox__indicator {\n\twidth: 15px;\n\theight: 15px;\n}\n\n/* Tool lock button */\n\n.tlui-main-toolbar__lock-button {\n\tposition: absolute;\n\tpointer-events: all;\n\theight: 40px;\n\twidth: 40px;\n\tborder-radius: var(--tl-radius-2);\n}\n\n.tlui-main-toolbar--horizontal .tlui-main-toolbar__lock-button {\n\ttop: 4px;\n\tright: 0px;\n\tmin-width: 0px;\n}\n\n.tlui-main-toolbar--vertical .tlui-main-toolbar__lock-button {\n\tbottom: 0px;\n\tmin-height: 0px;\n}\n\n.tlui-main-toolbar__lock-button::after {\n\ttop: 4px;\n\tleft: 8px;\n\tinset: 4px;\n}\n\n/* Tool button */\n\n.tlui-button__tool {\n\tposition: relative;\n\theight: 48px;\n\twidth: 48px;\n}\n\n.tlui-button__tool::after {\n\tinset: 4px;\n\tborder-radius: 8px;\n}\n\n.tlui-button__tool[aria-pressed='true'] {\n\tcolor: var(--tl-color-selected-contrast);\n}\n\n.tlui-button__tool[aria-pressed='true']:not(:disabled, [data-disabled], :focus-visible):active {\n\tcolor: var(--tl-color-selected-contrast);\n}\n\n.tlui-button__tool[aria-pressed='true']:not(:disabled, [data-disabled])::after {\n\tbackground: var(--tl-color-selected);\n\topacity: 1;\n}\n\n.tlui-layout__mobile .tlui-main-toolbar--horizontal .tlui-button__tool {\n\theight: 48px;\n\twidth: 43px;\n}\n\n.tlui-layout__mobile .tlui-main-toolbar--horizontal .tlui-button__tool > .tlui-icon {\n\theight: 16px;\n\twidth: 16px;\n}\n\n/* Row layout */\n\n.tlui-row {\n\tdisplay: flex;\n\tflex-direction: row;\n\tpadding: 0 2px;\n}\n.tlui-row > * {\n\tmargin-left: -2px;\n\tmargin-right: -2px;\n}\n\n/* Column layout */\n\n.tlui-column {\n\tdisplay: flex;\n\tflex-direction: column;\n\tpadding: 2px 0;\n}\n.tlui-column > * {\n\tmargin-top: -2px;\n\tmargin-bottom: -2px;\n}\n\n/* Grid layout */\n\n.tlui-grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(4, 1fr);\n\tgrid-auto-flow: row;\n\toverflow: hidden;\n\tpadding: 2px;\n}\n.tlui-grid > * {\n\tmargin: -2px;\n}\n\n/* Zoom button */\n\n.tlui-zoom-menu__button {\n\twidth: 60px;\n\tmin-width: 60px;\n\ttext-align: center;\n}\n\n.tlui-zoom-menu__button[data-state='open']::after {\n\tbackground: linear-gradient(0deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\topacity: 1;\n}\n\n/* --------------------- Layout --------------------- */\n\n.tlui-layout {\n\tposition: relative;\n\tdisplay: grid;\n\tgrid-template-columns: 1fr;\n\tgrid-template-rows: minmax(0px, 1fr) auto;\n\tgrid-auto-rows: auto;\n\theight: 100%;\n\tmax-height: 100%;\n\toverflow: clip;\n\tpointer-events: none;\n\tuser-select: none;\n\tcontain: strict;\n\tz-index: var(--tl-layer-panels);\n\ttransform: translate3d(0, 0, 0);\n\t--tl-sab: env(safe-area-inset-bottom);\n\tfont-weight: 500;\n\tline-height: 1.6;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n\tfont-smooth: antialiased;\n\ttext-rendering: optimizeLegibility;\n}\n\n.tlui-layout__top {\n\tgrid-column: 1;\n\tgrid-row: 1;\n\tdisplay: flex;\n\tmin-width: 0px;\n\tjustify-content: space-between;\n}\n\n.tlui-layout__top__left {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-start;\n\tjustify-content: flex-start;\n\twidth: 100%;\n\theight: 100%;\n\tflex: 0 1 0;\n}\n\n.tlui-layout__top__right {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-end;\n\tjustify-content: flex-start;\n\theight: 100%;\n\tflex: 0 0 auto;\n\tmin-width: 0px;\n}\n\n.tlui-scrollable,\n.tlui-scrollable * {\n\tpointer-events: all;\n\ttouch-action: auto;\n\toverscroll-behavior: none;\n}\n\n/* ----------------- Helper Buttons ---------------- */\n\n.tlui-helper-buttons {\n\tposition: relative;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: flex-start;\n\talign-items: flex-start;\n\twidth: min-content;\n\tgap: var(--tl-space-3);\n\tmargin: var(--tl-space-2) var(--tl-space-3);\n\twhite-space: nowrap;\n\tpointer-events: none;\n\tz-index: var(--tl-layer-panels);\n}\n\n/* ---------------------- Icon ---------------------- */\n\n.tlui-icon {\n\tflex-shrink: 0;\n\twidth: 18px;\n\theight: 18px;\n\tbackground-color: currentColor;\n}\n\n.tlui-icon__placeholder {\n\tflex-shrink: 0;\n\twidth: 18px;\n\theight: 18px;\n\tbackground-color: transparent;\n}\n\n.tlui-icon__small {\n\twidth: 15px;\n\theight: 15px;\n}\n\n.tlui-icon__tiny {\n\twidth: 12px;\n\theight: 12px;\n}\n\n/* --------------------- Slider --------------------- */\n\n.tlui-slider__container {\n\twidth: 100%;\n\tpadding: 0px var(--tl-space-4);\n}\n\n.tlui-slider {\n\tposition: relative;\n\tdisplay: flex;\n\talign-items: center;\n\tuser-select: none;\n\ttouch-action: none;\n\twidth: 100%;\n}\n\n.tlui-slider__track {\n\tposition: relative;\n\tflex-grow: 1;\n\theight: 44px;\n\tcursor: pointer;\n}\n\n.tlui-slider__track::after {\n\tdisplay: block;\n\tposition: absolute;\n\ttop: calc(50% - 2px);\n\tcontent: '';\n\theight: 3px;\n\twidth: 100%;\n\tbackground-color: var(--tl-color-muted-1);\n\tborder-radius: 14px;\n}\n\n.tlui-slider__range {\n\tposition: absolute;\n\ttop: calc(50% - 2px);\n\tleft: 0px;\n\theight: 3px;\n\tbackground-color: var(--tl-color-selected);\n\tborder-radius: 14px;\n}\n\n.tlui-slider__thumb {\n\tall: unset;\n\tcursor: grab;\n\tdisplay: block;\n\twidth: 18px;\n\theight: 18px;\n\tposition: relative;\n\ttop: -1px;\n\tbackground-color: var(--tl-color-panel);\n\tborder-radius: 999px;\n\tbox-shadow: inset 0px 0px 0px 2px var(--tl-color-text-1);\n}\n\n.tlui-slider__thumb:active {\n\tcursor: grabbing;\n\tbox-shadow:\n\t\tinset 0px 0px 0px 2px var(--tl-color-text-1),\n\t\tvar(--tl-shadow-1);\n}\n\n/* ---------------------- Input --------------------- */\n\n.tlui-input {\n\tbackground: none;\n\tmargin: 0px;\n\tposition: relative;\n\tz-index: var(--tl-layer-above);\n\theight: 40px;\n\tmax-height: 40px;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-family: inherit;\n\tfont-size: 12px;\n\tfont-weight: inherit;\n\tcolor: var(--tl-color-text-1);\n\tpadding: var(--tl-space-4);\n\tpadding-left: 0px;\n\tborder: none;\n\toutline: none;\n\ttext-overflow: ellipsis;\n\twidth: 100%;\n\tuser-select: all;\n\ttext-rendering: optimizeLegibility;\n\t-webkit-user-select: auto !important;\n}\n\n.tlui-input::placeholder {\n\tcolor: var(--tl-color-text-3);\n}\n\n.tlui-input__wrapper {\n\twidth: 100%;\n\theight: 44px;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: var(--tl-space-4);\n\tcolor: var(--tl-color-text);\n}\n\n.tlui-input__wrapper > .tlui-icon {\n\tflex-shrink: 0;\n}\n\n/* The label is a fixed bit of chrome next to the input; it should never be squeezed. Without this,\n an inherited `overflow-wrap: anywhere` (e.g. from .tlui-dialog__body) drops its min-content width\n to a single character and short labels wrap mid-word, e.g. \"URL\" breaking after \"UR\". */\n.tlui-input__wrapper > label {\n\tflex-shrink: 0;\n\toverflow-wrap: normal;\n}\n\n/* If mobile use 16px as font size */\n/* On iOS, font size under 16px in an input will make the page zoom into the input \uD83E\uDD26\u200D\u2642\uFE0F */\n/* https://css-tricks.com/16px-or-larger-text-prevents-ios-form-zoom/ */\n@supports (-webkit-touch-callout: none) {\n\t/* CSS specific to iOS devices */\n\t.tlui-input {\n\t\tfont-size: 16px;\n\t}\n}\n\n/* --------------------- Select --------------------- */\n\n/* Trigger extends .tlui-button with space-between layout */\n.tlui-select__trigger {\n\tjustify-content: space-between;\n\tgap: var(--tl-space-3);\n}\n\n.tlui-select__trigger[data-state='open']::after {\n\topacity: 1;\n}\n\n.tlui-select__value {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: var(--tl-space-2);\n}\n\n.tlui-select__chevron {\n\tflex-shrink: 0;\n\tcolor: var(--tl-color-text-2);\n}\n\n/* Content extends .tlui-menu with Radix-aware sizing */\n.tlui-select__content {\n\tmin-width: var(--radix-select-trigger-width);\n\tmax-height: var(--radix-select-content-available-height);\n}\n\n.tlui-select__viewport {\n\tpadding: 4px 0;\n}\n\n/* Item extends .tlui-button__checkbox with left-aligned text */\n.tlui-select__item {\n\tjustify-content: flex-start;\n}\n\n.tlui-select__item-indicator {\n\tvisibility: hidden;\n}\n\n.tlui-select__item[data-state='checked'] .tlui-select__item-indicator {\n\tvisibility: visible;\n}\n\n/* ----------------------- Kbd ---------------------- */\n\n.tlui-kbd {\n\tfont-family: inherit;\n\tfont-size: 11px;\n\tline-height: 11px;\n\tdisplay: grid;\n\tjustify-items: center;\n\tgrid-auto-flow: column;\n\tgrid-template-columns: auto;\n\tgrid-auto-columns: minmax(1em, auto);\n\talign-self: bottom;\n\tcolor: currentColor;\n\tmargin-inline-start: var(--tl-space-4);\n}\n\n.tlui-kbd > span {\n\twidth: 100%;\n\ttext-align: center;\n\tdisplay: inline;\n\tmargin: 0px;\n\tpadding: 2px;\n\tborder-radius: 2px;\n}\n\n.tlui-kbd > span:last-child {\n\tpadding-inline-end: 0;\n}\n\n.tlui-kbd:not(:last-child) {\n\tmargin-inline-end: var(--tl-space-2);\n}\n\n/* Focus Mode Button */\n\n.tlui-focus-button {\n\tz-index: var(--tl-layer-panels);\n\tpointer-events: all;\n}\n\n/* ---------------------- Menu ---------------------- */\n\n.tlui-menu:empty {\n\tdisplay: none;\n}\n\n.tlui-menu {\n\tz-index: var(--tl-layer-menus);\n\theight: fit-content;\n\twidth: fit-content;\n\tborder-radius: var(--tl-radius-3);\n\tpointer-events: all;\n\ttouch-action: auto;\n\toverflow-y: auto;\n\toverscroll-behavior: none;\n\tbackground-color: var(--tl-color-panel);\n\tbox-shadow: var(--tl-shadow-3);\n}\n\n@media (max-height: 600px) {\n\t.tlui-menu {\n\t\tmax-height: 70vh;\n\t}\n}\n\n.tlui-menu::-webkit-scrollbar {\n\tdisplay: none;\n}\n\n/* Menu groups */\n\n.tlui-menu__group {\n\twidth: 100%;\n}\n\n.tlui-menu__group:empty {\n\tdisplay: none;\n}\n\n.tlui-menu__group {\n\tborder-bottom: 1px solid var(--tl-color-divider);\n}\n.tlui-menu__group:nth-last-of-type(1) {\n\tborder-bottom: none;\n}\n\n/* Submenu triggers */\n\n.tlui-menu__submenu__trigger {\n\t--gradient-angle: 90deg;\n}\n\n.tlui-menu__submenu__trigger[data-direction='left'] {\n\t--gradient-angle: 270deg;\n}\n\n.tlui-menu__submenu__trigger[data-state='open']::after {\n\topacity: 1;\n\tbackground: linear-gradient(\n\t\tvar(--gradient-angle),\n\t\trgba(144, 144, 144, 0) 0%,\n\t\tvar(--tl-color-muted-2) 100%\n\t);\n}\n\n@media (hover: hover) {\n\t.tlui-menu__submenu__trigger:is(:hover, [data-state='open'])::after {\n\t\topacity: 1;\n\t\tbackground: linear-gradient(\n\t\t\tvar(--gradient-angle),\n\t\t\trgba(144, 144, 144, 0) 0%,\n\t\t\tvar(--tl-color-muted-2) 100%\n\t\t);\n\t}\n}\n\n/* Menu Sizes */\n\n.tlui-menu[data-size='large'] > .tlui-menu__group {\n\tmin-width: initial;\n}\n\n.tlui-menu[data-size='medium'] > .tlui-menu__group {\n\tmin-width: 144px;\n}\n\n.tlui-menu[data-size='small'] > .tlui-menu__group {\n\tmin-width: 96px;\n}\n\n.tlui-menu[data-size='tiny'] > .tlui-menu__group {\n\tmin-width: 0px;\n}\n\n.tlui-menu-click-capture {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: var(--tl-layer-menu-click-capture);\n}\n\n/* --------------------- Popover -------------------- */\n\n.tlui-popover {\n\tposition: relative;\n\tdisplay: flex;\n\talign-content: stretch;\n}\n\n.tlui-popover__content {\n\tposition: relative;\n\tmax-height: calc(var(--radix-popover-content-available-height) - 8px);\n\tmargin: 0px;\n\tborder: none;\n\tborder-radius: var(--tl-radius-3);\n\tbackground-color: var(--tl-color-panel);\n\tbox-shadow: var(--tl-shadow-3);\n\tz-index: var(--tl-layer-menus);\n\toverflow: hidden;\n\toverflow-y: auto;\n\ttouch-action: auto;\n\toverscroll-behavior: none;\n\tscrollbar-width: none;\n\t-ms-overflow-style: none;\n}\n\n.tlui-hover-card__content {\n\tposition: relative;\n\tmargin: 0px;\n\tborder: none;\n\tborder-radius: var(--tl-radius-3);\n\tbackground-color: var(--tl-color-panel);\n\tbox-shadow: var(--tl-shadow-3);\n\tz-index: var(--tl-layer-menus);\n}\n\n/* -------------------- Menu Zone ------------------- */\n\n.tlui-menu-zone {\n\tposition: relative;\n\tz-index: var(--tl-layer-panels);\n\twidth: fit-content;\n\tborder-right: 2px solid var(--tl-color-background);\n\tborder-bottom: 2px solid var(--tl-color-background);\n\tborder-bottom-right-radius: var(--tl-radius-4);\n\tbackground-color: var(--tl-color-low);\n}\n\n.tlui-menu-zone *[data-state='open']::after {\n\tbackground: linear-gradient(180deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\topacity: 1;\n}\n\n@media (hover: hover) {\n\t.tlui-menu-zone *[data-state='open']:not(:hover)::after {\n\t\tbackground: linear-gradient(180deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\t\topacity: 1;\n\t}\n}\n\n/* ------------------- Page Select ------------------ */\n\n.tlui-page-menu__wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n\twidth: 220px;\n}\n\n.tlui-page-menu__trigger {\n\twidth: auto;\n}\n\n.tlui-page-menu__trigger[data-state='open']::after {\n\tbackground: linear-gradient(180deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\topacity: 1;\n}\n\n.tlui-page-menu__list {\n\tflex: 0 0 auto;\n\toverflow-x: hidden;\n\toverflow-y: auto;\n\tscrollbar-width: none;\n\t-ms-overflow-style: none;\n}\n\n.tlui-page-menu__list::-webkit-scrollbar {\n\tdisplay: none;\n}\n\n.tlui-page-menu__list__content {\n\tposition: relative;\n\toverflow: hidden;\n}\n\n.tlui-page-menu__resize-handle {\n\tflex: 0 0 auto;\n\tposition: relative;\n\theight: 1px;\n\tcursor: ns-resize;\n\ttouch-action: none;\n\tbackground: var(--tl-color-divider);\n\ttransition: background-color ease-in-out 150ms 80ms;\n\tz-index: 999;\n}\n\n/* Hit test area */\n.tlui-page-menu__resize-handle::after {\n\tcontent: '';\n\tposition: absolute;\n\tinset: -4px 0;\n\tbackground: none;\n\tz-index: 2000;\n\tpointer-events: all;\n}\n\n/* Thick blue indicator */\n.tlui-page-menu__resize-handle::before {\n\tcontent: '';\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\theight: 1.5px;\n\tbackground: transparent;\n\tpointer-events: none;\n\tz-index: 2001;\n}\n\n.tlui-page-menu__resize-handle[data-resizing='true']::before {\n\tbackground: var(--tl-color-selected);\n\ttransition: none;\n}\n\n.tlui-page-menu__create-button {\n\tflex: 0 0 auto;\n\tmargin: 0;\n}\n\n.tlui-page-menu__item__button {\n\tflex: 1 1 auto;\n\tmin-width: 0px;\n}\n\n.tlui-page-menu__item__button::after,\n.tlui-page-menu__item__drag-handle::after {\n\tcontent: none;\n}\n\n.tlui-page-menu__item__button > .tlui-button__label {\n\tpadding-inline-end: calc(40px - 12px);\n}\n\n.tlui-page-menu__item {\n\tposition: absolute;\n\ttop: 0px;\n\tinset-inline-start: 0px;\n\twidth: 100%;\n\theight: 40px;\n\tdisplay: flex;\n\tflex-direction: row;\n\talign-items: center;\n\toverflow: hidden;\n\ttransition: transform 0.08s ease-out;\n}\n\n.tlui-page-menu__item::before {\n\tcontent: '';\n\tposition: absolute;\n\tinset: 4px;\n\tborder-radius: var(--tl-radius-2);\n\tbackground: var(--tl-color-muted-2);\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.tlui-page-menu__item > * {\n\tposition: relative;\n\tz-index: var(--tl-layer-above);\n}\n\n.tlui-page-menu__item[data-iscurrent='true']:not([data-editing='true'])::before {\n\tbackground: var(--tl-color-hint);\n\topacity: 1;\n}\n\n.tlui-page-menu__item__title {\n\tflex: 1;\n\theight: 40px;\n\tposition: relative;\n}\n\n.tlui-page-menu__item__title::before {\n\tcontent: '';\n\tposition: absolute;\n\tinset: 4px;\n\tborder-radius: var(--tl-radius-2);\n\tborder: 1px solid var(--tl-color-primary);\n\tpointer-events: none;\n}\n\n.tlui-page-menu__item__title > .tlui-input__wrapper {\n\theight: 100%;\n}\n\n.tlui-page-menu__item[data-dragging='true'],\n.tlui-page-menu__item[data-dragging='true'] .tlui-page-menu__item__button,\n.tlui-page-menu__item[data-dragging='true'] .tlui-page-menu__item__drag-handle {\n\tcursor: grabbing;\n\ttransition: none;\n}\n\n.tlui-page-menu__item[data-dragging='true'] {\n\tbackground: var(--tl-color-panel);\n\topacity: 1;\n}\n\n.tlui-page-menu__item__drag-handle {\n\tflex: 0 0 32px;\n\tmin-width: 32px;\n\tpadding: 0px;\n\tcolor: var(--tl-color-text-3);\n\tcursor: grab;\n\ttouch-action: none;\n}\n\n.tlui-page-menu__item__drag-handle > .tlui-icon {\n\tposition: relative;\n\tinset-inline-start: 1px;\n}\n\n.tlui-page-menu__item__drag-handle + .tlui-page-menu__item__button {\n\tpadding-inline-start: 0px;\n}\n\n.tlui-page-menu__item__input {\n\tmargin-inline-start: 12px;\n}\n\n.tlui-page-menu__item__submenu {\n\tposition: absolute;\n\tinset-inline-end: 0px;\n\ttop: 0px;\n\theight: 100%;\n\tz-index: 10;\n}\n\n.tlui-page-menu__item__submenu > .tlui-button {\n\topacity: 0;\n\tcolor: var(--tl-color-text-3);\n}\n\n.tlui-page-menu__item__submenu > .tlui-button::after {\n\tcontent: none;\n}\n\n.tlui-page-menu__item:has(.tlui-button:focus-visible)\n\t> .tlui-page-menu__item__submenu\n\t> .tlui-button,\n.tlui-page-menu__item__submenu > .tlui-button[data-state='open'],\n.tlui-page-menu__item__submenu > .tlui-button:focus-visible {\n\topacity: 1;\n}\n\n/* Hide the kebab while any row is being dragged: :hover sticks during pointer */\n/* capture, which would otherwise leave the kebab visible mid-drag. */\n.tlui-page-menu__list__content[data-dragging='true']\n\t.tlui-page-menu__item\n\t> .tlui-page-menu__item__submenu\n\t> .tlui-button {\n\topacity: 0;\n}\n\n.tl-container[data-coarse='true'] .tlui-page-menu__item__submenu > .tlui-button {\n\topacity: 1;\n}\n\n.tl-container[data-coarse='true'] .tlui-page-menu__item__button > .tlui-button__label {\n\tpadding-inline-end: calc(40px - 12px);\n}\n\n@media (hover: hover) {\n\t.tlui-page-menu__resize-handle:hover::before {\n\t\tbackground: var(--tl-color-selected);\n\t\theight: 2px;\n\t\ttransition: background-color 150ms 80ms;\n\t}\n\n\t.tlui-page-menu__item:not([data-editing='true']):hover::before {\n\t\topacity: 1;\n\t}\n\n\t.tlui-page-menu__item:hover > .tlui-page-menu__item__submenu > .tlui-button {\n\t\topacity: 1;\n\t}\n\n\t.tlui-page-menu__item__button > .tlui-button__label {\n\t\tpadding-inline-end: 0px;\n\t}\n\n\t.tlui-page-menu__item:hover > .tlui-page-menu__item__button > .tlui-button__label {\n\t\tpadding-inline-end: calc(40px - 12px);\n\t}\n\n\t.tlui-page-menu__item__submenu > .tlui-button:hover {\n\t\tcolor: var(--tl-color-text);\n\t}\n}\n/* -------------- Skip to main content -------------- */\n\n.tl-skip-to-main-content {\n\tposition: fixed;\n\ttop: 48px;\n\tleft: -9999px;\n\tpadding: 8px 16px;\n\tz-index: var(--tl-layer-toasts);\n}\n\n.tl-skip-to-main-content:focus {\n\tleft: 8px;\n}\n\n/*\n * focusing skip-to-main-content hide top ui buttons to make sure we avoid\n * z-index clashes on hover (see issue #8328)\n */\n.tl-skip-to-main-content:focus + .tlui-layout__top .tlui-helper-buttons {\n\tdisplay: none;\n}\n\n/* ---------------- Offline indicator --------------- */\n\n.tlui-offline-indicator {\n\tdisplay: flex;\n\tcolor: var(--tl-color-background);\n\tbackground-color: var(--tl-color-text);\n\tborder: none;\n\twidth: 32px;\n\theight: 32px;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 99px;\n\tcursor: default;\n\topacity: 0;\n\tanimation: tl-fade-in;\n\tanimation-duration: 0.12s;\n\tanimation-delay: 2s;\n\tanimation-fill-mode: forwards;\n}\n\n/* ------------------- Style panel ------------------ */\n\n.tlui-style-panel__wrapper {\n\tbox-shadow: var(--tl-shadow-2);\n\tborder-radius: var(--tl-radius-3);\n\tpointer-events: all;\n\tbackground-color: var(--tl-color-panel);\n\theight: fit-content;\n\tmax-height: 100%;\n\tmargin: 8px;\n\tmargin-top: 4px;\n\ttouch-action: auto;\n\toverscroll-behavior: none;\n\toverflow-y: auto;\n\toverflow-x: hidden;\n\tcolor: var(--tl-color-text);\n}\n/* if the style panel is the only child (ie no share menu), increase the margin */\n.tlui-style-panel__wrapper:only-child {\n\tmargin-top: 8px;\n}\n\n.tlui-style-panel {\n\tposition: relative;\n\tz-index: var(--tl-layer-panels);\n\tpointer-events: all;\n\twidth: 148px;\n\tmax-width: 148px;\n}\n\n.tlui-style-panel[data-enhanced-a11y-mode='true'] .tlui-button[data-isactive='true'] {\n\tborder-radius: 10px;\n\toutline: 2px solid var(--tl-color-text);\n\toutline-offset: -5px;\n}\n\n.tlui-style-panel::-webkit-scrollbar {\n\tdisplay: none;\n}\n\n.tlui-style-panel .tlui-button.select {\n\twidth: 100%;\n}\n\n.tlui-style-panel__section {\n\tdisplay: flex;\n\tposition: relative;\n\tflex-direction: column;\n}\n\n/*\nadd a border to the bottom of all but the last section. we have to handle empty sections too, which\nare hidden and shouldn't be counted\n*/\n.tlui-style-panel__section:not(:nth-last-child(-n + 1 of .tlui-style-panel__section:not(:empty))) {\n\tborder-bottom: 1px solid var(--tl-color-divider);\n}\n/*\nif a section ends with a slider and we're adding a border, we need some extra space for visual\nbalance. we need to handle empty sections as above. is this the most complex css selector in all of\ntldraw? probably.\n*/\n.tlui-style-panel__section:has(.tlui-slider__container:last-child):not(\n\t\t:nth-last-child(-n + 1 of .tlui-style-panel__section:not(:empty))\n\t) {\n\tmargin-bottom: 7px;\n}\n\n.tlui-style-panel__section:empty {\n\tdisplay: none;\n}\n\n/*\n * This is used in a couple places, like Align and Vertical Align.\n * It's because we have a toolbar with a Toggle Group but then an adjacent button\n * next to it that opens a popup.\n */\n.tlui-style-panel__section .tlui-toolbar:has(.tlui-toolbar) {\n\tflex-wrap: wrap;\n}\n\n.tlui-style-panel__section .tlui-toolbar:has(.tlui-toolbar) .tlui-style-panel__subheading {\n\tmargin-left: -2px;\n}\n\n.tlui-style-panel__section__common:not(:only-child) {\n\tmargin-bottom: 7px;\n\tborder-bottom: 1px solid var(--tl-color-divider);\n}\n\n.tlui-toolbar-toggle-group-item,\n.tlui-style-panel__dropdown-picker > .tlui-button__icon {\n\twidth: 40px;\n\tpadding: 0;\n}\n\n.tlui-style-panel__dropdown-picker:only-child {\n\tflex: 1;\n}\n\n.tlui-style-panel__double-select-picker {\n\tdisplay: flex;\n\tgrid-template-columns: 1fr auto;\n\talign-items: center;\n\tpadding-left: var(--tl-space-4);\n\tcolor: var(--tl-color-text-1);\n\tfont-size: 12px;\n}\n\n.tlui-style-panel__double-select-picker-label {\n\ttext-overflow: ellipsis;\n\toverflow: hidden;\n\twhite-space: nowrap;\n\tflex-grow: 2;\n\tmax-width: 100%;\n}\n\n.tlui-style-panel .tlui-button[data-state='open']::after {\n\topacity: 1;\n\tbackground: linear-gradient(270deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n}\n\n@media (hover: hover) {\n\t.tlui-style-panel .tlui-button[aria-expanded='true'] {\n\t\tbackground: none;\n\t}\n\t.tlui-style-panel .tlui-button[data-state='open']:not(:hover)::after {\n\t\topacity: 1;\n\t\tbackground: linear-gradient(270deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\t}\n}\n\n/* Accessibility subheadings */\n\n.tlui-style-panel__section .tlui-style-panel__subheading,\n.tlui-style-panel__section__common .tlui-style-panel__subheading,\n.tlui-style-panel__subheading + .tlui-slider__container {\n\tmargin: 0;\n\tpadding: var(--tl-space-2) var(--tl-space-3) 0px var(--tl-space-4);\n\tfont-size: 12px;\n\tfont-weight: inherit;\n\tline-height: inherit;\n}\n\n.tlui-style-panel .tlui-style-panel__subheading:nth-of-type(1) {\n\tpadding-top: var(--tl-space-3);\n}\n\n.tlui-style-panel__subheading + .tlui-slider__container {\n\tpadding-top: 0px;\n}\n\n/* --------------------- Bottom --------------------- */\n\n.tlui-layout__bottom {\n\tgrid-row: 2;\n\twidth: 100%;\n\tmin-width: 0px;\n}\n\n.tlui-layout__bottom__main {\n\twidth: 100%;\n\tdisplay: flex;\n\talign-items: flex-end;\n\tjustify-content: center;\n}\n\n/* ------------------- Navigation ------------------- */\n\n.tlui-navigation-panel {\n\tdisplay: flex;\n\twidth: min-content;\n\tflex-direction: column;\n\tz-index: var(--tl-layer-panels);\n\tpointer-events: all;\n\tposition: absolute;\n\tleft: 0px;\n}\n\n.tl-container[dir='rtl'] .tlui-navigation-panel {\n\tleft: auto;\n\tright: 0px;\n}\n\n.tlui-navigation-panel::before {\n\tcontent: '';\n\tdisplay: block;\n\tposition: absolute;\n\tz-index: -1;\n\tinset: -2px -2px 0px 0px;\n\tborder-radius: 0;\n\tborder-top: 2px solid var(--tl-color-background);\n\tborder-right: 2px solid var(--tl-color-background);\n\tborder-top-right-radius: var(--tl-radius-4);\n\tbackground-color: var(--tl-color-low);\n}\n\n.tl-container[dir='rtl'] .tlui-navigation-panel::before {\n\tinset: -2px 0px 0px -2px;\n\tborder-right: 0;\n\tborder-left: 2px solid var(--tl-color-background);\n\tborder-top-right-radius: 0;\n\tborder-top-left-radius: var(--tl-radius-4);\n}\n\n.tlui-navigation-panel[data-a11y='true']::before {\n\tdisplay: none;\n}\n\n/* Minimap */\n\n.tlui-minimap {\n\twidth: 100%;\n\theight: 96px;\n\tmin-height: 96px;\n\toverflow: hidden;\n\tpadding: var(--tl-space-3);\n\tpadding-top: 0px;\n}\n\n.tlui-minimap__canvas {\n\tposition: relative;\n\twidth: 100%;\n\theight: 100%;\n}\n\n/* --------------------- Toolbar -------------------- */\n\n/* Wide container */\n.tlui-main-toolbar {\n\tgrid-column: 1 / span 3;\n\tgrid-row: 1;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tflex-grow: 2;\n}\n\n.tlui-main-toolbar--horizontal {\n\tpadding-bottom: calc(var(--tl-space-3) + var(--tl-sab));\n\tmax-width: 100%;\n}\n\n.tlui-main-toolbar--vertical {\n\tposition: absolute;\n\tleft: 0;\n\ttop: 90px; /* height of page menu + 'back to content' button */\n\tbottom: 140px; /* height of expanded mini-map */\n\tpadding-left: calc(var(--tl-space-3) + var(--tl-sab));\n}\n\n[data-breakpoints-below*='6'] .tlui-main-toolbar--vertical {\n\tbottom: 90px; /* collapsed mini-map, but same as top to keep things looking even */\n}\n\n/* Centered Content */\n.tlui-main-toolbar__inner {\n\tposition: relative;\n\twidth: fit-content;\n\tdisplay: flex;\n\tgap: var(--tl-space-3);\n\talign-items: flex-start;\n}\n\n.tlui-main-toolbar--vertical .tlui-main-toolbar__inner {\n\tflex-direction: column;\n}\n\n.tlui-main-toolbar--horizontal .tlui-main-toolbar__left {\n\twidth: fit-content;\n}\n.tlui-main-toolbar--vertical .tlui-main-toolbar__left {\n\tdisplay: flex;\n\theight: fit-content;\n}\n\n/* Row of controls + lock button */\n.tlui-main-toolbar__extras {\n\tposition: relative;\n\tz-index: var(--tl-layer-above);\n\tpointer-events: none;\n\talign-self: stretch;\n}\n\n.tlui-main-toolbar__extras:empty {\n\tdisplay: none;\n}\n\n.tlui-main-toolbar--horizontal .tlui-main-toolbar__extras {\n\theight: 48px;\n\ttop: 6px;\n}\n\n.tlui-main-toolbar--vertical .tlui-main-toolbar__extras {\n\twidth: 48px;\n\torder: 1;\n}\n\n.tlui-main-toolbar__extras__controls {\n\tposition: relative;\n\tz-index: var(--tl-layer-above);\n\tbackground-color: var(--tl-color-low);\n\tborder: 2px solid var(--tl-color-background);\n\tpointer-events: all;\n}\n\n.tlui-main-toolbar--horizontal .tlui-main-toolbar__extras__controls {\n\tborder-top-left-radius: var(--tl-radius-4);\n\tborder-top-right-radius: var(--tl-radius-4);\n\tmargin-left: 8px;\n\tmargin-right: 0px;\n\twidth: fit-content;\n}\n\n.tlui-main-toolbar--vertical .tlui-main-toolbar__extras__controls {\n\tborder-top-right-radius: var(--tl-radius-4);\n\tborder-bottom-right-radius: var(--tl-radius-4);\n\tmargin-top: 8px;\n\tmargin-left: -2px;\n\tmargin-bottom: 0px;\n\twidth: fit-content;\n}\n\n.tlui-main-toolbar__tools {\n\tborder-radius: var(--tl-radius-4);\n\tz-index: var(--tl-layer-panels);\n\tpointer-events: all;\n\tposition: relative;\n\tbackground: var(--tl-color-panel);\n\tbox-shadow: var(--tl-shadow-2);\n\tcontain: layout style paint;\n}\n\n.tlui-main-toolbar--horizontal .tlui-main-toolbar__mobile-style-panel {\n\talign-self: flex-end;\n}\n.tlui-main-toolbar--vertical .tlui-main-toolbar__mobile-style-panel {\n\talign-self: flex-start;\n}\n\n.tlui-main-toolbar--horizontal .tlui-main-toolbar__overflow {\n\twidth: 40px;\n\tmargin-left: 2px;\n}\n.tlui-main-toolbar--vertical .tlui-main-toolbar__overflow {\n\theight: 40px;\n\tmargin-top: 2px;\n}\n\n.tlui-layout__mobile .tlui-main-toolbar--horizontal .tlui-main-toolbar__overflow {\n\twidth: 32px;\n\tpadding: 0px;\n}\n\n.tlui-main-toolbar--horizontal *[data-state='open']::after {\n\tbackground: linear-gradient(0deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\topacity: 1;\n}\n.tlui-main-toolbar--vertical *[data-state='open']::after {\n\tbackground: linear-gradient(90deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\topacity: 1;\n}\n\n.tlui-main-toolbar__overflow-content {\n\ttouch-action: none;\n}\n\n.tlui-main-toolbar__tools [data-toolbar-visible='false'],\n.tlui-main-toolbar__overflow-content [data-toolbar-visible='false'] {\n\tdisplay: none;\n}\n\n.tlui-main-toolbar__group:empty {\n\tdisplay: none;\n}\n.tlui-row.tlui-main-toolbar__group:not(:nth-last-child(-n + 1 of [data-toolbar-visible='true'])) {\n\tborder-right: 1px solid var(--tl-color-divider);\n\tmargin-right: 2px;\n}\n.tlui-column.tlui-main-toolbar__group:not(\n\t\t:nth-last-child(-n + 1 of [data-toolbar-visible='true'])\n\t) {\n\tborder-bottom: 1px solid var(--tl-color-divider);\n\tmargin-bottom: 2px;\n}\n.tlui-grid.tlui-main-toolbar__group {\n\tgrid-column: 1 / span 4;\n}\n.tlui-grid.tlui-main-toolbar__group:not(:nth-last-child(-n + 1 of [data-toolbar-visible='true'])) {\n\tborder-bottom: 1px solid var(--tl-color-divider);\n\tmargin-bottom: 2px;\n}\n\n@media (hover: hover) {\n\t.tlui-main-toolbar--horizontal *[data-state='open']:not(:hover)::after {\n\t\tbackground: linear-gradient(0deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\t\topacity: 1;\n\t}\n\t.tlui-main-toolbar--vertical *[data-state='open']:not(:hover)::after {\n\t\tbackground: linear-gradient(90deg, rgba(144, 144, 144, 0) 0%, var(--tl-color-muted-2) 100%);\n\t\topacity: 1;\n\t}\n}\n\n/* ------------------- Tooltip -------------------- */\n\n.tlui-tooltip {\n\tfont-size: 12px;\n\tpadding: 2px 8px;\n\tborder-radius: 4px;\n\tbackground-color: var(--tl-color-tooltip);\n\tbox-shadow: none;\n\tcolor: var(--tl-color-text-shadow);\n\tmax-width: 400px;\n\twidth: fit-content;\n\ttext-align: center;\n\twill-change: transform, opacity;\n\tz-index: 2;\n}\n\n.tlui-tooltip__arrow {\n\tfill: var(--tl-color-tooltip);\n\twill-change: opacity;\n}\n\n[data-radix-popper-content-wrapper]:has(.tlui-tooltip) {\n\tz-index: var(--tl-layer-toasts) !important;\n\tpointer-events: none;\n}\n\n/* ------------------- Debug panel ------------------ */\n\n.tlui-debug-panel {\n\tbackground-color: var(--tl-color-low);\n\twidth: 100%;\n\tdisplay: grid;\n\talign-items: center;\n\tgrid-template-columns: 1fr auto auto auto;\n\tjustify-content: space-between;\n\tpadding-inline-start: var(--tl-space-4);\n\tborder-top: 1px solid var(--tl-color-background);\n\tfont-size: 12px;\n\tcolor: var(--tl-color-text-1);\n\tz-index: var(--tl-layer-panels);\n\tpointer-events: all;\n}\n\n.tlui-debug-panel__current-state {\n\toverflow: hidden;\n\twhite-space: nowrap;\n\ttext-overflow: ellipsis;\n}\n\n.tlui-debug-panel__fps {\n\tmargin-right: 8px;\n}\n\n.tlui-debug-panel__fps__slow {\n\tfont-weight: bold;\n\tcolor: var(--tl-color-danger);\n}\n\n.tlui-a11y-audit {\n\tborder-collapse: collapse;\n}\n\n.tlui-a11y-audit th,\n.tlui-a11y-audit td {\n\tpadding: 8px;\n\tborder: 1px solid var(--tl-color-low-border);\n}\n\n/* --------------------- Toasts --------------------- */\n\n.tlui-toast__viewport {\n\tposition: absolute;\n\tinset: 0px;\n\tmargin: 0px;\n\tdisplay: flex;\n\talign-items: flex-end;\n\tjustify-content: flex-end;\n\tflex-direction: column;\n\tgap: var(--tl-space-3);\n\tpointer-events: none;\n\tpadding: 0px var(--tl-space-3) 64px 0px;\n\tz-index: var(--tl-layer-toasts);\n}\n\n.tl-container[dir='rtl'] .tlui-toast__viewport {\n\tpadding: 0px 0px 64px var(--tl-space-3);\n}\n\n.tlui-toast__viewport > * {\n\tpointer-events: all;\n}\n\n.tlui-toast__icon {\n\tpadding-top: 11px;\n\tpadding-inline-start: var(--tl-space-4);\n\tcolor: var(--tl-color-text-1);\n}\n\n.tlui-toast__container {\n\tmin-width: 200px;\n\tdisplay: flex;\n\tflex-direction: row;\n\tbackground-color: var(--tl-color-panel);\n\tbox-shadow: var(--tl-shadow-2);\n\tborder-radius: var(--tl-radius-3);\n\tfont-size: 12px;\n}\n\n.tlui-toast__container[data-severity='success'] .tlui-icon {\n\tcolor: var(--tl-color-success);\n}\n\n.tlui-toast__container[data-severity='info'] .tlui-icon {\n\tcolor: var(--tl-color-info);\n}\n\n.tlui-toast__container[data-severity='warning'] .tlui-icon {\n\tcolor: var(--tl-color-warning);\n}\n\n.tlui-toast__container[data-severity='error'] .tlui-icon {\n\tcolor: var(--tl-color-danger);\n}\n\n.tlui-toast__main {\n\tflex-grow: 2;\n\tmax-width: 280px;\n\tmin-width: 0;\n}\n\n.tlui-toast__content {\n\tpadding: var(--tl-space-4);\n\tdisplay: flex;\n\tline-height: 1.4;\n\tflex-direction: column;\n\tgap: var(--tl-space-3);\n\tmin-width: 0;\n}\n\n.tlui-toast__main[data-actions='true'] .tlui-toast__content {\n\tpadding-bottom: var(--tl-space-2);\n}\n\n.tlui-toast__title {\n\tfont-weight: bold;\n\tcolor: var(--tl-color-text-1);\n\t/* this makes the default toast look better */\n\tline-height: 16px;\n\toverflow-wrap: anywhere;\n}\n\n.tlui-toast__description {\n\tcolor: var(--tl-color-text-1);\n\tpadding: var(--tl-space-3);\n\tmargin: 0px;\n\tpadding: 0px;\n\toverflow-wrap: anywhere;\n}\n\n.tlui-toast__icon + .tlui-toast__main > .tlui-toast__actions {\n\tpadding-left: 0px;\n}\n\n.tlui-toast__actions {\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: flex-start;\n\tmargin-left: 0;\n}\n\n.tlui-toast__close {\n\talign-self: flex-end;\n\tflex-shrink: 0;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n\t.tlui-toast__container[data-state='open'] {\n\t\tanimation: tlui-slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);\n\t}\n\n\t.tlui-toast__container[data-state='closed'] {\n\t\tanimation: tlui-fade-out 100ms ease-in;\n\t}\n\n\t.tlui-toast__container[data-swipe='move'] {\n\t\ttransform: translateX(var(--radix-toast-swipe-move-x));\n\t}\n\n\t.tlui-toast__container[data-swipe='cancel'] {\n\t\ttransform: translateX(0);\n\t\ttransition: transform 200ms ease-out;\n\t}\n\n\t.tlui-toast__container[data-swipe='end'] {\n\t\tanimation: tlui-slide-out 100ms ease-out;\n\t}\n\n\t.tl-container[dir='rtl'] .tlui-toast__container[data-state='open'] {\n\t\tanimation: tlui-slide-in-rtl 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);\n\t}\n\n\t.tl-container[dir='rtl'] .tlui-toast__container[data-state='closed'] {\n\t\tanimation: tlui-fade-out 100ms ease-in;\n\t}\n\n\t.tl-container[dir='rtl'] .tlui-toast__container[data-swipe='end'] {\n\t\tanimation: tlui-slide-out-rtl 100ms ease-out;\n\t}\n}\n\n/* ---------------- Dialog ---------------- */\n\n.tlui-dialog__overlay {\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\twidth: 100%;\n\theight: 100%;\n\tz-index: var(--tl-layer-canvas-overlays);\n\tbackground-color: var(--tl-color-overlay);\n\tanimation: tl-fade-in 0.12s ease-out;\n}\n\n.tlui-dialog__positioner {\n\tposition: absolute;\n\ttop: 0px;\n\tleft: 0px;\n\twidth: 100%;\n\theight: 100%;\n\tz-index: var(--tl-layer-canvas-overlays);\n\tpointer-events: all;\n\tdisplay: grid;\n\tplace-items: center;\n\toverflow-y: auto;\n\tpadding: 0px var(--tl-space-3);\n}\n\n.tlui-dialog__content {\n\tdisplay: flex;\n\tflex-direction: column;\n\tposition: relative;\n\tcursor: default;\n\tbackground-color: var(--tl-color-panel);\n\tbox-shadow: var(--tl-shadow-3);\n\tborder-radius: var(--tl-radius-3);\n\tfont-size: 12px;\n\toverflow: hidden;\n\tmin-width: 300px;\n\tmax-width: 100%;\n\tmax-height: 80%;\n}\n\n.tlui-dialog__header {\n\tposition: relative;\n\tdisplay: flex;\n\talign-items: center;\n\tflex: 0;\n\tz-index: var(--tl-layer-header-footer);\n\tpadding-inline-start: var(--tl-space-4);\n\tcolor: var(--tl-color-text);\n\theight: 40px;\n}\n\n.tlui-dialog__header__title {\n\tflex: 1;\n\tfont-weight: inherit;\n\tfont-size: 12px;\n\tmargin: 0px;\n\tcolor: var(--tl-color-text-1);\n}\n\n.tlui-dialog__header__close {\n\tjustify-self: flex-end;\n}\n\n.tlui-dialog__body {\n\tpadding: var(--tl-space-4) var(--tl-space-4);\n\tflex: 0 1;\n\toverflow-y: auto;\n\toverflow-x: hidden;\n\tcolor: var(--tl-color-text-1);\n\tuser-select: all;\n\t-webkit-user-select: text;\n\toverflow-wrap: anywhere;\n}\n.tlui-dialog__body a {\n\tcolor: var(--tl-color-selected);\n}\n\n.tlui-dialog__body ul,\n.tlui-dialog__body ol {\n\tpadding-left: 16px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--tl-space-2);\n}\n\n.tlui-dialog__footer {\n\tposition: relative;\n\tmin-height: 12px;\n\tz-index: var(--tl-layer-header-footer);\n}\n\n.tlui-dialog__footer__actions {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n}\n\n.tlui-dialog__footer__actions > .tlui-button:nth-last-child(n + 2) {\n\tmargin-right: -4px;\n}\n\n/* --------------------- Dialogs -------------------- */\n\n/* Edit Link Dialog */\n\n.tlui-edit-link-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--tl-space-4);\n\tcolor: var(--tl-color-text);\n}\n\n.tlui-edit-link-dialog__input {\n\tbackground-color: var(--tl-color-muted-2);\n\tflex-grow: 2;\n\tborder-radius: var(--tl-radius-2);\n\tpadding: 0px var(--tl-space-4);\n}\n\n/* Embed Dialog */\n\n.tlui-embed__spacer {\n\tflex-grow: 2;\n\tmin-height: 0px;\n\tmargin-left: calc(-1 * var(--tl-space-4));\n\tmargin-top: calc(-1 * var(--tl-space-4));\n\tpointer-events: none;\n}\n\n.tlui-embed-dialog__list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tpadding: 0px var(--tl-space-3) var(--tl-space-4) var(--tl-space-3);\n}\n\n.tlui-embed-dialog__item__image {\n\twidth: 24px;\n\theight: 24px;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbackground-size: contain;\n\tbackground-repeat: no-repeat;\n\tbackground-position: center center;\n\tbackground-color: var(--tl-color-selected-contrast);\n\tborder-radius: var(--tl-radius-1);\n}\n\n.tlui-embed-dialog__enter {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--tl-space-4);\n\tcolor: var(--tl-color-text-1);\n}\n\n.tlui-embed-dialog__input {\n\tbackground-color: var(--tl-color-muted-2);\n\tflex-grow: 2;\n\tborder-radius: var(--tl-radius-2);\n\tpadding: 0px var(--tl-space-4);\n}\n\n.tlui-embed-dialog__warning {\n\tcolor: var(--tl-color-danger);\n\ttext-shadow: none;\n}\n\n.tlui-embed-dialog__instruction__link {\n\tdisplay: flex;\n\tgap: var(--tl-space-1);\n\tmargin-top: var(--tl-space-4);\n}\n\n.tlui-embed-dialog__enter a {\n\tcolor: var(--tl-color-text-1);\n}\n\n/* --------------- Keyboard shortcuts --------------- */\n\n.tlui-shortcuts-dialog__header {\n\tborder-bottom: 1px solid var(--tl-color-divider);\n}\n\n.tlui-shortcuts-dialog__body {\n\tposition: relative;\n\tcolumns: 3;\n\tcolumn-gap: var(--tl-space-9);\n\tpointer-events: all;\n\ttouch-action: auto;\n\n\t/* Terrible fix to allow firefox users to scroll the dialog */\n\toverflow-x: auto;\n\n\tscrollbar-width: none;\n}\n\n.tlui-shortcuts-dialog__body::-webkit-scrollbar {\n\tdisplay: none;\n}\n\n.tlui-shortcuts-dialog__body__tablet {\n\tcolumns: 2;\n}\n\n.tlui-shortcuts-dialog__body__mobile {\n\tcolumns: 1;\n}\n\n.tlui-shortcuts-dialog__group {\n\tbreak-inside: avoid-column;\n\tpadding-bottom: var(--tl-space-6);\n}\n\n.tlui-shortcuts-dialog__group__title {\n\tfont-size: inherit;\n\tfont-weight: inherit;\n\tmargin: 0px;\n\tcolor: var(--tl-color-text-3);\n\theight: 32px;\n\tdisplay: flex;\n\talign-items: center;\n}\n\n.tlui-shortcuts-dialog__group__content {\n\tdisplay: flex;\n\tflex-direction: column;\n\tcolor: var(--tl-color-text-1);\n}\n\n.tlui-shortcuts-dialog__key-pair {\n\tdisplay: flex;\n\tgap: var(--tl-space-4);\n\talign-items: center;\n\tjustify-content: space-between;\n\theight: 32px;\n}\n\n.tlui-shortcuts-dialog__key-pair__key {\n\tflex: 1;\n\tfont-size: 12px;\n}\n\n/* ------------------ Language menu ----------------- */\n\n.tlui-language-menu {\n\tmax-height: 500px;\n}\n\n.tlui-language-menu::after {\n\tcontent: '';\n\tdisplay: block;\n\tposition: absolute;\n\tbottom: 0px;\n\tleft: 0px;\n\tright: 0px;\n\theight: 24px;\n\tbackground: linear-gradient(\n\t\tto bottom,\n\t\tvar(--tl-color-panel-transparent) 0%,\n\t\tvar(--tl-color-panel) 90%,\n\t\tvar(--tl-color-panel) 100%\n\t);\n\tborder-bottom-left-radius: var(--tl-radius-3);\n\tborder-bottom-right-radius: var(--tl-radius-3);\n\tpointer-events: none;\n}\n\n/* ------------------ Actions menu ------------------ */\n\n.tlui-actions-menu {\n\tmax-height: calc(100vh - 150px);\n}\n\n/* -------------------- Help menu ------------------- */\n\n.tlui-help-menu {\n\tpointer-events: all;\n\tposition: absolute;\n\tbottom: var(--tl-space-2);\n\tright: var(--tl-space-2);\n\tz-index: var(--tl-layer-panels);\n\tborder: 2px solid var(--tl-color-background);\n\tborder-radius: 100%;\n}\n\n/* ------------------- Da share zone ------------------ */\n\n.tlui-share-zone {\n\tpadding: 0px 0px 0px 0px;\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: flex-end;\n\tz-index: var(--tl-layer-panels);\n\talign-items: center;\n\tpadding-top: 2px;\n\tpadding-right: 4px;\n}\n\n/* ------------------- People Menu ------------------- */\n\n.tlui-people-menu__avatars-button {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n\tbackground: none;\n\tborder: none;\n\tcursor: pointer;\n\tpointer-events: all;\n\tborder-radius: var(--tl-radius-1);\n\tpadding-right: 1px;\n\theight: 100%;\n}\n\n.tlui-people-menu__avatars {\n\tdisplay: flex;\n\tflex-direction: row;\n}\n\n.tlui-people-menu__avatar {\n\theight: 24px;\n\twidth: 24px;\n\tborder: 2px solid var(--tl-color-background);\n\tbackground-color: var(--tl-color-low);\n\tborder-radius: 100%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tposition: relative;\n\tfont-size: 10px;\n\tfont-weight: bold;\n\ttext-align: center;\n\tcolor: var(--tl-color-selected-contrast);\n\tz-index: 2;\n}\n\n.tlui-people-menu__avatar:nth-of-type(n + 2) {\n\tmargin-inline-start: -12px;\n}\n\n.tlui-people-menu__avatars-button[data-state='open'] {\n\topacity: 1;\n}\n\n@media (hover: hover) {\n\t.tlui-people-menu__avatars-button:hover .tlui-people-menu__avatar {\n\t\tborder-color: var(--tl-color-low);\n\t}\n}\n\n.tlui-people-menu__more {\n\tmin-width: 0px;\n\tfont-size: 11px;\n\tfont-weight: 600;\n\tcolor: var(--tl-color-text-1);\n\tfont-family: inherit;\n\tpadding: 0px 4px;\n}\n.tlui-people-menu__more::after {\n\tborder-radius: var(--tl-radius-2);\n\tinset: 0px;\n}\n\n.tlui-people-menu__wrapper {\n\tposition: relative;\n\tdisplay: flex;\n\tflex-direction: column;\n\twidth: 220px;\n\theight: fit-content;\n\tmax-height: 50vh;\n}\n\n.tlui-people-menu__section {\n\tposition: relative;\n\ttouch-action: auto;\n\tflex-direction: column;\n\tmax-height: 100%;\n\toverflow-x: hidden;\n\toverflow-y: auto;\n\ttouch-action: auto;\n}\n\n.tlui-people-menu__section:first-child,\n.tlui-people-menu__section:last-child {\n\tflex-shrink: 0;\n}\n\n.tlui-people-menu__section:not(:last-child) {\n\tborder-bottom: 1px solid var(--tl-color-divider);\n}\n\n.tlui-people-menu__user {\n\tdisplay: flex;\n\tjustify-content: flex-start;\n\talign-items: center;\n}\n\n.tlui-people-menu__user__color,\n.tlui-people-menu__user__edit {\n\tflex-shrink: 0;\n}\n\n.tlui-people-menu__user__name {\n\ttext-align: start;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tfont-size: 12px;\n\tcolor: var(--tl-color-text-1);\n\tmax-width: 100%;\n\tflex-grow: 1;\n\tflex-shrink: 100;\n}\n\n.tlui-people-menu__user__label {\n\ttext-align: start;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tfont-size: 12px;\n\tcolor: var(--tl-color-text-3);\n\tflex-grow: 100;\n\tflex-shrink: 0;\n\tmargin-inline-start: 4px;\n}\n\n.tlui-people-menu__user__input {\n\tflex-grow: 2;\n\theight: 100%;\n\tpadding: 0px;\n\tmargin: 0px;\n}\n\n.tlui-people-menu__user > .tlui-input__wrapper {\n\twidth: auto;\n\tdisplay: flex;\n\talign-items: auto;\n\tflex-grow: 2;\n\tgap: 8px;\n\theight: 100%;\n\tpadding: 0px;\n}\n\n.tlui-people-menu__item {\n\tdisplay: flex;\n\talign-items: center;\n}\n\n.tlui-people-menu__item:last-of-type .tlui-button__menu {\n\tmargin-bottom: 0;\n}\n\n.tlui-people-menu__item__button {\n\tpadding-inline-start: 12px;\n\tpadding-inline-end: 0px;\n\toverflow: hidden;\n}\n\n.tlui-people-menu__item > .tlui-button__menu {\n\twidth: auto;\n\tdisplay: flex;\n\talign-items: auto;\n\tjustify-content: flex-start;\n\tflex-grow: 2;\n\tgap: 12px;\n}\n\n.tlui-people-menu__name {\n\ttext-align: start;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.tlui-people-menu__item__follow {\n\tflex-shrink: 0;\n}\n\n.tlui-people-menu__item[data-follow='true'] .tlui-people-menu__item__follow,\n.tlui-people-menu__item:has(.tlui-button:focus-visible) .tlui-people-menu__item__follow {\n\topacity: 1;\n}\n\n@media (hover: hover) {\n\t.tlui-people-menu__item__follow {\n\t\topacity: 0;\n\t}\n\n\t.tlui-people-menu__item:hover .tlui-people-menu__item__follow {\n\t\topacity: 1;\n\t}\n}\n\n/* --------------- Following indicator -------------- */\n\n.tlui-following-indicator {\n\tdisplay: block;\n\tposition: absolute;\n\tinset: 0px;\n\tborder-width: 2px;\n\tborder-style: solid;\n\tpointer-events: none;\n}\n\n/* --------------- Contextual toolbar --------------- */\n\n.tlui-contextual-toolbar {\n\tposition: absolute;\n}\n\n/**\n * N.B. We hide the toolbar while mousing down, so no need to turn these off.\n */\n.tlui-contextual-toolbar,\n.tlui-contextual-toolbar * {\n\tpointer-events: all;\n}\n\n.tlui-contextual-toolbar [data-isactive='true']::after {\n\tbackground-color: var(--tl-color-muted-2);\n\topacity: 1;\n}\n\n.tlui-contextual-toolbar {\n\topacity: 0;\n\ttransition: opacity 0.08s ease-in-out;\n}\n\n.tlui-contextual-toolbar,\n.tlui-contextual-toolbar * {\n\tpointer-events: none;\n}\n\n.tlui-contextual-toolbar[data-visible='true'] {\n\topacity: 1;\n\tz-index: var(--tl-layer-menus);\n}\n\n.tlui-contextual-toolbar[data-interactive='true'],\n.tlui-contextual-toolbar[data-interactive='true'] * {\n\tpointer-events: all;\n}\n\n.tlui-rich-text__toolbar-link-input {\n\tmargin-left: 12px;\n\t/*\n\t * Nice touch tweak: keep the link editor toolbar the same as the default toolbar.\n\t * This is so the toolbar size stays stable going in and out of the link editor.\n\t */\n\twidth: 148px;\n}\n\n.tlui-media__toolbar-alt-text-input {\n\tmargin-left: 12px;\n\t/*\n\t * Nice touch tweak: keep the link editor toolbar the same as the default toolbar.\n\t * This is so the toolbar size stays stable going in and out of the alt text editor.\n\t */\n\tmin-width: 200px;\n}\n\n.tlui-contextual-toolbar .tlui-input__wrapper {\n\theight: 40px;\n}\n\n.tlui-image__toolbar .tlui-slider__container {\n\twidth: 125px;\n}\n\n.tlui-image__toolbar .tlui-slider {\n\theight: 100%;\n}\n\n.tlui-image__toolbar .tlui-slider__track {\n\theight: 32px;\n}\n\n.tlui-image__toolbar .tlui-slider__thumb {\n\twidth: 14px;\n\theight: 14px;\n}\n\n/* ------------------- Animations ------------------- */\n@keyframes tlui-fade-out {\n\t0% {\n\t\topacity: 1;\n\t}\n\t100% {\n\t\topacity: 0;\n\t}\n}\n\n@keyframes tlui-slide-in {\n\tfrom {\n\t\ttransform: translateX(calc(100% + var(--tl-space-3)));\n\t}\n\tto {\n\t\ttransform: translateX(0px);\n\t}\n}\n\n@keyframes tlui-slide-out {\n\tfrom {\n\t\ttransform: translateX(var(--radix-toast-swipe-end-x));\n\t}\n\tto {\n\t\ttransform: translateX(calc(100% + var(--tl-space-3)));\n\t}\n}\n\n@keyframes tlui-slide-in-rtl {\n\tfrom {\n\t\ttransform: translateX(calc(-100% - var(--tl-space-3)));\n\t}\n\tto {\n\t\ttransform: translateX(0px);\n\t}\n}\n\n@keyframes tlui-slide-out-rtl {\n\tfrom {\n\t\ttransform: translateX(var(--radix-toast-swipe-end-x));\n\t}\n\tto {\n\t\ttransform: translateX(calc(-100% - var(--tl-space-3)));\n\t}\n}\n\n\n\n/* Workspace wrapper: fills the tab body like .dcv-root does for React Flow. */\n.dtv-root{position:relative;width:100%;height:100%;min-height:420px;overflow:hidden}\n.dtv-root .tl-container{--tl-font-sans:inherit}\n";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function TldrawWorkspace(): JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const DEFAULT_MAX_CACHE_BYTES: number;
|
|
2
|
+
export declare function setInspirationCacheDir(dir: string): void;
|
|
3
|
+
export declare function getInspirationCacheDir(): string;
|
|
4
|
+
export declare function cacheKey(sourceId: string, caseId: string): string;
|
|
5
|
+
export interface CachedImageMetadata {
|
|
6
|
+
contentType: string;
|
|
7
|
+
sourceId: string;
|
|
8
|
+
caseId: string;
|
|
9
|
+
savedAt: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function getCachedImagePath(key: string): string | null;
|
|
12
|
+
export declare function readCachedImage(key: string): {
|
|
13
|
+
data: Buffer;
|
|
14
|
+
contentType: string;
|
|
15
|
+
} | null;
|
|
16
|
+
export declare function writeCachedImage(key: string, data: Buffer, contentType: string, sourceId: string, caseId: string, maxBytes?: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* Enforce max cache size constraint by evicting oldest accessed files.
|
|
19
|
+
*/
|
|
20
|
+
export declare function evictCacheIfNeeded(maxBytes?: number): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PromptCase, PromptSource } from './types.js';
|
|
2
|
+
export declare const MAX_IMAGE_BYTES: number;
|
|
3
|
+
export declare const FETCH_TIMEOUT_MS = 6000;
|
|
4
|
+
/**
|
|
5
|
+
* Validate that an IP or hostname is not private/loopback (SSRF protection).
|
|
6
|
+
*/
|
|
7
|
+
export declare function isPrivateOrLocalAddress(hostname: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Check binary magic bytes to verify genuine image type.
|
|
10
|
+
* Rejects SVGs, HTML, scripts, executables.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sniffImageMimeType(buf: Buffer): 'image/jpeg' | 'image/png' | 'image/webp' | null;
|
|
13
|
+
/**
|
|
14
|
+
* Safely fetch a single URL with strict checks.
|
|
15
|
+
*/
|
|
16
|
+
export declare function fetchSingleImageUrl(urlStr: string, allowedDomains: string[]): Promise<{
|
|
17
|
+
data: Buffer;
|
|
18
|
+
contentType: string;
|
|
19
|
+
} | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Multi-tier racing / fallback fetcher:
|
|
22
|
+
* Tries candidate URLs in order until a valid image is retrieved.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fetchInspirationImage(source: PromptSource, promptCase: PromptCase): Promise<{
|
|
25
|
+
data: Buffer;
|
|
26
|
+
contentType: string;
|
|
27
|
+
} | null>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side route handlers for the Inspiration Library.
|
|
3
|
+
*/
|
|
4
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
export declare function serveInspirationCases(_req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Handle GET /plugins/dsh-image-gen/inspiration/image/:sourceId/:caseId
|
|
8
|
+
*/
|
|
9
|
+
export declare function serveInspirationImage(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Handle POST /plugins/dsh-image-gen/inspiration/update
|
|
12
|
+
*/
|
|
13
|
+
export declare function serveInspirationUpdate(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PromptCase, PromptSource } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Find the path to the inspiration snapshots folder.
|
|
4
|
+
* Supports both development (src/) and built distribution (root / lib).
|
|
5
|
+
*/
|
|
6
|
+
export declare function getSnapshotsDir(): string;
|
|
7
|
+
/**
|
|
8
|
+
* Load built-in prompt sources from packaged JSON snapshots.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadBuiltinSources(): PromptSource[];
|
|
11
|
+
/**
|
|
12
|
+
* Clear cached sources to allow reloading upon update.
|
|
13
|
+
*/
|
|
14
|
+
export declare function invalidateSourcesCache(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Get a specific prompt source by ID.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getPromptSource(sourceId: string): PromptSource | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Find a specific case within a source.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getPromptCase(sourceId: string, caseId: string): {
|
|
23
|
+
source: PromptSource;
|
|
24
|
+
promptCase: PromptCase;
|
|
25
|
+
} | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve candidate URLs for downloading an inspiration case's image.
|
|
28
|
+
* Uses a prioritized multi-tier fallback:
|
|
29
|
+
* 1. Fast, reliable global CDN (fastly.jsdelivr.net) - free, doesn't load author's server
|
|
30
|
+
* 2. Canghe.ai official website static origin (fast in mainland China)
|
|
31
|
+
* 3. GitHub raw official fallback
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveCaseImageCandidateUrls(source: PromptSource, promptCase: PromptCase): string[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions and contracts for the Prompt Library / Inspiration system.
|
|
3
|
+
*/
|
|
4
|
+
export interface PromptCase {
|
|
5
|
+
/** Unique case ID within the source (e.g. "544" or "case-544") */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Human-readable title */
|
|
8
|
+
title: string;
|
|
9
|
+
/** The full prompt text to be reused */
|
|
10
|
+
prompt: string;
|
|
11
|
+
/** Image identifier or path (e.g. "/images/case544.jpg" or "case544.jpg") */
|
|
12
|
+
image?: string | undefined;
|
|
13
|
+
/** Primary category (e.g. "UI & Interfaces", "Products & E-commerce") */
|
|
14
|
+
category?: string | undefined;
|
|
15
|
+
/** Visual styles (e.g. ["UI", "Poster", "Realistic"]) */
|
|
16
|
+
styles?: string[] | undefined;
|
|
17
|
+
/** Application scenarios (e.g. ["Tech", "Commerce"]) */
|
|
18
|
+
scenes?: string[] | undefined;
|
|
19
|
+
/** Original creator or source platform display name */
|
|
20
|
+
sourceLabel?: string | undefined;
|
|
21
|
+
/** Link to original social post or source article */
|
|
22
|
+
sourceUrl?: string | undefined;
|
|
23
|
+
/** Original author name if known */
|
|
24
|
+
author?: string | undefined;
|
|
25
|
+
/** Author homepage or profile */
|
|
26
|
+
authorUrl?: string | undefined;
|
|
27
|
+
/** License name if explicitly declared */
|
|
28
|
+
license?: string | undefined;
|
|
29
|
+
/** License URL if explicitly declared */
|
|
30
|
+
licenseUrl?: string | undefined;
|
|
31
|
+
/** Recommended model hint if available */
|
|
32
|
+
modelHint?: string | undefined;
|
|
33
|
+
/** Whether the case is marked as featured / staff pick */
|
|
34
|
+
featured?: boolean | undefined;
|
|
35
|
+
/** Direct link back to upstream repository documentation */
|
|
36
|
+
githubUrl?: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
export interface PromptSource {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
homepage?: string | undefined;
|
|
42
|
+
version: string;
|
|
43
|
+
schemaVersion: number;
|
|
44
|
+
/** Domain whitelist allowed for fetching images for this source */
|
|
45
|
+
allowedDomains: string[];
|
|
46
|
+
/** Array of prompt cases */
|
|
47
|
+
cases: PromptCase[];
|
|
48
|
+
/** Available categories extracted from cases */
|
|
49
|
+
categories?: string[] | undefined;
|
|
50
|
+
/** Available styles extracted from cases */
|
|
51
|
+
styles?: string[] | undefined;
|
|
52
|
+
/** Available scenes extracted from cases */
|
|
53
|
+
scenes?: string[] | undefined;
|
|
54
|
+
}
|
|
55
|
+
export interface InspirationManifest {
|
|
56
|
+
schemaVersion: number;
|
|
57
|
+
sourceVersion: string;
|
|
58
|
+
updatedAt: string;
|
|
59
|
+
sha256: string;
|
|
60
|
+
totalCases: number;
|
|
61
|
+
}
|
|
62
|
+
export interface InspirationCasesResponse {
|
|
63
|
+
sources: PromptSource[];
|
|
64
|
+
activeSourceId: string;
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-image-gen",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Bring ChatGPT-like image generation to DeepSeek Harness — Gemini, OpenAI, Seedream, DashScope, local ComfyUI & more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "shanliuling",
|
|
@@ -56,13 +56,6 @@
|
|
|
56
56
|
"platform": "web"
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
|
-
"scripts": {
|
|
60
|
-
"typecheck": "tsc --noEmit",
|
|
61
|
-
"build": "tsc && tsdown",
|
|
62
|
-
"prepare": "pnpm run build",
|
|
63
|
-
"test": "vitest run",
|
|
64
|
-
"pack:check": "pnpm pack --dry-run"
|
|
65
|
-
},
|
|
66
59
|
"engines": {
|
|
67
60
|
"node": "^22.19.0 || >=24.0.0"
|
|
68
61
|
},
|
|
@@ -134,9 +127,14 @@
|
|
|
134
127
|
"unrun": "^0.3.1",
|
|
135
128
|
"vitest": "^4.1.8"
|
|
136
129
|
},
|
|
137
|
-
"packageManager": "pnpm@11.7.0",
|
|
138
130
|
"dependencies": {
|
|
139
131
|
"lucide-react": "^1.38.0",
|
|
140
132
|
"tldraw": "5.3.0"
|
|
133
|
+
},
|
|
134
|
+
"scripts": {
|
|
135
|
+
"typecheck": "tsc --noEmit",
|
|
136
|
+
"build": "tsc && tsdown",
|
|
137
|
+
"test": "vitest run",
|
|
138
|
+
"pack:check": "pnpm pack --dry-run"
|
|
141
139
|
}
|
|
142
|
-
}
|
|
140
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: install-dsh-image-gen
|
|
3
|
-
description: Install, configure, verify, or remove the dsh-image-gen Bundle in a DeepSeek Harness Web profile. Use when a user wants Google, OpenAI-compatible relay, Seedream, DashScope, or local ComfyUI image generation in DSH, cannot find the Image generation settings card, or needs help testing generate_image.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Install DSH Image Gen
|
|
7
|
-
|
|
8
|
-
1. Confirm `dsh` runs and record `dsh --version`.
|
|
9
|
-
2. Ask which Web profile to change if the user did not name one. Default to `web` only when that profile already exists.
|
|
10
|
-
3. Install the pinned package or repository spec the user provides:
|
|
11
|
-
|
|
12
|
-
```sh
|
|
13
|
-
dsh plugin --profile <profile> add <package-or-git-spec>
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
4. If pnpm blocks the Git dependency's `prepare` script, explain that the allowance executes repository code during installation. Add only the exact package key pnpm reports to the profile's `pnpm-workspace.yaml`, then retry after the user approves.
|
|
17
|
-
5. Verify `dsh --profile <profile> --dump-config` contains the `dsh-image-gen` layer and `image-gen` row.
|
|
18
|
-
6. Start the profile. If port 3080 is already in use, identify the existing DSH process before stopping anything.
|
|
19
|
-
7. In Settings > Plugins > Plugin configuration > Image generation, select a cloud Provider and save its API key, or select Local ComfyUI, enter its URL, and import a ComfyUI API Format workflow JSON containing `{{prompt}}`. Never print, read back, or commit a key.
|
|
20
|
-
8. Test with “生成一张戴墨镜的赛博朋克猫图片”. Confirm one `generate_image` call succeeds and its image renders in the conversation.
|
|
21
|
-
|
|
22
|
-
For removal, run `dsh plugin --profile <profile> remove dsh-image-gen`. Do not delete the user's credentials unless they explicitly request it.
|
|
1
|
+
---
|
|
2
|
+
name: install-dsh-image-gen
|
|
3
|
+
description: Install, configure, verify, or remove the dsh-image-gen Bundle in a DeepSeek Harness Web profile. Use when a user wants Google, OpenAI-compatible relay, Seedream, DashScope, or local ComfyUI image generation in DSH, cannot find the Image generation settings card, or needs help testing generate_image.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Install DSH Image Gen
|
|
7
|
+
|
|
8
|
+
1. Confirm `dsh` runs and record `dsh --version`.
|
|
9
|
+
2. Ask which Web profile to change if the user did not name one. Default to `web` only when that profile already exists.
|
|
10
|
+
3. Install the pinned package or repository spec the user provides:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
dsh plugin --profile <profile> add <package-or-git-spec>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
4. If pnpm blocks the Git dependency's `prepare` script, explain that the allowance executes repository code during installation. Add only the exact package key pnpm reports to the profile's `pnpm-workspace.yaml`, then retry after the user approves.
|
|
17
|
+
5. Verify `dsh --profile <profile> --dump-config` contains the `dsh-image-gen` layer and `image-gen` row.
|
|
18
|
+
6. Start the profile. If port 3080 is already in use, identify the existing DSH process before stopping anything.
|
|
19
|
+
7. In Settings > Plugins > Plugin configuration > Image generation, select a cloud Provider and save its API key, or select Local ComfyUI, enter its URL, and import a ComfyUI API Format workflow JSON containing `{{prompt}}`. Never print, read back, or commit a key.
|
|
20
|
+
8. Test with “生成一张戴墨镜的赛博朋克猫图片”. Confirm one `generate_image` call succeeds and its image renders in the conversation.
|
|
21
|
+
|
|
22
|
+
For removal, run `dsh plugin --profile <profile> remove dsh-image-gen`. Do not delete the user's credentials unless they explicitly request it.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workbench chat panel: the right-side DSH conversation surface.
|
|
3
|
-
*
|
|
4
|
-
* A dedicated archived agent session lives on the host side (studio-chat.ts);
|
|
5
|
-
* this panel pulls its projected event feed through CHAT_ROUTE with adaptive
|
|
6
|
-
* polling (?since=<seq> increments; faster while a turn is running, slower
|
|
7
|
-
* when idle, paused while the webview is hidden) and submits user turns with
|
|
8
|
-
* POST. Tool-generated images are mirrored onto the tldraw canvas through the
|
|
9
|
-
* landing queue and upserted into the gallery (the recent list refreshes via
|
|
10
|
-
* its subscription). The panel is intentionally unmount-friendly: re-pulls
|
|
11
|
-
* from seq 0 on remount, and both landing and gallery writes are deduped by
|
|
12
|
-
* attachmentId, so tab switches are safe.
|
|
13
|
-
*/
|
|
14
|
-
import { type FC } from 'react';
|
|
15
|
-
/** Workspace scoping mirrored onto gallery records; shape-compatible with StudioWorkspaceProps. */
|
|
16
|
-
export interface ChatWorkspaceScope {
|
|
17
|
-
workspaceId?: string | undefined;
|
|
18
|
-
path?: string | undefined;
|
|
19
|
-
}
|
|
20
|
-
export declare const StudioChatPanel: FC<{
|
|
21
|
-
lang: 'zh' | 'en';
|
|
22
|
-
workspace?: ChatWorkspaceScope | null | undefined;
|
|
23
|
-
}>;
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Land one create_diagram result onto a tldraw page.
|
|
3
|
-
*
|
|
4
|
-
* Shapes and their arrow bindings are created in a single editor.run
|
|
5
|
-
* transaction, and a history stopping point is marked BEFORE the batch:
|
|
6
|
-
* tldraw's HistoryManager pops diffs until the next stop mark on undo, so
|
|
7
|
-
* without the mark two diagrams landing back-to-back would undo together.
|
|
8
|
-
* With it, each diagram is one independent undo step (undo/redo verified
|
|
9
|
-
* against the HistoryManager source and on the real canvas).
|
|
10
|
-
*
|
|
11
|
-
* Arrows are ELBOW connectors with BOTH terminals bound. Each terminal's
|
|
12
|
-
* binding side is derived from dagre's polyline: the first/last segment's
|
|
13
|
-
* dominant axis says which node edge the route uses (back edges leave the
|
|
14
|
-
* source's top and re-enter the target's bottom through the corridor dagre
|
|
15
|
-
* reserved, instead of tldraw guessing a center-to-center route). tldraw
|
|
16
|
-
* then re-routes the middle segments itself from the bindings — the
|
|
17
|
-
* polyline is a per-terminal side hint, not a full path contract. Whether
|
|
18
|
-
* the final arrows avoid crossing nodes and whether labels sit clear of
|
|
19
|
-
* geometry is validated on the real canvas, not assumed here.
|
|
20
|
-
*
|
|
21
|
-
* Derived anchors are clamped to the middle band (0.2–0.8) of their edge:
|
|
22
|
-
* tldraw's getEdgeFromNormalizedAnchor resolves the side via |x-0.5| vs
|
|
23
|
-
* |y-0.5|, and a corner-ish anchor could resolve to the wrong side.
|
|
24
|
-
*
|
|
25
|
-
* Edge labels set labelPosition from dagre's arc-length ratio — a
|
|
26
|
-
* best-effort estimate, since tldraw applies the ratio to its own routed
|
|
27
|
-
* path.
|
|
28
|
-
*
|
|
29
|
-
* Self loops (empty dagre polyline) fall back to center bindings: tldraw
|
|
30
|
-
* draws bound self-loop arrows natively.
|
|
31
|
-
*
|
|
32
|
-
* Every created shape carries meta.diagramId, the same dedupe identity the
|
|
33
|
-
* landing reconcile uses for images' meta.galleryId.
|
|
34
|
-
*/
|
|
35
|
-
import { type Editor } from 'tldraw';
|
|
36
|
-
import { type DiagramLayout, type DiagramLayoutEdge, type DiagramLayoutNode, type DiagramPoint } from './diagram-layout.js';
|
|
37
|
-
import type { TlDiagramLanding } from './tl-canvas-bridge.js';
|
|
38
|
-
/** Terminal binding plan: the precise anchor, or a center fallback. */
|
|
39
|
-
export interface TerminalPlan {
|
|
40
|
-
anchor: {
|
|
41
|
-
x: number;
|
|
42
|
-
y: number;
|
|
43
|
-
};
|
|
44
|
-
precise: boolean;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Derive one terminal's binding side + precise anchor from dagre's
|
|
48
|
-
* polyline. The boundary point sits ON the node outline (dagre guarantees
|
|
49
|
-
* this via its node-intersection pass), so the binding side is the OUTLINE
|
|
50
|
-
* EDGE CLOSEST TO THE BOUNDARY POINT — not the segment direction. The last
|
|
51
|
-
* segment often enters at an angle because dagre projects the corridor
|
|
52
|
-
* point onto the outline (e.g. a back edge entering the target's bottom
|
|
53
|
-
* with a final dx=57, dy=-48 segment), and direction-dominance would
|
|
54
|
-
* misread that as `right`. The segment direction is only used to break
|
|
55
|
-
* exact corner ties. The boundary point gives the cross-axis position,
|
|
56
|
-
* clamped to the anchor band.
|
|
57
|
-
*/
|
|
58
|
-
export declare function terminalPlan(node: Pick<DiagramLayoutNode, 'x' | 'y' | 'w' | 'h'>, boundary: DiagramPoint, next: DiagramPoint): TerminalPlan;
|
|
59
|
-
/**
|
|
60
|
-
* Binding plans for one edge's two terminals. Self loops (no dagre
|
|
61
|
-
* polyline) get center plans: tldraw renders bound self loops natively.
|
|
62
|
-
*/
|
|
63
|
-
export declare function edgeTerminalPlans(edge: DiagramLayoutEdge, fromNode: DiagramLayoutNode, toNode: DiagramLayoutNode): {
|
|
64
|
-
start: TerminalPlan;
|
|
65
|
-
end: TerminalPlan;
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* Land one diagram with its top-left at `origin`. The caller passes the
|
|
69
|
-
* precomputed layout (it needs the layout bounds to pick the origin, so the
|
|
70
|
-
* layout is computed once and shared). No-op on empty layouts.
|
|
71
|
-
*/
|
|
72
|
-
export declare function landDiagram(editor: Editor, landing: TlDiagramLanding, layout: DiagramLayout, origin: {
|
|
73
|
-
x: number;
|
|
74
|
-
y: number;
|
|
75
|
-
}): void;
|