json-canvas-viewer 4.2.1 → 4.3.1

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.
Files changed (40) hide show
  1. package/dist/chimp.js +73 -7
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/kernel/BaseModule.js +1 -1
  5. package/dist/kernel/BaseModule.js.map +1 -1
  6. package/dist/kernel/Controller.d.ts +2 -2
  7. package/dist/kernel/Controller.js +1 -1
  8. package/dist/kernel/Controller.js.map +1 -1
  9. package/dist/kernel/DataManager.d.ts +1 -5
  10. package/dist/kernel/DataManager.js +1 -1
  11. package/dist/kernel/DataManager.js.map +1 -1
  12. package/dist/kernel/InteractionHandler.d.ts +1 -1
  13. package/dist/kernel/InteractionHandler.js +1 -1
  14. package/dist/kernel/InteractionHandler.js.map +1 -1
  15. package/dist/kernel/OverlayManager.d.ts +4 -4
  16. package/dist/kernel/OverlayManager.js +1 -1
  17. package/dist/kernel/OverlayManager.js.map +1 -1
  18. package/dist/kernel/Renderer.d.ts +1 -8
  19. package/dist/kernel/Renderer.js +2 -2
  20. package/dist/kernel/Renderer.js.map +1 -1
  21. package/dist/kernel/StyleManager.d.ts +1 -1
  22. package/dist/kernel/StyleManager.js +1 -1
  23. package/dist/kernel/StyleManager.js.map +1 -1
  24. package/dist/kernel/index.d.ts +3 -4
  25. package/dist/kernel/index.js +1 -1
  26. package/dist/kernel/index.js.map +1 -1
  27. package/dist/kernel/types.d.ts +1 -1
  28. package/dist/kernel/utilities.d.ts +6 -6
  29. package/dist/kernel/utilities.js +1 -1
  30. package/dist/kernel/utilities.js.map +1 -1
  31. package/dist/modules/Controls/index.js.map +1 -1
  32. package/dist/modules/DebugPanel/index.js.map +1 -1
  33. package/dist/modules/Minimap/index.js.map +1 -1
  34. package/dist/modules/MistouchPreventer/index.js.map +1 -1
  35. package/dist/utilities/fetch-canvas.js.map +1 -1
  36. package/dist/utilities/parser.js.map +1 -1
  37. package/dist/utilities/render-to-string.d.ts +0 -1
  38. package/dist/utilities/render-to-string.js +1 -1
  39. package/dist/utilities/render-to-string.js.map +1 -1
  40. package/package.json +1 -1
@@ -8,10 +8,7 @@ import { BaseOptions } from "./index.js";
8
8
  type Options = {
9
9
  shadowed?: boolean;
10
10
  canvas?: JSONCanvas;
11
- attachmentDir?: string;
12
- extraCSS?: string;
13
11
  attachments?: Record<string, string>;
14
- noAttachmentRelocation?: boolean;
15
12
  } & BaseOptions;
16
13
  type Augmentation = {
17
14
  resetView: DataManager['resetView'];
@@ -34,12 +31,11 @@ type EdgeItem = {
34
31
  type NodeMap = Record<string, NodeItem>;
35
32
  type EdgeMap = Record<string, EdgeItem>;
36
33
  declare class DataManager extends BaseModule<Options, Augmentation> {
37
- onToggleFullscreen: Hook<["enter" | "exit"], false>;
34
+ onToggleFullscreen: Hook<["enter" | "exit"]>;
38
35
  data: {
39
36
  canvasData: Required<JSONCanvas>;
40
37
  nodeMap: NodeMap;
41
38
  edgeMap: EdgeMap;
42
- canvasBaseDir: string;
43
39
  nodeBounds: NodeBounds;
44
40
  offsetX: number;
45
41
  offsetY: number;
@@ -1,2 +1,2 @@
1
- import{BaseModule as e}from"./BaseModule.js";import t from"./styles.js";import{applyStyles as n,getAnchorCoord as r,makeHook as i}from"./utilities.js";var a=class extends e{onToggleFullscreen=i();data={canvasBaseDir:`./`,canvasData:{edges:[],nodes:[]},container:document.createElement(`div`),edgeMap:{},nodeBounds:{centerX:0,centerY:0,height:0,maxX:0,maxY:0,minX:0,minY:0,width:0},nodeMap:{},offsetX:0,offsetY:0,scale:1};constructor(...e){super(...e);let r=this.options.container;for(;r.firstElementChild;)r.firstElementChild.remove();r.innerHTML=``;let i=this.options.shadowed?r.attachShadow({mode:`open`}):r;n(i,t+this.options.extraCSS),this.data.container.classList.add(`JSON-Canvas-Viewer`),i.appendChild(this.data.container),this.augment({onToggleFullscreen:this.onToggleFullscreen,resetView:this.resetView,toggleFullscreen:this.toggleFullscreen}),this.onStart(this.start),this.onRestart(this.start),this.onDispose(this.dispose)}start=()=>{let e={edges:[],nodes:[],...this.options.canvas};Object.assign(this.data,{canvasBaseDir:this.processBaseDir(this.options.attachmentDir),canvasData:e,edgeMap:{},nodeBounds:this.calculateNodeBounds(e),nodeMap:{},offsetX:0,offsetY:0,scale:1}),this.data.canvasData.nodes.forEach(e=>{let t={box:this.getNodeBox(e),ref:e};if(this.data.nodeMap[e.id]=t,e.type===`file`){let n=e.file.split(`/`).pop()??``;if(t.fileName=n,!e.file.startsWith(`http://`)&&!e.file.startsWith(`https://`)){let t=this.options.attachments?.[n];t?e.file=t:this.options.noAttachmentRelocation||(e.file=this.data.canvasBaseDir+n)}}}),this.data.canvasData.edges.forEach(e=>{this.data.edgeMap[e.id]={box:this.getEdgeBox(e),ref:e}}),this.resetView()};processBaseDir=e=>e?e?.slice(-1)===`/`?e:`${e}/`:`./`;getNodeBox=e=>({bottom:e.y+e.height,left:e.x,right:e.width+e.x,top:e.type===`file`||e.type===`group`?e.y-40:e.y});getEdgeBox=e=>{let t=this.data.nodeMap,n=t[e.fromNode].ref,i=t[e.toNode].ref,a=r(n,e.fromSide),o=r(i,e.toSide),s={bottom:Math.max(a.y,o.y),left:Math.min(a.x,o.x),right:Math.max(a.x,o.x),top:Math.min(a.y,o.y)},c=s.right-s.left,l=s.bottom-s.top,u=Math.min(c,l),d=Math.log2(Math.max(c,l)/(u===0?1:u))*10;return{bottom:s.bottom+d,left:s.left-d,right:s.right+d,top:s.top-d}};calculateNodeBounds(e){let t=1/0,n=1/0,r=-1/0,i=-1/0;e.nodes.forEach(e=>{t=Math.min(t,e.x),n=Math.min(n,e.y),r=Math.max(r,e.x+e.width),i=Math.max(i,e.y+e.height)});let a=r-t,o=i-n;return{centerX:t+a/2,centerY:n+o/2,height:o,maxX:r,maxY:i,minX:t,minY:n,width:a}}toggleFullscreen=async e=>{!document.fullscreenElement&&(!e||e===`enter`)?(await this.data.container.requestFullscreen(),this.onToggleFullscreen(`enter`)):document.fullscreenElement&&(!e||e===`exit`)&&(await document.exitFullscreen(),this.onToggleFullscreen(`exit`))};resetView=()=>{let e=this.data.nodeBounds,t=this.data.container;if(!e||!t)return;let n=e.width+200,r=e.height+200,i=t.clientWidth,a=t.clientHeight,o=i/n,s=a/r,c=Math.round(Math.min(o,s)*1e3)/1e3,l=e.centerX,u=e.centerY,d={offsetX:i/2-l*c,offsetY:a/2-u*c,scale:c};this.data.offsetX=d.offsetX,this.data.offsetY=d.offsetY,this.data.scale=d.scale};middleViewer=()=>{let e=this.data.container;return{height:e.clientHeight,width:e.clientWidth,x:e.clientWidth/2,y:e.clientHeight/2}};dispose=()=>{this.data.container.remove()}};export{a as default};
1
+ import{BaseModule as e}from"./BaseModule.js";import t from"./styles.js";import{applyStyles as n,getAnchorCoord as r,hook as i}from"./utilities.js";var a=class extends e{onToggleFullscreen=i();data={canvasData:{edges:[],nodes:[]},container:document.createElement(`div`),edgeMap:{},nodeBounds:{centerX:0,centerY:0,height:0,maxX:0,maxY:0,minX:0,minY:0,width:0},nodeMap:{},offsetX:0,offsetY:0,scale:1};constructor(...e){super(...e);let r=this.options.container;for(;r.firstElementChild;)r.firstElementChild.remove();r.innerHTML=``;let i=this.options.shadowed?r.attachShadow({mode:`open`}):r;n(i,t),this.data.container.classList.add(`JSON-Canvas-Viewer`),i.appendChild(this.data.container),this.augment({onToggleFullscreen:this.onToggleFullscreen,resetView:this.resetView,toggleFullscreen:this.toggleFullscreen}),this.onStart(this.start),this.onRestart(this.start),this.onDispose(this.dispose)}start=()=>{let e={edges:[],nodes:[],...this.options.canvas};Object.assign(this.data,{canvasData:e,edgeMap:{},nodeBounds:this.calculateNodeBounds(e),nodeMap:{},offsetX:0,offsetY:0,scale:1}),this.data.canvasData.nodes.forEach(e=>{let t={box:this.getNodeBox(e),ref:e};if(this.data.nodeMap[e.id]=t,e.type===`file`){let n=e.file,r=n.split(`/`).pop()??``,i=r.lastIndexOf(`?`);if(t.fileName=i===-1?r:r.slice(0,i),!e.file.includes(`://`)){let t=this.options.attachments?.[n];t&&(e.file=t)}}}),this.data.canvasData.edges.forEach(e=>{this.data.edgeMap[e.id]={box:this.getEdgeBox(e),ref:e}}),this.resetView()};processBaseDir=e=>e?e?.slice(-1)===`/`?e:`${e}/`:`./`;getNodeBox=e=>({bottom:e.y+e.height,left:e.x,right:e.width+e.x,top:e.type===`file`||e.type===`group`?e.y-40:e.y});getEdgeBox=e=>{let t=this.data.nodeMap,n=t[e.fromNode].ref,i=t[e.toNode].ref,a=r(n,e.fromSide),o=r(i,e.toSide),s={bottom:Math.max(a.y,o.y),left:Math.min(a.x,o.x),right:Math.max(a.x,o.x),top:Math.min(a.y,o.y)},c=s.right-s.left,l=s.bottom-s.top,u=Math.min(c,l),d=Math.log2(Math.max(c,l)/(u===0?1:u))*10;return{bottom:s.bottom+d,left:s.left-d,right:s.right+d,top:s.top-d}};calculateNodeBounds(e){let t=1/0,n=1/0,r=-1/0,i=-1/0;e.nodes.forEach(e=>{t=Math.min(t,e.x),n=Math.min(n,e.y),r=Math.max(r,e.x+e.width),i=Math.max(i,e.y+e.height)});let a=r-t,o=i-n;return{centerX:t+a/2,centerY:n+o/2,height:o,maxX:r,maxY:i,minX:t,minY:n,width:a}}toggleFullscreen=async e=>{!document.fullscreenElement&&(!e||e===`enter`)?(await this.data.container.requestFullscreen(),this.onToggleFullscreen(`enter`)):document.fullscreenElement&&(!e||e===`exit`)&&(await document.exitFullscreen(),this.onToggleFullscreen(`exit`))};resetView=()=>{let e=this.data.nodeBounds,t=this.data.container;if(!e||!t)return;let n=e.width+200,r=e.height+200,i=t.clientWidth,a=t.clientHeight,o=i/n,s=a/r,c=Math.round(Math.min(o,s)*1e3)/1e3,l=e.centerX,u=e.centerY,d={offsetX:i/2-l*c,offsetY:a/2-u*c,scale:c};this.data.offsetX=d.offsetX,this.data.offsetY=d.offsetY,this.data.scale=d.scale};middleViewer=()=>{let e=this.data.container;return{height:e.clientHeight,width:e.clientWidth,x:e.clientWidth/2,y:e.clientHeight/2}};dispose=()=>{this.data.container.remove()}};export{a as default};
2
2
  //# sourceMappingURL=DataManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DataManager.js","names":["style"],"sources":["../../src/kernel/DataManager.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { Box, NodeBounds } from '$/types';\nimport type { Hook } from '$/utilities';\nimport type { JSONCanvas, JSONCanvasEdge, JSONCanvasNode } from '@repo/shared';\nimport { BaseModule } from '$/BaseModule';\nimport style from '$/styles.scss?inline';\nimport { applyStyles, getAnchorCoord, makeHook } from '$/utilities';\n\nconst INITIAL_VIEWPORT_PADDING = 100;\nconst NODE_LABEL_MARGIN = 40;\nconst EDGE_BOX_HEURISTICS_BASE_MARGIN = 10;\n\ntype Options = {\n\tshadowed?: boolean;\n\tcanvas?: JSONCanvas;\n\tattachmentDir?: string;\n\textraCSS?: string;\n\tattachments?: Record<string, string>;\n\tnoAttachmentRelocation?: boolean;\n} & BaseOptions;\n\ntype Augmentation = {\n\tresetView: DataManager['resetView'];\n\ttoggleFullscreen: DataManager['toggleFullscreen'];\n\tonToggleFullscreen: DataManager['onToggleFullscreen'];\n};\n\nexport type NodeItem = {\n\tref: JSONCanvasNode;\n\tbox: Box;\n\tfileName?: string;\n\tonBeforeUnmount?: Hook;\n\tonActive?: Hook;\n\tonLoseActive?: Hook;\n};\n\nexport type EdgeItem = {\n\tref: JSONCanvasEdge;\n\tbox: Box;\n\tcontrolPoints?: Array<number>;\n};\n\ntype NodeMap = Record<string, NodeItem>;\ntype EdgeMap = Record<string, EdgeItem>;\n\nexport default class DataManager extends BaseModule<Options, Augmentation> {\n\tonToggleFullscreen = makeHook<['enter' | 'exit']>();\n\n\tdata: {\n\t\tcanvasData: Required<JSONCanvas>;\n\t\tnodeMap: NodeMap;\n\t\tedgeMap: EdgeMap;\n\t\tcanvasBaseDir: string;\n\t\tnodeBounds: NodeBounds;\n\t\toffsetX: number;\n\t\toffsetY: number;\n\t\tscale: number;\n\t\tcontainer: HTMLDivElement;\n\t} = {\n\t\tcanvasBaseDir: './',\n\t\tcanvasData: {\n\t\t\tedges: [],\n\t\t\tnodes: [],\n\t\t},\n\t\tcontainer: document.createElement('div'),\n\t\tedgeMap: {},\n\t\tnodeBounds: {\n\t\t\tcenterX: 0,\n\t\t\tcenterY: 0,\n\t\t\theight: 0,\n\t\t\tmaxX: 0,\n\t\t\tmaxY: 0,\n\t\t\tminX: 0,\n\t\t\tminY: 0,\n\t\t\twidth: 0,\n\t\t},\n\t\tnodeMap: {},\n\t\toffsetX: 0,\n\t\toffsetY: 0,\n\t\tscale: 1,\n\t};\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tconst viewerContainer = this.options.container;\n\t\twhile (viewerContainer.firstElementChild) viewerContainer.firstElementChild.remove();\n\t\tviewerContainer.innerHTML = '';\n\n\t\tconst realContainer = this.options.shadowed\n\t\t\t? viewerContainer.attachShadow({ mode: 'open' })\n\t\t\t: viewerContainer;\n\n\t\tapplyStyles(realContainer, style + this.options.extraCSS);\n\n\t\tthis.data.container.classList.add('JSON-Canvas-Viewer');\n\t\trealContainer.appendChild(this.data.container);\n\n\t\tthis.augment({\n\t\t\tonToggleFullscreen: this.onToggleFullscreen,\n\t\t\tresetView: this.resetView,\n\t\t\ttoggleFullscreen: this.toggleFullscreen,\n\t\t});\n\t\tthis.onStart(this.start);\n\t\tthis.onRestart(this.start);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly start = () => {\n\t\tconst canvasData = {\n\t\t\tedges: [],\n\t\t\tnodes: [],\n\t\t\t...this.options.canvas,\n\t\t};\n\n\t\tObject.assign(this.data, {\n\t\t\tcanvasBaseDir: this.processBaseDir(this.options.attachmentDir),\n\t\t\tcanvasData,\n\t\t\tedgeMap: {},\n\t\t\tnodeBounds: this.calculateNodeBounds(canvasData),\n\t\t\tnodeMap: {},\n\t\t\toffsetX: 0,\n\t\t\toffsetY: 0,\n\t\t\tscale: 1,\n\t\t});\n\n\t\tthis.data.canvasData.nodes.forEach((node) => {\n\t\t\tconst item: NodeItem = {\n\t\t\t\tbox: this.getNodeBox(node),\n\t\t\t\tref: node,\n\t\t\t};\n\t\t\tthis.data.nodeMap[node.id] = item;\n\n\t\t\t// Re-process attachments\n\t\t\tif (node.type === 'file') {\n\t\t\t\tconst path = node.file.split('/');\n\t\t\t\tconst fileName = path.pop() ?? '';\n\t\t\t\titem.fileName = fileName;\n\t\t\t\tif (!node.file.startsWith('http://') && !node.file.startsWith('https://')) {\n\t\t\t\t\tconst userDefinedAttachment = this.options.attachments?.[fileName];\n\t\t\t\t\tif (userDefinedAttachment) node.file = userDefinedAttachment;\n\t\t\t\t\telse if (!this.options.noAttachmentRelocation)\n\t\t\t\t\t\tnode.file = this.data.canvasBaseDir + fileName;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.data.canvasData.edges.forEach((edge) => {\n\t\t\tthis.data.edgeMap[edge.id] = {\n\t\t\t\tbox: this.getEdgeBox(edge),\n\t\t\t\tref: edge,\n\t\t\t};\n\t\t});\n\t\tthis.resetView();\n\t};\n\n\tprivate readonly processBaseDir = (baseDir?: string) => {\n\t\tif (!baseDir) return './';\n\t\tconst lastChar = baseDir?.slice(-1);\n\t\tif (lastChar === '/') return baseDir;\n\t\treturn `${baseDir}/`;\n\t};\n\n\tprivate readonly getNodeBox = (node: JSONCanvasNode) => ({\n\t\tbottom: node.y + node.height,\n\t\tleft: node.x,\n\t\tright: node.width + node.x,\n\t\ttop: node.type === 'file' || node.type === 'group' ? node.y - NODE_LABEL_MARGIN : node.y,\n\t});\n\n\tprivate readonly getEdgeBox = (edge: JSONCanvasEdge) => {\n\t\tconst nodes = this.data.nodeMap;\n\t\tconst from = nodes[edge.fromNode].ref;\n\t\tconst to = nodes[edge.toNode].ref;\n\t\tconst fromAnchor = getAnchorCoord(from, edge.fromSide);\n\t\tconst toAnchor = getAnchorCoord(to, edge.toSide);\n\t\tconst strictBox = {\n\t\t\tbottom: Math.max(fromAnchor.y, toAnchor.y),\n\t\t\tleft: Math.min(fromAnchor.x, toAnchor.x),\n\t\t\tright: Math.max(fromAnchor.x, toAnchor.x),\n\t\t\ttop: Math.min(fromAnchor.y, toAnchor.y),\n\t\t};\n\t\t// Edge size heuristics\n\t\tconst width = strictBox.right - strictBox.left;\n\t\tconst height = strictBox.bottom - strictBox.top;\n\t\tconst _min = Math.min(width, height);\n\t\tconst min = _min === 0 ? 1 : _min;\n\t\tconst max = Math.max(width, height);\n\t\tconst edgeFactor = Math.log2(max / min);\n\t\tconst margin = edgeFactor * EDGE_BOX_HEURISTICS_BASE_MARGIN;\n\t\treturn {\n\t\t\tbottom: strictBox.bottom + margin,\n\t\t\tleft: strictBox.left - margin,\n\t\t\tright: strictBox.right + margin,\n\t\t\ttop: strictBox.top - margin,\n\t\t};\n\t};\n\n\tprivate calculateNodeBounds(canvasData: Required<JSONCanvas>) {\n\t\tlet minX = Infinity,\n\t\t\tminY = Infinity,\n\t\t\tmaxX = -Infinity,\n\t\t\tmaxY = -Infinity;\n\t\tcanvasData.nodes.forEach((node) => {\n\t\t\tminX = Math.min(minX, node.x);\n\t\t\tminY = Math.min(minY, node.y);\n\t\t\tmaxX = Math.max(maxX, node.x + node.width);\n\t\t\tmaxY = Math.max(maxY, node.y + node.height);\n\t\t});\n\t\tconst width = maxX - minX;\n\t\tconst height = maxY - minY;\n\t\tconst centerX = minX + width / 2;\n\t\tconst centerY = minY + height / 2;\n\t\treturn { centerX, centerY, height, maxX, maxY, minX, minY, width };\n\t}\n\ttoggleFullscreen = async (option?: 'enter' | 'exit') => {\n\t\tif (!document.fullscreenElement && (!option || option === 'enter')) {\n\t\t\tawait this.data.container.requestFullscreen();\n\t\t\tthis.onToggleFullscreen('enter');\n\t\t} else if (document.fullscreenElement && (!option || option === 'exit')) {\n\t\t\tawait document.exitFullscreen();\n\t\t\tthis.onToggleFullscreen('exit');\n\t\t}\n\t};\n\tresetView = () => {\n\t\tconst bounds = this.data.nodeBounds;\n\t\tconst container = this.data.container;\n\t\tif (!bounds || !container) return;\n\t\tconst contentWidth = bounds.width + INITIAL_VIEWPORT_PADDING * 2;\n\t\tconst contentHeight = bounds.height + INITIAL_VIEWPORT_PADDING * 2;\n\t\t// Use logical dimensions for scaling calculations\n\t\tconst viewWidth = container.clientWidth;\n\t\tconst viewHeight = container.clientHeight;\n\t\tconst scaleX = viewWidth / contentWidth;\n\t\tconst scaleY = viewHeight / contentHeight;\n\t\tconst newScale = Math.round(Math.min(scaleX, scaleY) * 1000) / 1000;\n\t\tconst contentCenterX = bounds.centerX;\n\t\tconst contentCenterY = bounds.centerY;\n\t\tconst initialView = {\n\t\t\toffsetX: viewWidth / 2 - contentCenterX * newScale,\n\t\t\toffsetY: viewHeight / 2 - contentCenterY * newScale,\n\t\t\tscale: newScale,\n\t\t};\n\t\tthis.data.offsetX = initialView.offsetX;\n\t\tthis.data.offsetY = initialView.offsetY;\n\t\tthis.data.scale = initialView.scale;\n\t};\n\n\tmiddleViewer = () => {\n\t\tconst container = this.data.container;\n\t\treturn {\n\t\t\theight: container.clientHeight,\n\t\t\twidth: container.clientWidth,\n\t\t\tx: container.clientWidth / 2,\n\t\t\ty: container.clientHeight / 2,\n\t\t};\n\t};\n\n\tprivate readonly dispose = () => {\n\t\tthis.data.container.remove();\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataManager.js","names":["style"],"sources":["../../src/kernel/DataManager.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { Box, NodeBounds } from '$/types';\nimport type { Hook } from '$/utilities';\nimport type { JSONCanvas, JSONCanvasEdge, JSONCanvasNode } from '@repo/shared';\nimport { BaseModule } from '$/BaseModule';\nimport style from '$/styles.scss?inline';\nimport { applyStyles, getAnchorCoord, hook } from '$/utilities';\n\nconst INITIAL_VIEWPORT_PADDING = 100;\nconst NODE_LABEL_MARGIN = 40;\nconst EDGE_BOX_HEURISTICS_BASE_MARGIN = 10;\n\ntype Options = {\n\tshadowed?: boolean;\n\tcanvas?: JSONCanvas;\n\tattachments?: Record<string, string>;\n} & BaseOptions;\n\ntype Augmentation = {\n\tresetView: DataManager['resetView'];\n\ttoggleFullscreen: DataManager['toggleFullscreen'];\n\tonToggleFullscreen: DataManager['onToggleFullscreen'];\n};\n\nexport type NodeItem = {\n\tref: JSONCanvasNode;\n\tbox: Box;\n\tfileName?: string;\n\tonBeforeUnmount?: Hook;\n\tonActive?: Hook;\n\tonLoseActive?: Hook;\n};\n\nexport type EdgeItem = {\n\tref: JSONCanvasEdge;\n\tbox: Box;\n\tcontrolPoints?: Array<number>;\n};\n\ntype NodeMap = Record<string, NodeItem>;\ntype EdgeMap = Record<string, EdgeItem>;\n\nexport default class DataManager extends BaseModule<Options, Augmentation> {\n\tonToggleFullscreen = hook<['enter' | 'exit']>();\n\n\tdata: {\n\t\tcanvasData: Required<JSONCanvas>;\n\t\tnodeMap: NodeMap;\n\t\tedgeMap: EdgeMap;\n\t\tnodeBounds: NodeBounds;\n\t\toffsetX: number;\n\t\toffsetY: number;\n\t\tscale: number;\n\t\tcontainer: HTMLDivElement;\n\t} = {\n\t\tcanvasData: {\n\t\t\tedges: [],\n\t\t\tnodes: [],\n\t\t},\n\t\tcontainer: document.createElement('div'),\n\t\tedgeMap: {},\n\t\tnodeBounds: {\n\t\t\tcenterX: 0,\n\t\t\tcenterY: 0,\n\t\t\theight: 0,\n\t\t\tmaxX: 0,\n\t\t\tmaxY: 0,\n\t\t\tminX: 0,\n\t\t\tminY: 0,\n\t\t\twidth: 0,\n\t\t},\n\t\tnodeMap: {},\n\t\toffsetX: 0,\n\t\toffsetY: 0,\n\t\tscale: 1,\n\t};\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tconst viewerContainer = this.options.container;\n\t\twhile (viewerContainer.firstElementChild) viewerContainer.firstElementChild.remove();\n\t\tviewerContainer.innerHTML = '';\n\n\t\tconst realContainer = this.options.shadowed\n\t\t\t? viewerContainer.attachShadow({ mode: 'open' })\n\t\t\t: viewerContainer;\n\n\t\tapplyStyles(realContainer, style);\n\n\t\tthis.data.container.classList.add('JSON-Canvas-Viewer');\n\t\trealContainer.appendChild(this.data.container);\n\n\t\tthis.augment({\n\t\t\tonToggleFullscreen: this.onToggleFullscreen,\n\t\t\tresetView: this.resetView,\n\t\t\ttoggleFullscreen: this.toggleFullscreen,\n\t\t});\n\t\tthis.onStart(this.start);\n\t\tthis.onRestart(this.start);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly start = () => {\n\t\tconst canvasData = {\n\t\t\tedges: [],\n\t\t\tnodes: [],\n\t\t\t...this.options.canvas,\n\t\t};\n\n\t\tObject.assign(this.data, {\n\t\t\tcanvasData,\n\t\t\tedgeMap: {},\n\t\t\tnodeBounds: this.calculateNodeBounds(canvasData),\n\t\t\tnodeMap: {},\n\t\t\toffsetX: 0,\n\t\t\toffsetY: 0,\n\t\t\tscale: 1,\n\t\t});\n\n\t\tthis.data.canvasData.nodes.forEach((node) => {\n\t\t\tconst item: NodeItem = {\n\t\t\t\tbox: this.getNodeBox(node),\n\t\t\t\tref: node,\n\t\t\t};\n\t\t\tthis.data.nodeMap[node.id] = item;\n\n\t\t\t// Re-process attachments\n\t\t\tif (node.type === 'file') {\n\t\t\t\tconst path = node.file;\n\t\t\t\tconst basename = path.split('/').pop() ?? '';\n\t\t\t\tconst lastIndex = basename.lastIndexOf('?');\n\t\t\t\titem.fileName = lastIndex !== -1 ? basename.slice(0, lastIndex) : basename;\n\t\t\t\tif (!node.file.includes('://')) {\n\t\t\t\t\tconst userDefinedAttachment = this.options.attachments?.[path];\n\t\t\t\t\tif (userDefinedAttachment) node.file = userDefinedAttachment;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.data.canvasData.edges.forEach((edge) => {\n\t\t\tthis.data.edgeMap[edge.id] = {\n\t\t\t\tbox: this.getEdgeBox(edge),\n\t\t\t\tref: edge,\n\t\t\t};\n\t\t});\n\t\tthis.resetView();\n\t};\n\n\tprivate readonly processBaseDir = (baseDir?: string) => {\n\t\tif (!baseDir) return './';\n\t\tconst lastChar = baseDir?.slice(-1);\n\t\tif (lastChar === '/') return baseDir;\n\t\treturn `${baseDir}/`;\n\t};\n\n\tprivate readonly getNodeBox = (node: JSONCanvasNode) => ({\n\t\tbottom: node.y + node.height,\n\t\tleft: node.x,\n\t\tright: node.width + node.x,\n\t\ttop: node.type === 'file' || node.type === 'group' ? node.y - NODE_LABEL_MARGIN : node.y,\n\t});\n\n\tprivate readonly getEdgeBox = (edge: JSONCanvasEdge) => {\n\t\tconst nodes = this.data.nodeMap;\n\t\tconst from = nodes[edge.fromNode].ref;\n\t\tconst to = nodes[edge.toNode].ref;\n\t\tconst fromAnchor = getAnchorCoord(from, edge.fromSide);\n\t\tconst toAnchor = getAnchorCoord(to, edge.toSide);\n\t\tconst strictBox = {\n\t\t\tbottom: Math.max(fromAnchor.y, toAnchor.y),\n\t\t\tleft: Math.min(fromAnchor.x, toAnchor.x),\n\t\t\tright: Math.max(fromAnchor.x, toAnchor.x),\n\t\t\ttop: Math.min(fromAnchor.y, toAnchor.y),\n\t\t};\n\t\t// Edge size heuristics\n\t\tconst width = strictBox.right - strictBox.left;\n\t\tconst height = strictBox.bottom - strictBox.top;\n\t\tconst _min = Math.min(width, height);\n\t\tconst min = _min === 0 ? 1 : _min;\n\t\tconst max = Math.max(width, height);\n\t\tconst edgeFactor = Math.log2(max / min);\n\t\tconst margin = edgeFactor * EDGE_BOX_HEURISTICS_BASE_MARGIN;\n\t\treturn {\n\t\t\tbottom: strictBox.bottom + margin,\n\t\t\tleft: strictBox.left - margin,\n\t\t\tright: strictBox.right + margin,\n\t\t\ttop: strictBox.top - margin,\n\t\t};\n\t};\n\n\tprivate calculateNodeBounds(canvasData: Required<JSONCanvas>) {\n\t\tlet minX = Infinity,\n\t\t\tminY = Infinity,\n\t\t\tmaxX = -Infinity,\n\t\t\tmaxY = -Infinity;\n\t\tcanvasData.nodes.forEach((node) => {\n\t\t\tminX = Math.min(minX, node.x);\n\t\t\tminY = Math.min(minY, node.y);\n\t\t\tmaxX = Math.max(maxX, node.x + node.width);\n\t\t\tmaxY = Math.max(maxY, node.y + node.height);\n\t\t});\n\t\tconst width = maxX - minX;\n\t\tconst height = maxY - minY;\n\t\tconst centerX = minX + width / 2;\n\t\tconst centerY = minY + height / 2;\n\t\treturn { centerX, centerY, height, maxX, maxY, minX, minY, width };\n\t}\n\ttoggleFullscreen = async (option?: 'enter' | 'exit') => {\n\t\tif (!document.fullscreenElement && (!option || option === 'enter')) {\n\t\t\tawait this.data.container.requestFullscreen();\n\t\t\tthis.onToggleFullscreen('enter');\n\t\t} else if (document.fullscreenElement && (!option || option === 'exit')) {\n\t\t\tawait document.exitFullscreen();\n\t\t\tthis.onToggleFullscreen('exit');\n\t\t}\n\t};\n\tresetView = () => {\n\t\tconst bounds = this.data.nodeBounds;\n\t\tconst container = this.data.container;\n\t\tif (!bounds || !container) return;\n\t\tconst contentWidth = bounds.width + INITIAL_VIEWPORT_PADDING * 2;\n\t\tconst contentHeight = bounds.height + INITIAL_VIEWPORT_PADDING * 2;\n\t\t// Use logical dimensions for scaling calculations\n\t\tconst viewWidth = container.clientWidth;\n\t\tconst viewHeight = container.clientHeight;\n\t\tconst scaleX = viewWidth / contentWidth;\n\t\tconst scaleY = viewHeight / contentHeight;\n\t\tconst newScale = Math.round(Math.min(scaleX, scaleY) * 1000) / 1000;\n\t\tconst contentCenterX = bounds.centerX;\n\t\tconst contentCenterY = bounds.centerY;\n\t\tconst initialView = {\n\t\t\toffsetX: viewWidth / 2 - contentCenterX * newScale,\n\t\t\toffsetY: viewHeight / 2 - contentCenterY * newScale,\n\t\t\tscale: newScale,\n\t\t};\n\t\tthis.data.offsetX = initialView.offsetX;\n\t\tthis.data.offsetY = initialView.offsetY;\n\t\tthis.data.scale = initialView.scale;\n\t};\n\n\tmiddleViewer = () => {\n\t\tconst container = this.data.container;\n\t\treturn {\n\t\t\theight: container.clientHeight,\n\t\t\twidth: container.clientWidth,\n\t\t\tx: container.clientWidth / 2,\n\t\t\ty: container.clientHeight / 2,\n\t\t};\n\t};\n\n\tprivate readonly dispose = () => {\n\t\tthis.data.container.remove();\n\t};\n}\n"],"mappings":"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"}
@@ -18,7 +18,7 @@ type Augmentation = {
18
18
  declare class InteractionHandler extends BaseModule<Options$1, Augmentation> {
19
19
  pointeract: PointeractInterface<LoadedModules>;
20
20
  private readonly DM;
21
- onClick: Hook<[string | undefined], false>;
21
+ onClick: Hook<[string | undefined]>;
22
22
  constructor(...args: BaseArgs);
23
23
  private readonly start;
24
24
  private readonly startInteract;
@@ -1,2 +1,2 @@
1
- import{BaseModule as e}from"./BaseModule.js";import{makeHook as t}from"./utilities.js";import n from"./DataManager.js";import r from"./OverlayManager.js";import{Click as i,Drag as a,Lubricator as o,MultitouchPanZoom as s,Pointeract as c,PreventDefault as l,WheelPanZoom as u,lubricatorDragPreset as d,lubricatorPanPreset as f,lubricatorZoomPreset as p}from"pointeract";var m=class extends e{pointeract;DM;onClick=t();constructor(...e){super(...e),this.DM=this.container.get(n);let t=Object.assign(this.options.pointeract??{},{coordinateOutput:`relative`,element:this.DM.data.container,lubricator:{drag:d,pan:f,zoom:p}});this.pointeract=new c(t,[i,a,u,l,s,o]);let m=this.container.get(r);m.onInteractionStart.subscribe(this.stopInteract),m.onInteractionEnd.subscribe(this.startInteract),this.augment({pan:this.pan,panToCoords:this.panToCoords,zoom:this.zoom,zoomToScale:this.zoomToScale}),this.onStart(this.start),this.onDispose(this.dispose)}start=()=>{this.pointeract.on(`pan`,this.onPan).on(`drag`,this.onPan).on(`zoom`,this.onZoom).on(`trueClick`,this.onTrueClick).start()};startInteract=()=>{this.pointeract.start()};stopInteract=()=>{this.pointeract.stop()};onPan=e=>{this.truePan({x:e.deltaX,y:e.deltaY})};onZoom=e=>{this.trueZoom(e.factor,e)};trueZoom=(e,t)=>{let n=Math.max(Math.min(this.DM.data.scale*e,20),.05);if(n===this.DM.data.scale)return;let r=n/this.DM.data.scale,i=this.C2C(t);this.DM.data.offsetX=t.x-i.x*r,this.DM.data.offsetY=t.y-i.y*r,this.DM.data.scale=n};truePan=({x:e,y:t})=>{this.DM.data.offsetX+=e,this.DM.data.offsetY+=t};zoom=(e,t)=>{this.pointeract.dispatch(`zoom`,{factor:e,...t})};pan=({x:e,y:t})=>{this.pointeract.dispatch(`pan`,{deltaX:e,deltaY:t})};zoomToScale=(e,t)=>{let n=e/this.DM.data.scale;this.pointeract.dispatch(`zoom`,{factor:n,...t})};panToCoords=({x:e,y:t})=>{this.pointeract.dispatch(`pan`,{deltaX:e-this.DM.data.offsetX,deltaY:t-this.DM.data.offsetY})};C2C=({x:e,y:t})=>({x:e-this.DM.data.offsetX,y:t-this.DM.data.offsetY});onTrueClick=e=>{let t=e.target?e.target:void 0;if(this.isUIControl(t))return;let n=this.findNodeId(t);this.onClick(n)};isUIControl=e=>e?e.closest(`.controls`)||e.closest(`button`)||e.closest(`input`):!1;findNodeId=e=>{if(!e)return;let t=e;for(;(!t.id||t.id===``)&&t.parentElement;)t=t.parentElement;if(!(t.id===`overlays`||!t.id||t.id===``))return t.id};dispose=()=>this.pointeract.dispose()};export{m as default};
1
+ import{BaseModule as e}from"./BaseModule.js";import{hook as t}from"./utilities.js";import n from"./DataManager.js";import r from"./OverlayManager.js";import{Click as i,Drag as a,Lubricator as o,MultitouchPanZoom as s,Pointeract as c,PreventDefault as l,WheelPanZoom as u,lubricatorDragPreset as d,lubricatorPanPreset as f,lubricatorZoomPreset as p}from"pointeract";var m=class extends e{pointeract;DM;onClick=t();constructor(...e){super(...e),this.DM=this.container.get(n);let t=Object.assign(this.options.pointeract??{},{coordinateOutput:`relative`,element:this.DM.data.container,lubricator:{drag:d,pan:f,zoom:p}});this.pointeract=new c(t,[i,a,u,l,s,o]);let m=this.container.get(r);m.onInteractionStart.subscribe(this.stopInteract),m.onInteractionEnd.subscribe(this.startInteract),this.augment({pan:this.pan,panToCoords:this.panToCoords,zoom:this.zoom,zoomToScale:this.zoomToScale}),this.onStart(this.start),this.onDispose(this.dispose)}start=()=>{this.pointeract.on(`pan`,this.onPan).on(`drag`,this.onPan).on(`zoom`,this.onZoom).on(`trueClick`,this.onTrueClick).start()};startInteract=()=>{this.pointeract.start()};stopInteract=()=>{this.pointeract.stop()};onPan=e=>{this.truePan({x:e.deltaX,y:e.deltaY})};onZoom=e=>{this.trueZoom(e.factor,e)};trueZoom=(e,t)=>{let n=Math.max(Math.min(this.DM.data.scale*e,20),.05);if(n===this.DM.data.scale)return;let r=n/this.DM.data.scale,i=this.C2C(t);this.DM.data.offsetX=t.x-i.x*r,this.DM.data.offsetY=t.y-i.y*r,this.DM.data.scale=n};truePan=({x:e,y:t})=>{this.DM.data.offsetX+=e,this.DM.data.offsetY+=t};zoom=(e,t)=>{this.pointeract.dispatch(`zoom`,{factor:e,...t})};pan=({x:e,y:t})=>{this.pointeract.dispatch(`pan`,{deltaX:e,deltaY:t})};zoomToScale=(e,t)=>{let n=e/this.DM.data.scale;this.pointeract.dispatch(`zoom`,{factor:n,...t})};panToCoords=({x:e,y:t})=>{this.pointeract.dispatch(`pan`,{deltaX:e-this.DM.data.offsetX,deltaY:t-this.DM.data.offsetY})};C2C=({x:e,y:t})=>({x:e-this.DM.data.offsetX,y:t-this.DM.data.offsetY});onTrueClick=e=>{let t=e.target?e.target:void 0;if(this.isUIControl(t))return;let n=this.findNodeId(t);this.onClick(n)};isUIControl=e=>e?e.closest(`.controls`)||e.closest(`button`)||e.closest(`input`):!1;findNodeId=e=>{if(!e)return;let t=e;for(;(!t.id||t.id===``)&&t.parentElement;)t=t.parentElement;if(!(t.id===`overlays`||!t.id||t.id===``))return t.id};dispose=()=>this.pointeract.dispose()};export{m as default};
2
2
  //# sourceMappingURL=InteractionHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InteractionHandler.js","names":[],"sources":["../../src/kernel/InteractionHandler.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { Coordinates } from '$/types';\nimport type { Options as PointeractOptions, Events, PointeractInterface } from 'pointeract';\nimport { BaseModule } from '$/BaseModule';\nimport DataManager from '$/DataManager';\nimport OverlayManager from '$/OverlayManager';\nimport { makeHook } from '$/utilities';\nimport {\n\tClick,\n\tDrag,\n\tMultitouchPanZoom,\n\tPointeract,\n\tPreventDefault,\n\tWheelPanZoom,\n\tLubricator,\n\tlubricatorPanPreset as pan,\n\tlubricatorZoomPreset as zoom,\n\tlubricatorDragPreset as drag,\n} from 'pointeract';\n\ntype LoadedModules = [Click, Drag, WheelPanZoom, PreventDefault, MultitouchPanZoom, Lubricator];\n\ntype LoadedEvents = Events<LoadedModules>;\n\ntype Options = {\n\tpointeract?: PointeractOptions<LoadedModules>;\n} & BaseOptions;\n\ntype Augmentation = {\n\tpan: InteractionHandler['pan'];\n\tpanToCoords: InteractionHandler['panToCoords'];\n\tzoom: InteractionHandler['zoom'];\n\tzoomToScale: InteractionHandler['zoomToScale'];\n};\n\nexport default class InteractionHandler extends BaseModule<Options, Augmentation> {\n\tpointeract: PointeractInterface<LoadedModules>;\n\tprivate readonly DM: DataManager;\n\tonClick = makeHook<[string | undefined]>();\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tthis.DM = this.container.get(DataManager);\n\t\tconst options = Object.assign(this.options.pointeract ?? {}, {\n\t\t\tcoordinateOutput: 'relative',\n\t\t\telement: this.DM.data.container,\n\t\t\tlubricator: { drag, pan, zoom },\n\t\t} satisfies PointeractOptions<LoadedModules>);\n\t\tthis.pointeract = new Pointeract(options, [\n\t\t\tClick,\n\t\t\tDrag,\n\t\t\tWheelPanZoom,\n\t\t\tPreventDefault,\n\t\t\tMultitouchPanZoom,\n\t\t\tLubricator,\n\t\t]);\n\t\tconst OM = this.container.get(OverlayManager);\n\t\tOM.onInteractionStart.subscribe(this.stopInteract);\n\t\tOM.onInteractionEnd.subscribe(this.startInteract);\n\n\t\tthis.augment({\n\t\t\tpan: this.pan,\n\t\t\tpanToCoords: this.panToCoords,\n\t\t\tzoom: this.zoom,\n\t\t\tzoomToScale: this.zoomToScale,\n\t\t});\n\t\tthis.onStart(this.start);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly start = () => {\n\t\tthis.pointeract\n\t\t\t.on('pan', this.onPan)\n\t\t\t.on('drag', this.onPan)\n\t\t\t.on('zoom', this.onZoom)\n\t\t\t.on('trueClick', this.onTrueClick)\n\t\t\t.start();\n\t};\n\n\tprivate readonly startInteract = () => {\n\t\tthis.pointeract.start();\n\t};\n\tprivate readonly stopInteract = () => {\n\t\tthis.pointeract.stop();\n\t};\n\n\tprivate readonly onPan = (event: LoadedEvents['pan']) => {\n\t\tthis.truePan({\n\t\t\tx: event.deltaX,\n\t\t\ty: event.deltaY,\n\t\t});\n\t};\n\tprivate readonly onZoom = (event: LoadedEvents['zoom']) => {\n\t\tthis.trueZoom(event.factor, event);\n\t};\n\n\ttrueZoom = (_factor: number, origin: Coordinates) => {\n\t\tconst newScale = Math.max(Math.min(this.DM.data.scale * _factor, 20), 0.05);\n\t\tconst scale = this.DM.data.scale;\n\t\tif (newScale === scale) return;\n\t\tconst factor = newScale / this.DM.data.scale;\n\t\tconst canvasCoords = this.C2C(origin);\n\t\tthis.DM.data.offsetX = origin.x - canvasCoords.x * factor;\n\t\tthis.DM.data.offsetY = origin.y - canvasCoords.y * factor;\n\t\tthis.DM.data.scale = newScale;\n\t};\n\ttruePan = ({ x, y }: Coordinates) => {\n\t\tthis.DM.data.offsetX += x;\n\t\tthis.DM.data.offsetY += y;\n\t};\n\n\tzoom = (_factor: number, origin: Coordinates) => {\n\t\tthis.pointeract.dispatch('zoom', { factor: _factor, ...origin });\n\t};\n\tpan = ({ x, y }: Coordinates) => {\n\t\tthis.pointeract.dispatch('pan', { deltaX: x, deltaY: y });\n\t};\n\tzoomToScale = (newScale: number, origin: Coordinates) => {\n\t\tconst factor = newScale / this.DM.data.scale;\n\t\tthis.pointeract.dispatch('zoom', { factor, ...origin });\n\t};\n\tpanToCoords = ({ x, y }: Coordinates) => {\n\t\tthis.pointeract.dispatch('pan', {\n\t\t\tdeltaX: x - this.DM.data.offsetX,\n\t\t\tdeltaY: y - this.DM.data.offsetY,\n\t\t});\n\t};\n\n\t// Container Coords to Canvas Coords relative to the top-left corner of the scaled canvas\n\tprivate readonly C2C = ({ x: containerX, y: containerY }: Coordinates) => ({\n\t\tx: containerX - this.DM.data.offsetX,\n\t\ty: containerY - this.DM.data.offsetY,\n\t});\n\n\tprivate readonly onTrueClick = (e: LoadedEvents['trueClick']) => {\n\t\tconst element = e.target ? (e.target as HTMLElement) : undefined;\n\t\tif (this.isUIControl(element)) return;\n\t\tconst node = this.findNodeId(element);\n\t\tthis.onClick(node);\n\t};\n\n\tprivate readonly isUIControl = (target?: HTMLElement) => {\n\t\tif (!target) return false;\n\t\treturn target.closest('.controls') || target.closest('button') || target.closest('input');\n\t};\n\n\tprivate readonly findNodeId = (element?: HTMLElement) => {\n\t\tif (!element) return;\n\t\tlet ele = element;\n\t\twhile (!ele.id || ele.id === '') {\n\t\t\tif (!ele.parentElement) break;\n\t\t\tele = ele.parentElement;\n\t\t}\n\t\tif (ele.id === 'overlays' || !ele.id || ele.id === '') return;\n\t\treturn ele.id;\n\t};\n\n\tprivate readonly dispose = () => this.pointeract.dispose();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InteractionHandler.js","names":[],"sources":["../../src/kernel/InteractionHandler.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { Coordinates } from '$/types';\nimport type { Options as PointeractOptions, Events, PointeractInterface } from 'pointeract';\nimport { BaseModule } from '$/BaseModule';\nimport DataManager from '$/DataManager';\nimport OverlayManager from '$/OverlayManager';\nimport { hook } from '$/utilities';\nimport {\n\tClick,\n\tDrag,\n\tMultitouchPanZoom,\n\tPointeract,\n\tPreventDefault,\n\tWheelPanZoom,\n\tLubricator,\n\tlubricatorPanPreset as pan,\n\tlubricatorZoomPreset as zoom,\n\tlubricatorDragPreset as drag,\n} from 'pointeract';\n\ntype LoadedModules = [Click, Drag, WheelPanZoom, PreventDefault, MultitouchPanZoom, Lubricator];\n\ntype LoadedEvents = Events<LoadedModules>;\n\ntype Options = {\n\tpointeract?: PointeractOptions<LoadedModules>;\n} & BaseOptions;\n\ntype Augmentation = {\n\tpan: InteractionHandler['pan'];\n\tpanToCoords: InteractionHandler['panToCoords'];\n\tzoom: InteractionHandler['zoom'];\n\tzoomToScale: InteractionHandler['zoomToScale'];\n};\n\nexport default class InteractionHandler extends BaseModule<Options, Augmentation> {\n\tpointeract: PointeractInterface<LoadedModules>;\n\tprivate readonly DM: DataManager;\n\tonClick = hook<[string | undefined]>();\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tthis.DM = this.container.get(DataManager);\n\t\tconst options = Object.assign(this.options.pointeract ?? {}, {\n\t\t\tcoordinateOutput: 'relative',\n\t\t\telement: this.DM.data.container,\n\t\t\tlubricator: { drag, pan, zoom },\n\t\t} satisfies PointeractOptions<LoadedModules>);\n\t\tthis.pointeract = new Pointeract(options, [\n\t\t\tClick,\n\t\t\tDrag,\n\t\t\tWheelPanZoom,\n\t\t\tPreventDefault,\n\t\t\tMultitouchPanZoom,\n\t\t\tLubricator,\n\t\t]);\n\t\tconst OM = this.container.get(OverlayManager);\n\t\tOM.onInteractionStart.subscribe(this.stopInteract);\n\t\tOM.onInteractionEnd.subscribe(this.startInteract);\n\n\t\tthis.augment({\n\t\t\tpan: this.pan,\n\t\t\tpanToCoords: this.panToCoords,\n\t\t\tzoom: this.zoom,\n\t\t\tzoomToScale: this.zoomToScale,\n\t\t});\n\t\tthis.onStart(this.start);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly start = () => {\n\t\tthis.pointeract\n\t\t\t.on('pan', this.onPan)\n\t\t\t.on('drag', this.onPan)\n\t\t\t.on('zoom', this.onZoom)\n\t\t\t.on('trueClick', this.onTrueClick)\n\t\t\t.start();\n\t};\n\n\tprivate readonly startInteract = () => {\n\t\tthis.pointeract.start();\n\t};\n\tprivate readonly stopInteract = () => {\n\t\tthis.pointeract.stop();\n\t};\n\n\tprivate readonly onPan = (event: LoadedEvents['pan']) => {\n\t\tthis.truePan({\n\t\t\tx: event.deltaX,\n\t\t\ty: event.deltaY,\n\t\t});\n\t};\n\tprivate readonly onZoom = (event: LoadedEvents['zoom']) => {\n\t\tthis.trueZoom(event.factor, event);\n\t};\n\n\ttrueZoom = (_factor: number, origin: Coordinates) => {\n\t\tconst newScale = Math.max(Math.min(this.DM.data.scale * _factor, 20), 0.05);\n\t\tconst scale = this.DM.data.scale;\n\t\tif (newScale === scale) return;\n\t\tconst factor = newScale / this.DM.data.scale;\n\t\tconst canvasCoords = this.C2C(origin);\n\t\tthis.DM.data.offsetX = origin.x - canvasCoords.x * factor;\n\t\tthis.DM.data.offsetY = origin.y - canvasCoords.y * factor;\n\t\tthis.DM.data.scale = newScale;\n\t};\n\ttruePan = ({ x, y }: Coordinates) => {\n\t\tthis.DM.data.offsetX += x;\n\t\tthis.DM.data.offsetY += y;\n\t};\n\n\tzoom = (_factor: number, origin: Coordinates) => {\n\t\tthis.pointeract.dispatch('zoom', { factor: _factor, ...origin });\n\t};\n\tpan = ({ x, y }: Coordinates) => {\n\t\tthis.pointeract.dispatch('pan', { deltaX: x, deltaY: y });\n\t};\n\tzoomToScale = (newScale: number, origin: Coordinates) => {\n\t\tconst factor = newScale / this.DM.data.scale;\n\t\tthis.pointeract.dispatch('zoom', { factor, ...origin });\n\t};\n\tpanToCoords = ({ x, y }: Coordinates) => {\n\t\tthis.pointeract.dispatch('pan', {\n\t\t\tdeltaX: x - this.DM.data.offsetX,\n\t\t\tdeltaY: y - this.DM.data.offsetY,\n\t\t});\n\t};\n\n\t// Container Coords to Canvas Coords relative to the top-left corner of the scaled canvas\n\tprivate readonly C2C = ({ x: containerX, y: containerY }: Coordinates) => ({\n\t\tx: containerX - this.DM.data.offsetX,\n\t\ty: containerY - this.DM.data.offsetY,\n\t});\n\n\tprivate readonly onTrueClick = (e: LoadedEvents['trueClick']) => {\n\t\tconst element = e.target ? (e.target as HTMLElement) : undefined;\n\t\tif (this.isUIControl(element)) return;\n\t\tconst node = this.findNodeId(element);\n\t\tthis.onClick(node);\n\t};\n\n\tprivate readonly isUIControl = (target?: HTMLElement) => {\n\t\tif (!target) return false;\n\t\treturn target.closest('.controls') || target.closest('button') || target.closest('input');\n\t};\n\n\tprivate readonly findNodeId = (element?: HTMLElement) => {\n\t\tif (!element) return;\n\t\tlet ele = element;\n\t\twhile (!ele.id || ele.id === '') {\n\t\t\tif (!ele.parentElement) break;\n\t\t\tele = ele.parentElement;\n\t\t}\n\t\tif (ele.id === 'overlays' || !ele.id || ele.id === '') return;\n\t\treturn ele.id;\n\t};\n\n\tprivate readonly dispose = () => this.pointeract.dispose();\n}\n"],"mappings":"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"}
@@ -40,10 +40,10 @@ declare class OverlayManager extends BaseModule<Options, Augmentation> {
40
40
  private readonly parse;
41
41
  private readonly componentDict;
42
42
  private get overlaysLayer();
43
- onInteractionStart: Hook<[], false>;
44
- onInteractionEnd: Hook<[], false>;
45
- onNodeActive: Hook<[JSONCanvasNode], false>;
46
- onNodeLosesActive: Hook<[JSONCanvasNode], false>;
43
+ onInteractionStart: Hook<[]>;
44
+ onInteractionEnd: Hook<[]>;
45
+ onNodeActive: Hook<[JSONCanvasNode]>;
46
+ onNodeLosesActive: Hook<[JSONCanvasNode]>;
47
47
  constructor(...args: BaseArgs);
48
48
  private readonly start;
49
49
  private readonly restart;
@@ -1,2 +1,2 @@
1
- import{BaseModule as e}from"./BaseModule.js";import{destroyError as t,makeHook as n}from"./utilities.js";import r from"./DataManager.js";import i from"./StyleManager.js";import a from"./Controller.js";import o from"./InteractionHandler.js";const s={audio:/\.(mp3|wav|ogg|opus|aac|m4a|flac)$/i,image:/\.(png|jpg|jpeg|gif|svg|webp|avif|bmp|ico|heic|heif)$/i,markdown:/\.(md|mdx|markdown|txt)$/i,video:/\.(mp4|webm|ogv|mov|m3u8|mpd)$/i},c=[`markdown`,`image`,`audio`,`video`];var l=class extends e{_overlaysLayer=document.createElement(`div`);overlays={};selectedId;aborted=!1;eventListeners={};DM;SM;parse;componentDict={audio:({container:e,content:t})=>{let n=document.createElement(`audio`);n.className=`JCV-audio`,n.src=t,n.controls=!0,e.appendChild(n)},image:({container:e,content:t})=>{let n=document.createElement(`img`);n.className=`JCV-img`,n.src=t,n.loading=`lazy`,e.appendChild(n)},link:({container:e,content:t})=>{let n=document.createElement(`iframe`);n.src=t,n.sandbox=`allow-scripts allow-same-origin`,n.className=`JCV-link-iframe`,n.loading=`lazy`,e.appendChild(n)},markdown:async({container:e,content:t})=>{e.classList.add(`JCV-markdown-content`);let n=document.createElement(`div`);n.textContent=`Loading...`,n.classList.add(`JCV-parsed-content-wrapper`),e.appendChild(n);let r;try{let e=await(await fetch(t)).text(),n=/^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(e);r=await this.parse(n?n[2]:e)}catch(e){console.error(`[JSON Canvas Viewer] Failed to load markdown:`,e),r=`Failed to load content.`}n.innerHTML=r},text:({container:e,content:t})=>{e.classList.add(`JCV-markdown-content`);let n=document.createElement(`div`);n.innerHTML=t,n.classList.add(`JCV-parsed-content-wrapper`),e.appendChild(n)},video:({container:e,content:t})=>{let n=document.createElement(`video`);n.className=`JCV-video`,n.src=t,n.controls=!0,e.appendChild(n)}};get overlaysLayer(){if(!this._overlaysLayer)throw t;return this._overlaysLayer}onInteractionStart=n();onInteractionEnd=n();onNodeActive=n();onNodeLosesActive=n();constructor(...e){super(...e),this.parse=this.options.parser??(e=>e),this.DM=this.container.get(r),this.SM=this.container.get(i),this.container.get(a).onRefresh.subscribe(this.updateOverlays),this.SM.onChangeTheme.subscribe(this.themeChanged),this._overlaysLayer=document.createElement(`div`),this._overlaysLayer.className=`JCV-overlays`,this._overlaysLayer.id=`overlays`,this.DM.data.container.appendChild(this.overlaysLayer);let t=this.options.nodeComponents;t&&Object.assign(this.componentDict,t),this.augment({onNodeActive:this.onNodeActive,onNodeLosesActive:this.onNodeLosesActive}),this.onStart(this.start),this.onRestart(this.restart),this.onDispose(this.dispose)}start=()=>{this.container.get(o).onClick.subscribe(this.select),this.renderOverlays()};restart=()=>{this.clearOverlays(),this.renderOverlays()};renderOverlays=()=>{let e=async e=>{switch(e.type){case`text`:this.createOverlay(e,await this.parse(e.text),`text`);break;case`file`:for(let t of c)if(e.file.match(s[t])){this.createOverlay(e,e.file,t);break}break;case`link`:this.createOverlay(e,e.url,`link`);break}};Object.values(this.DM.data.nodeMap).forEach(async t=>{await e(t.ref)})};themeChanged=()=>{Object.values(this.overlays).forEach(e=>{let t=this.DM.data.nodeMap[e.id].ref,n=this.SM.getColor(t.color);this.setOverlayColor(e,n)})};select=e=>{let t=this.selectedId,n=t?this.overlays[t]:void 0,r=e?this.overlays[e]:void 0;if(n&&t){n.classList.remove(`JCV-active`);let e=this.DM.data.nodeMap[t];this.onNodeLosesActive(e.ref),e.onLoseActive?.()}if(r&&e){r.classList.add(`JCV-active`),this.onInteractionStart();let t=this.DM.data.nodeMap[e];this.onNodeActive(t.ref),t.onActive?.()}else this.onInteractionEnd();this.selectedId=e};updateOverlays=()=>{let e=this.DM.data;this.overlaysLayer.style.transform=`translate(${e.offsetX}px, ${e.offsetY}px) scale(${e.scale})`};createOverlay=(...e)=>{if(this.aborted)return;let t=e[0],n=this.overlays[t.id];if(!n){if(n=this.constructOverlay(...e),this.aborted)return;this.overlaysLayer.appendChild(n),this.overlays[t.id]=n,n.style.left=`${t.x}px`,n.style.top=`${t.y}px`,n.style.width=`${t.width}px`,n.style.height=`${t.height}px`}};constructOverlay=(...e)=>{let t=e[0],r=document.createElement(`div`);r.classList.add(`JCV-overlay-container`),r.id=t.id,this.setOverlayColor(r,this.SM.getColor(t.color));let i=document.createElement(`div`);i.classList.add(`JCV-content`),r.appendChild(i);let a=document.createElement(`div`);a.className=`JCV-click-layer`,r.appendChild(a);let o=document.createElement(`div`);o.className=`JCV-overlay-border`,r.appendChild(o);let s=this.DM.data.nodeMap[t.id];s.onActive=n(),s.onLoseActive=n(),s.onBeforeUnmount=n(),this.componentDict[e[2]]({container:i,content:e[1],node:e[0],onActive:s.onActive,onBeforeUnmount:s.onBeforeUnmount,onLoseActive:s.onLoseActive});let c=()=>{t.id===this.selectedId&&this.onInteractionStart()},l=()=>{t.id===this.selectedId&&this.onInteractionEnd()};return r.addEventListener(`pointerenter`,c),r.addEventListener(`pointerleave`,l),r.addEventListener(`touchstart`,c),r.addEventListener(`touchend`,l),this.eventListeners[t.id]=[c,l],r};setOverlayColor=(e,t)=>{Object.entries(t).forEach(([t,n])=>{e.style.setProperty(`--overlay-${t}`,n)})};clearOverlays=()=>{Object.entries(this.overlays).forEach(([e,n])=>{if(this.DM.data.nodeMap[e].onBeforeUnmount?.(),this.eventListeners[e]){let r=this.eventListeners[e][0],i=this.eventListeners[e][1];if(!r||!i)throw t;n.removeEventListener(`pointerenter`,r),n.removeEventListener(`pointerleave`,i),n.removeEventListener(`touchstart`,r),n.removeEventListener(`touchend`,i),this.eventListeners[e][0]=void 0,this.eventListeners[e][1]=void 0}n.remove(),delete this.overlays[e]})};dispose=()=>{this.aborted=!0,this.clearOverlays(),this.overlaysLayer.remove(),this._overlaysLayer=void 0}};export{l as default};
1
+ import{BaseModule as e}from"./BaseModule.js";import{destroyError as t,hook as n}from"./utilities.js";import r from"./DataManager.js";import i from"./StyleManager.js";import a from"./Controller.js";import o from"./InteractionHandler.js";const s={audio:/\.(mp3|wav|ogg|opus|aac|m4a|flac)$/i,image:/\.(png|jpg|jpeg|gif|svg|webp|avif|bmp|ico|heic|heif)$/i,markdown:/\.(md|mdx|markdown|txt)$/i,video:/\.(mp4|webm|ogv|mov|m3u8|mpd)$/i},c=[`markdown`,`image`,`audio`,`video`];var l=class extends e{_overlaysLayer=document.createElement(`div`);overlays={};selectedId;aborted=!1;eventListeners={};DM;SM;parse;componentDict={audio:({container:e,content:t})=>{let n=document.createElement(`audio`);n.className=`JCV-audio`,n.src=t,n.controls=!0,e.appendChild(n)},image:({container:e,content:t})=>{let n=document.createElement(`img`);n.className=`JCV-img`,n.src=t,n.loading=`lazy`,e.appendChild(n)},link:({container:e,content:t})=>{let n=document.createElement(`iframe`);n.src=t,n.sandbox=`allow-scripts allow-same-origin`,n.className=`JCV-link-iframe`,n.loading=`lazy`,e.appendChild(n)},markdown:async({container:e,content:t})=>{e.classList.add(`JCV-markdown-content`);let n=document.createElement(`div`);n.textContent=`Loading...`,n.classList.add(`JCV-parsed-content-wrapper`),e.appendChild(n);let r;try{let e=await(await fetch(t)).text(),n=/^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(e);r=await this.parse(n?n[2]:e)}catch(e){console.error(`[JSON Canvas Viewer] Failed to load markdown:`,e),r=`Failed to load content.`}n.innerHTML=r},text:({container:e,content:t})=>{e.classList.add(`JCV-markdown-content`);let n=document.createElement(`div`);n.innerHTML=t,n.classList.add(`JCV-parsed-content-wrapper`),e.appendChild(n)},video:({container:e,content:t})=>{let n=document.createElement(`video`);n.className=`JCV-video`,n.src=t,n.controls=!0,e.appendChild(n)}};get overlaysLayer(){if(!this._overlaysLayer)throw t;return this._overlaysLayer}onInteractionStart=n();onInteractionEnd=n();onNodeActive=n();onNodeLosesActive=n();constructor(...e){super(...e),this.parse=this.options.parser??(e=>e),this.DM=this.container.get(r),this.SM=this.container.get(i),this.container.get(a).onRefresh.subscribe(this.updateOverlays),this.SM.onChangeTheme.subscribe(this.themeChanged),this._overlaysLayer=document.createElement(`div`),this._overlaysLayer.className=`JCV-overlays`,this._overlaysLayer.id=`overlays`,this.DM.data.container.appendChild(this.overlaysLayer);let t=this.options.nodeComponents;t&&Object.assign(this.componentDict,t),this.augment({onNodeActive:this.onNodeActive,onNodeLosesActive:this.onNodeLosesActive}),this.onStart(this.start),this.onRestart(this.restart),this.onDispose(this.dispose)}start=()=>{this.container.get(o).onClick.subscribe(this.select),this.renderOverlays()};restart=()=>{this.clearOverlays(),this.renderOverlays()};renderOverlays=()=>{let e=async({ref:e,fileName:t})=>{switch(e.type){case`text`:this.createOverlay(e,await this.parse(e.text),`text`);break;case`file`:for(let n of c)if(t?.match(s[n])){this.createOverlay(e,e.file,n);break}break;case`link`:this.createOverlay(e,e.url,`link`);break}};Object.values(this.DM.data.nodeMap).forEach(async t=>await e(t))};themeChanged=()=>{Object.values(this.overlays).forEach(e=>{let t=this.DM.data.nodeMap[e.id].ref,n=this.SM.getColor(t.color);this.setOverlayColor(e,n)})};select=e=>{let t=this.selectedId,n=t?this.overlays[t]:void 0,r=e?this.overlays[e]:void 0;if(n&&t){n.classList.remove(`JCV-active`);let e=this.DM.data.nodeMap[t];this.onNodeLosesActive(e.ref),e.onLoseActive?.()}if(r&&e){r.classList.add(`JCV-active`),this.onInteractionStart();let t=this.DM.data.nodeMap[e];this.onNodeActive(t.ref),t.onActive?.()}else this.onInteractionEnd();this.selectedId=e};updateOverlays=()=>{let e=this.DM.data;this.overlaysLayer.style.transform=`translate(${e.offsetX}px, ${e.offsetY}px) scale(${e.scale})`};createOverlay=(...e)=>{if(this.aborted)return;let t=e[0],n=this.overlays[t.id];if(!n){if(n=this.constructOverlay(...e),this.aborted)return;this.overlaysLayer.appendChild(n),this.overlays[t.id]=n,n.style.left=`${t.x}px`,n.style.top=`${t.y}px`,n.style.width=`${t.width}px`,n.style.height=`${t.height}px`}};constructOverlay=(...e)=>{let t=e[0],r=document.createElement(`div`);r.classList.add(`JCV-overlay-container`),r.id=t.id,this.setOverlayColor(r,this.SM.getColor(t.color));let i=document.createElement(`div`);i.classList.add(`JCV-content`),r.appendChild(i);let a=document.createElement(`div`);a.className=`JCV-click-layer`,r.appendChild(a);let o=document.createElement(`div`);o.className=`JCV-overlay-border`,r.appendChild(o);let s=this.DM.data.nodeMap[t.id];s.onActive=n(),s.onLoseActive=n(),s.onBeforeUnmount=n(),this.componentDict[e[2]]({container:i,content:e[1],node:e[0],onActive:s.onActive,onBeforeUnmount:s.onBeforeUnmount,onLoseActive:s.onLoseActive});let c=()=>{t.id===this.selectedId&&this.onInteractionStart()},l=()=>{t.id===this.selectedId&&this.onInteractionEnd()};return r.addEventListener(`pointerenter`,c),r.addEventListener(`pointerleave`,l),r.addEventListener(`touchstart`,c),r.addEventListener(`touchend`,l),this.eventListeners[t.id]=[c,l],r};setOverlayColor=(e,t)=>{Object.entries(t).forEach(([t,n])=>{e.style.setProperty(`--overlay-${t}`,n)})};clearOverlays=()=>{Object.entries(this.overlays).forEach(([e,n])=>{if(this.DM.data.nodeMap[e].onBeforeUnmount?.(),this.eventListeners[e]){let r=this.eventListeners[e][0],i=this.eventListeners[e][1];if(!r||!i)throw t;n.removeEventListener(`pointerenter`,r),n.removeEventListener(`pointerleave`,i),n.removeEventListener(`touchstart`,r),n.removeEventListener(`touchend`,i),this.eventListeners[e][0]=void 0,this.eventListeners[e][1]=void 0}n.remove(),delete this.overlays[e]})};dispose=()=>{this.aborted=!0,this.clearOverlays(),this.overlaysLayer.remove(),this._overlaysLayer=void 0}};export{l as default};
2
2
  //# sourceMappingURL=OverlayManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayManager.js","names":[],"sources":["../../src/kernel/OverlayManager.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { WithBorderWidth as Color } from '$/StyleManager';\nimport type { Hook } from '$/utilities';\nimport type {\n\tJSONCanvasFileNode,\n\tJSONCanvasLinkNode,\n\tJSONCanvasNode,\n\tJSONCanvasTextNode,\n\tParser,\n} from '@repo/shared';\nimport { BaseModule } from '$/BaseModule';\nimport Controller from '$/Controller';\nimport DataManager from '$/DataManager';\nimport InteractionHandler from '$/InteractionHandler';\nimport StyleManager from '$/StyleManager';\nimport { destroyError, makeHook } from '$/utilities';\n\ntype Options = {\n\tparser?: Parser;\n\tnodeComponents?: Partial<ComponentDict>;\n} & BaseOptions;\n\ntype Augmentation = {\n\tonNodeActive: OverlayManager['onNodeActive'];\n\tonNodeLosesActive: OverlayManager['onNodeLosesActive'];\n};\n\nconst fileRegex = {\n\taudio: /\\.(mp3|wav|ogg|opus|aac|m4a|flac)$/i,\n\timage: /\\.(png|jpg|jpeg|gif|svg|webp|avif|bmp|ico|heic|heif)$/i,\n\tmarkdown: /\\.(md|mdx|markdown|txt)$/i,\n\tvideo: /\\.(mp4|webm|ogv|mov|m3u8|mpd)$/i,\n};\n\ntype NodeComponentHook<N extends JSONCanvasNode> = (options: {\n\tcontainer: HTMLDivElement;\n\tcontent: string;\n\tnode: N;\n\tonBeforeUnmount: Hook;\n\tonActive: Hook;\n\tonLoseActive: Hook;\n}) => void | Promise<void>;\n\ntype CreateOverlayArgs =\n\t| [ComponentNodeMap['text'], string, 'text']\n\t| [ComponentNodeMap['markdown'], string, 'markdown']\n\t| [ComponentNodeMap['image'], string, 'image']\n\t| [ComponentNodeMap['audio'], string, 'audio']\n\t| [ComponentNodeMap['video'], string, 'video']\n\t| [ComponentNodeMap['link'], string, 'link'];\n\ntype ComponentNodeMap = {\n\ttext: JSONCanvasTextNode;\n\tmarkdown: JSONCanvasFileNode;\n\timage: JSONCanvasFileNode;\n\taudio: JSONCanvasFileNode;\n\tvideo: JSONCanvasFileNode;\n\tlink: JSONCanvasLinkNode;\n};\n\ntype ComponentDict = {\n\t[K in keyof ComponentNodeMap]: NodeComponentHook<ComponentNodeMap[K]>;\n};\n\nconst supportedTypes = ['markdown', 'image', 'audio', 'video'] as const;\n\nexport default class OverlayManager extends BaseModule<Options, Augmentation> {\n\tprivate _overlaysLayer?: HTMLDivElement = document.createElement('div');\n\tprivate overlays: Record<string, HTMLDivElement> = {}; // { id: node } the overlays in viewport\n\tprivate selectedId?: string;\n\tprivate aborted = false;\n\tprivate eventListeners: Record<string, Array<EventListener | undefined>> = {};\n\tprivate readonly DM: DataManager;\n\tprivate readonly SM: StyleManager;\n\tprivate readonly parse: Parser;\n\tprivate readonly componentDict: ComponentDict = {\n\t\taudio: ({ container, content }) => {\n\t\t\tconst audio = document.createElement('audio');\n\t\t\taudio.className = 'JCV-audio';\n\t\t\taudio.src = content;\n\t\t\taudio.controls = true;\n\t\t\tcontainer.appendChild(audio);\n\t\t},\n\t\timage: ({ container, content }) => {\n\t\t\tconst img = document.createElement('img');\n\t\t\timg.className = 'JCV-img';\n\t\t\timg.src = content;\n\t\t\timg.loading = 'lazy';\n\t\t\tcontainer.appendChild(img);\n\t\t},\n\t\tlink: ({ container, content }) => {\n\t\t\tconst iframe = document.createElement('iframe');\n\t\t\tiframe.src = content;\n\t\t\tiframe.sandbox = 'allow-scripts allow-same-origin';\n\t\t\tiframe.className = 'JCV-link-iframe';\n\t\t\tiframe.loading = 'lazy';\n\t\t\tcontainer.appendChild(iframe);\n\t\t},\n\t\tmarkdown: async ({ container, content }) => {\n\t\t\tcontainer.classList.add('JCV-markdown-content');\n\t\t\tconst parsedContentWrapper = document.createElement('div');\n\t\t\tparsedContentWrapper.textContent = 'Loading...';\n\t\t\tparsedContentWrapper.classList.add('JCV-parsed-content-wrapper');\n\t\t\tcontainer.appendChild(parsedContentWrapper);\n\t\t\tlet parsedContent: string;\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(content);\n\t\t\t\tconst result = await response.text();\n\t\t\t\tconst frontmatterMatch = /^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/.exec(result);\n\t\t\t\tparsedContent = await this.parse(frontmatterMatch ? frontmatterMatch[2] : result);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[JSON Canvas Viewer] Failed to load markdown:', error);\n\t\t\t\tparsedContent = 'Failed to load content.';\n\t\t\t}\n\t\t\tparsedContentWrapper.innerHTML = parsedContent;\n\t\t},\n\t\ttext: ({ container, content }) => {\n\t\t\tcontainer.classList.add('JCV-markdown-content');\n\t\t\tconst parsedContentWrapper = document.createElement('div');\n\t\t\tparsedContentWrapper.innerHTML = content;\n\t\t\tparsedContentWrapper.classList.add('JCV-parsed-content-wrapper');\n\t\t\tcontainer.appendChild(parsedContentWrapper);\n\t\t},\n\t\tvideo: ({ container, content }) => {\n\t\t\tconst video = document.createElement('video');\n\t\t\tvideo.className = 'JCV-video';\n\t\t\tvideo.src = content;\n\t\t\tvideo.controls = true;\n\t\t\tcontainer.appendChild(video);\n\t\t},\n\t};\n\n\tprivate get overlaysLayer() {\n\t\tif (!this._overlaysLayer) throw destroyError;\n\t\treturn this._overlaysLayer;\n\t}\n\n\tonInteractionStart = makeHook();\n\tonInteractionEnd = makeHook();\n\tonNodeActive = makeHook<[JSONCanvasNode]>();\n\tonNodeLosesActive = makeHook<[JSONCanvasNode]>();\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tthis.parse = this.options.parser ?? ((markdown: string) => markdown);\n\t\tthis.DM = this.container.get(DataManager);\n\t\tthis.SM = this.container.get(StyleManager);\n\t\tconst controller = this.container.get(Controller);\n\t\tcontroller.onRefresh.subscribe(this.updateOverlays);\n\t\tthis.SM.onChangeTheme.subscribe(this.themeChanged);\n\n\t\tthis._overlaysLayer = document.createElement('div');\n\t\tthis._overlaysLayer.className = 'JCV-overlays';\n\t\tthis._overlaysLayer.id = 'overlays';\n\t\tthis.DM.data.container.appendChild(this.overlaysLayer);\n\n\t\tconst components = this.options.nodeComponents;\n\t\tif (components) Object.assign(this.componentDict, components);\n\n\t\tthis.augment({\n\t\t\tonNodeActive: this.onNodeActive,\n\t\t\tonNodeLosesActive: this.onNodeLosesActive,\n\t\t});\n\t\tthis.onStart(this.start);\n\t\tthis.onRestart(this.restart);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly start = () => {\n\t\tthis.container.get(InteractionHandler).onClick.subscribe(this.select);\n\t\tthis.renderOverlays();\n\t};\n\n\tprivate readonly restart = () => {\n\t\tthis.clearOverlays();\n\t\tthis.renderOverlays();\n\t};\n\n\tprivate readonly renderOverlays = () => {\n\t\tconst overlayMatcher = async (node: JSONCanvasNode) => {\n\t\t\tswitch (node.type) {\n\t\t\t\tcase 'text': {\n\t\t\t\t\tthis.createOverlay(node, await this.parse(node.text), 'text');\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'file': {\n\t\t\t\t\tfor (const type of supportedTypes) {\n\t\t\t\t\t\tif (!node.file.match(fileRegex[type])) continue;\n\t\t\t\t\t\tthis.createOverlay(node, node.file, type);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'link': {\n\t\t\t\t\tthis.createOverlay(node, node.url, 'link');\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tObject.values(this.DM.data.nodeMap).forEach(async (node) => {\n\t\t\tawait overlayMatcher(node.ref);\n\t\t});\n\t};\n\n\tprivate readonly themeChanged = () => {\n\t\tObject.values(this.overlays).forEach((overlay) => {\n\t\t\tconst node = this.DM.data.nodeMap[overlay.id].ref;\n\t\t\tconst color = this.SM.getColor(node.color);\n\t\t\tthis.setOverlayColor(overlay, color);\n\t\t});\n\t};\n\n\tprivate readonly select = (id?: string) => {\n\t\tconst previousId = this.selectedId;\n\t\tconst previous = previousId ? this.overlays[previousId] : undefined;\n\t\tconst current = id ? this.overlays[id] : undefined;\n\t\tif (previous && previousId) {\n\t\t\tprevious.classList.remove('JCV-active');\n\t\t\tconst nodeItem = this.DM.data.nodeMap[previousId];\n\t\t\tthis.onNodeLosesActive(nodeItem.ref);\n\t\t\tnodeItem.onLoseActive?.();\n\t\t}\n\t\tif (current && id) {\n\t\t\tcurrent.classList.add('JCV-active');\n\t\t\tthis.onInteractionStart();\n\t\t\tconst nodeItem = this.DM.data.nodeMap[id];\n\t\t\tthis.onNodeActive(nodeItem.ref);\n\t\t\tnodeItem.onActive?.();\n\t\t} else this.onInteractionEnd();\n\t\tthis.selectedId = id;\n\t};\n\n\tprivate readonly updateOverlays = () => {\n\t\tconst data = this.DM.data;\n\t\tthis.overlaysLayer.style.transform = `translate(${data.offsetX}px, ${data.offsetY}px) scale(${data.scale})`;\n\t};\n\n\tprivate readonly createOverlay = (...args: CreateOverlayArgs) => {\n\t\tif (this.aborted) return;\n\t\tconst node = args[0];\n\t\tlet element = this.overlays[node.id];\n\t\tif (!element) {\n\t\t\telement = this.constructOverlay(...args);\n\t\t\tif (this.aborted) return;\n\t\t\tthis.overlaysLayer.appendChild(element);\n\t\t\tthis.overlays[node.id] = element;\n\t\t\telement.style.left = `${node.x}px`;\n\t\t\telement.style.top = `${node.y}px`;\n\t\t\telement.style.width = `${node.width}px`;\n\t\t\telement.style.height = `${node.height}px`;\n\t\t}\n\t};\n\n\tprivate readonly constructOverlay = (...args: CreateOverlayArgs) => {\n\t\tconst node = args[0];\n\t\tconst overlay = document.createElement('div');\n\t\toverlay.classList.add('JCV-overlay-container');\n\t\toverlay.id = node.id;\n\t\tthis.setOverlayColor(overlay, this.SM.getColor(node.color));\n\t\tconst contentWrapper = document.createElement('div');\n\t\tcontentWrapper.classList.add('JCV-content');\n\t\toverlay.appendChild(contentWrapper);\n\t\tconst clickLayer = document.createElement('div');\n\t\tclickLayer.className = 'JCV-click-layer';\n\t\toverlay.appendChild(clickLayer);\n\t\tconst overlayBorder = document.createElement('div');\n\t\toverlayBorder.className = 'JCV-overlay-border';\n\t\toverlay.appendChild(overlayBorder);\n\t\tconst nodeItem = this.DM.data.nodeMap[node.id];\n\n\t\tnodeItem.onActive = makeHook();\n\t\tnodeItem.onLoseActive = makeHook();\n\t\tnodeItem.onBeforeUnmount = makeHook();\n\n\t\tvoid this.componentDict[args[2]]({\n\t\t\tcontainer: contentWrapper,\n\t\t\tcontent: args[1],\n\t\t\tnode: args[0] as never,\n\t\t\tonActive: nodeItem.onActive,\n\t\t\tonBeforeUnmount: nodeItem.onBeforeUnmount,\n\t\t\tonLoseActive: nodeItem.onLoseActive,\n\t\t});\n\t\tconst onStart = () => {\n\t\t\tif (node.id === this.selectedId) this.onInteractionStart();\n\t\t};\n\t\tconst onEnd = () => {\n\t\t\tif (node.id === this.selectedId) this.onInteractionEnd();\n\t\t};\n\t\toverlay.addEventListener('pointerenter', onStart);\n\t\toverlay.addEventListener('pointerleave', onEnd);\n\t\toverlay.addEventListener('touchstart', onStart);\n\t\toverlay.addEventListener('touchend', onEnd);\n\t\tthis.eventListeners[node.id] = [onStart, onEnd];\n\t\treturn overlay;\n\t};\n\n\tprivate readonly setOverlayColor = (overlay: HTMLDivElement, color: Color) => {\n\t\tObject.entries(color).forEach(([key, value]) => {\n\t\t\toverlay.style.setProperty(`--overlay-${key}`, value);\n\t\t});\n\t};\n\n\tprivate readonly clearOverlays = () => {\n\t\tObject.entries(this.overlays).forEach(([id, overlay]) => {\n\t\t\tthis.DM.data.nodeMap[id].onBeforeUnmount?.();\n\t\t\tif (this.eventListeners[id]) {\n\t\t\t\tconst onStart = this.eventListeners[id][0];\n\t\t\t\tconst onEnd = this.eventListeners[id][1];\n\t\t\t\tif (!onStart || !onEnd) throw destroyError;\n\t\t\t\toverlay.removeEventListener('pointerenter', onStart);\n\t\t\t\toverlay.removeEventListener('pointerleave', onEnd);\n\t\t\t\toverlay.removeEventListener('touchstart', onStart);\n\t\t\t\toverlay.removeEventListener('touchend', onEnd);\n\t\t\t\tthis.eventListeners[id][0] = undefined;\n\t\t\t\tthis.eventListeners[id][1] = undefined;\n\t\t\t}\n\t\t\toverlay.remove();\n\t\t\tdelete this.overlays[id];\n\t\t});\n\t};\n\n\tprivate readonly dispose = () => {\n\t\tthis.aborted = true;\n\t\tthis.clearOverlays();\n\t\tthis.overlaysLayer.remove();\n\t\tthis._overlaysLayer = undefined;\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"OverlayManager.js","names":[],"sources":["../../src/kernel/OverlayManager.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { NodeItem } from '$/DataManager';\nimport type { WithBorderWidth as Color } from '$/StyleManager';\nimport type { Hook } from '$/utilities';\nimport type {\n\tJSONCanvasFileNode,\n\tJSONCanvasLinkNode,\n\tJSONCanvasNode,\n\tJSONCanvasTextNode,\n\tParser,\n} from '@repo/shared';\nimport { BaseModule } from '$/BaseModule';\nimport Controller from '$/Controller';\nimport DataManager from '$/DataManager';\nimport InteractionHandler from '$/InteractionHandler';\nimport StyleManager from '$/StyleManager';\nimport { destroyError, hook } from '$/utilities';\n\ntype Options = {\n\tparser?: Parser;\n\tnodeComponents?: Partial<ComponentDict>;\n} & BaseOptions;\n\ntype Augmentation = {\n\tonNodeActive: OverlayManager['onNodeActive'];\n\tonNodeLosesActive: OverlayManager['onNodeLosesActive'];\n};\n\nconst fileRegex = {\n\taudio: /\\.(mp3|wav|ogg|opus|aac|m4a|flac)$/i,\n\timage: /\\.(png|jpg|jpeg|gif|svg|webp|avif|bmp|ico|heic|heif)$/i,\n\tmarkdown: /\\.(md|mdx|markdown|txt)$/i,\n\tvideo: /\\.(mp4|webm|ogv|mov|m3u8|mpd)$/i,\n};\n\ntype NodeComponentHook<N extends JSONCanvasNode> = (options: {\n\tcontainer: HTMLDivElement;\n\tcontent: string;\n\tnode: N;\n\tonBeforeUnmount: Hook;\n\tonActive: Hook;\n\tonLoseActive: Hook;\n}) => void | Promise<void>;\n\ntype CreateOverlayArgs =\n\t| [ComponentNodeMap['text'], string, 'text']\n\t| [ComponentNodeMap['markdown'], string, 'markdown']\n\t| [ComponentNodeMap['image'], string, 'image']\n\t| [ComponentNodeMap['audio'], string, 'audio']\n\t| [ComponentNodeMap['video'], string, 'video']\n\t| [ComponentNodeMap['link'], string, 'link'];\n\ntype ComponentNodeMap = {\n\ttext: JSONCanvasTextNode;\n\tmarkdown: JSONCanvasFileNode;\n\timage: JSONCanvasFileNode;\n\taudio: JSONCanvasFileNode;\n\tvideo: JSONCanvasFileNode;\n\tlink: JSONCanvasLinkNode;\n};\n\ntype ComponentDict = {\n\t[K in keyof ComponentNodeMap]: NodeComponentHook<ComponentNodeMap[K]>;\n};\n\nconst supportedTypes = ['markdown', 'image', 'audio', 'video'] as const;\n\nexport default class OverlayManager extends BaseModule<Options, Augmentation> {\n\tprivate _overlaysLayer?: HTMLDivElement = document.createElement('div');\n\tprivate overlays: Record<string, HTMLDivElement> = {}; // { id: node } the overlays in viewport\n\tprivate selectedId?: string;\n\tprivate aborted = false;\n\tprivate eventListeners: Record<string, Array<EventListener | undefined>> = {};\n\tprivate readonly DM: DataManager;\n\tprivate readonly SM: StyleManager;\n\tprivate readonly parse: Parser;\n\tprivate readonly componentDict: ComponentDict = {\n\t\taudio: ({ container, content }) => {\n\t\t\tconst audio = document.createElement('audio');\n\t\t\taudio.className = 'JCV-audio';\n\t\t\taudio.src = content;\n\t\t\taudio.controls = true;\n\t\t\tcontainer.appendChild(audio);\n\t\t},\n\t\timage: ({ container, content }) => {\n\t\t\tconst img = document.createElement('img');\n\t\t\timg.className = 'JCV-img';\n\t\t\timg.src = content;\n\t\t\timg.loading = 'lazy';\n\t\t\tcontainer.appendChild(img);\n\t\t},\n\t\tlink: ({ container, content }) => {\n\t\t\tconst iframe = document.createElement('iframe');\n\t\t\tiframe.src = content;\n\t\t\tiframe.sandbox = 'allow-scripts allow-same-origin';\n\t\t\tiframe.className = 'JCV-link-iframe';\n\t\t\tiframe.loading = 'lazy';\n\t\t\tcontainer.appendChild(iframe);\n\t\t},\n\t\tmarkdown: async ({ container, content }) => {\n\t\t\tcontainer.classList.add('JCV-markdown-content');\n\t\t\tconst parsedContentWrapper = document.createElement('div');\n\t\t\tparsedContentWrapper.textContent = 'Loading...';\n\t\t\tparsedContentWrapper.classList.add('JCV-parsed-content-wrapper');\n\t\t\tcontainer.appendChild(parsedContentWrapper);\n\t\t\tlet parsedContent: string;\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(content);\n\t\t\t\tconst result = await response.text();\n\t\t\t\tconst frontmatterMatch = /^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/.exec(result);\n\t\t\t\tparsedContent = await this.parse(frontmatterMatch ? frontmatterMatch[2] : result);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[JSON Canvas Viewer] Failed to load markdown:', error);\n\t\t\t\tparsedContent = 'Failed to load content.';\n\t\t\t}\n\t\t\tparsedContentWrapper.innerHTML = parsedContent;\n\t\t},\n\t\ttext: ({ container, content }) => {\n\t\t\tcontainer.classList.add('JCV-markdown-content');\n\t\t\tconst parsedContentWrapper = document.createElement('div');\n\t\t\tparsedContentWrapper.innerHTML = content;\n\t\t\tparsedContentWrapper.classList.add('JCV-parsed-content-wrapper');\n\t\t\tcontainer.appendChild(parsedContentWrapper);\n\t\t},\n\t\tvideo: ({ container, content }) => {\n\t\t\tconst video = document.createElement('video');\n\t\t\tvideo.className = 'JCV-video';\n\t\t\tvideo.src = content;\n\t\t\tvideo.controls = true;\n\t\t\tcontainer.appendChild(video);\n\t\t},\n\t};\n\n\tprivate get overlaysLayer() {\n\t\tif (!this._overlaysLayer) throw destroyError;\n\t\treturn this._overlaysLayer;\n\t}\n\n\tonInteractionStart = hook();\n\tonInteractionEnd = hook();\n\tonNodeActive = hook<[JSONCanvasNode]>();\n\tonNodeLosesActive = hook<[JSONCanvasNode]>();\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tthis.parse = this.options.parser ?? ((markdown: string) => markdown);\n\t\tthis.DM = this.container.get(DataManager);\n\t\tthis.SM = this.container.get(StyleManager);\n\t\tconst controller = this.container.get(Controller);\n\t\tcontroller.onRefresh.subscribe(this.updateOverlays);\n\t\tthis.SM.onChangeTheme.subscribe(this.themeChanged);\n\n\t\tthis._overlaysLayer = document.createElement('div');\n\t\tthis._overlaysLayer.className = 'JCV-overlays';\n\t\tthis._overlaysLayer.id = 'overlays';\n\t\tthis.DM.data.container.appendChild(this.overlaysLayer);\n\n\t\tconst components = this.options.nodeComponents;\n\t\tif (components) Object.assign(this.componentDict, components);\n\n\t\tthis.augment({\n\t\t\tonNodeActive: this.onNodeActive,\n\t\t\tonNodeLosesActive: this.onNodeLosesActive,\n\t\t});\n\t\tthis.onStart(this.start);\n\t\tthis.onRestart(this.restart);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly start = () => {\n\t\tthis.container.get(InteractionHandler).onClick.subscribe(this.select);\n\t\tthis.renderOverlays();\n\t};\n\n\tprivate readonly restart = () => {\n\t\tthis.clearOverlays();\n\t\tthis.renderOverlays();\n\t};\n\n\tprivate readonly renderOverlays = () => {\n\t\tconst overlayMatcher = async ({ ref, fileName }: NodeItem) => {\n\t\t\tswitch (ref.type) {\n\t\t\t\tcase 'text': {\n\t\t\t\t\tthis.createOverlay(ref, await this.parse(ref.text), 'text');\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'file': {\n\t\t\t\t\tfor (const type of supportedTypes) {\n\t\t\t\t\t\tif (!fileName?.match(fileRegex[type])) continue;\n\t\t\t\t\t\tthis.createOverlay(ref, ref.file, type);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'link': {\n\t\t\t\t\tthis.createOverlay(ref, ref.url, 'link');\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tObject.values(this.DM.data.nodeMap).forEach(async (node) => await overlayMatcher(node));\n\t};\n\n\tprivate readonly themeChanged = () => {\n\t\tObject.values(this.overlays).forEach((overlay) => {\n\t\t\tconst node = this.DM.data.nodeMap[overlay.id].ref;\n\t\t\tconst color = this.SM.getColor(node.color);\n\t\t\tthis.setOverlayColor(overlay, color);\n\t\t});\n\t};\n\n\tprivate readonly select = (id?: string) => {\n\t\tconst previousId = this.selectedId;\n\t\tconst previous = previousId ? this.overlays[previousId] : undefined;\n\t\tconst current = id ? this.overlays[id] : undefined;\n\t\tif (previous && previousId) {\n\t\t\tprevious.classList.remove('JCV-active');\n\t\t\tconst nodeItem = this.DM.data.nodeMap[previousId];\n\t\t\tthis.onNodeLosesActive(nodeItem.ref);\n\t\t\tnodeItem.onLoseActive?.();\n\t\t}\n\t\tif (current && id) {\n\t\t\tcurrent.classList.add('JCV-active');\n\t\t\tthis.onInteractionStart();\n\t\t\tconst nodeItem = this.DM.data.nodeMap[id];\n\t\t\tthis.onNodeActive(nodeItem.ref);\n\t\t\tnodeItem.onActive?.();\n\t\t} else this.onInteractionEnd();\n\t\tthis.selectedId = id;\n\t};\n\n\tprivate readonly updateOverlays = () => {\n\t\tconst data = this.DM.data;\n\t\tthis.overlaysLayer.style.transform = `translate(${data.offsetX}px, ${data.offsetY}px) scale(${data.scale})`;\n\t};\n\n\tprivate readonly createOverlay = (...args: CreateOverlayArgs) => {\n\t\tif (this.aborted) return;\n\t\tconst node = args[0];\n\t\tlet element = this.overlays[node.id];\n\t\tif (!element) {\n\t\t\telement = this.constructOverlay(...args);\n\t\t\tif (this.aborted) return;\n\t\t\tthis.overlaysLayer.appendChild(element);\n\t\t\tthis.overlays[node.id] = element;\n\t\t\telement.style.left = `${node.x}px`;\n\t\t\telement.style.top = `${node.y}px`;\n\t\t\telement.style.width = `${node.width}px`;\n\t\t\telement.style.height = `${node.height}px`;\n\t\t}\n\t};\n\n\tprivate readonly constructOverlay = (...args: CreateOverlayArgs) => {\n\t\tconst node = args[0];\n\t\tconst overlay = document.createElement('div');\n\t\toverlay.classList.add('JCV-overlay-container');\n\t\toverlay.id = node.id;\n\t\tthis.setOverlayColor(overlay, this.SM.getColor(node.color));\n\t\tconst contentWrapper = document.createElement('div');\n\t\tcontentWrapper.classList.add('JCV-content');\n\t\toverlay.appendChild(contentWrapper);\n\t\tconst clickLayer = document.createElement('div');\n\t\tclickLayer.className = 'JCV-click-layer';\n\t\toverlay.appendChild(clickLayer);\n\t\tconst overlayBorder = document.createElement('div');\n\t\toverlayBorder.className = 'JCV-overlay-border';\n\t\toverlay.appendChild(overlayBorder);\n\t\tconst nodeItem = this.DM.data.nodeMap[node.id];\n\n\t\tnodeItem.onActive = hook();\n\t\tnodeItem.onLoseActive = hook();\n\t\tnodeItem.onBeforeUnmount = hook();\n\n\t\tvoid this.componentDict[args[2]]({\n\t\t\tcontainer: contentWrapper,\n\t\t\tcontent: args[1],\n\t\t\tnode: args[0] as never,\n\t\t\tonActive: nodeItem.onActive,\n\t\t\tonBeforeUnmount: nodeItem.onBeforeUnmount,\n\t\t\tonLoseActive: nodeItem.onLoseActive,\n\t\t});\n\t\tconst onStart = () => {\n\t\t\tif (node.id === this.selectedId) this.onInteractionStart();\n\t\t};\n\t\tconst onEnd = () => {\n\t\t\tif (node.id === this.selectedId) this.onInteractionEnd();\n\t\t};\n\t\toverlay.addEventListener('pointerenter', onStart);\n\t\toverlay.addEventListener('pointerleave', onEnd);\n\t\toverlay.addEventListener('touchstart', onStart);\n\t\toverlay.addEventListener('touchend', onEnd);\n\t\tthis.eventListeners[node.id] = [onStart, onEnd];\n\t\treturn overlay;\n\t};\n\n\tprivate readonly setOverlayColor = (overlay: HTMLDivElement, color: Color) => {\n\t\tObject.entries(color).forEach(([key, value]) => {\n\t\t\toverlay.style.setProperty(`--overlay-${key}`, value);\n\t\t});\n\t};\n\n\tprivate readonly clearOverlays = () => {\n\t\tObject.entries(this.overlays).forEach(([id, overlay]) => {\n\t\t\tthis.DM.data.nodeMap[id].onBeforeUnmount?.();\n\t\t\tif (this.eventListeners[id]) {\n\t\t\t\tconst onStart = this.eventListeners[id][0];\n\t\t\t\tconst onEnd = this.eventListeners[id][1];\n\t\t\t\tif (!onStart || !onEnd) throw destroyError;\n\t\t\t\toverlay.removeEventListener('pointerenter', onStart);\n\t\t\t\toverlay.removeEventListener('pointerleave', onEnd);\n\t\t\t\toverlay.removeEventListener('touchstart', onStart);\n\t\t\t\toverlay.removeEventListener('touchend', onEnd);\n\t\t\t\tthis.eventListeners[id][0] = undefined;\n\t\t\t\tthis.eventListeners[id][1] = undefined;\n\t\t\t}\n\t\t\toverlay.remove();\n\t\t\tdelete this.overlays[id];\n\t\t});\n\t};\n\n\tprivate readonly dispose = () => {\n\t\tthis.aborted = true;\n\t\tthis.clearOverlays();\n\t\tthis.overlaysLayer.remove();\n\t\tthis._overlaysLayer = undefined;\n\t};\n}\n"],"mappings":"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"}
@@ -1,22 +1,15 @@
1
1
  import { BaseArgs, BaseModule } from "./BaseModule.js";
2
- import { BaseOptions } from "./index.js";
3
2
 
4
3
  //#region src/kernel/Renderer.d.ts
5
- type Options = {
6
- zoomInOptimization?: boolean;
7
- } & BaseOptions;
8
- declare class Renderer extends BaseModule<Options> {
4
+ declare class Renderer extends BaseModule {
9
5
  private _canvas?;
10
6
  private readonly ctx;
11
7
  private readonly DM;
12
8
  private readonly SM;
13
- private readonly zoomInOptimize;
14
9
  private get canvas();
15
10
  constructor(...args: BaseArgs);
16
11
  private readonly optimizeDPR;
17
12
  private readonly redraw;
18
- private trueRedraw;
19
- private fakeRedraw;
20
13
  private readonly isInside;
21
14
  private readonly isOutside;
22
15
  private readonly getCurrentViewport;
@@ -1,3 +1,3 @@
1
- import{BaseModule as e}from"./BaseModule.js";import{destroyError as t,drawRoundRect as n,getAnchorCoord as r,resizeCanvasForDPR as i}from"./utilities.js";import a from"./DataManager.js";import o from"./StyleManager.js";import s from"./Controller.js";var c=class extends e{_canvas;ctx;DM;SM;zoomInOptimize={lastCallTime:0,lastDrawnScale:0,lastDrawnViewport:{bottom:0,left:0,right:0,top:0}};get canvas(){if(!this._canvas)throw t;return this._canvas}constructor(...e){super(...e);let t=this.container.get(s);this.SM=this.container.get(o),t.onRefresh.subscribe(this.redraw),t.onResize.subscribe(this.optimizeDPR),this.DM=this.container.get(a),this._canvas=document.createElement(`canvas`),this._canvas.className=`JCV-main-canvas`,this.ctx=this._canvas.getContext(`2d`),this.DM.data.container.appendChild(this._canvas),this.onDispose(this.dispose)}optimizeDPR=()=>{let e=this.DM.data.container;i(this.canvas,e.offsetWidth,e.offsetHeight)};redraw=()=>{let e=this.DM.data.offsetX,t=this.DM.data.offsetY,n=this.DM.data.scale,r=this.getCurrentViewport(e,t,n);if(!this.options.zoomInOptimization){this.trueRedraw(e,t,n,r);return}this.zoomInOptimize.timeout&&(clearTimeout(this.zoomInOptimize.timeout),this.zoomInOptimize.timeout=void 0);let i=Date.now();if(this.isInside(r,this.zoomInOptimize.lastDrawnViewport)&&n!==this.zoomInOptimize.lastDrawnScale&&i-this.zoomInOptimize.lastCallTime<500){this.zoomInOptimize.timeout=window.setTimeout(()=>{this.trueRedraw(e,t,n,r),this.zoomInOptimize.lastCallTime=i,this.zoomInOptimize.timeout=void 0},60),this.fakeRedraw(r,n);return}this.zoomInOptimize.lastCallTime=i,this.trueRedraw(e,t,n,r)};trueRedraw(e,t,n,r){this.zoomInOptimize.lastDrawnViewport=r,this.zoomInOptimize.lastDrawnScale=n,this.canvas.style.transform=``,this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.save(),this.drawGridDots(n,e,t),this.ctx.translate(e,t),this.ctx.scale(n,n),Object.values(this.DM.data.nodeMap).forEach(e=>{if(this.isOutside(e.box,r))return;let t=e.ref;t.type===`file`?this.drawFile(e):t.type===`group`&&this.drawGroup(t,n)}),Object.values(this.DM.data.edgeMap).forEach(e=>{this.isOutside(e.box,r)||this.drawEdge(e)}),this.ctx.restore()}fakeRedraw(e,t){let n=t/this.zoomInOptimize.lastDrawnScale,r=(this.zoomInOptimize.lastDrawnViewport.left-e.left)*t,i=(this.zoomInOptimize.lastDrawnViewport.top-e.top)*t;this.canvas.style.transform=`translate(${r}px, ${i}px) scale(${n})`}isInside=(e,t)=>e.left>t.left&&e.top>t.top&&e.right<t.right&&e.bottom<t.bottom;isOutside=(e,t)=>e.right<t.left||e.bottom<t.top||e.left>t.right||e.top>t.bottom;getCurrentViewport=(e,t,n)=>{let r=-e/n,i=-t/n,a=this.DM.data.container,o=r+a.clientWidth/n;return{bottom:i+a.clientHeight/n,left:r,right:o,top:i}};drawLabelBar=(e,t,n,r,i,a)=>{let o=30*a,s=6*a,c=8*a,l=16*a,u=6*a;this.ctx.save(),this.ctx.translate(e,t),this.ctx.scale(1/a,1/a),this.ctx.font=`${l}px 'Inter', sans-serif`;let d=this.ctx.measureText(n).width+2*u;this.ctx.translate(0,-o-c),this.ctx.fillStyle=r,this.ctx.beginPath(),this.ctx.moveTo(s,0),this.ctx.lineTo(d-s,0),this.ctx.quadraticCurveTo(d,0,d,s),this.ctx.lineTo(d,o-s),this.ctx.quadraticCurveTo(d,o,d-s,o),this.ctx.lineTo(s,o),this.ctx.quadraticCurveTo(0,o,0,o-s),this.ctx.lineTo(0,s),this.ctx.quadraticCurveTo(0,0,s,0),this.ctx.closePath(),this.ctx.fill(),this.ctx.fillStyle=i,this.ctx.fillText(n,u,o*.65),this.ctx.restore()};drawNodeBackground=e=>{let t=this.SM.getColor(e.color);this.ctx.globalAlpha=1,this.ctx.fillStyle=t.background,n(this.ctx,e.x+1,e.y+1,e.width-2,e.height-2,12),this.ctx.fill(),this.ctx.strokeStyle=t.border,this.ctx.lineWidth=2,n(this.ctx,e.x,e.y,e.width,e.height,12),this.ctx.stroke()};drawGroup=(e,t)=>{if(this.drawNodeBackground(e),e.label){let n=this.SM.getColor(e.color);this.drawLabelBar(e.x,e.y,e.label,n.active,n.text,t)}};drawFile=e=>{this.ctx.fillStyle=this.SM.getColor().text;let t=e.ref;this.ctx.font=`16px sans-serif`,this.ctx.fillText(e.fileName??``,t.x+5,t.y-10)};drawEdge=e=>{let t=e.ref,n=this.DM.data.nodeMap[t.fromNode].ref,i=this.DM.data.nodeMap[t.toNode].ref,{x:a,y:o}=r(n,t.fromSide),{x:s,y:c}=r(i,t.toSide),l=this.SM.getColor(t.color),u,d,f,p;e.controlPoints?[u,d,f,p]=e.controlPoints:([u,d,f,p]=this.getControlPoints(a,o,s,c,t.fromSide,t.toSide),e.controlPoints=[u,d,f,p]),this.drawCurvedPath(a,o,s,c,u,d,f,p,l.active),this.drawArrowhead(s,c,f,p,l.active),t.label&&this.drawEdgeLabel(a,o,s,c,u,d,f,p,t.label,l.active,l.text)};drawEdgeLabel=(e,t,r,i,a,o,s,c,l,u,d)=>{let f=.5,p=(1-f)**3*e+3*(1-f)**2*f*a+3*(1-f)*f*f*s+f**3*r,m=(1-f)**3*t+3*(1-f)**2*f*o+3*(1-f)*f*f*c+f**3*i;this.ctx.font=`18px sans-serif`;let h=l.split(`
2
- `),g=0;for(let e of h){let t=this.ctx.measureText(e).width;t>g&&(g=t)}let _=g+16,v=h.length*17+6;this.ctx.fillStyle=u,this.ctx.beginPath(),n(this.ctx,p-_/2,m-v/2-2,_,v,4),this.ctx.fill(),this.ctx.fillStyle=d,this.ctx.textAlign=`center`,this.ctx.textBaseline=`middle`;for(let e=0;e<h.length;e++){let t=(e-(h.length-1)/2)*17;this.ctx.fillText(h[e],p,m-2+t)}this.ctx.textAlign=`left`,this.ctx.textBaseline=`alphabetic`};getControlPoints=(e,t,n,r,i,a)=>{let o=n-e,s=r-t,c=((e,t,n)=>Math.max(t,Math.min(n,e)))((Math.min(Math.abs(o),Math.abs(s))+.3*Math.max(Math.abs(o),Math.abs(s)))*.5,60,300),l=e,u=t,d=n,f=r;switch(i){case`top`:u=t-c;break;case`bottom`:u=t+c;break;case`left`:l=e-c;break;case`right`:l=e+c;break}switch(a){case`top`:f=r-c;break;case`bottom`:f=r+c;break;case`left`:d=n-c;break;case`right`:d=n+c;break}return[l,u,d,f]};drawGridDots=(e,t,n)=>{let r=10*2**-Math.floor(Math.log2(e))*e,i=this.canvas.width,a=this.canvas.height,o=t%r,s=n%r;this.ctx.fillStyle=this.SM.getNamedColor(`dots`);for(let e=o;e<=i;e+=r)for(let t=s;t<=a;t+=r)this.ctx.beginPath(),this.ctx.arc(e,t,1,0,2*Math.PI),this.ctx.fill()};drawCurvedPath=(e,t,n,r,i,a,o,s,c)=>{this.ctx.beginPath(),this.ctx.moveTo(e,t),this.ctx.bezierCurveTo(i,a,o,s,n,r),this.ctx.strokeStyle=c,this.ctx.lineWidth=2,this.ctx.stroke()};drawArrowhead=(e,t,n,r,i)=>{let a=e-n,o=t-r,s=Math.sqrt(a*a+o*o);if(s===0)return;let c=a/s,l=o/s,u=e-c*12-l*4,d=t-l*12+c*4,f=e-c*12+l*4,p=t-l*12-c*4;this.ctx.beginPath(),this.ctx.fillStyle=i,this.ctx.moveTo(e,t),this.ctx.lineTo(u,d),this.ctx.lineTo(f,p),this.ctx.closePath(),this.ctx.fill()};dispose=()=>{this.zoomInOptimize.timeout&&(clearTimeout(this.zoomInOptimize.timeout),this.zoomInOptimize.timeout=void 0),this.canvas.remove(),this._canvas=void 0}};export{c as default};
1
+ import{BaseModule as e}from"./BaseModule.js";import{destroyError as t,drawRoundRect as n,getAnchorCoord as r,resizeCanvasForDPR as i}from"./utilities.js";import a from"./DataManager.js";import o from"./StyleManager.js";import s from"./Controller.js";var c=class extends e{_canvas;ctx;DM;SM;get canvas(){if(!this._canvas)throw t;return this._canvas}constructor(...e){super(...e);let t=this.container.get(s);this.SM=this.container.get(o),t.onRefresh.subscribe(this.redraw),t.onResize.subscribe(this.optimizeDPR),this.DM=this.container.get(a),this._canvas=document.createElement(`canvas`),this._canvas.className=`JCV-main-canvas`,this.ctx=this._canvas.getContext(`2d`),this.DM.data.container.appendChild(this._canvas),this.onDispose(this.dispose)}optimizeDPR=()=>{let e=this.DM.data.container;i(this.canvas,e.offsetWidth,e.offsetHeight)};redraw=()=>{let e=this.DM.data.offsetX,t=this.DM.data.offsetY,n=this.DM.data.scale,r=this.getCurrentViewport(e,t,n);this.canvas.style.transform=``,this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.save(),this.drawGridDots(n,e,t),this.ctx.translate(e,t),this.ctx.scale(n,n),Object.values(this.DM.data.nodeMap).forEach(e=>{if(this.isOutside(e.box,r))return;let t=e.ref;t.type===`file`?this.drawFile(e):t.type===`group`&&this.drawGroup(t,n)}),Object.values(this.DM.data.edgeMap).forEach(e=>{this.isOutside(e.box,r)||this.drawEdge(e)}),this.ctx.restore()};isInside=(e,t)=>e.left>t.left&&e.top>t.top&&e.right<t.right&&e.bottom<t.bottom;isOutside=(e,t)=>e.right<t.left||e.bottom<t.top||e.left>t.right||e.top>t.bottom;getCurrentViewport=(e,t,n)=>{let r=-e/n,i=-t/n,a=this.DM.data.container,o=r+a.clientWidth/n;return{bottom:i+a.clientHeight/n,left:r,right:o,top:i}};drawLabelBar=(e,t,n,r,i,a)=>{let o=30*a,s=6*a,c=8*a,l=16*a,u=6*a;this.ctx.save(),this.ctx.translate(e,t),this.ctx.scale(1/a,1/a),this.ctx.font=`${l}px 'Inter', sans-serif`;let d=this.ctx.measureText(n).width+2*u;this.ctx.translate(0,-o-c),this.ctx.fillStyle=r,this.ctx.beginPath(),this.ctx.moveTo(s,0),this.ctx.lineTo(d-s,0),this.ctx.quadraticCurveTo(d,0,d,s),this.ctx.lineTo(d,o-s),this.ctx.quadraticCurveTo(d,o,d-s,o),this.ctx.lineTo(s,o),this.ctx.quadraticCurveTo(0,o,0,o-s),this.ctx.lineTo(0,s),this.ctx.quadraticCurveTo(0,0,s,0),this.ctx.closePath(),this.ctx.fill(),this.ctx.fillStyle=i,this.ctx.fillText(n,u,o*.65),this.ctx.restore()};drawNodeBackground=e=>{let t=this.SM.getColor(e.color);this.ctx.globalAlpha=1,this.ctx.fillStyle=t.background,n(this.ctx,e.x+1,e.y+1,e.width-2,e.height-2,12),this.ctx.fill(),this.ctx.strokeStyle=t.border,this.ctx.lineWidth=2,n(this.ctx,e.x,e.y,e.width,e.height,12),this.ctx.stroke()};drawGroup=(e,t)=>{if(this.drawNodeBackground(e),e.label){let n=this.SM.getColor(e.color);this.drawLabelBar(e.x,e.y,e.label,n.active,n.text,t)}};drawFile=e=>{this.ctx.fillStyle=this.SM.getColor().text;let t=e.ref;this.ctx.font=`16px sans-serif`,this.ctx.fillText(e.fileName??``,t.x+5,t.y-10)};drawEdge=e=>{let t=e.ref,n=this.DM.data.nodeMap[t.fromNode].ref,i=this.DM.data.nodeMap[t.toNode].ref,{x:a,y:o}=r(n,t.fromSide),{x:s,y:c}=r(i,t.toSide),l=this.SM.getColor(t.color),u,d,f,p;e.controlPoints?[u,d,f,p]=e.controlPoints:([u,d,f,p]=this.getControlPoints(a,o,s,c,t.fromSide,t.toSide),e.controlPoints=[u,d,f,p]),this.drawCurvedPath(a,o,s,c,u,d,f,p,l.active),this.drawArrowhead(s,c,f,p,l.active),t.label&&this.drawEdgeLabel(a,o,s,c,u,d,f,p,t.label,l.active,l.text)};drawEdgeLabel=(e,t,r,i,a,o,s,c,l,u,d)=>{let f=.5,p=(1-f)**3*e+3*(1-f)**2*f*a+3*(1-f)*f*f*s+f**3*r,m=(1-f)**3*t+3*(1-f)**2*f*o+3*(1-f)*f*f*c+f**3*i;this.ctx.font=`18px sans-serif`;let h=l.split(`
2
+ `),g=0;for(let e of h){let t=this.ctx.measureText(e).width;t>g&&(g=t)}let _=g+16,v=h.length*17+6;this.ctx.fillStyle=u,this.ctx.beginPath(),n(this.ctx,p-_/2,m-v/2-2,_,v,4),this.ctx.fill(),this.ctx.fillStyle=d,this.ctx.textAlign=`center`,this.ctx.textBaseline=`middle`;for(let e=0;e<h.length;e++){let t=(e-(h.length-1)/2)*17;this.ctx.fillText(h[e],p,m-2+t)}this.ctx.textAlign=`left`,this.ctx.textBaseline=`alphabetic`};getControlPoints=(e,t,n,r,i,a)=>{let o=n-e,s=r-t,c=((e,t,n)=>Math.max(t,Math.min(n,e)))((Math.min(Math.abs(o),Math.abs(s))+.3*Math.max(Math.abs(o),Math.abs(s)))*.5,60,300),l=e,u=t,d=n,f=r;switch(i){case`top`:u=t-c;break;case`bottom`:u=t+c;break;case`left`:l=e-c;break;case`right`:l=e+c;break}switch(a){case`top`:f=r-c;break;case`bottom`:f=r+c;break;case`left`:d=n-c;break;case`right`:d=n+c;break}return[l,u,d,f]};drawGridDots=(e,t,n)=>{let r=10*2**-Math.floor(Math.log2(e))*e,i=this.canvas.width,a=this.canvas.height,o=t%r,s=n%r;this.ctx.fillStyle=this.SM.getNamedColor(`dots`);for(let e=o;e<=i;e+=r)for(let t=s;t<=a;t+=r)this.ctx.beginPath(),this.ctx.arc(e,t,1,0,2*Math.PI),this.ctx.fill()};drawCurvedPath=(e,t,n,r,i,a,o,s,c)=>{this.ctx.beginPath(),this.ctx.moveTo(e,t),this.ctx.bezierCurveTo(i,a,o,s,n,r),this.ctx.strokeStyle=c,this.ctx.lineWidth=2,this.ctx.stroke()};drawArrowhead=(e,t,n,r,i)=>{let a=e-n,o=t-r,s=Math.sqrt(a*a+o*o);if(s===0)return;let c=a/s,l=o/s,u=e-c*12-l*4,d=t-l*12+c*4,f=e-c*12+l*4,p=t-l*12-c*4;this.ctx.beginPath(),this.ctx.fillStyle=i,this.ctx.moveTo(e,t),this.ctx.lineTo(u,d),this.ctx.lineTo(f,p),this.ctx.closePath(),this.ctx.fill()};dispose=()=>{this.canvas.remove(),this._canvas=void 0}};export{c as default};
3
3
  //# sourceMappingURL=Renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Renderer.js","names":[],"sources":["../../src/kernel/Renderer.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { EdgeItem, NodeItem } from '$/DataManager';\nimport type { Box } from '$/types';\nimport type { JSONCanvasGroupNode, JSONCanvasNode } from '@repo/shared';\nimport { BaseModule } from '$/BaseModule';\nimport Controller from '$/Controller';\nimport DataManager from '$/DataManager';\nimport StyleManager from '$/StyleManager';\nimport { destroyError, drawRoundRect, getAnchorCoord, resizeCanvasForDPR } from '$/utilities';\n\nconst ARROW_LENGTH = 12;\nconst ARROW_WIDTH = 4;\nconst NODE_RADIUS = 12;\nconst CSS_ZOOM_REDRAW_INTERVAL = 500;\nconst NODE_BORDER_WIDTH = 2;\nconst DOT_RADIUS = 1; // Dot radius in CSS pixels\nconst DOT_BASE_GAP = 10; // Base gap between dots in CSS pixels\n\nconst NODE_BORDER_HALF_WIDTH = NODE_BORDER_WIDTH / 2;\n\ntype Options = {\n\tzoomInOptimization?: boolean;\n} & BaseOptions;\n\nexport default class Renderer extends BaseModule<Options> {\n\tprivate _canvas?: HTMLCanvasElement;\n\tprivate readonly ctx: CanvasRenderingContext2D;\n\tprivate readonly DM: DataManager;\n\tprivate readonly SM: StyleManager;\n\tprivate readonly zoomInOptimize: {\n\t\tlastDrawnScale: number;\n\t\tlastDrawnViewport: Box;\n\t\ttimeout?: number;\n\t\tlastCallTime: number;\n\t} = {\n\t\tlastCallTime: 0,\n\t\tlastDrawnScale: 0,\n\t\tlastDrawnViewport: {\n\t\t\tbottom: 0,\n\t\t\tleft: 0,\n\t\t\tright: 0,\n\t\t\ttop: 0,\n\t\t},\n\t};\n\n\tprivate get canvas() {\n\t\tif (!this._canvas) throw destroyError;\n\t\treturn this._canvas;\n\t}\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tconst controller = this.container.get(Controller);\n\t\tthis.SM = this.container.get(StyleManager);\n\t\tcontroller.onRefresh.subscribe(this.redraw);\n\t\tcontroller.onResize.subscribe(this.optimizeDPR);\n\t\tthis.DM = this.container.get(DataManager);\n\t\tthis._canvas = document.createElement('canvas');\n\t\tthis._canvas.className = 'JCV-main-canvas';\n\t\tthis.ctx = this._canvas.getContext('2d') as CanvasRenderingContext2D;\n\t\tthis.DM.data.container.appendChild(this._canvas);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly optimizeDPR = () => {\n\t\tconst container = this.DM.data.container;\n\t\tresizeCanvasForDPR(this.canvas, container.offsetWidth, container.offsetHeight);\n\t};\n\n\tprivate readonly redraw = () => {\n\t\tconst offsetX = this.DM.data.offsetX;\n\t\tconst offsetY = this.DM.data.offsetY;\n\t\tconst scale = this.DM.data.scale;\n\t\tconst currentViewport = this.getCurrentViewport(offsetX, offsetY, scale);\n\t\tif (!this.options.zoomInOptimization) {\n\t\t\tthis.trueRedraw(offsetX, offsetY, scale, currentViewport);\n\t\t\treturn;\n\t\t}\n\t\tif (this.zoomInOptimize.timeout) {\n\t\t\tclearTimeout(this.zoomInOptimize.timeout);\n\t\t\tthis.zoomInOptimize.timeout = undefined;\n\t\t}\n\t\tconst now = Date.now();\n\t\tif (\n\t\t\tthis.isInside(currentViewport, this.zoomInOptimize.lastDrawnViewport) &&\n\t\t\tscale !== this.zoomInOptimize.lastDrawnScale\n\t\t) {\n\t\t\tconst timeSinceLast = now - this.zoomInOptimize.lastCallTime;\n\t\t\tif (timeSinceLast < CSS_ZOOM_REDRAW_INTERVAL) {\n\t\t\t\tthis.zoomInOptimize.timeout = window.setTimeout(() => {\n\t\t\t\t\tthis.trueRedraw(offsetX, offsetY, scale, currentViewport);\n\t\t\t\t\tthis.zoomInOptimize.lastCallTime = now;\n\t\t\t\t\tthis.zoomInOptimize.timeout = undefined;\n\t\t\t\t}, 60);\n\t\t\t\tthis.fakeRedraw(currentViewport, scale);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis.zoomInOptimize.lastCallTime = now;\n\t\tthis.trueRedraw(offsetX, offsetY, scale, currentViewport);\n\t};\n\n\tprivate trueRedraw(offsetX: number, offsetY: number, scale: number, currentViewport: Box) {\n\t\tthis.zoomInOptimize.lastDrawnViewport = currentViewport;\n\t\tthis.zoomInOptimize.lastDrawnScale = scale;\n\t\tthis.canvas.style.transform = '';\n\t\tthis.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\t\tthis.ctx.save();\n\t\tthis.drawGridDots(scale, offsetX, offsetY);\n\t\tthis.ctx.translate(offsetX, offsetY);\n\t\tthis.ctx.scale(scale, scale);\n\t\tObject.values(this.DM.data.nodeMap).forEach((item) => {\n\t\t\tif (this.isOutside(item.box, currentViewport)) return;\n\t\t\tconst node = item.ref;\n\t\t\tif (node.type === 'file') this.drawFile(item);\n\t\t\telse if (node.type === 'group') this.drawGroup(node, scale);\n\t\t});\n\t\tObject.values(this.DM.data.edgeMap).forEach((item) => {\n\t\t\tif (this.isOutside(item.box, currentViewport)) return;\n\t\t\tthis.drawEdge(item);\n\t\t});\n\t\tthis.ctx.restore();\n\t}\n\n\tprivate fakeRedraw(currentViewport: Box, scale: number) {\n\t\tconst cssScale = scale / this.zoomInOptimize.lastDrawnScale;\n\t\tconst currentOffsetX =\n\t\t\t(this.zoomInOptimize.lastDrawnViewport.left - currentViewport.left) * scale;\n\t\tconst currentOffsetY =\n\t\t\t(this.zoomInOptimize.lastDrawnViewport.top - currentViewport.top) * scale;\n\t\tthis.canvas.style.transform = `translate(${currentOffsetX}px, ${currentOffsetY}px) scale(${cssScale})`;\n\t}\n\n\tprivate readonly isInside = (inner: Box, outer: Box) =>\n\t\tinner.left > outer.left &&\n\t\tinner.top > outer.top &&\n\t\tinner.right < outer.right &&\n\t\tinner.bottom < outer.bottom;\n\n\tprivate readonly isOutside = (inner: Box, outer: Box) =>\n\t\tinner.right < outer.left ||\n\t\tinner.bottom < outer.top ||\n\t\tinner.left > outer.right ||\n\t\tinner.top > outer.bottom;\n\n\tprivate readonly getCurrentViewport = (offsetX: number, offsetY: number, scale: number) => {\n\t\tconst left = -offsetX / scale;\n\t\tconst top = -offsetY / scale;\n\t\tconst container = this.DM.data.container;\n\t\tconst right = left + container.clientWidth / scale;\n\t\tconst bottom = top + container.clientHeight / scale;\n\t\treturn { bottom, left, right, top };\n\t};\n\n\tprivate readonly drawLabelBar = (\n\t\tx: number,\n\t\ty: number,\n\t\tlabel: string,\n\t\tcolor: string,\n\t\ttextColor: string,\n\t\tscale: number,\n\t) => {\n\t\tconst barHeight = 30 * scale;\n\t\tconst radius = 6 * scale;\n\t\tconst yOffset = 8 * scale;\n\t\tconst fontSize = 16 * scale;\n\t\tconst xPadding = 6 * scale;\n\t\tthis.ctx.save();\n\t\tthis.ctx.translate(x, y);\n\t\tthis.ctx.scale(1 / scale, 1 / scale);\n\t\tthis.ctx.font = `${fontSize}px 'Inter', sans-serif`;\n\t\tconst barWidth = this.ctx.measureText(label).width + 2 * xPadding;\n\t\tthis.ctx.translate(0, -barHeight - yOffset);\n\t\tthis.ctx.fillStyle = color;\n\t\tthis.ctx.beginPath();\n\t\tthis.ctx.moveTo(radius, 0);\n\t\tthis.ctx.lineTo(barWidth - radius, 0);\n\t\tthis.ctx.quadraticCurveTo(barWidth, 0, barWidth, radius);\n\t\tthis.ctx.lineTo(barWidth, barHeight - radius);\n\t\tthis.ctx.quadraticCurveTo(barWidth, barHeight, barWidth - radius, barHeight);\n\t\tthis.ctx.lineTo(radius, barHeight);\n\t\tthis.ctx.quadraticCurveTo(0, barHeight, 0, barHeight - radius);\n\t\tthis.ctx.lineTo(0, radius);\n\t\tthis.ctx.quadraticCurveTo(0, 0, radius, 0);\n\t\tthis.ctx.closePath();\n\t\tthis.ctx.fill();\n\t\tthis.ctx.fillStyle = textColor;\n\t\tthis.ctx.fillText(label, xPadding, barHeight * 0.65);\n\t\tthis.ctx.restore();\n\t};\n\n\tprivate readonly drawNodeBackground = (node: JSONCanvasNode) => {\n\t\tconst colors = this.SM.getColor(node.color);\n\t\tconst radius = NODE_RADIUS;\n\t\tthis.ctx.globalAlpha = 1;\n\t\tthis.ctx.fillStyle = colors.background;\n\t\tdrawRoundRect(\n\t\t\tthis.ctx,\n\t\t\tnode.x + NODE_BORDER_HALF_WIDTH,\n\t\t\tnode.y + NODE_BORDER_HALF_WIDTH,\n\t\t\tnode.width - NODE_BORDER_WIDTH,\n\t\t\tnode.height - NODE_BORDER_WIDTH,\n\t\t\tradius,\n\t\t);\n\t\tthis.ctx.fill();\n\t\tthis.ctx.strokeStyle = colors.border;\n\t\tthis.ctx.lineWidth = NODE_BORDER_WIDTH;\n\t\tdrawRoundRect(this.ctx, node.x, node.y, node.width, node.height, radius);\n\t\tthis.ctx.stroke();\n\t};\n\n\tprivate readonly drawGroup = (node: JSONCanvasGroupNode, scale: number) => {\n\t\tthis.drawNodeBackground(node);\n\t\tif (node.label) {\n\t\t\tconst color = this.SM.getColor(node.color);\n\t\t\tthis.drawLabelBar(node.x, node.y, node.label, color.active, color.text, scale);\n\t\t}\n\t};\n\n\tprivate readonly drawFile = (item: NodeItem) => {\n\t\tthis.ctx.fillStyle = this.SM.getColor().text;\n\t\tconst node = item.ref;\n\t\tthis.ctx.font = '16px sans-serif';\n\t\tthis.ctx.fillText(item.fileName ?? '', node.x + 5, node.y - 10);\n\t};\n\n\tprivate readonly drawEdge = (item: EdgeItem) => {\n\t\tconst edge = item.ref;\n\t\tconst fromNode = this.DM.data.nodeMap[edge.fromNode].ref;\n\t\tconst toNode = this.DM.data.nodeMap[edge.toNode].ref;\n\t\tconst { x: startX, y: startY } = getAnchorCoord(fromNode, edge.fromSide);\n\t\tconst { x: endX, y: endY } = getAnchorCoord(toNode, edge.toSide);\n\t\tconst color = this.SM.getColor(edge.color);\n\t\tlet startControlX, startControlY, endControlX, endControlY: number;\n\t\tif (!item.controlPoints) {\n\t\t\t[startControlX, startControlY, endControlX, endControlY] = this.getControlPoints(\n\t\t\t\tstartX,\n\t\t\t\tstartY,\n\t\t\t\tendX,\n\t\t\t\tendY,\n\t\t\t\tedge.fromSide,\n\t\t\t\tedge.toSide,\n\t\t\t);\n\t\t\titem.controlPoints = [startControlX, startControlY, endControlX, endControlY];\n\t\t} else [startControlX, startControlY, endControlX, endControlY] = item.controlPoints;\n\t\tthis.drawCurvedPath(\n\t\t\tstartX,\n\t\t\tstartY,\n\t\t\tendX,\n\t\t\tendY,\n\t\t\tstartControlX,\n\t\t\tstartControlY,\n\t\t\tendControlX,\n\t\t\tendControlY,\n\t\t\tcolor.active,\n\t\t);\n\t\tthis.drawArrowhead(endX, endY, endControlX, endControlY, color.active);\n\t\tif (edge.label)\n\t\t\tthis.drawEdgeLabel(\n\t\t\t\tstartX,\n\t\t\t\tstartY,\n\t\t\t\tendX,\n\t\t\t\tendY,\n\t\t\t\tstartControlX,\n\t\t\t\tstartControlY,\n\t\t\t\tendControlX,\n\t\t\t\tendControlY,\n\t\t\t\tedge.label,\n\t\t\t\tcolor.active,\n\t\t\t\tcolor.text,\n\t\t\t);\n\t};\n\n\tprivate readonly drawEdgeLabel = (\n\t\tstartX: number,\n\t\tstartY: number,\n\t\tendX: number,\n\t\tendY: number,\n\t\tstartControlX: number,\n\t\tstartControlY: number,\n\t\tendControlX: number,\n\t\tendControlY: number,\n\t\tlabel: string,\n\t\tcolor: string,\n\t\ttextColor: string,\n\t) => {\n\t\tconst t = 0.5;\n\t\tconst x =\n\t\t\t(1 - t) ** 3 * startX +\n\t\t\t3 * (1 - t) ** 2 * t * startControlX +\n\t\t\t3 * (1 - t) * t * t * endControlX +\n\t\t\tt ** 3 * endX;\n\t\tconst y =\n\t\t\t(1 - t) ** 3 * startY +\n\t\t\t3 * (1 - t) ** 2 * t * startControlY +\n\t\t\t3 * (1 - t) * t * t * endControlY +\n\t\t\tt ** 3 * endY;\n\t\tthis.ctx.font = '18px sans-serif';\n\t\tconst lines = label.split('\\n');\n\t\tconst lineHeight = 17;\n\t\tlet maxWidth = 0;\n\t\tfor (const line of lines) {\n\t\t\tconst w = this.ctx.measureText(line).width;\n\t\t\tif (w > maxWidth) maxWidth = w;\n\t\t}\n\t\tconst paddingX = 8;\n\t\tconst paddingY = 3;\n\t\tconst labelWidth = maxWidth + paddingX * 2;\n\t\tconst labelHeight = lines.length * lineHeight + paddingY * 2; // Dynamic height\n\t\tthis.ctx.fillStyle = color;\n\t\tthis.ctx.beginPath();\n\t\tdrawRoundRect(\n\t\t\tthis.ctx,\n\t\t\tx - labelWidth / 2,\n\t\t\ty - labelHeight / 2 - 2,\n\t\t\tlabelWidth,\n\t\t\tlabelHeight,\n\t\t\t4,\n\t\t);\n\t\tthis.ctx.fill();\n\t\tthis.ctx.fillStyle = textColor;\n\t\tthis.ctx.textAlign = 'center';\n\t\tthis.ctx.textBaseline = 'middle';\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst offsetY = (i - (lines.length - 1) / 2) * lineHeight;\n\t\t\tthis.ctx.fillText(lines[i], x, y - 2 + offsetY);\n\t\t}\n\t\tthis.ctx.textAlign = 'left';\n\t\tthis.ctx.textBaseline = 'alphabetic';\n\t};\n\n\tprivate readonly getControlPoints = (\n\t\tstartX: number,\n\t\tstartY: number,\n\t\tendX: number,\n\t\tendY: number,\n\t\tfromSide: string,\n\t\ttoSide: string,\n\t) => {\n\t\tconst distanceX = endX - startX;\n\t\tconst distanceY = endY - startY;\n\t\tconst realDistance =\n\t\t\tMath.min(Math.abs(distanceX), Math.abs(distanceY)) +\n\t\t\t0.3 * Math.max(Math.abs(distanceX), Math.abs(distanceY));\n\t\tconst clamp = (val: number, min: number, max: number) => Math.max(min, Math.min(max, val));\n\t\tconst PADDING = clamp(realDistance * 0.5, 60, 300);\n\t\tlet startControlX = startX;\n\t\tlet startControlY = startY;\n\t\tlet endControlX = endX;\n\t\tlet endControlY = endY;\n\t\tswitch (fromSide) {\n\t\t\tcase 'top': {\n\t\t\t\tstartControlY = startY - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'bottom': {\n\t\t\t\tstartControlY = startY + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\tstartControlX = startX - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'right': {\n\t\t\t\tstartControlX = startX + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tswitch (toSide) {\n\t\t\tcase 'top': {\n\t\t\t\tendControlY = endY - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'bottom': {\n\t\t\t\tendControlY = endY + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\tendControlX = endX - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'right': {\n\t\t\t\tendControlX = endX + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn [startControlX, startControlY, endControlX, endControlY];\n\t};\n\n\tprivate readonly drawGridDots = (scale: number, offsetX: number, offsetY: number) => {\n\t\tconst scaleLevel = -Math.floor(Math.log2(scale));\n\t\tconst actualGap = DOT_BASE_GAP * 2 ** scaleLevel * scale;\n\t\tconst width = this.canvas.width;\n\t\tconst height = this.canvas.height;\n\t\tconst startX = offsetX % actualGap;\n\t\tconst startY = offsetY % actualGap;\n\t\tthis.ctx.fillStyle = this.SM.getNamedColor('dots');\n\t\tfor (let x = startX; x <= width; x += actualGap)\n\t\t\tfor (let y = startY; y <= height; y += actualGap) {\n\t\t\t\tthis.ctx.beginPath();\n\t\t\t\tthis.ctx.arc(x, y, DOT_RADIUS, 0, 2 * Math.PI);\n\t\t\t\tthis.ctx.fill();\n\t\t\t}\n\t};\n\n\tprivate readonly drawCurvedPath = (\n\t\tstartX: number,\n\t\tstartY: number,\n\t\tendX: number,\n\t\tendY: number,\n\t\tc1x: number,\n\t\tc1y: number,\n\t\tc2x: number,\n\t\tc2y: number,\n\t\tcolor: string,\n\t) => {\n\t\tthis.ctx.beginPath();\n\t\tthis.ctx.moveTo(startX, startY);\n\t\tthis.ctx.bezierCurveTo(c1x, c1y, c2x, c2y, endX, endY);\n\t\tthis.ctx.strokeStyle = color;\n\t\tthis.ctx.lineWidth = 2;\n\t\tthis.ctx.stroke();\n\t};\n\n\tprivate readonly drawArrowhead = (\n\t\ttipX: number,\n\t\ttipY: number,\n\t\tfromX: number,\n\t\tfromY: number,\n\t\tcolor: string,\n\t) => {\n\t\tconst dx = tipX - fromX;\n\t\tconst dy = tipY - fromY;\n\t\tconst length = Math.sqrt(dx * dx + dy * dy);\n\t\tif (length === 0) return;\n\t\tconst unitX = dx / length;\n\t\tconst unitY = dy / length;\n\t\tconst leftX = tipX - unitX * ARROW_LENGTH - unitY * ARROW_WIDTH;\n\t\tconst leftY = tipY - unitY * ARROW_LENGTH + unitX * ARROW_WIDTH;\n\t\tconst rightX = tipX - unitX * ARROW_LENGTH + unitY * ARROW_WIDTH;\n\t\tconst rightY = tipY - unitY * ARROW_LENGTH - unitX * ARROW_WIDTH;\n\t\tthis.ctx.beginPath();\n\t\tthis.ctx.fillStyle = color;\n\t\tthis.ctx.moveTo(tipX, tipY);\n\t\tthis.ctx.lineTo(leftX, leftY);\n\t\tthis.ctx.lineTo(rightX, rightY);\n\t\tthis.ctx.closePath();\n\t\tthis.ctx.fill();\n\t};\n\n\tprivate readonly dispose = () => {\n\t\tif (this.zoomInOptimize.timeout) {\n\t\t\tclearTimeout(this.zoomInOptimize.timeout);\n\t\t\tthis.zoomInOptimize.timeout = undefined;\n\t\t}\n\t\tthis.canvas.remove();\n\t\tthis._canvas = undefined;\n\t};\n}\n"],"mappings":"0PAyBA,IAAqB,EAArB,cAAsC,CAAoB,CACzD,QACA,IACA,GACA,GACA,eAKI,CACH,aAAc,EACd,eAAgB,EAChB,kBAAmB,CAClB,OAAQ,EACR,KAAM,EACN,MAAO,EACP,IAAK,EACL,CACD,CAED,IAAY,QAAS,CACpB,GAAI,CAAC,KAAK,QAAS,MAAM,EACzB,OAAO,KAAK,QAGb,YAAY,GAAG,EAAgB,CAC9B,MAAM,GAAG,EAAK,CACd,IAAM,EAAa,KAAK,UAAU,IAAI,EAAW,CACjD,KAAK,GAAK,KAAK,UAAU,IAAI,EAAa,CAC1C,EAAW,UAAU,UAAU,KAAK,OAAO,CAC3C,EAAW,SAAS,UAAU,KAAK,YAAY,CAC/C,KAAK,GAAK,KAAK,UAAU,IAAI,EAAY,CACzC,KAAK,QAAU,SAAS,cAAc,SAAS,CAC/C,KAAK,QAAQ,UAAY,kBACzB,KAAK,IAAM,KAAK,QAAQ,WAAW,KAAK,CACxC,KAAK,GAAG,KAAK,UAAU,YAAY,KAAK,QAAQ,CAChD,KAAK,UAAU,KAAK,QAAQ,CAG7B,gBAAqC,CACpC,IAAM,EAAY,KAAK,GAAG,KAAK,UAC/B,EAAmB,KAAK,OAAQ,EAAU,YAAa,EAAU,aAAa,EAG/E,WAAgC,CAC/B,IAAM,EAAU,KAAK,GAAG,KAAK,QACvB,EAAU,KAAK,GAAG,KAAK,QACvB,EAAQ,KAAK,GAAG,KAAK,MACrB,EAAkB,KAAK,mBAAmB,EAAS,EAAS,EAAM,CACxE,GAAI,CAAC,KAAK,QAAQ,mBAAoB,CACrC,KAAK,WAAW,EAAS,EAAS,EAAO,EAAgB,CACzD,OAEG,KAAK,eAAe,UACvB,aAAa,KAAK,eAAe,QAAQ,CACzC,KAAK,eAAe,QAAU,IAAA,IAE/B,IAAM,EAAM,KAAK,KAAK,CACtB,GACC,KAAK,SAAS,EAAiB,KAAK,eAAe,kBAAkB,EACrE,IAAU,KAAK,eAAe,gBAER,EAAM,KAAK,eAAe,aAC5B,IAA0B,CAC7C,KAAK,eAAe,QAAU,OAAO,eAAiB,CACrD,KAAK,WAAW,EAAS,EAAS,EAAO,EAAgB,CACzD,KAAK,eAAe,aAAe,EACnC,KAAK,eAAe,QAAU,IAAA,IAC5B,GAAG,CACN,KAAK,WAAW,EAAiB,EAAM,CACvC,OAGF,KAAK,eAAe,aAAe,EACnC,KAAK,WAAW,EAAS,EAAS,EAAO,EAAgB,EAG1D,WAAmB,EAAiB,EAAiB,EAAe,EAAsB,CACzF,KAAK,eAAe,kBAAoB,EACxC,KAAK,eAAe,eAAiB,EACrC,KAAK,OAAO,MAAM,UAAY,GAC9B,KAAK,IAAI,UAAU,EAAG,EAAG,KAAK,OAAO,MAAO,KAAK,OAAO,OAAO,CAC/D,KAAK,IAAI,MAAM,CACf,KAAK,aAAa,EAAO,EAAS,EAAQ,CAC1C,KAAK,IAAI,UAAU,EAAS,EAAQ,CACpC,KAAK,IAAI,MAAM,EAAO,EAAM,CAC5B,OAAO,OAAO,KAAK,GAAG,KAAK,QAAQ,CAAC,QAAS,GAAS,CACrD,GAAI,KAAK,UAAU,EAAK,IAAK,EAAgB,CAAE,OAC/C,IAAM,EAAO,EAAK,IACd,EAAK,OAAS,OAAQ,KAAK,SAAS,EAAK,CACpC,EAAK,OAAS,SAAS,KAAK,UAAU,EAAM,EAAM,EAC1D,CACF,OAAO,OAAO,KAAK,GAAG,KAAK,QAAQ,CAAC,QAAS,GAAS,CACjD,KAAK,UAAU,EAAK,IAAK,EAAgB,EAC7C,KAAK,SAAS,EAAK,EAClB,CACF,KAAK,IAAI,SAAS,CAGnB,WAAmB,EAAsB,EAAe,CACvD,IAAM,EAAW,EAAQ,KAAK,eAAe,eACvC,GACJ,KAAK,eAAe,kBAAkB,KAAO,EAAgB,MAAQ,EACjE,GACJ,KAAK,eAAe,kBAAkB,IAAM,EAAgB,KAAO,EACrE,KAAK,OAAO,MAAM,UAAY,aAAa,EAAe,MAAM,EAAe,YAAY,EAAS,GAGrG,UAA6B,EAAY,IACxC,EAAM,KAAO,EAAM,MACnB,EAAM,IAAM,EAAM,KAClB,EAAM,MAAQ,EAAM,OACpB,EAAM,OAAS,EAAM,OAEtB,WAA8B,EAAY,IACzC,EAAM,MAAQ,EAAM,MACpB,EAAM,OAAS,EAAM,KACrB,EAAM,KAAO,EAAM,OACnB,EAAM,IAAM,EAAM,OAEnB,oBAAuC,EAAiB,EAAiB,IAAkB,CAC1F,IAAM,EAAO,CAAC,EAAU,EAClB,EAAM,CAAC,EAAU,EACjB,EAAY,KAAK,GAAG,KAAK,UACzB,EAAQ,EAAO,EAAU,YAAc,EAE7C,MAAO,CAAE,OADM,EAAM,EAAU,aAAe,EAC7B,OAAM,QAAO,MAAK,EAGpC,cACC,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAY,GAAK,EACjB,EAAS,EAAI,EACb,EAAU,EAAI,EACd,EAAW,GAAK,EAChB,EAAW,EAAI,EACrB,KAAK,IAAI,MAAM,CACf,KAAK,IAAI,UAAU,EAAG,EAAE,CACxB,KAAK,IAAI,MAAM,EAAI,EAAO,EAAI,EAAM,CACpC,KAAK,IAAI,KAAO,GAAG,EAAS,wBAC5B,IAAM,EAAW,KAAK,IAAI,YAAY,EAAM,CAAC,MAAQ,EAAI,EACzD,KAAK,IAAI,UAAU,EAAG,CAAC,EAAY,EAAQ,CAC3C,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,WAAW,CACpB,KAAK,IAAI,OAAO,EAAQ,EAAE,CAC1B,KAAK,IAAI,OAAO,EAAW,EAAQ,EAAE,CACrC,KAAK,IAAI,iBAAiB,EAAU,EAAG,EAAU,EAAO,CACxD,KAAK,IAAI,OAAO,EAAU,EAAY,EAAO,CAC7C,KAAK,IAAI,iBAAiB,EAAU,EAAW,EAAW,EAAQ,EAAU,CAC5E,KAAK,IAAI,OAAO,EAAQ,EAAU,CAClC,KAAK,IAAI,iBAAiB,EAAG,EAAW,EAAG,EAAY,EAAO,CAC9D,KAAK,IAAI,OAAO,EAAG,EAAO,CAC1B,KAAK,IAAI,iBAAiB,EAAG,EAAG,EAAQ,EAAE,CAC1C,KAAK,IAAI,WAAW,CACpB,KAAK,IAAI,MAAM,CACf,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,SAAS,EAAO,EAAU,EAAY,IAAK,CACpD,KAAK,IAAI,SAAS,EAGnB,mBAAuC,GAAyB,CAC/D,IAAM,EAAS,KAAK,GAAG,SAAS,EAAK,MAAM,CAE3C,KAAK,IAAI,YAAc,EACvB,KAAK,IAAI,UAAY,EAAO,WAC5B,EACC,KAAK,IACL,EAAK,EAAI,EACT,EAAK,EAAI,EACT,EAAK,MAAQ,EACb,EAAK,OAAS,EACd,GACA,CACD,KAAK,IAAI,MAAM,CACf,KAAK,IAAI,YAAc,EAAO,OAC9B,KAAK,IAAI,UAAY,EACrB,EAAc,KAAK,IAAK,EAAK,EAAG,EAAK,EAAG,EAAK,MAAO,EAAK,OAAQ,GAAO,CACxE,KAAK,IAAI,QAAQ,EAGlB,WAA8B,EAA2B,IAAkB,CAE1E,GADA,KAAK,mBAAmB,EAAK,CACzB,EAAK,MAAO,CACf,IAAM,EAAQ,KAAK,GAAG,SAAS,EAAK,MAAM,CAC1C,KAAK,aAAa,EAAK,EAAG,EAAK,EAAG,EAAK,MAAO,EAAM,OAAQ,EAAM,KAAM,EAAM,GAIhF,SAA6B,GAAmB,CAC/C,KAAK,IAAI,UAAY,KAAK,GAAG,UAAU,CAAC,KACxC,IAAM,EAAO,EAAK,IAClB,KAAK,IAAI,KAAO,kBAChB,KAAK,IAAI,SAAS,EAAK,UAAY,GAAI,EAAK,EAAI,EAAG,EAAK,EAAI,GAAG,EAGhE,SAA6B,GAAmB,CAC/C,IAAM,EAAO,EAAK,IACZ,EAAW,KAAK,GAAG,KAAK,QAAQ,EAAK,UAAU,IAC/C,EAAS,KAAK,GAAG,KAAK,QAAQ,EAAK,QAAQ,IAC3C,CAAE,EAAG,EAAQ,EAAG,GAAW,EAAe,EAAU,EAAK,SAAS,CAClE,CAAE,EAAG,EAAM,EAAG,GAAS,EAAe,EAAQ,EAAK,OAAO,CAC1D,EAAQ,KAAK,GAAG,SAAS,EAAK,MAAM,CACtC,EAAe,EAAe,EAAa,EAC1C,EAAK,cAUH,CAAC,EAAe,EAAe,EAAa,GAAe,EAAK,eATtE,CAAC,EAAe,EAAe,EAAa,GAAe,KAAK,iBAC/D,EACA,EACA,EACA,EACA,EAAK,SACL,EAAK,OACL,CACD,EAAK,cAAgB,CAAC,EAAe,EAAe,EAAa,EAAY,EAE9E,KAAK,eACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAAM,OACN,CACD,KAAK,cAAc,EAAM,EAAM,EAAa,EAAa,EAAM,OAAO,CAClE,EAAK,OACR,KAAK,cACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAAK,MACL,EAAM,OACN,EAAM,KACN,EAGH,eACC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAI,GACJ,GACJ,EAAI,IAAM,EAAI,EACf,GAAK,EAAI,IAAM,EAAI,EAAI,EACvB,GAAK,EAAI,GAAK,EAAI,EAAI,EACtB,GAAK,EAAI,EACJ,GACJ,EAAI,IAAM,EAAI,EACf,GAAK,EAAI,IAAM,EAAI,EAAI,EACvB,GAAK,EAAI,GAAK,EAAI,EAAI,EACtB,GAAK,EAAI,EACV,KAAK,IAAI,KAAO,kBAChB,IAAM,EAAQ,EAAM,MAAM;EAAK,CAE3B,EAAW,EACf,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAI,KAAK,IAAI,YAAY,EAAK,CAAC,MACjC,EAAI,IAAU,EAAW,GAE9B,IAEM,EAAa,EAAW,GACxB,EAAc,EAAM,OAAS,GAAa,EAChD,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,WAAW,CACpB,EACC,KAAK,IACL,EAAI,EAAa,EACjB,EAAI,EAAc,EAAI,EACtB,EACA,EACA,EACA,CACD,KAAK,IAAI,MAAM,CACf,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,UAAY,SACrB,KAAK,IAAI,aAAe,SACxB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACtC,IAAM,GAAW,GAAK,EAAM,OAAS,GAAK,GAAK,GAC/C,KAAK,IAAI,SAAS,EAAM,GAAI,EAAG,EAAI,EAAI,EAAQ,CAEhD,KAAK,IAAI,UAAY,OACrB,KAAK,IAAI,aAAe,cAGzB,kBACC,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAY,EAAO,EACnB,EAAY,EAAO,EAKnB,IADS,EAAa,EAAa,IAAgB,KAAK,IAAI,EAAK,KAAK,IAAI,EAAK,EAAI,CAAC,GAFzF,KAAK,IAAI,KAAK,IAAI,EAAU,CAAE,KAAK,IAAI,EAAU,CAAC,CAClD,GAAM,KAAK,IAAI,KAAK,IAAI,EAAU,CAAE,KAAK,IAAI,EAAU,CAAC,EAEpB,GAAK,GAAI,IAAI,CAC9C,EAAgB,EAChB,EAAgB,EAChB,EAAc,EACd,EAAc,EAClB,OAAQ,EAAR,CACC,IAAK,MACJ,EAAgB,EAAS,EACzB,MAED,IAAK,SACJ,EAAgB,EAAS,EACzB,MAED,IAAK,OACJ,EAAgB,EAAS,EACzB,MAED,IAAK,QACJ,EAAgB,EAAS,EACzB,MAGF,OAAQ,EAAR,CACC,IAAK,MACJ,EAAc,EAAO,EACrB,MAED,IAAK,SACJ,EAAc,EAAO,EACrB,MAED,IAAK,OACJ,EAAc,EAAO,EACrB,MAED,IAAK,QACJ,EAAc,EAAO,EACrB,MAGF,MAAO,CAAC,EAAe,EAAe,EAAa,EAAY,EAGhE,cAAiC,EAAe,EAAiB,IAAoB,CAEpF,IAAM,EAAY,GAAe,GAAK,CADlB,KAAK,MAAM,KAAK,KAAK,EAAM,CAAC,CACG,EAC7C,EAAQ,KAAK,OAAO,MACpB,EAAS,KAAK,OAAO,OACrB,EAAS,EAAU,EACnB,EAAS,EAAU,EACzB,KAAK,IAAI,UAAY,KAAK,GAAG,cAAc,OAAO,CAClD,IAAK,IAAI,EAAI,EAAQ,GAAK,EAAO,GAAK,EACrC,IAAK,IAAI,EAAI,EAAQ,GAAK,EAAQ,GAAK,EACtC,KAAK,IAAI,WAAW,CACpB,KAAK,IAAI,IAAI,EAAG,EAAG,EAAY,EAAG,EAAI,KAAK,GAAG,CAC9C,KAAK,IAAI,MAAM,EAIlB,gBACC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,KAAK,IAAI,WAAW,CACpB,KAAK,IAAI,OAAO,EAAQ,EAAO,CAC/B,KAAK,IAAI,cAAc,EAAK,EAAK,EAAK,EAAK,EAAM,EAAK,CACtD,KAAK,IAAI,YAAc,EACvB,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,QAAQ,EAGlB,eACC,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAK,EAAO,EACZ,EAAK,EAAO,EACZ,EAAS,KAAK,KAAK,EAAK,EAAK,EAAK,EAAG,CAC3C,GAAI,IAAW,EAAG,OAClB,IAAM,EAAQ,EAAK,EACb,EAAQ,EAAK,EACb,EAAQ,EAAO,EAAQ,GAAe,EAAQ,EAC9C,EAAQ,EAAO,EAAQ,GAAe,EAAQ,EAC9C,EAAS,EAAO,EAAQ,GAAe,EAAQ,EAC/C,EAAS,EAAO,EAAQ,GAAe,EAAQ,EACrD,KAAK,IAAI,WAAW,CACpB,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,OAAO,EAAM,EAAK,CAC3B,KAAK,IAAI,OAAO,EAAO,EAAM,CAC7B,KAAK,IAAI,OAAO,EAAQ,EAAO,CAC/B,KAAK,IAAI,WAAW,CACpB,KAAK,IAAI,MAAM,EAGhB,YAAiC,CAC5B,KAAK,eAAe,UACvB,aAAa,KAAK,eAAe,QAAQ,CACzC,KAAK,eAAe,QAAU,IAAA,IAE/B,KAAK,OAAO,QAAQ,CACpB,KAAK,QAAU,IAAA"}
1
+ {"version":3,"file":"Renderer.js","names":[],"sources":["../../src/kernel/Renderer.ts"],"sourcesContent":["import type { BaseArgs } from '$/BaseModule';\nimport type { EdgeItem, NodeItem } from '$/DataManager';\nimport type { Box } from '$/types';\nimport type { JSONCanvasGroupNode, JSONCanvasNode } from '@repo/shared';\nimport { BaseModule } from '$/BaseModule';\nimport Controller from '$/Controller';\nimport DataManager from '$/DataManager';\nimport StyleManager from '$/StyleManager';\nimport { destroyError, drawRoundRect, getAnchorCoord, resizeCanvasForDPR } from '$/utilities';\n\nconst ARROW_LENGTH = 12;\nconst ARROW_WIDTH = 4;\nconst NODE_RADIUS = 12;\nconst NODE_BORDER_WIDTH = 2;\nconst DOT_RADIUS = 1; // Dot radius in CSS pixels\nconst DOT_BASE_GAP = 10; // Base gap between dots in CSS pixels\n\nconst NODE_BORDER_HALF_WIDTH = NODE_BORDER_WIDTH / 2;\n\nexport default class Renderer extends BaseModule {\n\tprivate _canvas?: HTMLCanvasElement;\n\tprivate readonly ctx: CanvasRenderingContext2D;\n\tprivate readonly DM: DataManager;\n\tprivate readonly SM: StyleManager;\n\n\tprivate get canvas() {\n\t\tif (!this._canvas) throw destroyError;\n\t\treturn this._canvas;\n\t}\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\t\tconst controller = this.container.get(Controller);\n\t\tthis.SM = this.container.get(StyleManager);\n\t\tcontroller.onRefresh.subscribe(this.redraw);\n\t\tcontroller.onResize.subscribe(this.optimizeDPR);\n\t\tthis.DM = this.container.get(DataManager);\n\t\tthis._canvas = document.createElement('canvas');\n\t\tthis._canvas.className = 'JCV-main-canvas';\n\t\tthis.ctx = this._canvas.getContext('2d') as CanvasRenderingContext2D;\n\t\tthis.DM.data.container.appendChild(this._canvas);\n\t\tthis.onDispose(this.dispose);\n\t}\n\n\tprivate readonly optimizeDPR = () => {\n\t\tconst container = this.DM.data.container;\n\t\tresizeCanvasForDPR(this.canvas, container.offsetWidth, container.offsetHeight);\n\t};\n\n\tprivate readonly redraw = () => {\n\t\tconst offsetX = this.DM.data.offsetX;\n\t\tconst offsetY = this.DM.data.offsetY;\n\t\tconst scale = this.DM.data.scale;\n\t\tconst currentViewport = this.getCurrentViewport(offsetX, offsetY, scale);\n\t\tthis.canvas.style.transform = '';\n\t\tthis.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\t\tthis.ctx.save();\n\t\tthis.drawGridDots(scale, offsetX, offsetY);\n\t\tthis.ctx.translate(offsetX, offsetY);\n\t\tthis.ctx.scale(scale, scale);\n\t\tObject.values(this.DM.data.nodeMap).forEach((item) => {\n\t\t\tif (this.isOutside(item.box, currentViewport)) return;\n\t\t\tconst node = item.ref;\n\t\t\tif (node.type === 'file') this.drawFile(item);\n\t\t\telse if (node.type === 'group') this.drawGroup(node, scale);\n\t\t});\n\t\tObject.values(this.DM.data.edgeMap).forEach((item) => {\n\t\t\tif (this.isOutside(item.box, currentViewport)) return;\n\t\t\tthis.drawEdge(item);\n\t\t});\n\t\tthis.ctx.restore();\n\t};\n\n\tprivate readonly isInside = (inner: Box, outer: Box) =>\n\t\tinner.left > outer.left &&\n\t\tinner.top > outer.top &&\n\t\tinner.right < outer.right &&\n\t\tinner.bottom < outer.bottom;\n\n\tprivate readonly isOutside = (inner: Box, outer: Box) =>\n\t\tinner.right < outer.left ||\n\t\tinner.bottom < outer.top ||\n\t\tinner.left > outer.right ||\n\t\tinner.top > outer.bottom;\n\n\tprivate readonly getCurrentViewport = (offsetX: number, offsetY: number, scale: number) => {\n\t\tconst left = -offsetX / scale;\n\t\tconst top = -offsetY / scale;\n\t\tconst container = this.DM.data.container;\n\t\tconst right = left + container.clientWidth / scale;\n\t\tconst bottom = top + container.clientHeight / scale;\n\t\treturn { bottom, left, right, top };\n\t};\n\n\tprivate readonly drawLabelBar = (\n\t\tx: number,\n\t\ty: number,\n\t\tlabel: string,\n\t\tcolor: string,\n\t\ttextColor: string,\n\t\tscale: number,\n\t) => {\n\t\tconst barHeight = 30 * scale;\n\t\tconst radius = 6 * scale;\n\t\tconst yOffset = 8 * scale;\n\t\tconst fontSize = 16 * scale;\n\t\tconst xPadding = 6 * scale;\n\t\tthis.ctx.save();\n\t\tthis.ctx.translate(x, y);\n\t\tthis.ctx.scale(1 / scale, 1 / scale);\n\t\tthis.ctx.font = `${fontSize}px 'Inter', sans-serif`;\n\t\tconst barWidth = this.ctx.measureText(label).width + 2 * xPadding;\n\t\tthis.ctx.translate(0, -barHeight - yOffset);\n\t\tthis.ctx.fillStyle = color;\n\t\tthis.ctx.beginPath();\n\t\tthis.ctx.moveTo(radius, 0);\n\t\tthis.ctx.lineTo(barWidth - radius, 0);\n\t\tthis.ctx.quadraticCurveTo(barWidth, 0, barWidth, radius);\n\t\tthis.ctx.lineTo(barWidth, barHeight - radius);\n\t\tthis.ctx.quadraticCurveTo(barWidth, barHeight, barWidth - radius, barHeight);\n\t\tthis.ctx.lineTo(radius, barHeight);\n\t\tthis.ctx.quadraticCurveTo(0, barHeight, 0, barHeight - radius);\n\t\tthis.ctx.lineTo(0, radius);\n\t\tthis.ctx.quadraticCurveTo(0, 0, radius, 0);\n\t\tthis.ctx.closePath();\n\t\tthis.ctx.fill();\n\t\tthis.ctx.fillStyle = textColor;\n\t\tthis.ctx.fillText(label, xPadding, barHeight * 0.65);\n\t\tthis.ctx.restore();\n\t};\n\n\tprivate readonly drawNodeBackground = (node: JSONCanvasNode) => {\n\t\tconst colors = this.SM.getColor(node.color);\n\t\tconst radius = NODE_RADIUS;\n\t\tthis.ctx.globalAlpha = 1;\n\t\tthis.ctx.fillStyle = colors.background;\n\t\tdrawRoundRect(\n\t\t\tthis.ctx,\n\t\t\tnode.x + NODE_BORDER_HALF_WIDTH,\n\t\t\tnode.y + NODE_BORDER_HALF_WIDTH,\n\t\t\tnode.width - NODE_BORDER_WIDTH,\n\t\t\tnode.height - NODE_BORDER_WIDTH,\n\t\t\tradius,\n\t\t);\n\t\tthis.ctx.fill();\n\t\tthis.ctx.strokeStyle = colors.border;\n\t\tthis.ctx.lineWidth = NODE_BORDER_WIDTH;\n\t\tdrawRoundRect(this.ctx, node.x, node.y, node.width, node.height, radius);\n\t\tthis.ctx.stroke();\n\t};\n\n\tprivate readonly drawGroup = (node: JSONCanvasGroupNode, scale: number) => {\n\t\tthis.drawNodeBackground(node);\n\t\tif (node.label) {\n\t\t\tconst color = this.SM.getColor(node.color);\n\t\t\tthis.drawLabelBar(node.x, node.y, node.label, color.active, color.text, scale);\n\t\t}\n\t};\n\n\tprivate readonly drawFile = (item: NodeItem) => {\n\t\tthis.ctx.fillStyle = this.SM.getColor().text;\n\t\tconst node = item.ref;\n\t\tthis.ctx.font = '16px sans-serif';\n\t\tthis.ctx.fillText(item.fileName ?? '', node.x + 5, node.y - 10);\n\t};\n\n\tprivate readonly drawEdge = (item: EdgeItem) => {\n\t\tconst edge = item.ref;\n\t\tconst fromNode = this.DM.data.nodeMap[edge.fromNode].ref;\n\t\tconst toNode = this.DM.data.nodeMap[edge.toNode].ref;\n\t\tconst { x: startX, y: startY } = getAnchorCoord(fromNode, edge.fromSide);\n\t\tconst { x: endX, y: endY } = getAnchorCoord(toNode, edge.toSide);\n\t\tconst color = this.SM.getColor(edge.color);\n\t\tlet startControlX, startControlY, endControlX, endControlY: number;\n\t\tif (!item.controlPoints) {\n\t\t\t[startControlX, startControlY, endControlX, endControlY] = this.getControlPoints(\n\t\t\t\tstartX,\n\t\t\t\tstartY,\n\t\t\t\tendX,\n\t\t\t\tendY,\n\t\t\t\tedge.fromSide,\n\t\t\t\tedge.toSide,\n\t\t\t);\n\t\t\titem.controlPoints = [startControlX, startControlY, endControlX, endControlY];\n\t\t} else [startControlX, startControlY, endControlX, endControlY] = item.controlPoints;\n\t\tthis.drawCurvedPath(\n\t\t\tstartX,\n\t\t\tstartY,\n\t\t\tendX,\n\t\t\tendY,\n\t\t\tstartControlX,\n\t\t\tstartControlY,\n\t\t\tendControlX,\n\t\t\tendControlY,\n\t\t\tcolor.active,\n\t\t);\n\t\tthis.drawArrowhead(endX, endY, endControlX, endControlY, color.active);\n\t\tif (edge.label)\n\t\t\tthis.drawEdgeLabel(\n\t\t\t\tstartX,\n\t\t\t\tstartY,\n\t\t\t\tendX,\n\t\t\t\tendY,\n\t\t\t\tstartControlX,\n\t\t\t\tstartControlY,\n\t\t\t\tendControlX,\n\t\t\t\tendControlY,\n\t\t\t\tedge.label,\n\t\t\t\tcolor.active,\n\t\t\t\tcolor.text,\n\t\t\t);\n\t};\n\n\tprivate readonly drawEdgeLabel = (\n\t\tstartX: number,\n\t\tstartY: number,\n\t\tendX: number,\n\t\tendY: number,\n\t\tstartControlX: number,\n\t\tstartControlY: number,\n\t\tendControlX: number,\n\t\tendControlY: number,\n\t\tlabel: string,\n\t\tcolor: string,\n\t\ttextColor: string,\n\t) => {\n\t\tconst t = 0.5;\n\t\tconst x =\n\t\t\t(1 - t) ** 3 * startX +\n\t\t\t3 * (1 - t) ** 2 * t * startControlX +\n\t\t\t3 * (1 - t) * t * t * endControlX +\n\t\t\tt ** 3 * endX;\n\t\tconst y =\n\t\t\t(1 - t) ** 3 * startY +\n\t\t\t3 * (1 - t) ** 2 * t * startControlY +\n\t\t\t3 * (1 - t) * t * t * endControlY +\n\t\t\tt ** 3 * endY;\n\t\tthis.ctx.font = '18px sans-serif';\n\t\tconst lines = label.split('\\n');\n\t\tconst lineHeight = 17;\n\t\tlet maxWidth = 0;\n\t\tfor (const line of lines) {\n\t\t\tconst w = this.ctx.measureText(line).width;\n\t\t\tif (w > maxWidth) maxWidth = w;\n\t\t}\n\t\tconst paddingX = 8;\n\t\tconst paddingY = 3;\n\t\tconst labelWidth = maxWidth + paddingX * 2;\n\t\tconst labelHeight = lines.length * lineHeight + paddingY * 2; // Dynamic height\n\t\tthis.ctx.fillStyle = color;\n\t\tthis.ctx.beginPath();\n\t\tdrawRoundRect(\n\t\t\tthis.ctx,\n\t\t\tx - labelWidth / 2,\n\t\t\ty - labelHeight / 2 - 2,\n\t\t\tlabelWidth,\n\t\t\tlabelHeight,\n\t\t\t4,\n\t\t);\n\t\tthis.ctx.fill();\n\t\tthis.ctx.fillStyle = textColor;\n\t\tthis.ctx.textAlign = 'center';\n\t\tthis.ctx.textBaseline = 'middle';\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst offsetY = (i - (lines.length - 1) / 2) * lineHeight;\n\t\t\tthis.ctx.fillText(lines[i], x, y - 2 + offsetY);\n\t\t}\n\t\tthis.ctx.textAlign = 'left';\n\t\tthis.ctx.textBaseline = 'alphabetic';\n\t};\n\n\tprivate readonly getControlPoints = (\n\t\tstartX: number,\n\t\tstartY: number,\n\t\tendX: number,\n\t\tendY: number,\n\t\tfromSide: string,\n\t\ttoSide: string,\n\t) => {\n\t\tconst distanceX = endX - startX;\n\t\tconst distanceY = endY - startY;\n\t\tconst realDistance =\n\t\t\tMath.min(Math.abs(distanceX), Math.abs(distanceY)) +\n\t\t\t0.3 * Math.max(Math.abs(distanceX), Math.abs(distanceY));\n\t\tconst clamp = (val: number, min: number, max: number) => Math.max(min, Math.min(max, val));\n\t\tconst PADDING = clamp(realDistance * 0.5, 60, 300);\n\t\tlet startControlX = startX;\n\t\tlet startControlY = startY;\n\t\tlet endControlX = endX;\n\t\tlet endControlY = endY;\n\t\tswitch (fromSide) {\n\t\t\tcase 'top': {\n\t\t\t\tstartControlY = startY - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'bottom': {\n\t\t\t\tstartControlY = startY + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\tstartControlX = startX - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'right': {\n\t\t\t\tstartControlX = startX + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tswitch (toSide) {\n\t\t\tcase 'top': {\n\t\t\t\tendControlY = endY - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'bottom': {\n\t\t\t\tendControlY = endY + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\tendControlX = endX - PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'right': {\n\t\t\t\tendControlX = endX + PADDING;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn [startControlX, startControlY, endControlX, endControlY];\n\t};\n\n\tprivate readonly drawGridDots = (scale: number, offsetX: number, offsetY: number) => {\n\t\tconst scaleLevel = -Math.floor(Math.log2(scale));\n\t\tconst actualGap = DOT_BASE_GAP * 2 ** scaleLevel * scale;\n\t\tconst width = this.canvas.width;\n\t\tconst height = this.canvas.height;\n\t\tconst startX = offsetX % actualGap;\n\t\tconst startY = offsetY % actualGap;\n\t\tthis.ctx.fillStyle = this.SM.getNamedColor('dots');\n\t\tfor (let x = startX; x <= width; x += actualGap)\n\t\t\tfor (let y = startY; y <= height; y += actualGap) {\n\t\t\t\tthis.ctx.beginPath();\n\t\t\t\tthis.ctx.arc(x, y, DOT_RADIUS, 0, 2 * Math.PI);\n\t\t\t\tthis.ctx.fill();\n\t\t\t}\n\t};\n\n\tprivate readonly drawCurvedPath = (\n\t\tstartX: number,\n\t\tstartY: number,\n\t\tendX: number,\n\t\tendY: number,\n\t\tc1x: number,\n\t\tc1y: number,\n\t\tc2x: number,\n\t\tc2y: number,\n\t\tcolor: string,\n\t) => {\n\t\tthis.ctx.beginPath();\n\t\tthis.ctx.moveTo(startX, startY);\n\t\tthis.ctx.bezierCurveTo(c1x, c1y, c2x, c2y, endX, endY);\n\t\tthis.ctx.strokeStyle = color;\n\t\tthis.ctx.lineWidth = 2;\n\t\tthis.ctx.stroke();\n\t};\n\n\tprivate readonly drawArrowhead = (\n\t\ttipX: number,\n\t\ttipY: number,\n\t\tfromX: number,\n\t\tfromY: number,\n\t\tcolor: string,\n\t) => {\n\t\tconst dx = tipX - fromX;\n\t\tconst dy = tipY - fromY;\n\t\tconst length = Math.sqrt(dx * dx + dy * dy);\n\t\tif (length === 0) return;\n\t\tconst unitX = dx / length;\n\t\tconst unitY = dy / length;\n\t\tconst leftX = tipX - unitX * ARROW_LENGTH - unitY * ARROW_WIDTH;\n\t\tconst leftY = tipY - unitY * ARROW_LENGTH + unitX * ARROW_WIDTH;\n\t\tconst rightX = tipX - unitX * ARROW_LENGTH + unitY * ARROW_WIDTH;\n\t\tconst rightY = tipY - unitY * ARROW_LENGTH - unitX * ARROW_WIDTH;\n\t\tthis.ctx.beginPath();\n\t\tthis.ctx.fillStyle = color;\n\t\tthis.ctx.moveTo(tipX, tipY);\n\t\tthis.ctx.lineTo(leftX, leftY);\n\t\tthis.ctx.lineTo(rightX, rightY);\n\t\tthis.ctx.closePath();\n\t\tthis.ctx.fill();\n\t};\n\n\tprivate readonly dispose = () => {\n\t\tthis.canvas.remove();\n\t\tthis._canvas = undefined;\n\t};\n}\n"],"mappings":"0PAmBA,IAAqB,EAArB,cAAsC,CAAW,CAChD,QACA,IACA,GACA,GAEA,IAAY,QAAS,CACpB,GAAI,CAAC,KAAK,QAAS,MAAM,EACzB,OAAO,KAAK,OACb,CAEA,YAAY,GAAG,EAAgB,CAC9B,MAAM,GAAG,CAAI,EACb,IAAM,EAAa,KAAK,UAAU,IAAI,CAAU,EAChD,KAAK,GAAK,KAAK,UAAU,IAAI,CAAY,EACzC,EAAW,UAAU,UAAU,KAAK,MAAM,EAC1C,EAAW,SAAS,UAAU,KAAK,WAAW,EAC9C,KAAK,GAAK,KAAK,UAAU,IAAI,CAAW,EACxC,KAAK,QAAU,SAAS,cAAc,QAAQ,EAC9C,KAAK,QAAQ,UAAY,kBACzB,KAAK,IAAM,KAAK,QAAQ,WAAW,IAAI,EACvC,KAAK,GAAG,KAAK,UAAU,YAAY,KAAK,OAAO,EAC/C,KAAK,UAAU,KAAK,OAAO,CAC5B,CAEA,gBAAqC,CACpC,IAAM,EAAY,KAAK,GAAG,KAAK,UAC/B,EAAmB,KAAK,OAAQ,EAAU,YAAa,EAAU,YAAY,CAC9E,EAEA,WAAgC,CAC/B,IAAM,EAAU,KAAK,GAAG,KAAK,QACvB,EAAU,KAAK,GAAG,KAAK,QACvB,EAAQ,KAAK,GAAG,KAAK,MACrB,EAAkB,KAAK,mBAAmB,EAAS,EAAS,CAAK,EACvE,KAAK,OAAO,MAAM,UAAY,GAC9B,KAAK,IAAI,UAAU,EAAG,EAAG,KAAK,OAAO,MAAO,KAAK,OAAO,MAAM,EAC9D,KAAK,IAAI,KAAK,EACd,KAAK,aAAa,EAAO,EAAS,CAAO,EACzC,KAAK,IAAI,UAAU,EAAS,CAAO,EACnC,KAAK,IAAI,MAAM,EAAO,CAAK,EAC3B,OAAO,OAAO,KAAK,GAAG,KAAK,OAAO,EAAE,QAAS,GAAS,CACrD,GAAI,KAAK,UAAU,EAAK,IAAK,CAAe,EAAG,OAC/C,IAAM,EAAO,EAAK,IACd,EAAK,OAAS,OAAQ,KAAK,SAAS,CAAI,EACnC,EAAK,OAAS,SAAS,KAAK,UAAU,EAAM,CAAK,CAC3D,CAAC,EACD,OAAO,OAAO,KAAK,GAAG,KAAK,OAAO,EAAE,QAAS,GAAS,CACjD,KAAK,UAAU,EAAK,IAAK,CAAe,GAC5C,KAAK,SAAS,CAAI,CACnB,CAAC,EACD,KAAK,IAAI,QAAQ,CAClB,EAEA,UAA6B,EAAY,IACxC,EAAM,KAAO,EAAM,MACnB,EAAM,IAAM,EAAM,KAClB,EAAM,MAAQ,EAAM,OACpB,EAAM,OAAS,EAAM,OAEtB,WAA8B,EAAY,IACzC,EAAM,MAAQ,EAAM,MACpB,EAAM,OAAS,EAAM,KACrB,EAAM,KAAO,EAAM,OACnB,EAAM,IAAM,EAAM,OAEnB,oBAAuC,EAAiB,EAAiB,IAAkB,CAC1F,IAAM,EAAO,CAAC,EAAU,EAClB,EAAM,CAAC,EAAU,EACjB,EAAY,KAAK,GAAG,KAAK,UACzB,EAAQ,EAAO,EAAU,YAAc,EAE7C,MAAO,CAAE,OADM,EAAM,EAAU,aAAe,EAC7B,OAAM,QAAO,KAAI,CACnC,EAEA,cACC,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAY,GAAK,EACjB,EAAS,EAAI,EACb,EAAU,EAAI,EACd,EAAW,GAAK,EAChB,EAAW,EAAI,EACrB,KAAK,IAAI,KAAK,EACd,KAAK,IAAI,UAAU,EAAG,CAAC,EACvB,KAAK,IAAI,MAAM,EAAI,EAAO,EAAI,CAAK,EACnC,KAAK,IAAI,KAAO,GAAG,EAAS,wBAC5B,IAAM,EAAW,KAAK,IAAI,YAAY,CAAK,EAAE,MAAQ,EAAI,EACzD,KAAK,IAAI,UAAU,EAAG,CAAC,EAAY,CAAO,EAC1C,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,UAAU,EACnB,KAAK,IAAI,OAAO,EAAQ,CAAC,EACzB,KAAK,IAAI,OAAO,EAAW,EAAQ,CAAC,EACpC,KAAK,IAAI,iBAAiB,EAAU,EAAG,EAAU,CAAM,EACvD,KAAK,IAAI,OAAO,EAAU,EAAY,CAAM,EAC5C,KAAK,IAAI,iBAAiB,EAAU,EAAW,EAAW,EAAQ,CAAS,EAC3E,KAAK,IAAI,OAAO,EAAQ,CAAS,EACjC,KAAK,IAAI,iBAAiB,EAAG,EAAW,EAAG,EAAY,CAAM,EAC7D,KAAK,IAAI,OAAO,EAAG,CAAM,EACzB,KAAK,IAAI,iBAAiB,EAAG,EAAG,EAAQ,CAAC,EACzC,KAAK,IAAI,UAAU,EACnB,KAAK,IAAI,KAAK,EACd,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,SAAS,EAAO,EAAU,EAAY,GAAI,EACnD,KAAK,IAAI,QAAQ,CAClB,EAEA,mBAAuC,GAAyB,CAC/D,IAAM,EAAS,KAAK,GAAG,SAAS,EAAK,KAAK,EAE1C,KAAK,IAAI,YAAc,EACvB,KAAK,IAAI,UAAY,EAAO,WAC5B,EACC,KAAK,IACL,EAAK,EAAI,EACT,EAAK,EAAI,EACT,EAAK,MAAQ,EACb,EAAK,OAAS,EACd,EACD,EACA,KAAK,IAAI,KAAK,EACd,KAAK,IAAI,YAAc,EAAO,OAC9B,KAAK,IAAI,UAAY,EACrB,EAAc,KAAK,IAAK,EAAK,EAAG,EAAK,EAAG,EAAK,MAAO,EAAK,OAAQ,EAAM,EACvE,KAAK,IAAI,OAAO,CACjB,EAEA,WAA8B,EAA2B,IAAkB,CAE1E,GADA,KAAK,mBAAmB,CAAI,EACxB,EAAK,MAAO,CACf,IAAM,EAAQ,KAAK,GAAG,SAAS,EAAK,KAAK,EACzC,KAAK,aAAa,EAAK,EAAG,EAAK,EAAG,EAAK,MAAO,EAAM,OAAQ,EAAM,KAAM,CAAK,CAC9E,CACD,EAEA,SAA6B,GAAmB,CAC/C,KAAK,IAAI,UAAY,KAAK,GAAG,SAAS,EAAE,KACxC,IAAM,EAAO,EAAK,IAClB,KAAK,IAAI,KAAO,kBAChB,KAAK,IAAI,SAAS,EAAK,UAAY,GAAI,EAAK,EAAI,EAAG,EAAK,EAAI,EAAE,CAC/D,EAEA,SAA6B,GAAmB,CAC/C,IAAM,EAAO,EAAK,IACZ,EAAW,KAAK,GAAG,KAAK,QAAQ,EAAK,UAAU,IAC/C,EAAS,KAAK,GAAG,KAAK,QAAQ,EAAK,QAAQ,IAC3C,CAAE,EAAG,EAAQ,EAAG,GAAW,EAAe,EAAU,EAAK,QAAQ,EACjE,CAAE,EAAG,EAAM,EAAG,GAAS,EAAe,EAAQ,EAAK,MAAM,EACzD,EAAQ,KAAK,GAAG,SAAS,EAAK,KAAK,EACrC,EAAe,EAAe,EAAa,EAC1C,EAAK,cAUH,CAAC,EAAe,EAAe,EAAa,GAAe,EAAK,eATtE,CAAC,EAAe,EAAe,EAAa,GAAe,KAAK,iBAC/D,EACA,EACA,EACA,EACA,EAAK,SACL,EAAK,MACN,EACA,EAAK,cAAgB,CAAC,EAAe,EAAe,EAAa,CAAW,GAE7E,KAAK,eACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAAM,MACP,EACA,KAAK,cAAc,EAAM,EAAM,EAAa,EAAa,EAAM,MAAM,EACjE,EAAK,OACR,KAAK,cACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAAK,MACL,EAAM,OACN,EAAM,IACP,CACF,EAEA,eACC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAI,GACJ,GACJ,EAAI,IAAM,EAAI,EACf,GAAK,EAAI,IAAM,EAAI,EAAI,EACvB,GAAK,EAAI,GAAK,EAAI,EAAI,EACtB,GAAK,EAAI,EACJ,GACJ,EAAI,IAAM,EAAI,EACf,GAAK,EAAI,IAAM,EAAI,EAAI,EACvB,GAAK,EAAI,GAAK,EAAI,EAAI,EACtB,GAAK,EAAI,EACV,KAAK,IAAI,KAAO,kBAChB,IAAM,EAAQ,EAAM,MAAM;CAAI,EAE1B,EAAW,EACf,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAI,KAAK,IAAI,YAAY,CAAI,EAAE,MACjC,EAAI,IAAU,EAAW,EAC9B,CACA,IAEM,EAAa,EAAW,GACxB,EAAc,EAAM,OAAS,GAAa,EAChD,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,UAAU,EACnB,EACC,KAAK,IACL,EAAI,EAAa,EACjB,EAAI,EAAc,EAAI,EACtB,EACA,EACA,CACD,EACA,KAAK,IAAI,KAAK,EACd,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,UAAY,SACrB,KAAK,IAAI,aAAe,SACxB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACtC,IAAM,GAAW,GAAK,EAAM,OAAS,GAAK,GAAK,GAC/C,KAAK,IAAI,SAAS,EAAM,GAAI,EAAG,EAAI,EAAI,CAAO,CAC/C,CACA,KAAK,IAAI,UAAY,OACrB,KAAK,IAAI,aAAe,YACzB,EAEA,kBACC,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAY,EAAO,EACnB,EAAY,EAAO,EAKnB,IADS,EAAa,EAAa,IAAgB,KAAK,IAAI,EAAK,KAAK,IAAI,EAAK,CAAG,CAAC,IAFxF,KAAK,IAAI,KAAK,IAAI,CAAS,EAAG,KAAK,IAAI,CAAS,CAAC,EACjD,GAAM,KAAK,IAAI,KAAK,IAAI,CAAS,EAAG,KAAK,IAAI,CAAS,CAAC,GAEnB,GAAK,GAAI,GAAG,EAC7C,EAAgB,EAChB,EAAgB,EAChB,EAAc,EACd,EAAc,EAClB,OAAQ,EAAR,CACC,IAAK,MACJ,EAAgB,EAAS,EACzB,MAED,IAAK,SACJ,EAAgB,EAAS,EACzB,MAED,IAAK,OACJ,EAAgB,EAAS,EACzB,MAED,IAAK,QACJ,EAAgB,EAAS,EACzB,KAEF,CACA,OAAQ,EAAR,CACC,IAAK,MACJ,EAAc,EAAO,EACrB,MAED,IAAK,SACJ,EAAc,EAAO,EACrB,MAED,IAAK,OACJ,EAAc,EAAO,EACrB,MAED,IAAK,QACJ,EAAc,EAAO,EACrB,KAEF,CACA,MAAO,CAAC,EAAe,EAAe,EAAa,CAAW,CAC/D,EAEA,cAAiC,EAAe,EAAiB,IAAoB,CAEpF,IAAM,EAAY,GAAe,GAAK,CADlB,KAAK,MAAM,KAAK,KAAK,CAAK,CAAC,EACI,EAC7C,EAAQ,KAAK,OAAO,MACpB,EAAS,KAAK,OAAO,OACrB,EAAS,EAAU,EACnB,EAAS,EAAU,EACzB,KAAK,IAAI,UAAY,KAAK,GAAG,cAAc,MAAM,EACjD,IAAK,IAAI,EAAI,EAAQ,GAAK,EAAO,GAAK,EACrC,IAAK,IAAI,EAAI,EAAQ,GAAK,EAAQ,GAAK,EACtC,KAAK,IAAI,UAAU,EACnB,KAAK,IAAI,IAAI,EAAG,EAAG,EAAY,EAAG,EAAI,KAAK,EAAE,EAC7C,KAAK,IAAI,KAAK,CAEjB,EAEA,gBACC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,IACI,CACJ,KAAK,IAAI,UAAU,EACnB,KAAK,IAAI,OAAO,EAAQ,CAAM,EAC9B,KAAK,IAAI,cAAc,EAAK,EAAK,EAAK,EAAK,EAAM,CAAI,EACrD,KAAK,IAAI,YAAc,EACvB,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,OAAO,CACjB,EAEA,eACC,EACA,EACA,EACA,EACA,IACI,CACJ,IAAM,EAAK,EAAO,EACZ,EAAK,EAAO,EACZ,EAAS,KAAK,KAAK,EAAK,EAAK,EAAK,CAAE,EAC1C,GAAI,IAAW,EAAG,OAClB,IAAM,EAAQ,EAAK,EACb,EAAQ,EAAK,EACb,EAAQ,EAAO,EAAQ,GAAe,EAAQ,EAC9C,EAAQ,EAAO,EAAQ,GAAe,EAAQ,EAC9C,EAAS,EAAO,EAAQ,GAAe,EAAQ,EAC/C,EAAS,EAAO,EAAQ,GAAe,EAAQ,EACrD,KAAK,IAAI,UAAU,EACnB,KAAK,IAAI,UAAY,EACrB,KAAK,IAAI,OAAO,EAAM,CAAI,EAC1B,KAAK,IAAI,OAAO,EAAO,CAAK,EAC5B,KAAK,IAAI,OAAO,EAAQ,CAAM,EAC9B,KAAK,IAAI,UAAU,EACnB,KAAK,IAAI,KAAK,CACf,EAEA,YAAiC,CAChC,KAAK,OAAO,OAAO,EACnB,KAAK,QAAU,IAAA,EAChB,CACD"}
@@ -27,7 +27,7 @@ type Augmentation = {
27
27
  };
28
28
  declare class StyleManager extends BaseModule<Options, Augmentation> {
29
29
  theme: 'dark' | 'light';
30
- onChangeTheme: Hook<["light" | "dark"], false>;
30
+ onChangeTheme: Hook<["light" | "dark"]>;
31
31
  definedColors: {
32
32
  dark: {
33
33
  '0': {
@@ -1,2 +1,2 @@
1
- import{BaseModule as e}from"./BaseModule.js";import{makeHook as t}from"./utilities.js";import n from"./DataManager.js";import{parseHex as r,parseHsl as i,parseRgb as a,rgbToHsl as o,toHslString as s}from"@ahmedsemih/color-fns";var c=class extends e{theme=`light`;onChangeTheme=t();definedColors={dark:{0:{hue:0,lightness:40,saturation:0},1:{hue:358,lightness:65,saturation:100},2:{hue:23,lightness:63,saturation:86},3:{hue:39,lightness:70,saturation:91},4:{hue:153,lightness:45,saturation:80},5:{hue:217,lightness:62,saturation:100},6:{hue:259,lightness:75,saturation:100}},light:{0:{hue:0,lightness:72,saturation:0},1:{hue:358,lightness:55,saturation:81},2:{hue:19,lightness:58,saturation:87},3:{hue:41,lightness:52,saturation:79},4:{hue:150,lightness:37,saturation:100},5:{hue:221,lightness:59,saturation:100},6:{hue:257,lightness:62,saturation:81}}};namedColors={dark:{background:`rgb(30, 30, 30)`,"background-secondary":`rgb(37, 37, 40)`,border:`hsla(0, 0%, 30%, 0.7)`,dots:`hsla(0, 0%, 40%, 0.3)`,shadow:`0px 0px 8px rgb(0, 0, 0, 0.2)`,text:`rgb(242, 242, 242)`},light:{background:`rgb(250, 250, 250)`,"background-secondary":`rgb(255, 255, 255)`,border:`hsla(0, 0%, 82%, 0.7)`,dots:`hsla(0, 0%, 72%, 0.4)`,shadow:`0px 0px 8px rgb(0, 0, 0, 0.1)`,text:`rgb(30, 30, 30)`}};colorCache={dark:{},light:{}};constructor(...e){super(...e);let t=this.options.colors;t&&[`light`,`dark`].forEach(e=>{if(!(e in t))return;let n=t[e];n&&Object.entries(n).forEach(([t,n])=>{if(!n)return;let r=this.namedColors[e],i=this.definedColors[e];if(t in r)r[t]=n;else if(t in i){let e=this.parseColor(n);if(!e){console.warn(`[JSON Canvas Viewer] Color ${n} unsupported.`);return}i[t]=e}})}),this.changeTheme(this.options.theme??`light`),this.augment({changeTheme:this.changeTheme,onChangeTheme:this.onChangeTheme})}hslProcessor=e=>{let{hue:t,saturation:n,lightness:r}=e,i=this.theme===`dark`?{active:e,background:{...e,alpha:.1},border:{...e,alpha:.7},card:{hue:t,lightness:r/3,saturation:n/3},text:e.lightness>=70?`rgb(30, 30, 30)`:`rgb(242, 242, 242)`}:{active:e,background:{...e,alpha:.1},border:{...e,alpha:.7},card:t===0?{hue:t,lightness:100,saturation:n}:{hue:t,lightness:90,saturation:n*.4},text:e.lightness>=70?`rgb(30, 30, 30)`:`rgb(242, 242, 242)`};return{active:s(i.active),background:s(i.background),border:s(i.border),card:s(i.card),text:i.text}};parseColor=e=>{if(e.startsWith(`rgb`))return o(a(e));if(e.startsWith(`#`))return o(r(e));if(e.startsWith(`hsl`))return i(e)};getColor=(e=`0`)=>{let t=this.theme,n;if(this.colorCache[t][e])return this.colorCache[t][e];n=e in this.definedColors[t]?this.hslProcessor(this.definedColors[t][e]):this.hslProcessor(o(r(e)));let i={...n,"border-width":e===`0`?`1px`:`2px`};return this.colorCache[t][e]=i,i};getNamedColor=e=>this.namedColors[this.theme][e];changeTheme=e=>{this.theme=e??(this.theme===`dark`?`light`:`dark`);let t=this.container.get(n).data.container;Object.entries(this.namedColors[this.theme]).forEach(([e,n])=>{t.style.setProperty(`--${e}`,n)}),this.onChangeTheme(this.theme)}};export{c as default};
1
+ import{BaseModule as e}from"./BaseModule.js";import{hook as t}from"./utilities.js";import n from"./DataManager.js";import{parseHex as r,parseHsl as i,parseRgb as a,rgbToHsl as o,toHslString as s}from"@ahmedsemih/color-fns";var c=class extends e{theme=`light`;onChangeTheme=t();definedColors={dark:{0:{hue:0,lightness:40,saturation:0},1:{hue:358,lightness:65,saturation:100},2:{hue:23,lightness:63,saturation:86},3:{hue:39,lightness:70,saturation:91},4:{hue:153,lightness:45,saturation:80},5:{hue:217,lightness:62,saturation:100},6:{hue:259,lightness:75,saturation:100}},light:{0:{hue:0,lightness:72,saturation:0},1:{hue:358,lightness:55,saturation:81},2:{hue:19,lightness:58,saturation:87},3:{hue:41,lightness:52,saturation:79},4:{hue:150,lightness:37,saturation:100},5:{hue:221,lightness:59,saturation:100},6:{hue:257,lightness:62,saturation:81}}};namedColors={dark:{background:`rgb(30, 30, 30)`,"background-secondary":`rgb(37, 37, 40)`,border:`hsla(0, 0%, 30%, 0.7)`,dots:`hsla(0, 0%, 40%, 0.3)`,shadow:`0px 0px 8px rgb(0, 0, 0, 0.2)`,text:`rgb(242, 242, 242)`},light:{background:`rgb(250, 250, 250)`,"background-secondary":`rgb(255, 255, 255)`,border:`hsla(0, 0%, 82%, 0.7)`,dots:`hsla(0, 0%, 72%, 0.4)`,shadow:`0px 0px 8px rgb(0, 0, 0, 0.1)`,text:`rgb(30, 30, 30)`}};colorCache={dark:{},light:{}};constructor(...e){super(...e);let t=this.options.colors;t&&[`light`,`dark`].forEach(e=>{if(!(e in t))return;let n=t[e];n&&Object.entries(n).forEach(([t,n])=>{if(!n)return;let r=this.namedColors[e],i=this.definedColors[e];if(t in r)r[t]=n;else if(t in i){let e=this.parseColor(n);if(!e){console.warn(`[JSON Canvas Viewer] Color ${n} unsupported.`);return}i[t]=e}})}),this.changeTheme(this.options.theme??`light`),this.augment({changeTheme:this.changeTheme,onChangeTheme:this.onChangeTheme})}hslProcessor=e=>{let{hue:t,saturation:n,lightness:r}=e,i=this.theme===`dark`?{active:e,background:{...e,alpha:.1},border:{...e,alpha:.7},card:{hue:t,lightness:r/3,saturation:n/3},text:e.lightness>=70?`rgb(30, 30, 30)`:`rgb(242, 242, 242)`}:{active:e,background:{...e,alpha:.1},border:{...e,alpha:.7},card:t===0?{hue:t,lightness:100,saturation:n}:{hue:t,lightness:90,saturation:n*.4},text:e.lightness>=70?`rgb(30, 30, 30)`:`rgb(242, 242, 242)`};return{active:s(i.active),background:s(i.background),border:s(i.border),card:s(i.card),text:i.text}};parseColor=e=>{if(e.startsWith(`rgb`))return o(a(e));if(e.startsWith(`#`))return o(r(e));if(e.startsWith(`hsl`))return i(e)};getColor=(e=`0`)=>{let t=this.theme,n;if(this.colorCache[t][e])return this.colorCache[t][e];n=e in this.definedColors[t]?this.hslProcessor(this.definedColors[t][e]):this.hslProcessor(o(r(e)));let i={...n,"border-width":e===`0`?`1px`:`2px`};return this.colorCache[t][e]=i,i};getNamedColor=e=>this.namedColors[this.theme][e];changeTheme=e=>{this.theme=e??(this.theme===`dark`?`light`:`dark`);let t=this.container.get(n).data.container;Object.entries(this.namedColors[this.theme]).forEach(([e,n])=>{t.style.setProperty(`--${e}`,n)}),this.onChangeTheme(this.theme)}};export{c as default};
2
2
  //# sourceMappingURL=StyleManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StyleManager.js","names":[],"sources":["../../src/kernel/StyleManager.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { HslColor } from '@ahmedsemih/color-fns';\nimport { BaseModule } from '$/BaseModule';\nimport DataManager from '$/DataManager';\nimport { makeHook } from '$/utilities';\nimport { parseHex, toHslString, rgbToHsl, parseHsl, parseRgb } from '@ahmedsemih/color-fns';\n\ntype Color = {\n\tborder: string;\n\tbackground: string;\n\tactive: string;\n\ttext: string;\n\tcard: string;\n};\n\nexport type WithBorderWidth = {\n\t'border-width': string;\n} & Color;\n\ntype ColorOptions = {\n\t[K in keyof (StyleManager['definedColors']['light'] &\n\t\tStyleManager['namedColors']['light'])]?: string;\n};\n\ntype Options = {\n\ttheme?: 'dark' | 'light';\n\tcolors?: {\n\t\tlight?: ColorOptions;\n\t\tdark?: ColorOptions;\n\t};\n} & BaseOptions;\n\ntype Augmentation = {\n\tchangeTheme: StyleManager['changeTheme'];\n\tonChangeTheme: StyleManager['onChangeTheme'];\n};\n\nexport default class StyleManager extends BaseModule<Options, Augmentation> {\n\ttheme: 'dark' | 'light' = 'light';\n\tonChangeTheme = makeHook<['light' | 'dark']>();\n\tdefinedColors = {\n\t\tdark: {\n\t\t\t'0': { hue: 0, lightness: 40, saturation: 0 },\n\t\t\t'1': { hue: 358, lightness: 65, saturation: 100 },\n\t\t\t'2': { hue: 23, lightness: 63, saturation: 86 },\n\t\t\t'3': { hue: 39, lightness: 70, saturation: 91 },\n\t\t\t'4': { hue: 153, lightness: 45, saturation: 80 },\n\t\t\t'5': { hue: 217, lightness: 62, saturation: 100 },\n\t\t\t'6': { hue: 259, lightness: 75, saturation: 100 },\n\t\t},\n\t\tlight: {\n\t\t\t'0': { hue: 0, lightness: 72, saturation: 0 },\n\t\t\t'1': { hue: 358, lightness: 55, saturation: 81 },\n\t\t\t'2': { hue: 19, lightness: 58, saturation: 87 },\n\t\t\t'3': { hue: 41, lightness: 52, saturation: 79 },\n\t\t\t'4': { hue: 150, lightness: 37, saturation: 100 },\n\t\t\t'5': { hue: 221, lightness: 59, saturation: 100 },\n\t\t\t'6': { hue: 257, lightness: 62, saturation: 81 },\n\t\t},\n\t};\n\n\tnamedColors = {\n\t\tdark: {\n\t\t\tbackground: 'rgb(30, 30, 30)',\n\t\t\t'background-secondary': 'rgb(37, 37, 40)',\n\t\t\tborder: 'hsla(0, 0%, 30%, 0.7)',\n\t\t\tdots: 'hsla(0, 0%, 40%, 0.3)',\n\t\t\tshadow: '0px 0px 8px rgb(0, 0, 0, 0.2)',\n\t\t\ttext: 'rgb(242, 242, 242)',\n\t\t},\n\t\tlight: {\n\t\t\tbackground: 'rgb(250, 250, 250)',\n\t\t\t'background-secondary': 'rgb(255, 255, 255)',\n\t\t\tborder: 'hsla(0, 0%, 82%, 0.7)',\n\t\t\tdots: 'hsla(0, 0%, 72%, 0.4)',\n\t\t\tshadow: '0px 0px 8px rgb(0, 0, 0, 0.1)',\n\t\t\ttext: 'rgb(30, 30, 30)',\n\t\t},\n\t};\n\n\tprivate readonly colorCache: {\n\t\tdark: Record<string, WithBorderWidth>;\n\t\tlight: Record<string, WithBorderWidth>;\n\t} = {\n\t\tdark: {},\n\t\tlight: {},\n\t};\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\n\t\t// User-defined color merging\n\t\tconst colors = this.options.colors;\n\t\tif (colors) {\n\t\t\tconst themes = ['light', 'dark'] as const;\n\t\t\tthemes.forEach((theme) => {\n\t\t\t\tif (!(theme in colors)) return;\n\t\t\t\tconst colorObject = colors[theme];\n\t\t\t\tif (!colorObject) return;\n\t\t\t\tObject.entries(colorObject).forEach(([key, value]) => {\n\t\t\t\t\tif (!value) return;\n\t\t\t\t\tconst namedColorsDict = this.namedColors[theme];\n\t\t\t\t\tconst definedColorsDict = this.definedColors[theme];\n\t\t\t\t\tif (key in namedColorsDict)\n\t\t\t\t\t\tnamedColorsDict[key as keyof typeof namedColorsDict] = value;\n\t\t\t\t\telse if (key in definedColorsDict) {\n\t\t\t\t\t\tconst color = this.parseColor(value);\n\t\t\t\t\t\tif (!color) {\n\t\t\t\t\t\t\tconsole.warn(`[JSON Canvas Viewer] Color ${value} unsupported.`);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefinedColorsDict[key as keyof typeof definedColorsDict] = color;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tthis.changeTheme(this.options.theme ?? 'light');\n\t\tthis.augment({\n\t\t\tchangeTheme: this.changeTheme,\n\t\t\tonChangeTheme: this.onChangeTheme,\n\t\t});\n\t}\n\n\tprivate readonly hslProcessor = (color: HslColor) => {\n\t\tconst { hue, saturation, lightness } = color;\n\t\tconst result =\n\t\t\tthis.theme === 'dark'\n\t\t\t\t? {\n\t\t\t\t\t\tactive: color,\n\t\t\t\t\t\tbackground: { ...color, alpha: 0.1 },\n\t\t\t\t\t\tborder: { ...color, alpha: 0.7 },\n\t\t\t\t\t\tcard: { hue, lightness: lightness / 3, saturation: saturation / 3 },\n\t\t\t\t\t\ttext: color.lightness >= 70 ? 'rgb(30, 30, 30)' : 'rgb(242, 242, 242)',\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tactive: color,\n\t\t\t\t\t\tbackground: { ...color, alpha: 0.1 },\n\t\t\t\t\t\tborder: { ...color, alpha: 0.7 },\n\t\t\t\t\t\tcard:\n\t\t\t\t\t\t\thue === 0\n\t\t\t\t\t\t\t\t? { hue, lightness: 100, saturation }\n\t\t\t\t\t\t\t\t: { hue, lightness: 90, saturation: saturation * 0.4 },\n\t\t\t\t\t\ttext: color.lightness >= 70 ? 'rgb(30, 30, 30)' : 'rgb(242, 242, 242)',\n\t\t\t\t\t};\n\t\treturn {\n\t\t\tactive: toHslString(result.active),\n\t\t\tbackground: toHslString(result.background),\n\t\t\tborder: toHslString(result.border),\n\t\t\tcard: toHslString(result.card),\n\t\t\ttext: result.text,\n\t\t};\n\t};\n\n\tprivate readonly parseColor = (color: string) => {\n\t\tif (color.startsWith('rgb')) return rgbToHsl(parseRgb(color));\n\t\tif (color.startsWith('#')) return rgbToHsl(parseHex(color));\n\t\tif (color.startsWith('hsl')) return parseHsl(color);\n\t};\n\n\tgetColor = (colorIndex = '0') => {\n\t\tconst theme = this.theme;\n\t\tlet color: Color;\n\t\tif (this.colorCache[theme][colorIndex]) return this.colorCache[theme][colorIndex];\n\t\telse if (colorIndex in this.definedColors[theme])\n\t\t\tcolor = this.hslProcessor(\n\t\t\t\tthis.definedColors[theme][colorIndex as keyof typeof this.definedColors.dark],\n\t\t\t);\n\t\telse color = this.hslProcessor(rgbToHsl(parseHex(colorIndex)));\n\t\tconst withBorderWidth: WithBorderWidth = {\n\t\t\t...color,\n\t\t\t'border-width': colorIndex === '0' ? '1px' : '2px',\n\t\t};\n\t\tthis.colorCache[theme][colorIndex] = withBorderWidth;\n\t\treturn withBorderWidth;\n\t};\n\n\tgetNamedColor = (name: keyof typeof this.namedColors.light) =>\n\t\tthis.namedColors[this.theme][name];\n\n\tchangeTheme = (theme?: 'dark' | 'light') => {\n\t\tthis.theme = theme ?? (this.theme === 'dark' ? 'light' : 'dark');\n\t\tconst container = this.container.get(DataManager).data.container;\n\t\tObject.entries(this.namedColors[this.theme]).forEach(([key, value]) => {\n\t\t\tcontainer.style.setProperty(`--${key}`, value);\n\t\t});\n\t\tthis.onChangeTheme(this.theme);\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"StyleManager.js","names":[],"sources":["../../src/kernel/StyleManager.ts"],"sourcesContent":["import type { BaseOptions } from '$';\nimport type { BaseArgs } from '$/BaseModule';\nimport type { HslColor } from '@ahmedsemih/color-fns';\nimport { BaseModule } from '$/BaseModule';\nimport DataManager from '$/DataManager';\nimport { hook } from '$/utilities';\nimport { parseHex, toHslString, rgbToHsl, parseHsl, parseRgb } from '@ahmedsemih/color-fns';\n\ntype Color = {\n\tborder: string;\n\tbackground: string;\n\tactive: string;\n\ttext: string;\n\tcard: string;\n};\n\nexport type WithBorderWidth = {\n\t'border-width': string;\n} & Color;\n\ntype ColorOptions = {\n\t[K in keyof (StyleManager['definedColors']['light'] &\n\t\tStyleManager['namedColors']['light'])]?: string;\n};\n\ntype Options = {\n\ttheme?: 'dark' | 'light';\n\tcolors?: {\n\t\tlight?: ColorOptions;\n\t\tdark?: ColorOptions;\n\t};\n} & BaseOptions;\n\ntype Augmentation = {\n\tchangeTheme: StyleManager['changeTheme'];\n\tonChangeTheme: StyleManager['onChangeTheme'];\n};\n\nexport default class StyleManager extends BaseModule<Options, Augmentation> {\n\ttheme: 'dark' | 'light' = 'light';\n\tonChangeTheme = hook<['light' | 'dark']>();\n\tdefinedColors = {\n\t\tdark: {\n\t\t\t'0': { hue: 0, lightness: 40, saturation: 0 },\n\t\t\t'1': { hue: 358, lightness: 65, saturation: 100 },\n\t\t\t'2': { hue: 23, lightness: 63, saturation: 86 },\n\t\t\t'3': { hue: 39, lightness: 70, saturation: 91 },\n\t\t\t'4': { hue: 153, lightness: 45, saturation: 80 },\n\t\t\t'5': { hue: 217, lightness: 62, saturation: 100 },\n\t\t\t'6': { hue: 259, lightness: 75, saturation: 100 },\n\t\t},\n\t\tlight: {\n\t\t\t'0': { hue: 0, lightness: 72, saturation: 0 },\n\t\t\t'1': { hue: 358, lightness: 55, saturation: 81 },\n\t\t\t'2': { hue: 19, lightness: 58, saturation: 87 },\n\t\t\t'3': { hue: 41, lightness: 52, saturation: 79 },\n\t\t\t'4': { hue: 150, lightness: 37, saturation: 100 },\n\t\t\t'5': { hue: 221, lightness: 59, saturation: 100 },\n\t\t\t'6': { hue: 257, lightness: 62, saturation: 81 },\n\t\t},\n\t};\n\n\tnamedColors = {\n\t\tdark: {\n\t\t\tbackground: 'rgb(30, 30, 30)',\n\t\t\t'background-secondary': 'rgb(37, 37, 40)',\n\t\t\tborder: 'hsla(0, 0%, 30%, 0.7)',\n\t\t\tdots: 'hsla(0, 0%, 40%, 0.3)',\n\t\t\tshadow: '0px 0px 8px rgb(0, 0, 0, 0.2)',\n\t\t\ttext: 'rgb(242, 242, 242)',\n\t\t},\n\t\tlight: {\n\t\t\tbackground: 'rgb(250, 250, 250)',\n\t\t\t'background-secondary': 'rgb(255, 255, 255)',\n\t\t\tborder: 'hsla(0, 0%, 82%, 0.7)',\n\t\t\tdots: 'hsla(0, 0%, 72%, 0.4)',\n\t\t\tshadow: '0px 0px 8px rgb(0, 0, 0, 0.1)',\n\t\t\ttext: 'rgb(30, 30, 30)',\n\t\t},\n\t};\n\n\tprivate readonly colorCache: {\n\t\tdark: Record<string, WithBorderWidth>;\n\t\tlight: Record<string, WithBorderWidth>;\n\t} = {\n\t\tdark: {},\n\t\tlight: {},\n\t};\n\n\tconstructor(...args: BaseArgs) {\n\t\tsuper(...args);\n\n\t\t// User-defined color merging\n\t\tconst colors = this.options.colors;\n\t\tif (colors) {\n\t\t\tconst themes = ['light', 'dark'] as const;\n\t\t\tthemes.forEach((theme) => {\n\t\t\t\tif (!(theme in colors)) return;\n\t\t\t\tconst colorObject = colors[theme];\n\t\t\t\tif (!colorObject) return;\n\t\t\t\tObject.entries(colorObject).forEach(([key, value]) => {\n\t\t\t\t\tif (!value) return;\n\t\t\t\t\tconst namedColorsDict = this.namedColors[theme];\n\t\t\t\t\tconst definedColorsDict = this.definedColors[theme];\n\t\t\t\t\tif (key in namedColorsDict)\n\t\t\t\t\t\tnamedColorsDict[key as keyof typeof namedColorsDict] = value;\n\t\t\t\t\telse if (key in definedColorsDict) {\n\t\t\t\t\t\tconst color = this.parseColor(value);\n\t\t\t\t\t\tif (!color) {\n\t\t\t\t\t\t\tconsole.warn(`[JSON Canvas Viewer] Color ${value} unsupported.`);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefinedColorsDict[key as keyof typeof definedColorsDict] = color;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tthis.changeTheme(this.options.theme ?? 'light');\n\t\tthis.augment({\n\t\t\tchangeTheme: this.changeTheme,\n\t\t\tonChangeTheme: this.onChangeTheme,\n\t\t});\n\t}\n\n\tprivate readonly hslProcessor = (color: HslColor) => {\n\t\tconst { hue, saturation, lightness } = color;\n\t\tconst result =\n\t\t\tthis.theme === 'dark'\n\t\t\t\t? {\n\t\t\t\t\t\tactive: color,\n\t\t\t\t\t\tbackground: { ...color, alpha: 0.1 },\n\t\t\t\t\t\tborder: { ...color, alpha: 0.7 },\n\t\t\t\t\t\tcard: { hue, lightness: lightness / 3, saturation: saturation / 3 },\n\t\t\t\t\t\ttext: color.lightness >= 70 ? 'rgb(30, 30, 30)' : 'rgb(242, 242, 242)',\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tactive: color,\n\t\t\t\t\t\tbackground: { ...color, alpha: 0.1 },\n\t\t\t\t\t\tborder: { ...color, alpha: 0.7 },\n\t\t\t\t\t\tcard:\n\t\t\t\t\t\t\thue === 0\n\t\t\t\t\t\t\t\t? { hue, lightness: 100, saturation }\n\t\t\t\t\t\t\t\t: { hue, lightness: 90, saturation: saturation * 0.4 },\n\t\t\t\t\t\ttext: color.lightness >= 70 ? 'rgb(30, 30, 30)' : 'rgb(242, 242, 242)',\n\t\t\t\t\t};\n\t\treturn {\n\t\t\tactive: toHslString(result.active),\n\t\t\tbackground: toHslString(result.background),\n\t\t\tborder: toHslString(result.border),\n\t\t\tcard: toHslString(result.card),\n\t\t\ttext: result.text,\n\t\t};\n\t};\n\n\tprivate readonly parseColor = (color: string) => {\n\t\tif (color.startsWith('rgb')) return rgbToHsl(parseRgb(color));\n\t\tif (color.startsWith('#')) return rgbToHsl(parseHex(color));\n\t\tif (color.startsWith('hsl')) return parseHsl(color);\n\t};\n\n\tgetColor = (colorIndex = '0') => {\n\t\tconst theme = this.theme;\n\t\tlet color: Color;\n\t\tif (this.colorCache[theme][colorIndex]) return this.colorCache[theme][colorIndex];\n\t\telse if (colorIndex in this.definedColors[theme])\n\t\t\tcolor = this.hslProcessor(\n\t\t\t\tthis.definedColors[theme][colorIndex as keyof typeof this.definedColors.dark],\n\t\t\t);\n\t\telse color = this.hslProcessor(rgbToHsl(parseHex(colorIndex)));\n\t\tconst withBorderWidth: WithBorderWidth = {\n\t\t\t...color,\n\t\t\t'border-width': colorIndex === '0' ? '1px' : '2px',\n\t\t};\n\t\tthis.colorCache[theme][colorIndex] = withBorderWidth;\n\t\treturn withBorderWidth;\n\t};\n\n\tgetNamedColor = (name: keyof typeof this.namedColors.light) =>\n\t\tthis.namedColors[this.theme][name];\n\n\tchangeTheme = (theme?: 'dark' | 'light') => {\n\t\tthis.theme = theme ?? (this.theme === 'dark' ? 'light' : 'dark');\n\t\tconst container = this.container.get(DataManager).data.container;\n\t\tObject.entries(this.namedColors[this.theme]).forEach(([key, value]) => {\n\t\t\tcontainer.style.setProperty(`--${key}`, value);\n\t\t});\n\t\tthis.onChangeTheme(this.theme);\n\t};\n}\n"],"mappings":"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"}
@@ -25,15 +25,14 @@ declare class JSONCanvasViewer<M extends ModuleInputCtor> {
25
25
  private disposed;
26
26
  options: AllOptions<M>;
27
27
  container: Container;
28
- onDispose: Hook<[], false>;
29
- onStart: Hook<[], false>;
30
- onRestart: Hook<[], false>;
28
+ onDispose: Hook<[]>;
29
+ onStart: Hook<[]>;
30
+ onRestart: Hook<[]>;
31
31
  constructor(options: AllOptions<M>, modules?: M);
32
32
  private readonly onVisibilityCheck;
33
33
  private readonly augment;
34
34
  load: (options?: {
35
35
  canvas?: JSONCanvas;
36
- attachmentDir?: string;
37
36
  attachments?: Record<string, string>;
38
37
  }) => void;
39
38
  dispose: () => void;
@@ -1,2 +1,2 @@
1
- import{makeHook as e}from"./utilities.js";import t from"./DataManager.js";import n from"./StyleManager.js";import r from"./Controller.js";import i from"./OverlayManager.js";import a from"./InteractionHandler.js";import o from"./Renderer.js";import{Container as s}from"@needle-di/core";const c=[t,n,r,i,a,o];var l=class{allModules;IO;started=!1;disposed=!1;options;container;onDispose=e(!0);onStart=e();onRestart=e();constructor(e,t){this.container=new s,this.options=e;let n=e=>{this.container.bind({provide:e,useFactory:()=>new e(this.container,this.options,this.onStart,this.onDispose,this.onRestart,this.augment)})};this.allModules=[...c,...t??[]],this.allModules.forEach(n),this.allModules.forEach(e=>{this.container.get(e)});let r=this.options.loading??`normal`;r===`normal`?this.load():r===`lazy`&&(this.IO=new IntersectionObserver(this.onVisibilityCheck,{rootMargin:`50px`,threshold:0}),this.IO.observe(this.options.container))}onVisibilityCheck=e=>{e.forEach(e=>{if(e.isIntersecting){this.load(),this.IO?.disconnect(),this.IO=void 0;return}})};augment=e=>{let t=Object.getOwnPropertyDescriptors(e);Object.defineProperties(this,t)};load=e=>{this.disposed||(e&&Object.assign(this.options,e),this.started?this.onRestart():(this.onStart(),this.started=!0))};dispose=()=>{if(!this.started||this.disposed)return;this.IO?.disconnect(),this.IO=void 0;let e=this.options.container;for(;e.firstChild;)e.firstChild.remove();this.onDispose(),this.container.unbindAll(),this.disposed=!0}};export{l as default};
1
+ import{hook as e}from"./utilities.js";import t from"./DataManager.js";import n from"./StyleManager.js";import r from"./Controller.js";import i from"./OverlayManager.js";import a from"./InteractionHandler.js";import o from"./Renderer.js";import{Container as s}from"@needle-di/core";const c=[t,n,r,i,a,o];var l=class{allModules;IO;started=!1;disposed=!1;options;container;onDispose=e(!0);onStart=e();onRestart=e();constructor(e,t){this.container=new s,this.options=e;let n=e=>{this.container.bind({provide:e,useFactory:()=>new e(this.container,this.options,this.onStart,this.onDispose,this.onRestart,this.augment)})};this.allModules=[...c,...t??[]],this.allModules.forEach(n),this.allModules.forEach(e=>{this.container.get(e)});let r=this.options.loading??`normal`;r===`normal`?this.load():r===`lazy`&&(this.IO=new IntersectionObserver(this.onVisibilityCheck,{rootMargin:`50px`,threshold:0}),this.IO.observe(this.options.container))}onVisibilityCheck=e=>{e.forEach(e=>{if(e.isIntersecting){this.load(),this.IO?.disconnect(),this.IO=void 0;return}})};augment=e=>{let t=Object.getOwnPropertyDescriptors(e);Object.defineProperties(this,t)};load=e=>{this.disposed||(e&&Object.assign(this.options,e),this.started?this.onRestart():(this.onStart(),this.started=!0))};dispose=()=>{if(!this.started||this.disposed)return;this.IO?.disconnect(),this.IO=void 0;let e=this.options.container;for(;e.firstChild;)e.firstChild.remove();this.onDispose(),this.container.unbindAll(),this.disposed=!0}};export{l as default};
2
2
  //# sourceMappingURL=index.js.map