(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
98
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix being able to add notes to another people's highlights in popup and sidebar and fix unsafe html

scanash00 68d26ff3 ad4ebbf2

+66 -25
+6 -1
extension/src/components/popup/App.tsx
··· 707 707 <AnnotationCard 708 708 key={item.uri || item.id} 709 709 item={item} 710 + sessionDid={session?.did} 710 711 formatDate={formatDate} 711 712 onAddToCollection={() => openCollectionModal(item.uri || item.id || '')} 712 713 onConverted={loadAnnotations} ··· 717 718 <AnnotationCard 718 719 key={item.uri || item.id} 719 720 item={item} 721 + sessionDid={session?.did} 720 722 formatDate={formatDate} 721 723 onAddToCollection={() => openCollectionModal(item.uri || item.id || '')} 722 724 onConverted={loadAnnotations} ··· 968 970 969 971 function AnnotationCard({ 970 972 item, 973 + sessionDid, 971 974 formatDate, 972 975 onAddToCollection, 973 976 onConverted, 974 977 }: { 975 978 item: Annotation; 979 + sessionDid?: string; 976 980 formatDate: (d?: string) => string; 977 981 onAddToCollection?: () => void; 978 982 onConverted?: () => void; ··· 987 991 const selector = item.target?.selector; 988 992 const quote = selector?.exact || ''; 989 993 const isHighlight = (item as any).type === 'Highlight'; 994 + const isOwned = sessionDid && author.did === sessionDid; 990 995 const highlightColor = item.color || (isHighlight ? '#fbbf24' : 'var(--accent)'); 991 996 992 997 async function handleConvert() { ··· 1043 1048 </span> 1044 1049 )} 1045 1050 <div className="ml-auto flex items-center gap-0.5"> 1046 - {isHighlight && !showNoteInput && ( 1051 + {isHighlight && isOwned && !showNoteInput && ( 1047 1052 <button 1048 1053 onClick={(e) => { 1049 1054 e.stopPropagation();
+6 -1
extension/src/components/sidepanel/App.tsx
··· 621 621 <AnnotationCard 622 622 key={item.uri || item.id} 623 623 item={item} 624 + sessionDid={session?.did} 624 625 formatDate={formatDate} 625 626 onAddToCollection={() => openCollectionModal(item.uri || item.id || '')} 626 627 onConverted={loadAnnotations} ··· 631 632 <AnnotationCard 632 633 key={item.uri || item.id} 633 634 item={item} 635 + sessionDid={session?.did} 634 636 formatDate={formatDate} 635 637 onAddToCollection={() => openCollectionModal(item.uri || item.id || '')} 636 638 onConverted={loadAnnotations} ··· 868 870 869 871 function AnnotationCard({ 870 872 item, 873 + sessionDid, 871 874 formatDate, 872 875 onAddToCollection, 873 876 onConverted, 874 877 }: { 875 878 item: Annotation; 879 + sessionDid?: string; 876 880 formatDate: (d?: string) => string; 877 881 onAddToCollection?: () => void; 878 882 onConverted?: () => void; ··· 887 891 const selector = item.target?.selector; 888 892 const quote = selector?.exact || ''; 889 893 const isHighlight = (item as any).type === 'Highlight'; 894 + const isOwned = sessionDid && author.did === sessionDid; 890 895 const highlightColor = item.color || (isHighlight ? '#fbbf24' : 'var(--accent)'); 891 896 892 897 async function handleConvert() { ··· 941 946 </span> 942 947 )} 943 948 <div className="ml-auto flex items-center gap-0.5"> 944 - {isHighlight && !showNoteInput && ( 949 + {isHighlight && isOwned && !showNoteInput && ( 945 950 <button 946 951 onClick={(e) => { 947 952 e.stopPropagation();
+54 -23
extension/src/utils/overlay.ts
··· 214 214 215 215 const truncatedQuote = quoteText.length > 150 ? quoteText.slice(0, 150) + '...' : quoteText; 216 216 217 - composeModal.innerHTML = ` 218 - <div class="compose-header"> 219 - <span class="compose-title">New Annotation</span> 220 - <button class="compose-close">${Icons.close}</button> 221 - </div> 222 - <div class="compose-body"> 223 - <div class="inline-compose-quote">"${escapeHtml(truncatedQuote)}"</div> 224 - <textarea class="inline-compose-textarea" placeholder="Write your annotation..."></textarea> 225 - </div> 226 - <div class="compose-footer"> 227 - <button class="btn-cancel">Cancel</button> 228 - <button class="btn-submit">Post</button> 229 - </div> 230 - `; 217 + const header = document.createElement('div'); 218 + header.className = 'compose-header'; 219 + 220 + const titleSpan = document.createElement('span'); 221 + titleSpan.className = 'compose-title'; 222 + titleSpan.textContent = 'New Annotation'; 223 + header.appendChild(titleSpan); 224 + 225 + const closeBtn = document.createElement('button'); 226 + closeBtn.className = 'compose-close'; 227 + closeBtn.innerHTML = Icons.close; 228 + header.appendChild(closeBtn); 229 + 230 + composeModal.appendChild(header); 231 + 232 + const body = document.createElement('div'); 233 + body.className = 'compose-body'; 234 + 235 + const quoteDiv = document.createElement('div'); 236 + quoteDiv.className = 'inline-compose-quote'; 237 + quoteDiv.textContent = `"${truncatedQuote}"`; 238 + body.appendChild(quoteDiv); 239 + 240 + const textarea = document.createElement('textarea'); 241 + textarea.className = 'inline-compose-textarea'; 242 + textarea.placeholder = 'Write your annotation...'; 243 + body.appendChild(textarea); 244 + 245 + composeModal.appendChild(body); 246 + 247 + const footer = document.createElement('div'); 248 + footer.className = 'compose-footer'; 249 + 250 + const cancelBtn = document.createElement('button'); 251 + cancelBtn.className = 'btn-cancel'; 252 + cancelBtn.textContent = 'Cancel'; 253 + footer.appendChild(cancelBtn); 254 + 255 + const submitBtn = document.createElement('button'); 256 + submitBtn.className = 'btn-submit'; 257 + submitBtn.textContent = 'Post'; 258 + footer.appendChild(submitBtn); 259 + 260 + composeModal.appendChild(footer); 231 261 232 262 composeModal.querySelector('.compose-close')?.addEventListener('click', () => { 233 263 composeModal?.remove(); 234 264 composeModal = null; 235 265 }); 236 266 237 - composeModal.querySelector('.btn-cancel')?.addEventListener('click', () => { 267 + cancelBtn.addEventListener('click', () => { 238 268 composeModal?.remove(); 239 269 composeModal = null; 240 270 }); 241 - 242 - const textarea = composeModal.querySelector('.inline-compose-textarea') as HTMLTextAreaElement; 243 - const submitBtn = composeModal.querySelector('.btn-submit') as HTMLButtonElement; 244 271 245 272 submitBtn.addEventListener('click', async () => { 246 273 const text = textarea?.value.trim(); ··· 362 389 363 390 const toast = document.createElement('div'); 364 391 toast.className = `margin-toast ${type === 'success' ? 'toast-success' : ''}`; 365 - toast.innerHTML = ` 366 - <span class="toast-icon">${type === 'success' ? Icons.check : Icons.close}</span> 367 - <span>${message}</span> 368 - `; 392 + const iconSpan = document.createElement('span'); 393 + iconSpan.className = 'toast-icon'; 394 + iconSpan.innerHTML = type === 'success' ? Icons.check : Icons.close; 395 + toast.appendChild(iconSpan); 396 + 397 + const msgSpan = document.createElement('span'); 398 + msgSpan.textContent = message; 399 + toast.appendChild(msgSpan); 369 400 370 401 container.appendChild(toast); 371 402 ··· 844 875 }); 845 876 846 877 popoverEl.querySelectorAll('.btn-share').forEach((btn) => { 847 - btn.addEventListener('click', async (e) => { 878 + btn.addEventListener('click', async (_e) => { 848 879 const text = (btn as HTMLElement).getAttribute('data-text') || ''; 849 880 try { 850 881 await navigator.clipboard.writeText(text);