Fix PR review: critical bugs, package config, remaining title attrs

Critical fixes:
- useSceneComputed: watchEffect on state.sceneVersion as fallback
  when replaceGraph() swaps the emitter (bridge stayed on old graph)
- useLayerDrag: capture monitorForElements() cleanup, onScopeDispose
  (was leaking global listeners on tab switch)

Package fixes:
- packages/vue: sideEffects: false for tree-shaking
- Build: --noEmit → --emitDeclarationOnly (was skipping .d.ts)
- canvaskit-wasm peerDep: optional via peerDependenciesMeta
- use-canvas.ts: onUnmounted → onScopeDispose

Remaining title= attrs migrated to <Tip>:
- AppMenu toggle UI, TypographySection missing font, ProviderSettings
This commit is contained in:
Danila Poyarkov 2026-03-17 15:30:28 +03:00
parent 6ffe201bec
commit 2757e9e6ec
7 changed files with 43 additions and 26 deletions

View file

@ -12,9 +12,10 @@
},
"main": "./src/index.ts",
"types": "./src/index.ts",
"sideEffects": false,
"files": ["src", "dist"],
"scripts": {
"build": "vue-tsc --noEmit",
"build": "vue-tsc --emitDeclarationOnly",
"prepublishOnly": "bun run build"
},
"repository": {
@ -40,6 +41,11 @@
"@open-pencil/core": "workspace:*",
"canvaskit-wasm": ">=0.39.0"
},
"peerDependenciesMeta": {
"canvaskit-wasm": {
"optional": true
}
},
"dependencies": {
"@vueuse/core": "^14.2.1"
}

View file

@ -1,5 +1,5 @@
import { useBreakpoints, useRafFn, useResizeObserver } from '@vueuse/core'
import { onMounted, onUnmounted, type Ref } from 'vue'
import { onMounted, onScopeDispose, type Ref } from 'vue'
import { getCanvasKit, getGpuBackend, SkiaRenderer } from '@open-pencil/core'
@ -169,7 +169,7 @@ export function useCanvas(
void init()
})
onUnmounted(() => {
onScopeDispose(() => {
destroyed = true
pause()
cancelAnimationFrame(resizeRaf)

View file

@ -1,4 +1,4 @@
import { ref, watchEffect, type Ref } from 'vue'
import { onScopeDispose, ref, watchEffect, type Ref } from 'vue'
import { draggable, dropTargetForElements, monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'
import {
@ -84,7 +84,7 @@ export function useLayerDrag(
})
}
monitorForElements({
const cleanupMonitor = monitorForElements({
onDrop: ({ source, location }) => {
const target = location.current.dropTargets[0]
if (!target) return
@ -117,6 +117,7 @@ export function useLayerDrag(
instructionTargetId.value = null
}
})
onScopeDispose(cleanupMonitor)
return {
draggingId,

View file

@ -1,4 +1,4 @@
import { computed, onScopeDispose, shallowRef, triggerRef, type ComputedRef } from 'vue'
import { computed, onScopeDispose, shallowRef, triggerRef, watchEffect, type ComputedRef } from 'vue'
import type { Editor } from '@open-pencil/core/editor'
@ -37,6 +37,11 @@ function getBridge(editor: Editor): SceneBridge {
editor.graph.emitter.on('node:reparented', trigger),
editor.graph.emitter.on('node:reordered', trigger)
)
const stop = watchEffect(() => {
void editor.state.sceneVersion
trigger()
})
unbinds.push(stop)
},
unsubscribe() {
if (--this.count > 0) return

View file

@ -201,14 +201,15 @@ const topMenus = [
@dblclick="startRename"
>{{ store.state.documentName }}</span
>
<button
data-test-id="app-toggle-ui"
class="flex size-6 shrink-0 cursor-pointer items-center justify-center rounded text-muted transition-colors hover:bg-hover hover:text-surface"
title="Toggle UI (⌘\)"
@click="store.state.showUI = !store.state.showUI"
>
<icon-lucide-sidebar class="size-3.5" />
</button>
<Tip label="Toggle UI (⌘\)">
<button
data-test-id="app-toggle-ui"
class="flex size-6 shrink-0 cursor-pointer items-center justify-center rounded text-muted transition-colors hover:bg-hover hover:text-surface"
@click="store.state.showUI = !store.state.showUI"
>
<icon-lucide-sidebar class="size-3.5" />
</button>
</Tip>
</div>
<div v-if="!IS_TAURI" class="flex items-center px-1 pb-1">
<MenubarRoot class="scrollbar-none flex items-center gap-0.5 overflow-x-auto">

View file

@ -92,13 +92,14 @@ function clearUnsplashKey() {
<template>
<PopoverRoot>
<PopoverTrigger
data-test-id="provider-settings-trigger"
class="rounded p-0.5 text-muted hover:bg-hover hover:text-surface"
title="Provider settings"
>
<icon-lucide-settings class="size-3" />
</PopoverTrigger>
<Tip label="Provider settings">
<PopoverTrigger
data-test-id="provider-settings-trigger"
class="rounded p-0.5 text-muted hover:bg-hover hover:text-surface"
>
<icon-lucide-settings class="size-3" />
</PopoverTrigger>
</Tip>
<PopoverPortal>
<PopoverContent

View file

@ -110,14 +110,17 @@ onMounted(async () => {
<div class="mb-1.5 flex items-center gap-1.5">
<FontPicker class="min-w-0 flex-1" :model-value="node.fontFamily" @select="selectFamily" />
<icon-lucide-alert-triangle
<Tip
v-if="hasMissingFonts"
data-test-id="typography-missing-font"
class="size-3.5 shrink-0 text-amber-400"
:title="
:label="
'Missing font' + (missingFonts.length > 1 ? 's' : '') + ': ' + missingFonts.join(', ')
"
/>
>
<icon-lucide-alert-triangle
data-test-id="typography-missing-font"
class="size-3.5 shrink-0 text-amber-400"
/>
</Tip>
</div>
<!-- Weight + Size -->