Skip to content

Commit 2747ad2

Browse files
committed
fix: show highlight comments in unified popup and margin markers
1 parent d543976 commit 2747ad2

1 file changed

Lines changed: 20 additions & 7 deletions

File tree

‎src/ReadingAnnotations.vue‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ function startMotion(){if(!motionFrame){motionTime=0;motionFrame=requestAnimatio
4141
function finishGesture(e,cancelled=false){if(!gesture||gesture.pointerId!==e.pointerId)return;if(!cancelled)move(e);const g=gesture;gesture=null;if(g.dragged)suppressClickUntil=performance.now()+400;
4242
const m=motion.value;if(!m||m.id!==g.a.id)return;
4343
if(!cancelled&&g.shake.recognized){remove(g.a);}else {m.tx=0;m.ty=0;if(reducedMotion.matches){cancelAnimationFrame(motionFrame);motionFrame=0;motion.value=null;}else {m.vy+=24;startMotion();}}}
44-
function clickAnnotation(a,e,note=false){if(performance.now()<suppressClickUntil){e.preventDefault();return;}activate(a);if(note&&props.annotations.some(b=>b.id===a.id)){popupFromNote.value=true;opened.value=a;}}
44+
function clickAnnotation(a,e,note=false){if(performance.now()<suppressClickUntil){e.preventDefault();return;}activate(a);if(note&&props.annotations.some(b=>b.id===a.id)){popupFromNote.value=true;if(a.kind!=='highlight'||!a.comment?.trim()){toolbar.value=false;opened.value=a;}}}
4545
46+
const toolbarComment=computed(()=>props.annotations.find(a=>a.id===selection.value?.id)?.comment?.trim()||'');
4647
const colors=['#FFFF00','#00FF00','#00FFFF','#FF0000'];
4748
const highlightOpacity=.42;
4849
const favorite=ref('#00FFFF');try{favorite.value=JSON.parse(localStorage.getItem('highlightColors')||'{}').favorite||'#00FFFF';}catch{}
@@ -76,7 +77,7 @@ function baseNoteRect(a){
7677
const line=a.rects[0],anchor=box||line,markerSize=34/props.zoom;
7778
return {x:noteRail.value,y:Math.max(0,Math.min(props.page.height-markerSize,anchor.y+(Math.min(line.height,anchor.height)-markerSize)/2)),width:markerSize,height:markerSize};
7879
}
79-
const notePositions=computed(()=>annotationNotePositions(visible.value.filter(a=>a.kind==='comment'||((a.origin==='translation')!==translatedAt(a))).map(a=>({id:a.id,y:baseNoteRect(a).y})),props.page.height,props.zoom));
80+
const notePositions=computed(()=>annotationNotePositions(visible.value.filter(a=>a.kind==='comment'||!!a.comment?.trim()||((a.origin==='translation')!==translatedAt(a))).map(a=>({id:a.id,y:baseNoteRect(a).y})),props.page.height,props.zoom));
8081
function noteRect(a){const rect=baseNoteRect(a);return {...rect,y:notePositions.value.get(a.id)??rect.y};}
8182
8283
function activate(a){if(!props.annotations.some(b=>b.id===a.id))return;popupFromNote.value=false;toolbar.value=false;selection.value=null;menu.value=null;focused.value=a;opened.value=null;if(a.kind==='highlight'){selection.value=a;toolbar.value=true;touch();}else opened.value=a;}
@@ -89,7 +90,7 @@ function point(){
8990
layoutVersion.value;
9091
const state=editor.value||opened.value||(toolbar.value?selection.value:focused.value),host=props.host()?.getBoundingClientRect();
9192
if(!state||!host)return {left:'8px',top:'8px'};
92-
const fromNote=!!state.id&&((state.origin==='translation')!==translatedAt(state)||(opened.value&&popupFromNote.value));
93+
const fromNote=!!state.id&&((state.origin==='translation')!==translatedAt(state)||popupFromNote.value);
9394
const line=fromNote?noteRect(state):state.rects.at(-1),popup=editor.value?editorPopup.value:opened.value?readerPopup.value:toolbarPopup.value;
9495
const width=popup?.offsetWidth||(editor.value?298:opened.value?280:220),height=popup?.offsetHeight||(editor.value?180:opened.value?130:48);
9596
const inset=12,gap=10,screenWidth=window.innerWidth,screenHeight=window.innerHeight;
@@ -113,7 +114,7 @@ watch(draft,saveComment);
113114
function commit(){if(!draft.value.trim())return;saveComment();focused.value=editor.value;editor.value=null;selection.value=null;window.getSelection()?.removeAllRanges();}
114115
function remove(a){if(erasing.value.has(a.id))return;
115116
// Persist immediately; the remaining marks are visual snapshots only.
116-
if(!reducedMotion.matches){const snapshot={...a,animateUntil:0,eraseHasNote:opened.value?.id!==a.id&&(a.kind==='comment'||(a.origin==='translation')!==translatedAt(a))};
117+
if(!reducedMotion.matches){const snapshot={...a,animateUntil:0,eraseHasNote:opened.value?.id!==a.id&&(a.kind==='comment'||!!a.comment?.trim()||(a.origin==='translation')!==translatedAt(a))};
117118
if(motion.value?.id===a.id)snapshot.eraseMotion={...motion.value};
118119
erasing.value.set(a.id,snapshot);eraseTimers.set(a.id,setTimeout(()=>{erasing.value.delete(a.id);eraseTimers.delete(a.id);},highlightDuration));}
119120
if(gesture?.a.id===a.id)gesture=null;if(motion.value?.id===a.id){cancelAnimationFrame(motionFrame);motionFrame=0;motion.value=null;}
@@ -146,16 +147,19 @@ watch(()=>props.zoom,()=>{toolbar.value=false;updatePopupLayout();});
146147
<div class="annotation-layer">
147148
<template v-for="a in visible" :key="a.id">
148149
<button v-for="(r,i) in ((a.origin==='translation')===translatedAt(a))?a.rects:[]" :key="i" class="annotation-mark annotation-highlight" :class="{'annotation-erasing':erasing.has(a.id),'annotation-new-highlight':isNew(a),'annotation-comment-highlight':a.kind==='comment'}" :style="{...highlightPosition(r,a),...motionStyle(a,r),...eraseStyle(a,i,r),background:a.kind==='comment'?'#9b9b9b':a.color,opacity:a.kind==='comment'?undefined:highlightOpacity,...strokeStyle(a,i)}" :aria-hidden="erasing.has(a.id)||undefined" :tabindex="erasing.has(a.id)?-1:0" :aria-label="a.kind==='comment'?'查看批注:'+a.comment:a.text" @pointerdown.stop="down(a,$event)" @pointermove="move" @pointerup="finishGesture($event)" @pointercancel="finishGesture($event,true)" @lostpointercapture="finishGesture($event,true)" @contextmenu.prevent.stop="openContextMenu(a,$event)" @click.stop="clickAnnotation(a,$event)"/>
149-
<button v-if="(erasing.has(a.id)?a.eraseHasNote:opened?.id!==a.id)&&(a.kind==='comment'||((a.origin==='translation')!==translatedAt(a)))" class="annotation-mark annotation-note" :class="{'annotation-comment-note':a.kind==='comment','annotation-erasing':erasing.has(a.id)}" title="查看批注" :style="{...position(noteRect(a)),...motionStyle(a,noteRect(a)),...eraseStyle(a,undefined,noteRect(a))}" :aria-hidden="erasing.has(a.id)||undefined" :tabindex="erasing.has(a.id)?-1:0" :aria-label="a.comment||a.text" @pointerdown.stop="down(a,$event)" @pointermove="move" @pointerup="finishGesture($event)" @pointercancel="finishGesture($event,true)" @lostpointercapture="finishGesture($event,true)" @click.stop="clickAnnotation(a,$event,true)" @contextmenu.prevent.stop="openContextMenu(a,$event)"><svg class="annotation-comment-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 3.5h14a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2h-8l-5 3v-3H5a2 2 0 0 1-2-2v-11a2 2 0 0 1 2-2Z"/><path d="M7 8h10M7 12h7"/></svg></button>
150+
<button v-if="(erasing.has(a.id)?a.eraseHasNote:opened?.id!==a.id)&&(a.kind==='comment'||!!a.comment?.trim()||((a.origin==='translation')!==translatedAt(a)))" class="annotation-mark annotation-note" :class="{'annotation-comment-note':a.kind==='comment'||!!a.comment?.trim(),'annotation-erasing':erasing.has(a.id)}" title="查看批注" :style="{...position(noteRect(a)),...motionStyle(a,noteRect(a)),...eraseStyle(a,undefined,noteRect(a))}" :aria-hidden="erasing.has(a.id)||undefined" :tabindex="erasing.has(a.id)?-1:0" :aria-label="a.comment||a.text" @pointerdown.stop="down(a,$event)" @pointermove="move" @pointerup="finishGesture($event)" @pointercancel="finishGesture($event,true)" @lostpointercapture="finishGesture($event,true)" @click.stop="clickAnnotation(a,$event,true)" @contextmenu.prevent.stop="openContextMenu(a,$event)"><svg class="annotation-comment-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 3.5h14a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2h-8l-5 3v-3H5a2 2 0 0 1-2-2v-11a2 2 0 0 1 2-2Z"/><path d="M7 8h10M7 12h7"/></svg></button>
150151
</template>
151152
</div>
152153
<Teleport to="body">
153154
<div v-if="assistance" :data-platform="popupPlatform" class="annotation-ui annotation-modal"><section class="annotation-confirm annotation-assistance" role="dialog" aria-modal="true" :aria-label="assistance.title"><strong>{{assistance.title}}</strong><p v-if="assistance.busy" role="status">正在处理…</p><p v-else>{{assistance.error||assistance.text}}</p><button @click="assistController?.abort();assistance=null">关闭</button></section></div>
154-
<Transition name="annotation-fade"><div v-if="toolbar" ref="toolbarPopup" :data-platform="popupPlatform" class="annotation-ui annotation-toolbar" :style="toolbarPoint()" role="toolbar" aria-label="文字批注" @pointerdown.prevent @pointermove="touch" @keydown="touch">
155+
<Transition name="annotation-fade"><div v-if="toolbar" ref="toolbarPopup" :data-platform="popupPlatform" class="annotation-ui annotation-toolbar" :style="toolbarPoint()" :class="{'annotation-toolbar-with-comment':toolbarComment}" @pointerdown.prevent @pointermove="touch" @keydown="touch">
156+
<div class="annotation-toolbar-actions" role="toolbar" aria-label="文字批注">
155157
<button v-for="(c,i) in colors" :key="i" :aria-label="['荧光黄','荧光绿','荧光青','荧光红'][i]" :title="['荧光黄 (1)','荧光绿 (2)','荧光青 (3)','荧光红 (4)'][i]" :aria-keyshortcuts="String(i+1)" :aria-pressed="selection?.id?selection.color?.toUpperCase()===c:undefined" @click="add('highlight',c)"><i class="annotation-color-swatch" :style="{background:`color-mix(in srgb, ${c} ${highlightOpacity*100}%, white)`}" aria-hidden="true"/></button><button v-if="false" aria-label="自定义高亮颜色" @click="clearTimeout(timer);colorInput.click()"><i class="annotation-color-swatch annotation-custom-color" :style="{'--annotation-color':favorite}" aria-hidden="true"/></button><input ref="colorInput" type="color" class="annotation-color-input" :value="favorite" @change="add('highlight',$event.target.value)"/><span class="annotation-separator"/><button aria-label="添加批注" title="添加批注 (5)" aria-keyshortcuts="5" @click="comment(selection?.id?selection:undefined)"><svg class="annotation-comment-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 3.5h14a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2h-8l-5 3v-3H5a2 2 0 0 1-2-2v-11a2 2 0 0 1 2-2Z"/><path d="M7 8h10M7 12h7"/></svg></button>
156158
<button v-if="selection?.id" class="annotation-delete" :aria-label="deleteArmed?'双击删除高亮':'删除高亮'" :title="deleteArmed?'双击删除高亮':'单击确认,双击删除高亮'" @click="armToolbarDelete" @dblclick.prevent.stop="deleteFromToolbar"><svg class="annotation-comment-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><template v-if="deleteArmed"><path d="M9 7a3 3 0 0 1 6 0c0 2-3 2.5-3 5"/><path d="M12 17h.01"/></template><template v-else><path d="M3 6h18M9 6V3h6v3M5 6l1 15h12l1-15M10 10v7M14 10v7"/></template></svg></button>
159+
</div>
160+
<section v-if="toolbarComment" class="annotation-toolbar-comment" aria-label="评论"><p>{{toolbarComment}}</p></section>
157161
</div></Transition>
158-
<Transition name="annotation-fade"><div v-if="opened" ref="readerPopup" :data-platform="popupPlatform" class="annotation-ui annotation-reader" :style="readerPoint()"><header class="annotation-reader-header"><span>{{opened.origin==='translation'?'译文':'原文'}}{{opened.kind==='highlight'?'高亮':'批注'}}</span><button @click="opened=null" aria-label="关闭"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="m4 4 8 8M12 4l-8 8"/></svg></button></header><p>{{opened.kind==='highlight'?(opened.text||'').replace(/[\r\n\u2028\u2029]+/g,'').trim():opened.comment||opened.text}}</p><time>{{opened.dateUnknown&&!opened.modifiedAt?'日期未知':new Date(opened.modifiedAt||opened.createdAt).toLocaleString()}}</time></div></Transition>
162+
<Transition name="annotation-fade"><div v-if="opened" ref="readerPopup" :data-platform="popupPlatform" class="annotation-ui annotation-reader" :style="readerPoint()"><header class="annotation-reader-header"><span>{{opened.origin==='translation'?'译文':'原文'}}{{opened.kind==='highlight'?'高亮':'批注'}}</span><button @click="opened=null" aria-label="关闭"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="m4 4 8 8M12 4l-8 8"/></svg></button></header><p>{{opened.kind==='highlight'&&!opened.comment?.trim()?(opened.text||'').replace(/[\r\n\u2028\u2029]+/g,'').trim():opened.comment||opened.text}}</p><time>{{opened.dateUnknown&&!opened.modifiedAt?'日期未知':new Date(opened.modifiedAt||opened.createdAt).toLocaleString()}}</time></div></Transition>
159163
<Transition name="annotation-fade"><div v-if="editor" :data-platform="popupPlatform" class="annotation-ui annotation-modal" @pointerdown.self="editor=null"><form ref="editorPopup" class="annotation-editor" :style="editorPoint()" role="dialog" aria-modal="true" aria-label="添加批注" @submit.prevent="commit"><textarea v-model="draft" placeholder="输入评论…" aria-label="评论"/><div><button type="button" @click="editor=null">关闭</button><button type="submit" :disabled="!draft.trim()">保存 ⌘↵</button></div></form></div></Transition>
160164
<div v-if="menu" :data-platform="popupPlatform" class="annotation-ui annotation-menu" :style="{left:Math.min(menu.x,innerWidth-190)+'px',top:Math.min(menu.y,innerHeight-330)+'px'}" role="menu"><button v-for="name in menu.a.kind==='highlight'?['复制','分享','谷歌搜索','谷歌学术搜索','Hand over to AI','删除']:['复制','分享','Hand over to AI','修改','删除']" :key="name" role="menuitem" @click="action(name)">{{name}}</button></div>
161165
<div v-if="confirming" :data-platform="popupPlatform" class="annotation-ui annotation-modal"><section ref="confirmPopup" class="annotation-confirm annotation-delete-confirm" role="alertdialog" aria-modal="true" :aria-label="`删除这条${confirming.kind==='highlight'?'高亮':'批注'}?`" @keydown.tab.prevent="confirmPopup.querySelectorAll('button')[($event.target===confirmPopup.querySelector('button'))?1:0].focus()"><p>删除这条{{confirming.kind==='highlight'?'高亮':'批注'}}?</p><div class="annotation-confirm-actions"><button @click="confirming=null">取消</button><button class="annotation-confirm-destructive" @click="remove(confirming)">删除</button></div></section></div>
@@ -273,3 +277,12 @@ watch(()=>props.zoom,()=>{toolbar.value=false;updatePopupLayout();});
273277
.annotation-ui[data-platform="darwin"] .annotation-delete-confirm{background:var(--annotation-surface-solid);border:1px solid var(--annotation-border);border-radius:14px;box-shadow:inset 0 1px 0 var(--annotation-inner-edge),var(--annotation-shadow);color:var(--annotation-foreground);}
274278
.annotation-confirm-actions button:focus-visible{outline:3px solid #007aff80;outline-offset:2px;}
275279
</style>
280+
281+
<style>
282+
.annotation-toolbar{display:flex;flex-direction:column;align-items:stretch;gap:0;max-width:calc(100vw - 24px);box-sizing:border-box;}
283+
.annotation-toolbar-actions{display:flex;align-items:center;gap:4px;}
284+
.annotation-toolbar-comment{border-top:1px solid var(--annotation-divider,#8883);margin-top:8px;padding:12px 8px 6px;min-width:0;}
285+
.annotation-toolbar-comment p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;max-height:min(220px,35vh);overflow:auto;line-height:1.5;user-select:text;}
286+
.annotation-toolbar-with-comment{width:max-content;}
287+
.annotation-toolbar-comment{width:0;min-width:100%;box-sizing:border-box;}
288+
</style>

0 commit comments

Comments
 (0)