From 26aa0847d9588ea74611a54e1ec48e314c72476c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pons?= Date: Mon, 6 Oct 2025 17:42:16 +0200 Subject: [PATCH] Fix comrpessing bug on null buffers, make pinned floaters resizable and optimize a few things here and there --- composables/useDatabase.ts | 5 ++- db.sqlite | Bin 761856 -> 761856 bytes db.sqlite-shm | Bin 32768 -> 32768 bytes db.sqlite-wal | Bin 8272 -> 8272 bytes server/api/file/content/[id].get.ts | 2 +- server/middleware/compress.ts | 5 +++ server/tasks/pull.ts | 4 +- shared/auth.util.ts | 2 +- shared/canvas.util.ts | 3 +- shared/components.util.ts | 66 +++++++++++++++++++++++---- shared/content.util.ts | 3 ++ shared/dom.util.ts | 14 ------ shared/floating.util.ts | 67 +++++++++++++++------------- shared/proses.ts | 53 +++++++++------------- 14 files changed, 133 insertions(+), 91 deletions(-) diff --git a/composables/useDatabase.ts b/composables/useDatabase.ts index d64ff95..622aa6a 100644 --- a/composables/useDatabase.ts +++ b/composables/useDatabase.ts @@ -2,7 +2,9 @@ import { Database } from "bun:sqlite"; import { BunSQLiteDatabase, drizzle } from "drizzle-orm/bun-sqlite"; import * as schema from '../db/schema'; -let instance: BunSQLiteDatabase; +let instance: BunSQLiteDatabase & { + $client: Database; +}; export default function useDatabase() { if(!instance) @@ -13,6 +15,7 @@ export default function useDatabase() instance.run("PRAGMA journal_mode = WAL;"); instance.run("PRAGMA foreign_keys = true;"); + instance.run("PRAGMA optimize=0x10002;"); } return instance; diff --git a/db.sqlite b/db.sqlite index 10bf56093338b600b943493ecaaa3cc65e1887d3..e8012265b7495d352a1b53254bfd0f75252622b5 100644 GIT binary patch delta 48579 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

x4+(V)p-(XChzmYN_6f;gB34MS97sNp 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 Iec%!R034+>n*aa+ delta 37026 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 diff --git a/db.sqlite-shm b/db.sqlite-shm index c97dcb9647dcfce2add43d9680200f2179982df8..944e3028ad7221dbda54971a0740ebc2ba5b32a4 100644 GIT binary patch delta 64 vcmZo@U}|V!njj&;Q@G&5J!g~USKs6~1|ALw50&}FFRt|12#?gpga!2gWl|ey delta 64 wcmZo@U}|V!njj%@d76sxETM(cDU&A!@wMLntao=xl)=ouAMi+ROjuA40DJiz;{X5v diff --git a/db.sqlite-wal b/db.sqlite-wal index dff6b9050722d858a95230499930cbde32d4cfd6..0c6d66eb775b9451e1d0a925f8b9dced9cfc383e 100644 GIT binary patch delta 102 zcmccMaKXXCyq>LzLHCga1A_nq2wZ)W;~022AY3f?{R`Eeu6sZsHXz;w6MrY}Qqeut kVd6%Mzx>jVA7ujNIFRKpEGXnzZsL4zbAZ5KerBKo08i^DO#lD@ delta 102 zcmccMaKXXCyq>LzLHCga1A_nq2&7D&6vWqh|Fe4KwDQXipS6HOY(TsVChqHe{&b-v mcj-opzx>j7{ig!uIFRLyRi<5*UMMtcbAZ5Ke&$CnegFXd(Ix%> diff --git a/server/api/file/content/[id].get.ts b/server/api/file/content/[id].get.ts index d729636..8a59e69 100644 --- a/server/api/file/content/[id].get.ts +++ b/server/api/file/content/[id].get.ts @@ -35,7 +35,7 @@ export default defineEventHandler(async (e) => { return data.content; } - return; + return null; } catch(_e) { diff --git a/server/middleware/compress.ts b/server/middleware/compress.ts index 91ce94d..58af3c3 100644 --- a/server/middleware/compress.ts +++ b/server/middleware/compress.ts @@ -16,5 +16,10 @@ export default defineEventHandler(async (event) => { //@ts-expect-error _end.call(event.node.res, await Bun.zstdCompress(buffer), ...args); } + else + { + //@ts-expect-error + _end.call(event.node.res, body, ...args); + } }; }); \ No newline at end of file diff --git a/server/tasks/pull.ts b/server/tasks/pull.ts index adca089..5fcd95b 100644 --- a/server/tasks/pull.ts +++ b/server/tasks/pull.ts @@ -105,8 +105,8 @@ function reshapeContent(content: string, type: FileType): string | null return content; case "canvas": const data = JSON.parse(content) as CanvasContent; - data.edges?.forEach(e => { console.log(e.color); e.color = typeof e.color === 'string' ? getColor(e.color) : undefined; console.log(e.color); }); - data.nodes?.forEach(e => { console.log(e.color); e.color = typeof e.color === 'string' ? getColor(e.color) : undefined; console.log(e.color); }); + data.edges?.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined); + data.nodes?.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined); return JSON.stringify(data); default: case 'folder': diff --git a/shared/auth.util.ts b/shared/auth.util.ts index 94017a0..9d5e462 100644 --- a/shared/auth.util.ts +++ b/shared/auth.util.ts @@ -2,7 +2,7 @@ export function hasPermissions(userPermissions: string[], neededPermissions: str { for(let i = 0; i < neededPermissions.length; i++) { - const list = neededPermissions[i].split(' '); + const list = neededPermissions[i]!.split(' '); if(list.every(e => userPermissions.includes(e))) { diff --git a/shared/canvas.util.ts b/shared/canvas.util.ts index 8a6d9e3..5217e74 100644 --- a/shared/canvas.util.ts +++ b/shared/canvas.util.ts @@ -173,7 +173,6 @@ export class Node extends EventTarget { border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40 dark:bg-dark-40` } } } - export class NodeEditable extends Node { edges: Set = new Set(); @@ -440,6 +439,7 @@ export class Canvas mount() { const dragMove = (e: MouseEvent) => { + e.preventDefault(); this.dragMove(e); }; const dragEnd = (e: MouseEvent) => { @@ -515,6 +515,7 @@ export class Canvas this.container.removeEventListener('touchmove', touchmove); }; const touchmove = (e: TouchEvent) => { + e.preventDefault(); const pos = center(e.touches); this._x = this.visualX = this._x - (this.lastX - pos.x) / this._zoom; this._y = this.visualY = this._y - (this.lastY - pos.y) / this._zoom; diff --git a/shared/components.util.ts b/shared/components.util.ts index 204fb70..9349301 100644 --- a/shared/components.util.ts +++ b/shared/components.util.ts @@ -502,32 +502,61 @@ export function tabgroup(tabs: Array<{ id: string, title: NodeChildren, content: }) return container as HTMLDivElement & { refresh: () => void }; } -export function floater(container: HTMLElement, content: NodeChildren | (() => NodeChildren), settings?: { class?: Class, position?: Placement, pinned?: boolean, cover?: 'width' | 'height' | 'all' | 'none' }) +export function floater(container: HTMLElement, content: NodeChildren | (() => NodeChildren), settings?: { class?: Class, position?: Placement, pinned?: boolean, cover?: 'width' | 'height' | 'all' | 'none', events?: { show: Array, hide: Array, onshow?: (this: HTMLElement) => boolean, onhide?: (this: HTMLElement) => boolean }, title?: string }) { let viewport = document.getElementById('mainContainer') ?? undefined; + let diffX, diffY, targetWidth, targetHeight; const dragstart = (e: MouseEvent) => { e.preventDefault(); e.stopImmediatePropagation(); window.addEventListener('mousemove', dragmove); window.addEventListener('mouseup', dragend); + + const box = floating.content.getBoundingClientRect(); + diffX = e.clientX - box.x; + diffY = e.clientY - box.y; }; + const resizestart = (e: MouseEvent) => { + e.preventDefault(); + e.stopImmediatePropagation(); + window.addEventListener('mousemove', resizemove); + window.addEventListener('mouseup', resizeend); + }; + const dragmove = (e: MouseEvent) => { const box = floating.content.getBoundingClientRect(); - const viewbox = viewport?.getBoundingClientRect(); - box.x = clamp(box.x + e.movementX, viewbox?.left ?? 0, (viewbox?.right ?? Infinity) - box.width); - box.y = clamp(box.y + e.movementY, viewbox?.top ?? 0, (viewbox?.bottom ?? Infinity) - box.height); + const viewbox = viewport?.getBoundingClientRect() ?? { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight, left: 0, right: window.innerWidth, top: 0, bottom: window.innerHeight }; + box.x = clamp(e.clientX - diffX!, viewbox?.left ?? 0, viewbox.right - box.width); + box.y = clamp(e.clientY - diffY!, viewbox?.top ?? 0, viewbox.bottom - box.height); Object.assign(floating.content.style, { left: `${box.x}px`, top: `${box.y}px`, - }) + }); }; + const resizemove = (e: MouseEvent) => { + const box = floating.content.getBoundingClientRect(); + const viewbox = viewport?.getBoundingClientRect() ?? { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight, left: 0, right: window.innerWidth, top: 0, bottom: window.innerHeight }; + box.width = clamp(e.clientX - box.x, 200, Math.min(750, viewbox.right - box.x)); + box.height = clamp(e.clientY - box.y, 150, Math.min(750, viewbox.bottom - box.y)); + + Object.assign(floating.content.style, { + width: `${box.width}px`, + height: `${box.height}px`, + }); + }; + const dragend = (e: MouseEvent) => { e.preventDefault(); window.removeEventListener('mousemove', dragmove); window.removeEventListener('mouseup', dragend); }; + const resizeend = (e: MouseEvent) => { + e.preventDefault(); + window.removeEventListener('mousemove', resizemove); + window.removeEventListener('mouseup', resizeend); + }; const floating = popper(container, { arrow: true, @@ -535,18 +564,37 @@ export function floater(container: HTMLElement, content: NodeChildren | (() => N offset: 12, cover: settings?.cover, placement: settings?.position, - class: 'bg-light-10 dark:bg-dark-10 border border-light-35 dark:border-dark-35 group-data-[pinned]:bg-light-15 dark:group-data-[pinned]:bg-dark-15 group-data-[pinned]:border-light-50 dark:group-data-[pinned]:border-dark-50 text-light-100 dark:text-dark-100 z-[45]', - content: () => [ settings?.pinned !== undefined ? dom('div', { class: 'hidden group-data-[pinned]:flex flex-row gap-4 justify-end border-b border-light-35 dark:border-dark-35 cursor-move', listeners: { mousedown: dragstart } }, [ tooltip(icon('radix-icons:cross-1', { width: 12, height: 12, listeners: { click: (e) => { e.preventDefault(); floating.hide(); } }, class: 'p-1 cursor-pointer' }), 'Fermer', 'right') ]) : undefined, div('min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px] w-full overflow-auto box-content', typeof content === 'function' ? content() : content) ], + class: 'bg-light-10 dark:bg-dark-10 border border-light-35 dark:border-dark-35 group-data-[pinned]:bg-light-15 dark:group-data-[pinned]:bg-dark-15 group-data-[pinned]:border-light-50 dark:group-data-[pinned]:border-dark-50 text-light-100 dark:text-dark-100 z-[45] relative group-data-[pinned]:h-full', + content: () => [ settings?.pinned !== undefined ? div('hidden group-data-[pinned]:flex flex-row justify-end border-b border-light-35 dark:border-dark-35', [ dom('span', { class: 'w-full cursor-move text-xs', listeners: { mousedown: dragstart }, text: settings?.title }), tooltip(dom('div', { class: 'cursor-pointer flex', listeners: { mousedown: (e) => { e.stopImmediatePropagation(); floating.hide(); } } }, [icon('radix-icons:cross-1', { width: 12, height: 12, class: 'p-1' })]), 'Fermer', 'right') ]) : undefined, div('h-full group-data-[pinned]:h-[calc(100%-21px)] w-full min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px] group-data-[pinned]:min-h-[initial] group-data-[pinned]:min-w-[initial] group-data-[pinned]:max-h-[initial] group-data-[pinned]:max-w-[initial] overflow-auto box-content', typeof content === 'function' ? content() : content), dom('span', { class: 'hidden group-data-[pinned]:flex absolute bottom-0 right-0 cursor-nw-resize z-50', listeners: { mousedown: resizestart } }, [ icon('ph:notches', { width: 12, height: 12 }) ]) ], viewport }); if(settings?.pinned === false) + { floating.content.addEventListener('click', () => { + if(floating.content.hasAttribute('data-pinned')) + return; + + const box = floating.content.children.item(0)!.getBoundingClientRect(); + Object.assign(floating.content.style, { + width: `${box.width + 21}px`, + height: `${box.height + 21}px`, + }); floating.stop(); + floating.content.addEventListener('mousedown', function() { + if(!floating.content.hasAttribute('data-pinned')) + return; + this.parentElement?.appendChild(this); - }, { passive: true, capture: true }) - }, { once: true }); + }, { passive: true }); + }); + } + else + { + floating.stop(); + floating.show(); + } return container; } diff --git a/shared/content.util.ts b/shared/content.util.ts index 630c3f7..7319b24 100644 --- a/shared/content.util.ts +++ b/shared/content.util.ts @@ -124,6 +124,9 @@ export class Content if(Content._ready) return Promise.resolve(true); + if(Content.initPromise) + return Content.initPromise; + Content.initPromise = new Promise(async (res) => { try { diff --git a/shared/dom.util.ts b/shared/dom.util.ts index 987c526..b1105ba 100644 --- a/shared/dom.util.ts +++ b/shared/dom.util.ts @@ -116,9 +116,6 @@ export interface IconProperties rotate?: number|string; style?: Record | string; class?: Class; - listeners?: { - [K in keyof HTMLElementEventMap]?: Listener - }; } const iconCache: Map = new Map(); export function icon(name: string, properties?: IconProperties): HTMLElement @@ -160,17 +157,6 @@ export function icon(name: string, properties?: IconProperties): HTMLElement else for(const [k, v] of Object.entries(properties.style)) if(v !== undefined) element.attributeStyleMap.set(k, v); } - if(properties?.listeners) - { - for(let [k, v] of Object.entries(properties.listeners)) - { - const key = k as keyof HTMLElementEventMap, value = v as Listener; - if(typeof value === 'function') - element.addEventListener(key, value.bind(element)); - else if(value) - element.addEventListener(key, value.listener.bind(element), value.options); - } - } return element; } diff --git a/shared/floating.util.ts b/shared/floating.util.ts index ead1ae7..8fb5808 100644 --- a/shared/floating.util.ts +++ b/shared/floating.util.ts @@ -21,9 +21,12 @@ export interface PopperProperties extends FloatingProperties { content?: NodeChildren | (() => NodeChildren); delay?: number; - - onShow?: () => boolean | void; - onHide?: () => boolean | void; + events?: { + show: Array; + hide: Array; + onshow?: () => boolean; + onhide?: () => boolean; + }; } export interface ModalProperties @@ -36,13 +39,13 @@ export interface ModalProperties let teleport: HTMLDivElement; export function init() { - teleport = dom('div', { attributes: { id: 'popper-container' }, class: 'absolute top-0 left-0' }); + teleport = dom('div', { attributes: { id: 'popper-container' }, class: 'absolute top-0 left-0 z-40' }); document.body.appendChild(teleport); } export function popper(container: HTMLElement, properties?: PopperProperties) { - let shown = false, manualStop = false, timeout: Timer; + let state: 'shown' | 'showing' | 'hidden' | 'hiding' | 'pinned' = 'hidden', manualStop = false, timeout: Timer; const arrow = svg('svg', { class: ' group-data-[pinned]:hidden absolute fill-light-35 dark:fill-dark-35', attributes: { width: "12", height: "8", viewBox: "0 0 20 10" } }, [svg('polygon', { attributes: { points: "0,0 20,0 10,10" } })]); const content = dom('div', { class: properties?.class, style: properties?.style }); const floater = dom('div', { class: 'fixed hidden group', attributes: { 'data-state': 'closed' } }, [ content, properties?.arrow ? arrow : undefined ]); @@ -86,23 +89,23 @@ export function popper(container: HTMLElement, properties?: PopperProperties) right: 'left', bottom: 'top', left: 'right', - }[side]!; + }[side]!; - const rotation = { + const rotation = { top: "0", bottom: "180", left: "270", right: "90" - }[side]!; + }[side]!; - Object.assign(arrow.style, { + Object.assign(arrow.style, { left: arrowX != null ? `${arrowX}px` : '', top: arrowY != null ? `${arrowY}px` : '', right: '', bottom: '', [staticSide]: `-7px`, transform: `rotate(${rotation}deg)`, - }); + }); } }); } @@ -110,7 +113,7 @@ export function popper(container: HTMLElement, properties?: PopperProperties) let _stop: () => void | undefined, empty = true; function show() { - if(shown || !properties?.onShow || properties?.onShow() !== false) + if(state !== 'shown' && state !== 'showing' && state !== 'pinned' && (!properties?.events?.onshow || properties?.events?.onshow() !== false)) { if(typeof properties?.content === 'function') properties.content = properties.content(); @@ -122,9 +125,10 @@ export function popper(container: HTMLElement, properties?: PopperProperties) } clearTimeout(timeout); - + state = 'showing'; + timeout = setTimeout(() => { - if(!shown) + if(state !== 'shown') { teleport!.appendChild(floater); @@ -132,6 +136,7 @@ export function popper(container: HTMLElement, properties?: PopperProperties) floater.classList.toggle('hidden', false); update(); + _stop && _stop(); _stop = FloatingUI.autoUpdate(container, floater, update, { animationFrame: true, layoutShift: false, @@ -139,35 +144,39 @@ export function popper(container: HTMLElement, properties?: PopperProperties) ancestorScroll: false, ancestorResize: false, }); + console.log("Starting", floater); } - shown = true; + state = 'shown'; }, properties?.delay ?? 0); } } function hide() { - if(!manualStop && (!properties?.onHide || properties?.onHide() !== false)) + if(state !== 'hiding' && state !== 'pinned' && (!properties?.events?.onhide || properties?.events?.onhide() !== false)) { clearTimeout(timeout); + state = 'hiding'; timeout = setTimeout(() => { floater.remove(); _stop && _stop(); + console.log('Stoping', floater); floater.setAttribute('data-state', 'closed'); floater.classList.toggle('hidden', true); - shown = false; - }, shown ? properties?.delay ?? 0 : 0); + state = 'hidden'; + }, properties?.delay ?? 0); } } function start() { - manualStop = false; + state = 'hidden'; floater.toggleAttribute('data-pinned', false); update(); + _stop && _stop(); _stop = FloatingUI.autoUpdate(container, floater, update, { animationFrame: true, layoutShift: false, @@ -175,33 +184,28 @@ export function popper(container: HTMLElement, properties?: PopperProperties) ancestorScroll: false, ancestorResize: false, }); + console.log('Starting', floater); } function stop() { - manualStop = true; + state = 'pinned'; floater.toggleAttribute('data-pinned', true); _stop && _stop(); + console.log('Stoping', floater); clearTimeout(timeout); } function link(element: HTMLElement) { - Object.entries({ - 'mouseenter': show, - 'mousemove': show, - 'mouseleave': hide, - 'focus': show, - 'blur': hide, - } as Record void>).forEach(([event, listener]) => { - element.addEventListener(event, listener); - }); + (properties?.events?.show ?? ['mouseenter', 'mousemove', 'focus']).forEach((e: keyof HTMLElementEventMap) => element.addEventListener(e, show)); + (properties?.events?.hide ?? ['mouseleave', 'blur']).forEach((e: keyof HTMLElementEventMap) => element.addEventListener(e, hide)); } link(container); link(floater); return { container, content: floater, stop, start, show: () => { - if(!shown) + if(state !== 'shown') { teleport!.appendChild(floater); @@ -210,10 +214,11 @@ export function popper(container: HTMLElement, properties?: PopperProperties) update(); } - shown = true; + state = 'shown'; }, hide: () => { floater.remove(); _stop && _stop(); + console.log('Stoping', floater); floater.setAttribute('data-state', 'closed'); floater.classList.toggle('hidden', true); @@ -221,7 +226,7 @@ export function popper(container: HTMLElement, properties?: PopperProperties) manualStop = false; floater.toggleAttribute('data-pinned', false); - shown = false; + state = 'hidden'; } }; } export function followermenu(target: FloatingUI.ReferenceElement, content: NodeChildren, properties?: FollowerProperties) diff --git a/shared/proses.ts b/shared/proses.ts index f53a417..6516c48 100644 --- a/shared/proses.ts +++ b/shared/proses.ts @@ -41,7 +41,7 @@ export const a: Prose = { { const canvas = new Canvas((_content as LocalContent<'canvas'>).content); queueMicrotask(() => canvas.mount()); - return dom('div', { class: 'w-[600px] h-[600px] relative' }, [canvas.container]); + return dom('div', { class: 'w-[600px] h-[600px] group-data-[pinned]:h-full group-data-[pinned]:w-full h-[600px] relative w-[600px] relative' }, [canvas.container]); } return div(''); })).current], { position: 'bottom-start', pinned: false }) : element; @@ -55,41 +55,32 @@ export const preview: Prose = { const overview = Content.getFromPath(pathname === '' && hash.length > 0 ? unifySlug(router.currentRoute.value.params.path ?? '') : pathname); - const el = dom('span', { class: ['cursor-pointer text-accent-blue inline-flex items-center', properties?.class] }, [ + const element = dom('span', { class: ['cursor-pointer text-accent-blue inline-flex items-center', properties?.class] }, [ ...(children ?? []), overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined ]); const magicKeys = useMagicKeys(); - return overview ? popper(el, { - arrow: true, - delay: 150, - offset: 12, - cover: "height", - placement: 'bottom-start', - class: ['data-[side=bottom]:animate-slideUpAndFade data-[side=right]:animate-slideLeftAndFade data-[side=left]:animate-slideRightAndFade data-[side=top]:animate-slideDownAndFade w-[300px] bg-light-10 dark:bg-dark-10 border border-light-35 dark:border-dark-35 data-[state=open]:transition-transform text-light-100 dark:text-dark-100 w-full z-[45]', - { 'min-w-[200px] min-h-[150px] max-w-[600px] max-h-[600px]': !properties?.size || properties.size === 'large', 'max-w-[400px] max-h-[250px]': properties.size === 'small' } - ], - content: () => { - return [async('large', Content.getContent(overview.id).then((_content) => { - if(_content?.type === 'markdown') - { - return render((_content as LocalContent<'markdown'>).content ?? '', hash.length > 0 ? hash.substring(1) : undefined, { class: 'w-full max-h-full overflow-auto py-4 px-6' }); - } - if(_content?.type === 'canvas') - { - const canvas = new Canvas((_content as LocalContent<'canvas'>).content); - queueMicrotask(() => canvas.mount()); - return dom('div', { class: 'w-[600px] h-[600px] relative' }, [canvas.container]); - } - return div(''); - })).current]; - }, - onShow() { - if(!magicKeys.current.has('control') || magicKeys.current.has('meta')) - return false; - }, - }).container : el; + return !!overview ? floater(element, () => [async('large', Content.getContent(overview.id).then((_content) => { + if(_content?.type === 'markdown') + { + return render((_content as LocalContent<'markdown'>).content ?? '', hash.length > 0 ? hash.substring(1) : undefined, { class: 'w-full max-h-full overflow-auto py-4 px-6' }); + } + if(_content?.type === 'canvas') + { + const canvas = new Canvas((_content as LocalContent<'canvas'>).content); + queueMicrotask(() => canvas.mount()); + return dom('div', { class: 'w-[600px] h-[600px] group-data-[pinned]:h-full group-data-[pinned]:w-full h-[600px] relative w-[600px] relative' }, [canvas.container]); + } + return div(''); + })).current], { position: 'bottom-start', pinned: false, + events: { + show: ['mouseenter', 'mousemove'], + hide: ['mouseleave'], + onshow() { + return !magicKeys.current.has('control') || !magicKeys.current.has('meta'); + } + }, }) : element; } } export const callout: Prose = {