{"_id":"@affino/diagram-vue","_rev":"3-355a62aaf9f54c78dc43ea08b56d5f28","name":"@affino/diagram-vue","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@affino/diagram-vue","version":"0.1.0","keywords":["affino","vue","vue3","diagram","headless"],"author":"Anton Pavlov <a.pavlov@affino.dev>","license":"MIT","_id":"@affino/diagram-vue@0.1.0","maintainers":[{"name":"affino","email":"anton.pavlov.personal@gmail.com"}],"homepage":"https://github.com/affinio/affinio/tree/main/packages/diagram-vue#readme","bugs":{"url":"https://github.com/affinio/affinio/issues"},"dist":{"shasum":"55a862db7b6dc7dc71862ec79eb76833c7a1c02c","tarball":"https://registry.npmjs.org/@affino/diagram-vue/-/diagram-vue-0.1.0.tgz","fileCount":29,"integrity":"sha512-UypKPAZ+7lR+qP5iqFIBI8Ws0gnBkUIFiU0O/3JeBB764kYYvagAJZ4LI5ZOxnMj5+9l8cht1keXgwBTZClr3w==","signatures":[{"sig":"MEYCIQDWAuEOd+vK4GK30srsaM80hrsdck7VJ56S9HLb5Sd08gIhAOvVDwNfcy01ocCtlsSs+XdKTi0qWOQM0nAJMuvNmMNi","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37060},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc --build tsconfig.json"},"_npmUser":{"name":"affino","email":"anton.pavlov.personal@gmail.com"},"repository":{"url":"git+https://github.com/affinio/affinio.git","type":"git","directory":"packages/diagram-vue"},"description":"Vue adapter for @affino/diagram-core","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@affino/diagram-core":"^0.1.0"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.4.0","vitest":"^4.0.18"},"peerDependencies":{"vue":"^3.3.0"},"_npmOperationalInternal":{"tmp":"tmp/diagram-vue_0.1.0_1781796956279_0.6862478515448562","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@affino/diagram-vue","version":"0.2.0","keywords":["affino","vue","vue3","diagram","headless"],"author":"Anton Pavlov <a.pavlov@affino.dev>","license":"MIT","_id":"@affino/diagram-vue@0.2.0","maintainers":[{"name":"affino","email":"anton.pavlov.personal@gmail.com"}],"homepage":"https://github.com/affinio/affinio/tree/main/packages/diagram-vue#readme","bugs":{"url":"https://github.com/affinio/affinio/issues"},"dist":{"shasum":"654267c603514c427ef453d776b8a83a50488de6","tarball":"https://registry.npmjs.org/@affino/diagram-vue/-/diagram-vue-0.2.0.tgz","fileCount":29,"integrity":"sha512-PDHLlsfxhPQFUwNmQAZ9qiXeUP5SIE/t3Da5hESSoW4hhtEmjfxCGhYTdFfMaYLhuvAXAKxCgKtqevgElPo1YA==","signatures":[{"sig":"MEUCIE5cIjKUZWiIscn/d1+xXNJOC35yll8AGFgLcNnYUIp4AiEA9ocDi3F/UjSbJixbFD5Zab74wmATC+Hz67RdK7lP1SU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDDgyNl3G8wsBSqUnF1NXp+jf3SddJ0f0dk70vjJcgXuQIhAP7uo2KAyUT02MGR7CUd03gy6WTji+X6teIU3E7qQqGx","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39285},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc --build tsconfig.json"},"_npmUser":{"name":"affino","email":"anton.pavlov.personal@gmail.com"},"repository":{"url":"git+https://github.com/affinio/affinio.git","type":"git","directory":"packages/diagram-vue"},"description":"Vue adapter for @affino/diagram-core","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"@affino/diagram-core":"^0.2.0"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.4.0","vitest":"^4.0.18"},"peerDependencies":{"vue":"^3.3.0"},"_npmOperationalInternal":{"tmp":"tmp/diagram-vue_0.2.0_1789728400235_0.3473810513376294","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@affino/diagram-vue@0.3.0","bugs":{"url":"https://github.com/affinio/affinio/issues"},"dist":{"shasum":"c7a8fa1b5b30bfc7ca9f3d4079a495c409c1ad45","tarball":"https://registry.npmjs.org/@affino/diagram-vue/-/diagram-vue-0.3.0.tgz","fileCount":29,"integrity":"sha512-KhJ1+bZVMNaXd0mBkuVMPiwTKCNEBO8wLPTfDbHgHSqwCY10oTFl4btLmqEzZi5QFrGm0UpWNKO1D1Zf1YCNIg==","signatures":[{"sig":"MEUCIC2egqqtWDhvg+zfMclwhqSRhNb1wrX0C9K2An1A8ol5AiEA3ABpIGYNIwfr+WXL4mvYx0vY9NZmgSAmEL3qK3/kFQY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEIGbJ7EXaLRBupAHMw/Zf3jm+sTyEONQxVhtTSnw+3JAiAlGKb3HybPApcK/LCg7xIa1gyqZcLCQPndgNJnW+WIuA=="}],"unpackedSize":41335},"main":"dist/index.js","name":"@affino/diagram-vue","type":"module","types":"dist/index.d.ts","author":"Anton Pavlov <a.pavlov@affino.dev>","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"license":"MIT","scripts":{"test":"vitest run","build":"tsc --build tsconfig.json"},"version":"0.3.0","_npmUser":{"name":"affino","email":"anton.pavlov.personal@gmail.com"},"homepage":"https://github.com/affinio/affinio/tree/main/packages/diagram-vue#readme","keywords":["affino","vue","vue3","diagram","headless"],"repository":{"url":"git+https://github.com/affinio/affinio.git","type":"git","directory":"packages/diagram-vue"},"description":"Vue adapter for @affino/diagram-core","directories":{},"maintainers":[{"name":"affino","email":"anton.pavlov.personal@gmail.com"}],"sideEffects":false,"dependencies":{"@affino/diagram-core":"^0.3.0"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.4.0","vitest":"^4.0.18"},"peerDependencies":{"vue":"^3.3.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/diagram-vue_0.3.0_1789834317273_0.4758793044399332"}}},"time":{"created":"2026-06-18T15:35:56.099Z","modified":"2026-09-19T16:11:57.511Z","0.1.0":"2026-06-18T15:35:56.415Z","0.2.0":"2026-09-18T10:46:40.325Z","0.3.0":"2026-09-19T16:11:57.348Z"},"bugs":{"url":"https://github.com/affinio/affinio/issues"},"author":"Anton Pavlov <a.pavlov@affino.dev>","license":"MIT","homepage":"https://github.com/affinio/affinio/tree/main/packages/diagram-vue#readme","keywords":["affino","vue","vue3","diagram","headless"],"repository":{"url":"git+https://github.com/affinio/affinio.git","type":"git","directory":"packages/diagram-vue"},"description":"Vue adapter for @affino/diagram-core","maintainers":[{"name":"affino","email":"anton.pavlov.personal@gmail.com"}],"readme":"# @affino/diagram-vue\n\nVue adapter for `@affino/diagram-core`. The package owns Vue refs, lifecycle cleanup, DOM pointer bridging, viewport resize bridging, and render projections. Diagram rules stay in core.\n\n## Install\n\n```bash\npnpm add @affino/diagram-core @affino/diagram-vue vue\n```\n\n## Basic setup\n\n```ts\nimport { useDiagramEngine, useDiagramSelection, useDiagramVisibleEntities } from \"@affino/diagram-vue\"\n\nconst diagram = useDiagramEngine({\n  nodes: [{ id: \"bay-a\", kind: \"node\", x: 0, y: 0, width: 160, height: 80 }],\n  ports: [{ id: \"bay-a-out\", kind: \"port\", nodeId: \"bay-a\", x: 160, y: 40 }],\n  viewport: { x: 0, y: 0, width: 900, height: 600, zoom: 1 },\n})\nconst selection = useDiagramSelection(diagram)\nconst visible = useDiagramVisibleEntities(diagram)\n\nselection.setSelection([\"bay-a\"], \"bay-a\")\n```\n\n## SVG-first rendering\n\nRender static diagram text as SVG `<text>`/`<tspan>` elements in world space. Use DOM only for the single active text editor overlay and floating UI such as context menus, toolbars, popovers, and form-like controls. The adapter returns render lists from `engine.queryVisible()`, so Vue code does not scan the whole scene on pan.\n\n```vue\n<script setup lang=\"ts\">\nimport { getSvgEntityProps, useDiagramEngine, useDiagramPointerController, useDiagramTextEditor, useDiagramVisibleEntities } from \"@affino/diagram-vue\"\n\nconst diagram = useDiagramEngine(initialScene)\nconst visible = useDiagramVisibleEntities(diagram, { overscan: 200 })\nconst pointer = useDiagramPointerController(diagram)\nconst textEditor = useDiagramTextEditor(diagram)\n</script>\n\n<template>\n  <div class=\"diagram-stage\">\n    <svg class=\"diagram-svg\" v-bind=\"pointer.getSvgPointerProps()\">\n      <polyline v-for=\"edge in visible.projection.value.edges\" :key=\"edge.id\" v-bind=\"getSvgEntityProps(edge)\" />\n      <rect v-for=\"node in visible.projection.value.nodes\" :key=\"node.id\" v-bind=\"getSvgEntityProps(node)\" />\n      <circle v-for=\"port in visible.projection.value.ports\" :key=\"port.id\" v-bind=\"getSvgEntityProps(port)\" />\n      <text v-for=\"text in visible.projection.value.texts\" :key=\"text.id\" v-bind=\"getSvgEntityProps(text)\">\n        {{ diagram.scene.value.entities.textsById.get(text.id)?.text }}\n      </text>\n    </svg>\n    <textarea v-if=\"textEditor.activeEditor.value\" :style=\"textEditor.activeEditor.value.style\" :value=\"textEditor.activeEditor.value.text\" />\n  </div>\n</template>\n```\n\n## Viewport resize\n\n`useDiagramViewport()` can attach a viewport element and write width/height changes into core through `setViewport` commands.\n\n```ts\nimport { shallowRef } from \"vue\"\nimport { useDiagramEngine, useDiagramViewport } from \"@affino/diagram-vue\"\n\nconst stage = shallowRef<HTMLElement | null>(null)\nconst diagram = useDiagramEngine(initialScene)\nconst viewport = useDiagramViewport(diagram, { element: stage })\n\nviewport.setViewport({ x: 120, y: 40, zoom: 1.25 })\n```\n\n## Projection contract\n\n`useDiagramVisibleEntities()` exposes:\n\n- `nodes`, `edges`, `ports`, `shapes`, and `texts` for SVG rendering;\n- `activeHandles` for selected resize/port handles;\n- `overlayAnchors` for context menus and toolbars.\n\nSelected entities are included even when outside the viewport, which keeps handles and overlays stable during keyboard or programmatic selection changes. `useDiagramTextEditor()` owns one shared DOM editor overlay for the active text entity; it reads bounds from core, positions through the viewport transform, and commits through the `editText` command.\n\n## What belongs in core\n\nDo not put domain rules, snapping decisions, hit testing, undo/redo, or geometry math in Vue components. Route those through `@affino/diagram-core` commands and queries, then render the projection here.\n\n## Viewport and camera migration\n\n`ResizeObserver.contentRect` is CSS pixels. `useDiagramViewport()` converts them to world extent by dividing by the current zoom and ignores `0×0` measurements, so a restored camera is not overwritten by a hidden tab. The SVG viewBox should be `x y width height` from the core viewport—do not divide width or height by zoom again.\n\nFor DOM overlays call `getDomEntityStyle(entity, viewport.viewport.value)`; text editor positioning already applies the same transform. Use core `zoomViewportAt()` for cursor focus and `zoomViewportCentered()` for toolbar zoom. Minimap dimensions use the world scene bounds and the same camera transform.\n\nPreview geometry remains transient until pointerup. Render the interaction preview and its handles from core state; entity IDs are opaque strings and may contain `:`. Use `handle.ownerId`, never parse `handle.id`. Escape/pointercancel cancels without history; pointerup commits one undo operation.\n\n### Migration from 0.1.0\n\n- Replace `viewBox.width = viewport.width / viewport.zoom` and the corresponding height formula with raw world `viewport.width` and `viewport.height`.\n- Remove application-side ResizeObserver writes of CSS width/height into core; use `useDiagramViewport()`.\n- Replace custom cursor zoom/focus and resize camera math with the exported core helpers.\n- Run initial Fit only for a new document without a persisted viewport; never Fit on every resize or scene restore.\n","readmeFilename":""}