@papyrus-sdk/ui-react-native 0.2.35 → 0.2.36

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.
@@ -20530,7 +20530,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20530
20530
  var doc=contents.document,win=contents.window,start=null,blocked=false;
20531
20531
  function point(e){var t=e.changedTouches&&e.changedTouches[0]||e.touches&&e.touches[0];return t?{x:t.clientX,y:t.clientY,time:Date.now()}:null;}
20532
20532
  function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
20533
- function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
20533
+ function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay],[data-papyrus-ink-capture]'));}
20534
20534
  function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
20535
20535
  function invalidateScroll(finish){if(config.current() && start && finish && config.mode()==='chapter-scroll' && !blocked && !selected() && Math.abs(finish.y-start.y)>12 && Math.abs(finish.y-start.y)>Math.abs(finish.x-start.x) && config.interact)config.interact();}
20536
20536
  function move(e){if(e.touches.length!==1)blocked=true;invalidateScroll(point(e));}
@@ -20555,6 +20555,84 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20555
20555
  typeof anchor.href === 'string' && !/^(javascript|data):/i.test(anchor.href) &&
20556
20556
  typeof anchor.quote === 'string' && anchor.quote.length > 0 && anchor.quote.length <= 100000;
20557
20557
  }
20558
+ function simplifyPath(points, tolerance, maxPoints) {
20559
+ if (!Array.isArray(points) || points.length <= 2) return Array.isArray(points) ? points.slice() : [];
20560
+ tolerance = Number.isFinite(tolerance) ? Math.max(0, tolerance) : 1.5;
20561
+ maxPoints = Number.isSafeInteger(maxPoints) ? Math.max(2, maxPoints) : 512;
20562
+ var keep = new Uint8Array(points.length); keep[0] = 1; keep[points.length - 1] = 1;
20563
+ var stack = [[0, points.length - 1]], toleranceSquared = tolerance * tolerance;
20564
+ while (stack.length) {
20565
+ var interval = stack.pop(), first = interval[0], last = interval[1], a = points[first], b = points[last];
20566
+ var dx = b.x - a.x, dy = b.y - a.y, lengthSquared = dx * dx + dy * dy, maxDistance = toleranceSquared, split = -1;
20567
+ for (var i = first + 1; i < last; i++) {
20568
+ var p = points[i], ratio = lengthSquared ? Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / lengthSquared)) : 0;
20569
+ var px = a.x + ratio * dx, py = a.y + ratio * dy, ox = p.x - px, oy = p.y - py, distance = ox * ox + oy * oy;
20570
+ if (distance > maxDistance) { maxDistance = distance; split = i; }
20571
+ }
20572
+ if (split >= 0) { keep[split] = 1; stack.push([first, split], [split, last]); }
20573
+ }
20574
+ var result = []; for (var j = 0; j < points.length; j++) if (keep[j]) result.push(points[j]);
20575
+ if (result.length <= maxPoints) return result;
20576
+ var capped = []; for (var k = 0; k < maxPoints; k++) capped.push(result[Math.round(k * (result.length - 1) / (maxPoints - 1))]);
20577
+ return capped;
20578
+ }
20579
+ function compactGesturePoints(points, maxPoints) {
20580
+ if (!Array.isArray(points)) return [];
20581
+ maxPoints = Number.isSafeInteger(maxPoints) ? Math.max(2, maxPoints) : 1024;
20582
+ if (points.length <= maxPoints) return points.slice();
20583
+ var compacted = [];
20584
+ for (var i = 0; i < maxPoints; i++) compacted.push(points[Math.round(i * (points.length - 1) / (maxPoints - 1))]);
20585
+ return compacted;
20586
+ }
20587
+ function densifyPath(points, maxStep, maxPoints) {
20588
+ if (!Array.isArray(points) || !points.length) return [];
20589
+ if (points.length === 1) return points.slice();
20590
+ maxStep = Number.isFinite(maxStep) ? Math.max(1, maxStep) : 12;
20591
+ maxPoints = Number.isSafeInteger(maxPoints) ? Math.max(2, maxPoints) : 512;
20592
+ var cumulative = [0], total = 0;
20593
+ for (var i = 1; i < points.length; i++) {
20594
+ if (![points[i-1].x,points[i-1].y,points[i].x,points[i].y].every(Number.isFinite)) return [];
20595
+ total += Math.hypot(points[i].x-points[i-1].x, points[i].y-points[i-1].y);
20596
+ cumulative.push(total);
20597
+ }
20598
+ if (!total) return [points[0], points[points.length-1]];
20599
+ var intervals = Math.min(maxPoints-1, Math.max(1, Math.ceil(total/maxStep))), result = [], segment = 1;
20600
+ for (var sample = 0; sample <= intervals; sample++) {
20601
+ var distance = total*sample/intervals;
20602
+ while (segment < cumulative.length-1 && cumulative[segment] < distance) segment++;
20603
+ var start = points[segment-1], end = points[segment], span = cumulative[segment]-cumulative[segment-1];
20604
+ var ratio = span ? (distance-cumulative[segment-1])/span : 0;
20605
+ result.push({x:start.x+(end.x-start.x)*ratio,y:start.y+(end.y-start.y)*ratio});
20606
+ }
20607
+ return result;
20608
+ }
20609
+ function createSpatialIndex(entries, cellSize) {
20610
+ cellSize = Number.isFinite(cellSize) ? Math.max(8, cellSize) : 64;
20611
+ var cells = new Map();
20612
+ entries.forEach(function(entry) {
20613
+ var left = Math.floor(entry.left / cellSize), right = Math.floor(entry.right / cellSize);
20614
+ var top = Math.floor(entry.top / cellSize), bottom = Math.floor(entry.bottom / cellSize);
20615
+ for (var x = left; x <= right; x++) for (var y = top; y <= bottom; y++) {
20616
+ var key = x + ':' + y, bucket = cells.get(key); if (!bucket) { bucket = []; cells.set(key, bucket); } bucket.push(entry);
20617
+ }
20618
+ });
20619
+ return {cells:cells,cellSize:cellSize};
20620
+ }
20621
+ function queryNearestTextRect(index, x, y, maxDistance) {
20622
+ if (!index || !Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(maxDistance) || maxDistance < 0) return null;
20623
+ var size = index.cellSize, minX = Math.floor((x-maxDistance)/size), maxX = Math.floor((x+maxDistance)/size);
20624
+ var minY = Math.floor((y-maxDistance)/size), maxY = Math.floor((y+maxDistance)/size), seen = new Set(), best = null;
20625
+ for (var cx=minX; cx<=maxX; cx++) for (var cy=minY; cy<=maxY; cy++) {
20626
+ var bucket = index.cells.get(cx+':'+cy); if (!bucket) continue;
20627
+ bucket.forEach(function(entry) {
20628
+ if (seen.has(entry)) return; seen.add(entry);
20629
+ var nearestX = Math.max(entry.left,Math.min(entry.right,x)), nearestY = Math.max(entry.top,Math.min(entry.bottom,y));
20630
+ var dx=x-nearestX,dy=y-nearestY,distance=Math.sqrt(dx*dx+dy*dy);
20631
+ if (distance<=maxDistance && (!best || distance<best.distance)) best={entry:entry,distance:distance,x:nearestX,y:nearestY};
20632
+ });
20633
+ }
20634
+ return best;
20635
+ }
20558
20636
  function textNodes(document) {
20559
20637
  var walker = document.createTreeWalker(document.body, 4);
20560
20638
  var nodes = [], text = '', node;
@@ -20604,13 +20682,196 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20604
20682
  if(win.getComputedStyle(doc.body).position==='static')doc.body.style.position='relative';
20605
20683
  doc.body.appendChild(overlay);
20606
20684
  var registry = new Map(), hitRegions = [];
20685
+ var inkRegistry = new Map(), rangeCache = new Map(), spatialIndex = null, activeGesture = null, inkConfig = {enabled:false,preset:'ink'};
20686
+ var inkSvg = doc.createElementNS('http://www.w3.org/2000/svg','svg');
20687
+ inkSvg.setAttribute('data-papyrus-ink-overlay','');
20688
+ inkSvg.style.cssText = 'position:absolute;left:0;top:0;overflow:visible;pointer-events:none;z-index:1';
20689
+ overlay.appendChild(inkSvg);
20690
+ var inkCanvas = doc.createElement('canvas');
20691
+ inkCanvas.dataset.papyrusInkCapture = '';
20692
+ inkCanvas.style.cssText = 'position:fixed;left:0;top:0;width:1px;height:1px;z-index:2147483000;background:transparent;pointer-events:none;touch-action:none';
20693
+ doc.body.appendChild(inkCanvas);
20694
+ var inkContext = null; try { if(typeof win.CanvasRenderingContext2D==='function')inkContext = inkCanvas.getContext('2d'); } catch (_) {}
20695
+ function resizeCanvas() {
20696
+ var bodyRect=doc.body.getBoundingClientRect(), ratio = Math.max(1,Math.min(3,win.devicePixelRatio || 1));
20697
+ var width=Math.max(1,Math.min(4096,doc.body.clientWidth||bodyRect.width||Math.min(4096,win.innerWidth||1)));
20698
+ var height=Math.max(1,Math.min(4096,doc.body.clientHeight||bodyRect.height||Math.min(4096,win.innerHeight||1)));
20699
+ inkCanvas.style.width=width+'px';inkCanvas.style.height=height+'px';
20700
+ inkCanvas.width = Math.round(width*ratio); inkCanvas.height = Math.round(height*ratio);
20701
+ if (inkContext) { inkContext.setTransform(ratio,0,0,ratio,0,0); inkContext.clearRect(0,0,width,height); }
20702
+ }
20703
+ function collectTextRects() {
20704
+ var walker = doc.createTreeWalker(doc.body,4), node, entries=[];
20705
+ while ((node=walker.nextNode())) {
20706
+ var parent=node.parentElement;
20707
+ if (!parent || parent.closest('script,style,noscript,textarea,[data-papyrus-overlay],[data-papyrus-ink-capture]')) continue;
20708
+ var text=node.nodeValue || '', style=win.getComputedStyle(parent), computed=parseFloat(style.lineHeight);
20709
+ for (var start=0; start<text.length;) {
20710
+ var end=Math.min(text.length,start+24), last=text.charCodeAt(end-1), next=text.charCodeAt(end);
20711
+ if(end<text.length&&last>=0xD800&&last<=0xDBFF&&next>=0xDC00&&next<=0xDFFF)end++;
20712
+ var range=doc.createRange(); range.setStart(node,start); range.setEnd(node,end);
20713
+ var rects=[]; try { rects=Array.from(range.getClientRects()); } catch (_) {}
20714
+ rects.forEach(function(rect) {
20715
+ if (!(rect.width>0 && rect.height>0)) return;
20716
+ entries.push({left:rect.left+win.scrollX,top:rect.top+win.scrollY,right:rect.right+win.scrollX,bottom:rect.bottom+win.scrollY,width:rect.width,height:rect.height,
20717
+ node:node,start:start,end:end,lineHeight:Number.isFinite(computed)&&computed>0?computed:rect.height});
20718
+ });
20719
+ start=end;
20720
+ }
20721
+ }
20722
+ return entries;
20723
+ }
20724
+ function rebuildSpatialIndex() { spatialIndex=createSpatialIndex(collectTextRects(),64); rangeCache.clear(); }
20725
+ function inkViewportExtent() {
20726
+ var root=doc.documentElement,body=doc.body,bodyRect=body.getBoundingClientRect();
20727
+ var width=root.clientWidth||body.clientWidth||bodyRect.width||win.innerWidth||1;
20728
+ var height=root.clientHeight||body.clientHeight||bodyRect.height||win.innerHeight||1;
20729
+ return Math.max(1,Math.min(4096,width),Math.min(4096,height));
20730
+ }
20731
+ function inkStrokeWidthPx(width,preset) {
20732
+ var scale=preset==='highlight'?0.8:preset==='underline'?0.1:0.15;
20733
+ return Math.max(1,Math.min(24,(Number.isFinite(width)?width:0.006)*inkViewportExtent()*scale));
20734
+ }
20735
+ function closestGlyph(entry,x,y,maxDistance) {
20736
+ var source=(entry.node.nodeValue || '').slice(entry.start,entry.end), best=null, offset=0;
20737
+ for (var glyph of source) {
20738
+ var range=doc.createRange(); range.setStart(entry.node,entry.start+offset); range.setEnd(entry.node,entry.start+offset+glyph.length); offset+=glyph.length;
20739
+ var rects=[]; try { rects=Array.from(range.getClientRects()); } catch (_) {}
20740
+ rects.forEach(function(rect) {
20741
+ if (!(rect.width>0 && rect.height>0)) return;
20742
+ var nx=Math.max(rect.left+win.scrollX,Math.min(rect.right+win.scrollX,x)), ny=Math.max(rect.top+win.scrollY,Math.min(rect.bottom+win.scrollY,y));
20743
+ var dx=x-nx,dy=y-ny,distance=Math.sqrt(dx*dx+dy*dy);
20744
+ if (distance<=maxDistance && (!best || distance<best.distance)) best={range:range,rect:rect,distance:distance};
20745
+ });
20746
+ }
20747
+ if (!best || typeof contents.cfiFromRange!=='function') return null;
20748
+ var quote=best.range.toString(), cfiRange; try { cfiRange=contents.cfiFromRange(best.range); } catch (_) { return null; }
20749
+ if (!quote || typeof cfiRange!=='string' || !/^epubcfi\([^\n<>]+\)$/.test(cfiRange)) return null;
20750
+ var lineHeight=Math.max(1,entry.lineHeight || best.rect.height), px=x-(best.rect.left+win.scrollX), py=y-(best.rect.top+win.scrollY);
20751
+ return {cfiRange:cfiRange,quote:quote,x:Math.max(-3,Math.min(3,px/lineHeight)),y:Math.max(-3,Math.min(3,py/lineHeight))};
20752
+ }
20753
+ function renderInkAnnotation(annotation,keep,bodyRect) {
20754
+ var baseRect=bodyRect || doc.body.getBoundingClientRect() || {left:0,top:0};
20755
+ var data=annotation.epubInk;
20756
+ if (!data || data.version!==1 || data.kind!=='epub-ink' || !Array.isArray(data.segments)) return;
20757
+ keep.add(annotation.id);
20758
+ var group=inkRegistry.get(annotation.id);
20759
+ if (!group) { group=doc.createElementNS('http://www.w3.org/2000/svg','g'); group.setAttribute('data-epub-ink-id',annotation.id); inkSvg.appendChild(group); inkRegistry.set(annotation.id,group); }
20760
+ group.replaceChildren();
20761
+ var color=/^#[0-9a-f]{6}$/i.test(annotation.color)?annotation.color:'#2563eb';
20762
+ var opacity=Number.isFinite(annotation.opacity)?Math.max(0,Math.min(1,annotation.opacity)):1;
20763
+ var preset=['ink','highlight','underline'].indexOf(data.preset)>=0?data.preset:'ink';
20764
+ var stroke=inkStrokeWidthPx(annotation.strokeWidth,preset);
20765
+ data.segments.forEach(function(segment) {
20766
+ if (segment.href!==options.href || !Array.isArray(segment.points)) return;
20767
+ var paths=[], current=[];
20768
+ segment.points.forEach(function(point) {
20769
+ var range=rangeCache.get(point.cfiRange);
20770
+ if (!range) { try { range=contents.range(point.cfiRange); } catch (_) { range=null; } if (range) rangeCache.set(point.cfiRange,range); }
20771
+ if (!range || range.toString()!==point.quote) { if(current.length>1)paths.push(current);current=[];return; }
20772
+ var rect; try { rect=range.getBoundingClientRect(); } catch (_) { rect=null; }
20773
+ if (!rect || !(rect.width>0 && rect.height>0)) { if(current.length>1)paths.push(current);current=[];return; }
20774
+ var lineHeight=Math.max(1,rect.height), x=rect.left-baseRect.left+point.x*lineHeight, y=rect.top-baseRect.top+point.y*lineHeight;
20775
+ var previous=current[current.length-1], jumpLimit=Math.max(24,lineHeight*2.5);
20776
+ if (previous && Math.hypot(x-previous.x,y-previous.y)>jumpLimit) { if(current.length>1)paths.push(current);current=[]; }
20777
+ current.push({x:x,y:y,lineHeight:lineHeight});
20778
+ });
20779
+ if(current.length>1)paths.push(current);
20780
+ paths.forEach(function(points) {
20781
+ var d='M '+points[0].x.toFixed(1)+' '+points[0].y.toFixed(1);
20782
+ for(var i=1;i<points.length;i++){var prev=points[i-1],point=points[i],mx=(prev.x+point.x)/2,my=(prev.y+point.y)/2;d+=' Q '+prev.x.toFixed(1)+' '+prev.y.toFixed(1)+' '+mx.toFixed(1)+' '+my.toFixed(1);}
20783
+ var last=points[points.length-1];d+=' L '+last.x.toFixed(1)+' '+last.y.toFixed(1);
20784
+ var path=doc.createElementNS('http://www.w3.org/2000/svg','path');path.setAttribute('d',d);path.setAttribute('fill','none');path.setAttribute('stroke',color);path.setAttribute('stroke-width',String(stroke));path.setAttribute('stroke-linecap','round');path.setAttribute('stroke-linejoin','round');path.setAttribute('opacity',String(opacity));group.appendChild(path);
20785
+ });
20786
+ });
20787
+ }
20788
+ function clearPreview() {
20789
+ if(inkContext)inkContext.clearRect(0,0,inkCanvas.width,inkCanvas.height);
20790
+ inkSvg.querySelectorAll('[data-papyrus-ink-preview]').forEach(function(path){path.remove();});
20791
+ }
20792
+ function createPreviewPath(x,y) {
20793
+ var path=doc.createElementNS('http://www.w3.org/2000/svg','path');
20794
+ path.setAttribute('data-papyrus-ink-preview','');path.setAttribute('d','M '+x.toFixed(1)+' '+y.toFixed(1));path.setAttribute('fill','none');
20795
+ path.setAttribute('stroke',inkConfig.color);path.setAttribute('stroke-width',String(inkStrokeWidthPx(inkConfig.strokeWidth,inkConfig.preset)));
20796
+ path.setAttribute('stroke-linecap','round');path.setAttribute('stroke-linejoin','round');path.setAttribute('opacity',String(inkConfig.opacity));
20797
+ path.style.pointerEvents='none';inkSvg.appendChild(path);return path;
20798
+ }
20799
+ function updatePreviewPath(clientX,clientY) {
20800
+ var bodyRect=doc.body.getBoundingClientRect(),x=clientX-bodyRect.left,y=clientY-bodyRect.top;
20801
+ var gesture=activeGesture;
20802
+ if(!gesture.previewPath||gesture.previewPathPoints>=32){
20803
+ gesture.previewPath=createPreviewPath(gesture.previewLast?gesture.previewLast.x:x,gesture.previewLast?gesture.previewLast.y:y);
20804
+ gesture.previewPathPoints=1;
20805
+ }
20806
+ var pathData=gesture.previewPath.getAttribute('d')||'';
20807
+ gesture.previewPath.setAttribute('d',pathData+' L '+x.toFixed(1)+' '+y.toFixed(1));
20808
+ gesture.previewPathPoints++;
20809
+ gesture.previewLast={x:x,y:y};
20810
+ }
20811
+ function cancelGesture() {
20812
+ var gesture=activeGesture; activeGesture=null; clearPreview();
20813
+ if(gesture)try{inkCanvas.releasePointerCapture(gesture.pointerId);}catch(_){}
20814
+ }
20815
+ function commitGesture() {
20816
+ var gesture=activeGesture; activeGesture=null; clearPreview();
20817
+ if(!gesture || !alive || !options.isCurrent() || !inkConfig.enabled || !spatialIndex) return;
20818
+ var simplified=simplifyPath(gesture.points,1.5,512), anchorSamples=densifyPath(simplified,12,512), segments=[], current=[];
20819
+ anchorSamples.forEach(function(point) {
20820
+ var coarse=queryNearestTextRect(spatialIndex,point.x,point.y,28);
20821
+ var cutoff=coarse?Math.max(12,Math.min(28,0.8*coarse.entry.lineHeight)):12;
20822
+ var anchored=coarse && closestGlyph(coarse.entry,point.x,point.y,cutoff);
20823
+ if(!anchored){if(current.length>1)segments.push({href:options.href,spineIndex:options.spineIndex,points:current});current=[];return;}
20824
+ current.push(anchored);
20825
+ });
20826
+ if(current.length>1)segments.push({href:options.href,spineIndex:options.spineIndex,points:current});
20827
+ segments=segments.slice(0,64);
20828
+ var pointCount=segments.reduce(function(total,segment){return total+segment.points.length;},0);
20829
+ if(!pointCount)return;
20830
+ var width=Math.max(1,doc.documentElement.scrollWidth,win.innerWidth),height=Math.max(1,doc.documentElement.scrollHeight,win.innerHeight);
20831
+ var normalized=simplified.map(function(p){return {x:Math.max(0,Math.min(1,p.x/width)),y:Math.max(0,Math.min(1,p.y/height))};});
20832
+ var minX=Math.min.apply(null,normalized.map(function(p){return p.x;})),maxX=Math.max.apply(null,normalized.map(function(p){return p.x;}));
20833
+ var minY=Math.min.apply(null,normalized.map(function(p){return p.y;})),maxY=Math.max.apply(null,normalized.map(function(p){return p.y;}));
20834
+ var first=segments[0].points[0], href=segments[0].href, documentId=typeof options.documentId==='function'?options.documentId():options.documentId;
20835
+ var annotation={id:'epub-ink-'+Date.now()+'-'+Math.random().toString(36).slice(2,9),type:'ink',pageIndex:(options.spineIndex||0)+1,
20836
+ rect:{x:minX,y:minY,width:Math.min(1-minX,Math.max(0.001,maxX-minX)),height:Math.min(1-minY,Math.max(0.001,maxY-minY))},path:normalized,color:inkConfig.color,opacity:inkConfig.opacity,strokeWidth:inkConfig.strokeWidth,createdAt:Date.now(),content:first.quote,
20837
+ anchor:{version:1,kind:'epub-cfi',cfiRange:first.cfiRange,href:href,spineIndex:options.spineIndex,documentId:documentId,quote:first.quote},
20838
+ epubInk:{version:1,kind:'epub-ink',documentId:documentId,preset:inkConfig.preset,segments:segments}};
20839
+ if(typeof options.onInkCommit==='function')options.onInkCommit(annotation);
20840
+ }
20841
+ function pointerDown(event) {
20842
+ if(!inkConfig.enabled || !alive || !options.isCurrent() || event.button!==undefined && event.button!==0)return;
20843
+ if(activeGesture){cancelGesture();return;}
20844
+ event.preventDefault();event.stopPropagation();
20845
+ activeGesture={pointerId:event.pointerId,points:[{x:event.clientX+win.scrollX,y:event.clientY+win.scrollY}],last:{x:event.clientX,y:event.clientY},previewPath:null,previewPathPoints:0,previewLast:null};
20846
+ updatePreviewPath(event.clientX,event.clientY);
20847
+ try{inkCanvas.setPointerCapture(event.pointerId);}catch(_){}
20848
+ }
20849
+ function pointerMove(event) {
20850
+ if(!activeGesture || activeGesture.pointerId!==event.pointerId)return;
20851
+ if(!options.isCurrent()||!inkConfig.enabled){cancelGesture();return;}
20852
+ event.preventDefault();event.stopPropagation();
20853
+ var point={x:event.clientX+win.scrollX,y:event.clientY+win.scrollY};activeGesture.points.push(point);
20854
+ if(activeGesture.points.length>2048)activeGesture.points=compactGesturePoints(activeGesture.points,1024);
20855
+ if(inkContext){inkContext.beginPath();inkContext.moveTo(activeGesture.last.x,activeGesture.last.y);inkContext.lineTo(event.clientX,event.clientY);inkContext.strokeStyle=inkConfig.color;inkContext.globalAlpha=inkConfig.opacity;inkContext.lineWidth=inkStrokeWidthPx(inkConfig.strokeWidth,inkConfig.preset);inkContext.lineCap='round';inkContext.lineJoin='round';inkContext.stroke();}
20856
+ updatePreviewPath(event.clientX,event.clientY);
20857
+ activeGesture.last={x:event.clientX,y:event.clientY};
20858
+ }
20859
+ function pointerUp(event) {if(!activeGesture || activeGesture.pointerId!==event.pointerId)return;event.preventDefault();event.stopPropagation();var end={x:event.clientX+win.scrollX,y:event.clientY+win.scrollY},last=activeGesture.points[activeGesture.points.length-1];if(Math.hypot(end.x-last.x,end.y-last.y)>0.5){activeGesture.points.push(end);updatePreviewPath(event.clientX,event.clientY);}commitGesture();}
20860
+ function pointerCancel(event) {if(activeGesture && (event.pointerId===undefined || activeGesture.pointerId===event.pointerId))cancelGesture();}
20861
+ resizeCanvas();rebuildSpatialIndex();
20862
+ inkCanvas.addEventListener('pointerdown',pointerDown);inkCanvas.addEventListener('pointermove',pointerMove);inkCanvas.addEventListener('pointerup',pointerUp);inkCanvas.addEventListener('pointercancel',pointerCancel);inkCanvas.addEventListener('lostpointercapture',pointerCancel);
20863
+ win.addEventListener('blur',pointerCancel);win.addEventListener('resize',resizeCanvas);
20607
20864
  function render() {
20608
20865
  frame = 0; if (!alive || !options.isCurrent()) return;
20609
- var keep = new Set(), noteGroups = new Map();
20866
+ var keep = new Set(), keepInk = new Set(), noteGroups = new Map();
20610
20867
  hitRegions = [];
20868
+ var bodyRect=doc.body.getBoundingClientRect() || {left:0,top:0};
20869
+ inkSvg.setAttribute('width',String(Math.max(doc.documentElement.scrollWidth,win.innerWidth)));
20870
+ inkSvg.setAttribute('height',String(Math.max(doc.documentElement.scrollHeight,win.innerHeight)));
20611
20871
  options.annotations().forEach(function(annotation) {
20612
20872
  var anchor = annotation.anchor;
20613
20873
  if (!validAnchor(anchor) || anchor.href !== options.href || anchor.documentId && options.documentId && anchor.documentId !== options.documentId) return;
20874
+ if(annotation.type==='ink' && annotation.epubInk){renderInkAnnotation(annotation,keepInk,bodyRect);return;}
20614
20875
  var range = resolveRange(contents,anchor); if (!range) return;
20615
20876
  var rects = Array.from(range.getClientRects()).filter(function(r){return r.width>0 && r.height>0;});
20616
20877
  if (!rects.length) return;
@@ -20649,22 +20910,26 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20649
20910
  group.button.addEventListener('click',function(e){e.stopPropagation();if(options.isCurrent())options.onTap(group.ids[0],group.ids);});
20650
20911
  });
20651
20912
  registry.forEach(function(group,id){if (!keep.has(id)) {group.remove();registry.delete(id);}});
20913
+ inkRegistry.forEach(function(group,id){if (!keepInk.has(id)) {group.remove();inkRegistry.delete(id);}});
20652
20914
  }
20653
20915
  function schedule() { if (alive && !frame) frame = win.requestAnimationFrame(render); }
20916
+ function rebuildAndSchedule(){if(!alive)return;rebuildSpatialIndex();schedule();}
20917
+ function handleResize(){resizeCanvas();rebuildAndSchedule();}
20918
+ function handleDocumentLoad(){rebuildAndSchedule();}
20654
20919
  function tapMarkup(event) {
20655
20920
  if(!alive || !options.isCurrent() || String(win.getSelection() || '').length || event.target.closest('[data-papyrus-overlay]'))return;
20656
20921
  var hit=hitRegions.find(function(item){return item.rects.some(function(r){return event.clientX>=r.left && event.clientX<=r.right && event.clientY>=r.top && event.clientY<=r.bottom;});});
20657
20922
  if(hit) {event.stopPropagation();options.onTap(hit.id);}
20658
20923
  }
20659
20924
  doc.addEventListener('click',tapMarkup);
20660
- var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
20925
+ var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(rebuildAndSchedule) : null;
20661
20926
  if (observer) observer.observe(doc.body);
20662
- win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
20663
- if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
20927
+ win.addEventListener('resize',handleResize);win.addEventListener('scroll',schedule); doc.addEventListener('load',handleDocumentLoad,true);
20928
+ if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(function(){if(alive){rebuildSpatialIndex();schedule();}});
20664
20929
  schedule();
20665
- return {schedule:schedule, hit:function(x,y){return hitRegions.some(function(item){return item.rects.some(function(r){return x>=r.left && x<=r.right && y>=r.top && y<=r.bottom;});});}, range:function(anchor){return resolveRange(contents,anchor);}, destroy:function(){alive=false;if(frame)win.cancelAnimationFrame(frame);if(observer)observer.disconnect();win.removeEventListener('resize',schedule);win.removeEventListener('scroll',schedule);doc.removeEventListener('load',schedule,true);doc.removeEventListener('click',tapMarkup);overlay.remove();doc.body.style.position=originalPosition;registry.clear();}};
20930
+ return {schedule:schedule, setInkMode:function(config){var preset=['ink','highlight','underline'].indexOf(config?.preset)>=0?config.preset:'ink';inkConfig={enabled:!!config?.enabled,preset:preset,color:/^#[0-9a-f]{6}$/i.test(config?.color)?config.color:'#2563eb',opacity:Number.isFinite(config?.opacity)?Math.max(0,Math.min(1,config.opacity)):1,strokeWidth:Number.isFinite(config?.strokeWidth)?Math.max(0.0025,Math.min(0.02,config.strokeWidth)):0.006};inkCanvas.style.pointerEvents=inkConfig.enabled?'auto':'none';if(!inkConfig.enabled)cancelGesture();}, invalidateLayout:rebuildAndSchedule, hit:function(x,y){return hitRegions.some(function(item){return item.rects.some(function(r){return x>=r.left && x<=r.right && y>=r.top && y<=r.bottom;});});}, range:function(anchor){return resolveRange(contents,anchor);}, destroy:function(){alive=false;cancelGesture();if(frame)win.cancelAnimationFrame(frame);if(observer)observer.disconnect();win.removeEventListener('resize',handleResize);win.removeEventListener('scroll',schedule);win.removeEventListener('blur',pointerCancel);doc.removeEventListener('load',handleDocumentLoad,true);doc.removeEventListener('click',tapMarkup);inkCanvas.removeEventListener('pointerdown',pointerDown);inkCanvas.removeEventListener('pointermove',pointerMove);inkCanvas.removeEventListener('pointerup',pointerUp);inkCanvas.removeEventListener('pointercancel',pointerCancel);inkCanvas.removeEventListener('lostpointercapture',pointerCancel);inkCanvas.remove();overlay.remove();doc.body.style.position=originalPosition;registry.clear();inkRegistry.clear();rangeCache.clear();}};
20666
20931
  }
20667
- return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20932
+ return {validAnchor:validAnchor,simplifyPath:simplifyPath,compactGesturePoints:compactGesturePoints,densifyPath:densifyPath,createSpatialIndex:createSpatialIndex,queryNearestTextRect:queryNearestTextRect,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20668
20933
  });
20669
20934
 
20670
20935
  /* @papyrus-epub-annotations:end */
@@ -20680,6 +20945,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20680
20945
  let annotationDocumentId;
20681
20946
  let annotationLabels = {};
20682
20947
  let epubAnnotations = [];
20948
+ let epubInkMode = {enabled:false,color:'#2563eb',opacity:1,strokeWidth:0.006};
20683
20949
  const epubAnnotationRenderers = new Map();
20684
20950
  let spineItems = [];
20685
20951
  let textPages = [];
@@ -21794,11 +22060,14 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21794
22060
  if (href && window.PapyrusEpubAnnotations) {
21795
22061
  const renderer = window.PapyrusEpubAnnotations.install(contents, {
21796
22062
  href, get documentId() { return annotationDocumentId; },
22063
+ spineIndex:contents.sectionIndex,
21797
22064
  isCurrent: () => session === documentSessionId && activeRendition === rendition,
21798
22065
  pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
21799
22066
  annotations: () => epubAnnotations, labels: () => annotationLabels,
22067
+ onInkCommit: annotation => sendEvent('EPUB_INK_CREATED',{documentSessionId:session,annotation}),
21800
22068
  onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
21801
22069
  });
22070
+ renderer.setInkMode(epubInkMode);
21802
22071
  epubAnnotationRenderers.set(contents,renderer);
21803
22072
  }
21804
22073
  const frame = contents && contents.document ? contents.document.defaultView.frameElement : null;
@@ -21890,7 +22159,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21890
22159
  sendEvent('EPUB_TEXT_SELECTED', payload);
21891
22160
  });
21892
22161
  ['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
21893
- if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
22162
+ if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => name==='relocated'?renderer.schedule():renderer.invalidateLayout());
21894
22163
  }));
21895
22164
  rendition.on('relocated', (location) => {
21896
22165
  if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
@@ -22187,6 +22456,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22187
22456
  documentSessionId = payload.documentSessionId || null;
22188
22457
  epubAnnotations = [];
22189
22458
  annotationDocumentId = undefined;
22459
+ epubInkMode = {enabled:false,color:'#2563eb',opacity:1,strokeWidth:0.006};
22190
22460
  zoom = 1.0;
22191
22461
  epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
22192
22462
  epubLocation = null;
@@ -22318,6 +22588,12 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22318
22588
  }
22319
22589
  sendResponse(id,true,null);return;
22320
22590
  }
22591
+ if (kind === 'set-epub-ink-mode') {
22592
+ if(currentType!=='epub'||!documentSessionId||payload.documentSessionId!==documentSessionId)throw new Error('Stale EPUB ink session');
22593
+ epubInkMode={enabled:payload.enabled===true,preset:['ink','highlight','underline'].indexOf(payload.preset)>=0?payload.preset:'ink',color:/^#[0-9a-f]{6}$/i.test(payload.color||'')?payload.color:'#2563eb',opacity:Number.isFinite(payload.opacity)?Math.max(0,Math.min(1,payload.opacity)):1,strokeWidth:Number.isFinite(payload.strokeWidth)?Math.max(0.0025,Math.min(0.02,payload.strokeWidth)):0.006};
22594
+ epubAnnotationRenderers.forEach(renderer=>renderer.setInkMode(epubInkMode));
22595
+ sendResponse(id,true,null);return;
22596
+ }
22321
22597
  if(kind === 'epub-go-to-location'){
22322
22598
  if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
22323
22599
  epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>{if(payload.documentSessionId!==documentSessionId)return;epubResizeAnchor?.invalidate();return rendition.display(payload.cfi);});