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

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,205 @@ 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 previewSvg = doc.createElementNS('http://www.w3.org/2000/svg','svg');
20691
+ previewSvg.setAttribute('data-papyrus-ink-preview-overlay','');
20692
+ previewSvg.style.cssText = 'position:fixed;left:0;top:0;overflow:hidden;pointer-events:none;z-index:2147482999';
20693
+ doc.documentElement.appendChild(previewSvg);
20694
+ var inkCanvas = doc.createElement('canvas');
20695
+ inkCanvas.dataset.papyrusInkCapture = '';
20696
+ inkCanvas.style.cssText = 'position:fixed;left:0;top:0;width:1px;height:1px;z-index:2147483000;background:transparent;pointer-events:none;touch-action:none';
20697
+ doc.documentElement.appendChild(inkCanvas);
20698
+ var inkContext = null; try { if(typeof win.CanvasRenderingContext2D==='function')inkContext = inkCanvas.getContext('2d'); } catch (_) {}
20699
+ function resizeCanvas() {
20700
+ var ratio = Math.max(1,Math.min(3,win.devicePixelRatio || 1));
20701
+ var width=Math.max(1,Math.min(4096,win.innerWidth||1));
20702
+ var height=Math.max(1,Math.min(4096,win.innerHeight||1));
20703
+ inkCanvas.style.width=width+'px';inkCanvas.style.height=height+'px';
20704
+ previewSvg.setAttribute('width',String(width));previewSvg.setAttribute('height',String(height));
20705
+ previewSvg.setAttribute('viewBox','0 0 '+width+' '+height);
20706
+ inkCanvas.width = Math.round(width*ratio); inkCanvas.height = Math.round(height*ratio);
20707
+ if (inkContext) { inkContext.setTransform(ratio,0,0,ratio,0,0); inkContext.clearRect(0,0,width,height); }
20708
+ }
20709
+ function collectTextRects() {
20710
+ var walker = doc.createTreeWalker(doc.body,4), node, entries=[];
20711
+ while ((node=walker.nextNode())) {
20712
+ var parent=node.parentElement;
20713
+ if (!parent || parent.closest('script,style,noscript,textarea,[data-papyrus-overlay],[data-papyrus-ink-capture]')) continue;
20714
+ var text=node.nodeValue || '', style=win.getComputedStyle(parent), computed=parseFloat(style.lineHeight);
20715
+ for (var start=0; start<text.length;) {
20716
+ var end=Math.min(text.length,start+24), last=text.charCodeAt(end-1), next=text.charCodeAt(end);
20717
+ if(end<text.length&&last>=0xD800&&last<=0xDBFF&&next>=0xDC00&&next<=0xDFFF)end++;
20718
+ var range=doc.createRange(); range.setStart(node,start); range.setEnd(node,end);
20719
+ var rects=[]; try { rects=Array.from(range.getClientRects()); } catch (_) {}
20720
+ rects.forEach(function(rect) {
20721
+ if (!(rect.width>0 && rect.height>0)) return;
20722
+ 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,
20723
+ node:node,start:start,end:end,lineHeight:Number.isFinite(computed)&&computed>0?computed:rect.height});
20724
+ });
20725
+ start=end;
20726
+ }
20727
+ }
20728
+ return entries;
20729
+ }
20730
+ function rebuildSpatialIndex() { spatialIndex=createSpatialIndex(collectTextRects(),64); rangeCache.clear(); }
20731
+ function inkViewportExtent() {
20732
+ var root=doc.documentElement,body=doc.body,bodyRect=body.getBoundingClientRect();
20733
+ var width=root.clientWidth||body.clientWidth||bodyRect.width||win.innerWidth||1;
20734
+ var height=root.clientHeight||body.clientHeight||bodyRect.height||win.innerHeight||1;
20735
+ return Math.max(1,Math.min(4096,width),Math.min(4096,height));
20736
+ }
20737
+ function inkStrokeWidthPx(width,preset) {
20738
+ var scale=preset==='highlight'?0.8:preset==='underline'?0.1:0.15;
20739
+ return Math.max(1,Math.min(24,(Number.isFinite(width)?width:0.006)*inkViewportExtent()*scale));
20740
+ }
20741
+ function closestGlyph(entry,x,y,maxDistance) {
20742
+ var source=(entry.node.nodeValue || '').slice(entry.start,entry.end), best=null, offset=0;
20743
+ for (var glyph of source) {
20744
+ var range=doc.createRange(); range.setStart(entry.node,entry.start+offset); range.setEnd(entry.node,entry.start+offset+glyph.length); offset+=glyph.length;
20745
+ var rects=[]; try { rects=Array.from(range.getClientRects()); } catch (_) {}
20746
+ rects.forEach(function(rect) {
20747
+ if (!(rect.width>0 && rect.height>0)) return;
20748
+ 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));
20749
+ var dx=x-nx,dy=y-ny,distance=Math.sqrt(dx*dx+dy*dy);
20750
+ if (distance<=maxDistance && (!best || distance<best.distance)) best={range:range,rect:rect,distance:distance};
20751
+ });
20752
+ }
20753
+ if (!best || typeof contents.cfiFromRange!=='function') return null;
20754
+ var quote=best.range.toString(), cfiRange; try { cfiRange=contents.cfiFromRange(best.range); } catch (_) { return null; }
20755
+ if (!quote || typeof cfiRange!=='string' || !/^epubcfi\([^\n<>]+\)$/.test(cfiRange)) return null;
20756
+ var lineHeight=Math.max(1,entry.lineHeight || best.rect.height), px=x-(best.rect.left+win.scrollX), py=y-(best.rect.top+win.scrollY);
20757
+ return {cfiRange:cfiRange,quote:quote,x:Math.max(-3,Math.min(3,px/lineHeight)),y:Math.max(-3,Math.min(3,py/lineHeight))};
20758
+ }
20759
+ function renderInkAnnotation(annotation,keep,bodyRect) {
20760
+ var baseRect=bodyRect || doc.body.getBoundingClientRect() || {left:0,top:0};
20761
+ var data=annotation.epubInk;
20762
+ if (!data || data.version!==1 || data.kind!=='epub-ink' || !Array.isArray(data.segments)) return;
20763
+ keep.add(annotation.id);
20764
+ var group=inkRegistry.get(annotation.id);
20765
+ 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); }
20766
+ group.replaceChildren();
20767
+ var color=/^#[0-9a-f]{6}$/i.test(annotation.color)?annotation.color:'#2563eb';
20768
+ var opacity=Number.isFinite(annotation.opacity)?Math.max(0,Math.min(1,annotation.opacity)):1;
20769
+ var preset=['ink','highlight','underline'].indexOf(data.preset)>=0?data.preset:'ink';
20770
+ var stroke=inkStrokeWidthPx(annotation.strokeWidth,preset);
20771
+ data.segments.forEach(function(segment) {
20772
+ if (segment.href!==options.href || !Array.isArray(segment.points)) return;
20773
+ var paths=[], current=[];
20774
+ segment.points.forEach(function(point) {
20775
+ var range=rangeCache.get(point.cfiRange);
20776
+ if (!range) { try { range=contents.range(point.cfiRange); } catch (_) { range=null; } if (range) rangeCache.set(point.cfiRange,range); }
20777
+ if (!range || range.toString()!==point.quote) { if(current.length>1)paths.push(current);current=[];return; }
20778
+ var rect; try { rect=range.getBoundingClientRect(); } catch (_) { rect=null; }
20779
+ if (!rect || !(rect.width>0 && rect.height>0)) { if(current.length>1)paths.push(current);current=[];return; }
20780
+ var lineHeight=Math.max(1,rect.height), x=rect.left-baseRect.left+point.x*lineHeight, y=rect.top-baseRect.top+point.y*lineHeight;
20781
+ var previous=current[current.length-1], jumpLimit=Math.max(24,lineHeight*2.5);
20782
+ if (previous && Math.hypot(x-previous.x,y-previous.y)>jumpLimit) { if(current.length>1)paths.push(current);current=[]; }
20783
+ current.push({x:x,y:y,lineHeight:lineHeight});
20784
+ });
20785
+ if(current.length>1)paths.push(current);
20786
+ paths.forEach(function(points) {
20787
+ var d='M '+points[0].x.toFixed(1)+' '+points[0].y.toFixed(1);
20788
+ 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);}
20789
+ var last=points[points.length-1];d+=' L '+last.x.toFixed(1)+' '+last.y.toFixed(1);
20790
+ 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);
20791
+ });
20792
+ });
20793
+ }
20794
+ function clearPreview() {
20795
+ if(inkContext)inkContext.clearRect(0,0,inkCanvas.width,inkCanvas.height);
20796
+ previewSvg.replaceChildren();
20797
+ }
20798
+ function createPreviewPath(x,y) {
20799
+ var path=doc.createElementNS('http://www.w3.org/2000/svg','path');
20800
+ path.setAttribute('data-papyrus-ink-preview','');path.setAttribute('d','M '+x.toFixed(1)+' '+y.toFixed(1));path.setAttribute('fill','none');
20801
+ path.setAttribute('stroke',inkConfig.color);path.setAttribute('stroke-width',String(inkStrokeWidthPx(inkConfig.strokeWidth,inkConfig.preset)));
20802
+ path.setAttribute('stroke-linecap','round');path.setAttribute('stroke-linejoin','round');path.setAttribute('opacity',String(inkConfig.opacity));
20803
+ path.style.pointerEvents='none';previewSvg.appendChild(path);return path;
20804
+ }
20805
+ function updatePreviewPath(clientX,clientY) {
20806
+ var x=clientX,y=clientY;
20807
+ var gesture=activeGesture;
20808
+ var startsSegment=!gesture.previewPath||gesture.previewPathPoints>=32;
20809
+ if(startsSegment){
20810
+ gesture.previewPath=createPreviewPath(gesture.previewLast?gesture.previewLast.x:x,gesture.previewLast?gesture.previewLast.y:y);
20811
+ gesture.previewPathPoints=1;
20812
+ }
20813
+ if(!startsSegment||gesture.previewLast){
20814
+ var pathData=gesture.previewPath.getAttribute('d')||'';
20815
+ gesture.previewPath.setAttribute('d',pathData+' L '+x.toFixed(1)+' '+y.toFixed(1));
20816
+ gesture.previewPathPoints++;
20817
+ }
20818
+ gesture.previewLast={x:x,y:y};
20819
+ }
20820
+ function cancelGesture() {
20821
+ var gesture=activeGesture; activeGesture=null; clearPreview();
20822
+ if(gesture)try{inkCanvas.releasePointerCapture(gesture.pointerId);}catch(_){}
20823
+ }
20824
+ function commitGesture() {
20825
+ var gesture=activeGesture; activeGesture=null; clearPreview();
20826
+ if(!gesture || !alive || !options.isCurrent() || !inkConfig.enabled || !spatialIndex) return;
20827
+ var simplified=simplifyPath(gesture.points,1.5,512), anchorSamples=densifyPath(simplified,12,512), segments=[], current=[];
20828
+ anchorSamples.forEach(function(point) {
20829
+ var coarse=queryNearestTextRect(spatialIndex,point.x,point.y,28);
20830
+ var cutoff=coarse?Math.max(12,Math.min(28,0.8*coarse.entry.lineHeight)):12;
20831
+ var anchored=coarse && closestGlyph(coarse.entry,point.x,point.y,cutoff);
20832
+ if(!anchored){if(current.length>1)segments.push({href:options.href,spineIndex:options.spineIndex,points:current});current=[];return;}
20833
+ current.push(anchored);
20834
+ });
20835
+ if(current.length>1)segments.push({href:options.href,spineIndex:options.spineIndex,points:current});
20836
+ segments=segments.slice(0,64);
20837
+ var pointCount=segments.reduce(function(total,segment){return total+segment.points.length;},0);
20838
+ if(!pointCount)return;
20839
+ var width=Math.max(1,doc.documentElement.scrollWidth,win.innerWidth),height=Math.max(1,doc.documentElement.scrollHeight,win.innerHeight);
20840
+ 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))};});
20841
+ 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;}));
20842
+ 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;}));
20843
+ var first=segments[0].points[0], href=segments[0].href, documentId=typeof options.documentId==='function'?options.documentId():options.documentId;
20844
+ var annotation={id:'epub-ink-'+Date.now()+'-'+Math.random().toString(36).slice(2,9),type:'ink',pageIndex:(options.spineIndex||0)+1,
20845
+ 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,
20846
+ anchor:{version:1,kind:'epub-cfi',cfiRange:first.cfiRange,href:href,spineIndex:options.spineIndex,documentId:documentId,quote:first.quote},
20847
+ epubInk:{version:1,kind:'epub-ink',documentId:documentId,preset:inkConfig.preset,segments:segments}};
20848
+ if(typeof options.onInkCommit==='function')options.onInkCommit(annotation);
20849
+ }
20850
+ function pointerDown(event) {
20851
+ if(!inkConfig.enabled || !alive || !options.isCurrent() || event.button!==undefined && event.button!==0)return;
20852
+ if(activeGesture){cancelGesture();return;}
20853
+ event.preventDefault();event.stopPropagation();
20854
+ 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};
20855
+ updatePreviewPath(event.clientX,event.clientY);
20856
+ try{inkCanvas.setPointerCapture(event.pointerId);}catch(_){}
20857
+ }
20858
+ function pointerMove(event) {
20859
+ if(!activeGesture || activeGesture.pointerId!==event.pointerId)return;
20860
+ if(!options.isCurrent()||!inkConfig.enabled){cancelGesture();return;}
20861
+ event.preventDefault();event.stopPropagation();
20862
+ var point={x:event.clientX+win.scrollX,y:event.clientY+win.scrollY};activeGesture.points.push(point);
20863
+ if(activeGesture.points.length>2048)activeGesture.points=compactGesturePoints(activeGesture.points,1024);
20864
+ 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();}
20865
+ updatePreviewPath(event.clientX,event.clientY);
20866
+ activeGesture.last={x:event.clientX,y:event.clientY};
20867
+ }
20868
+ 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();}
20869
+ function pointerCancel(event) {if(activeGesture && (event.pointerId===undefined || activeGesture.pointerId===event.pointerId))cancelGesture();}
20870
+ resizeCanvas();rebuildSpatialIndex();
20871
+ inkCanvas.addEventListener('pointerdown',pointerDown);inkCanvas.addEventListener('pointermove',pointerMove);inkCanvas.addEventListener('pointerup',pointerUp);inkCanvas.addEventListener('pointercancel',pointerCancel);inkCanvas.addEventListener('lostpointercapture',pointerCancel);
20872
+ win.addEventListener('blur',pointerCancel);
20607
20873
  function render() {
20608
20874
  frame = 0; if (!alive || !options.isCurrent()) return;
20609
- var keep = new Set(), noteGroups = new Map();
20875
+ var keep = new Set(), keepInk = new Set(), noteGroups = new Map();
20610
20876
  hitRegions = [];
20877
+ var bodyRect=doc.body.getBoundingClientRect() || {left:0,top:0};
20878
+ inkSvg.setAttribute('width',String(Math.max(doc.documentElement.scrollWidth,win.innerWidth)));
20879
+ inkSvg.setAttribute('height',String(Math.max(doc.documentElement.scrollHeight,win.innerHeight)));
20611
20880
  options.annotations().forEach(function(annotation) {
20612
20881
  var anchor = annotation.anchor;
20613
20882
  if (!validAnchor(anchor) || anchor.href !== options.href || anchor.documentId && options.documentId && anchor.documentId !== options.documentId) return;
20883
+ if(annotation.type==='ink' && annotation.epubInk){renderInkAnnotation(annotation,keepInk,bodyRect);return;}
20614
20884
  var range = resolveRange(contents,anchor); if (!range) return;
20615
20885
  var rects = Array.from(range.getClientRects()).filter(function(r){return r.width>0 && r.height>0;});
20616
20886
  if (!rects.length) return;
@@ -20649,22 +20919,26 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20649
20919
  group.button.addEventListener('click',function(e){e.stopPropagation();if(options.isCurrent())options.onTap(group.ids[0],group.ids);});
20650
20920
  });
20651
20921
  registry.forEach(function(group,id){if (!keep.has(id)) {group.remove();registry.delete(id);}});
20922
+ inkRegistry.forEach(function(group,id){if (!keepInk.has(id)) {group.remove();inkRegistry.delete(id);}});
20652
20923
  }
20653
20924
  function schedule() { if (alive && !frame) frame = win.requestAnimationFrame(render); }
20925
+ function rebuildAndSchedule(){if(!alive)return;rebuildSpatialIndex();schedule();}
20926
+ function handleResize(){resizeCanvas();rebuildAndSchedule();}
20927
+ function handleDocumentLoad(){rebuildAndSchedule();}
20654
20928
  function tapMarkup(event) {
20655
20929
  if(!alive || !options.isCurrent() || String(win.getSelection() || '').length || event.target.closest('[data-papyrus-overlay]'))return;
20656
20930
  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
20931
  if(hit) {event.stopPropagation();options.onTap(hit.id);}
20658
20932
  }
20659
20933
  doc.addEventListener('click',tapMarkup);
20660
- var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
20934
+ var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(rebuildAndSchedule) : null;
20661
20935
  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);
20936
+ win.addEventListener('resize',handleResize);win.addEventListener('scroll',schedule); doc.addEventListener('load',handleDocumentLoad,true);
20937
+ if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(function(){if(alive){rebuildSpatialIndex();schedule();}});
20664
20938
  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();}};
20939
+ 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();previewSvg.remove();overlay.remove();doc.body.style.position=originalPosition;registry.clear();inkRegistry.clear();rangeCache.clear();}};
20666
20940
  }
20667
- return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20941
+ return {validAnchor:validAnchor,simplifyPath:simplifyPath,compactGesturePoints:compactGesturePoints,densifyPath:densifyPath,createSpatialIndex:createSpatialIndex,queryNearestTextRect:queryNearestTextRect,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20668
20942
  });
20669
20943
 
20670
20944
  /* @papyrus-epub-annotations:end */
@@ -20680,6 +20954,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20680
20954
  let annotationDocumentId;
20681
20955
  let annotationLabels = {};
20682
20956
  let epubAnnotations = [];
20957
+ let epubInkMode = {enabled:false,color:'#2563eb',opacity:1,strokeWidth:0.006};
20683
20958
  const epubAnnotationRenderers = new Map();
20684
20959
  let spineItems = [];
20685
20960
  let textPages = [];
@@ -21794,11 +22069,14 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21794
22069
  if (href && window.PapyrusEpubAnnotations) {
21795
22070
  const renderer = window.PapyrusEpubAnnotations.install(contents, {
21796
22071
  href, get documentId() { return annotationDocumentId; },
22072
+ spineIndex:contents.sectionIndex,
21797
22073
  isCurrent: () => session === documentSessionId && activeRendition === rendition,
21798
22074
  pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
21799
22075
  annotations: () => epubAnnotations, labels: () => annotationLabels,
22076
+ onInkCommit: annotation => sendEvent('EPUB_INK_CREATED',{documentSessionId:session,annotation}),
21800
22077
  onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
21801
22078
  });
22079
+ renderer.setInkMode(epubInkMode);
21802
22080
  epubAnnotationRenderers.set(contents,renderer);
21803
22081
  }
21804
22082
  const frame = contents && contents.document ? contents.document.defaultView.frameElement : null;
@@ -21890,7 +22168,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21890
22168
  sendEvent('EPUB_TEXT_SELECTED', payload);
21891
22169
  });
21892
22170
  ['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
21893
- if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
22171
+ if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => name==='relocated'?renderer.schedule():renderer.invalidateLayout());
21894
22172
  }));
21895
22173
  rendition.on('relocated', (location) => {
21896
22174
  if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
@@ -22187,6 +22465,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22187
22465
  documentSessionId = payload.documentSessionId || null;
22188
22466
  epubAnnotations = [];
22189
22467
  annotationDocumentId = undefined;
22468
+ epubInkMode = {enabled:false,color:'#2563eb',opacity:1,strokeWidth:0.006};
22190
22469
  zoom = 1.0;
22191
22470
  epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
22192
22471
  epubLocation = null;
@@ -22318,6 +22597,12 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22318
22597
  }
22319
22598
  sendResponse(id,true,null);return;
22320
22599
  }
22600
+ if (kind === 'set-epub-ink-mode') {
22601
+ if(currentType!=='epub'||!documentSessionId||payload.documentSessionId!==documentSessionId)throw new Error('Stale EPUB ink session');
22602
+ 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};
22603
+ epubAnnotationRenderers.forEach(renderer=>renderer.setInkMode(epubInkMode));
22604
+ sendResponse(id,true,null);return;
22605
+ }
22321
22606
  if(kind === 'epub-go-to-location'){
22322
22607
  if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
22323
22608
  epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>{if(payload.documentSessionId!==documentSessionId)return;epubResizeAnchor?.invalidate();return rendition.display(payload.cfi);});