discord-html-transcripts-fix 1.3.0 → 1.6.0

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.
@@ -62,7 +62,8 @@ const defaultDicts = {
62
62
  edited: 'edited',
63
63
  editedAt: 'Edited at {time}',
64
64
  joined: 'joined the server',
65
- pinned: 'pinned a message to this channel.',
65
+ pinned: 'pinned {message} to this channel.',
66
+ pinnedLink: 'a message',
66
67
  boosted: 'boosted the server!',
67
68
  threadStarted: 'started a thread:',
68
69
  changedChannelName: 'changed the channel name:',
@@ -105,16 +106,17 @@ const defaultDicts = {
105
106
  filterHasImage: 'Has image',
106
107
  filterHasEmbed: 'Has embed',
107
108
  filterHasAttachment: 'Has attachment',
109
+ filterHasComponentV2: 'Has container',
108
110
  filterApply: 'Apply',
109
111
  filterReset: 'Reset',
110
- filterShowing: 'Showing {visible} of {total}',
111
112
  flags: 'Badges',
112
113
  },
113
114
  de: {
114
115
  edited: 'bearbeitet',
115
116
  editedAt: 'Bearbeitet am {time}',
116
117
  joined: 'ist dem Server beigetreten',
117
- pinned: 'hat eine Nachricht angepinnt.',
118
+ pinned: 'hat {message} in diesem Channel angepinnt.',
119
+ pinnedLink: 'eine Nachricht',
118
120
  boosted: 'hat den Server geboostet!',
119
121
  threadStarted: 'hat einen Thread gestartet:',
120
122
  changedChannelName: 'hat den Channel-Namen geändert:',
@@ -157,9 +159,9 @@ const defaultDicts = {
157
159
  filterHasImage: 'Mit Bild',
158
160
  filterHasEmbed: 'Mit Embed',
159
161
  filterHasAttachment: 'Mit Anhang',
162
+ filterHasComponentV2: 'Mit Container',
160
163
  filterApply: 'Anwenden',
161
164
  filterReset: 'Zurücksetzen',
162
- filterShowing: 'Zeige {visible} von {total}',
163
165
  flags: 'Abzeichen',
164
166
  },
165
167
  };
@@ -203,26 +205,173 @@ exports.mentionPopup = `(function(){
203
205
  function safeColor(c,fb){fb=fb||'#5865F2';return typeof c==='string' && /^#[0-9a-fA-F]{3,8}$/.test(c) ? c : fb;}
204
206
  function t(key){var d=DATA();var lang=d.lang||'en';var dict=(d.i18n&&d.i18n[lang])||(d.i18n&&d.i18n.en)||{};return dict[key]||key;}
205
207
  function fmt(iso){if(!iso)return '';try{var d=new Date(iso);return isNaN(d.getTime())?'':d.toLocaleString(DATA().lang==='de'?'de-DE':'en-US',{dateStyle:'medium',timeStyle:'short'});}catch(_){return '';}}
208
+ function copyBtn(text,label){if(text==null||text==='')return '';return '<button type="button" class="dht-popup-copy" data-copy="'+esc(text)+'" title="Copy '+esc(label||'value')+'" aria-label="Copy '+esc(label||'value')+'"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></button>';}
206
209
  var popup=null;
207
- function ensure(){if(popup)return popup;popup=document.createElement('div');popup.className='dht-popup';popup.setAttribute('role','dialog');popup.innerHTML='<div class="dht-popup-body"></div><button type="button" class="dht-popup-close" aria-label="Close">&times;</button>';document.body.appendChild(popup);popup.querySelector('.dht-popup-close').addEventListener('click',hide);document.addEventListener('click',function(e){if(!e||e.defaultPrevented)return;if(!popup||popup.style.display!=='block')return;if(popup.contains(e.target))return;var path=(typeof e.composedPath==='function')?e.composedPath():[];var onMention=false;for(var i=0;i<path.length;i++){var x=path[i];if(x&&x.getAttribute&&x.getAttribute('data-mention-id')){onMention=true;break;}}if(onMention)return;hide();});document.addEventListener('keydown',function(e){if(e.key==='Escape')hide();});window.addEventListener('resize',hide);return popup;}
210
+ function isTriggerInPath(path){
211
+ for(var i=0;i<path.length;i++){
212
+ var x=path[i];if(!x)continue;
213
+ if(x.getAttribute && x.getAttribute('data-mention-id'))return true;
214
+ if(x.tagName && typeof x.tagName==='string'){var tn=x.tagName.toLowerCase();if(tn==='discord-command')return true;}
215
+ if(x.classList){
216
+ if(x.classList.contains('discord-author-info')||x.classList.contains('discord-author-username')||x.classList.contains('discord-author-avatar')||x.classList.contains('discord-application-tag'))return true;
217
+ }
218
+ }
219
+ return false;
220
+ }
221
+ function ensure(){
222
+ if(popup)return popup;
223
+ popup=document.createElement('div');popup.className='dht-popup';popup.setAttribute('role','dialog');
224
+ popup.innerHTML='<div class="dht-popup-body"></div><button type="button" class="dht-popup-close" aria-label="Close">&times;</button>';
225
+ document.body.appendChild(popup);
226
+ popup.querySelector('.dht-popup-close').addEventListener('click',hide);
227
+ document.addEventListener('click',function(e){
228
+ if(!e||e.defaultPrevented)return;
229
+ if(!popup||popup.style.display!=='block')return;
230
+ if(popup.contains(e.target))return;
231
+ var path=(typeof e.composedPath==='function')?e.composedPath():[];
232
+ if(isTriggerInPath(path))return;
233
+ hide();
234
+ });
235
+ document.addEventListener('keydown',function(e){if(e.key==='Escape')hide();});
236
+ window.addEventListener('resize',hide);
237
+ return popup;
238
+ }
208
239
  function hide(){if(popup)popup.style.display='none';}
209
240
  function showAt(el,html){var p=ensure();p.querySelector('.dht-popup-body').innerHTML=html;p.style.display='block';var r=el.getBoundingClientRect();var pr=p.getBoundingClientRect();var pad=8;var top=r.bottom+6;var left=r.left;if(top+pr.height>window.innerHeight-pad)top=Math.max(pad,r.top-pr.height-6);if(left+pr.width>window.innerWidth-pad)left=Math.max(pad,window.innerWidth-pr.width-pad);if(left<pad)left=pad;p.style.top=top+'px';p.style.left=left+'px';}
210
241
  function rolePill(role){var c=safeColor(role&&role.color);return '<span class="dht-role-pill" style="--dht-role:'+c+';">'+esc(role&&role.name?role.name:'@role')+'</span>';}
211
242
  function flagBadgeHtml(flagName){try{var dh=globalThis.__DHT_FLAG_BADGES__||{};var b=dh[flagName];if(b)return '<span class="dht-flag" title="'+esc(b.label)+'" style="color:'+safeColor(b.color)+';">'+esc(b.emoji)+'</span>';}catch(_){};return '<span class="dht-flag" title="'+esc(flagName)+'">'+esc(flagName)+'</span>';}
212
- function userHtml(id){var d=DATA();var u=(d.users&&d.users[id])||null;var p=(d.profiles&&d.profiles[id])||null;if(!u&&!p)return '<i>?</i><div class="dht-popup-id">ID: <code>'+esc(id)+'</code></div>';var name=(u&&(u.displayName||u.username))||(p&&p.author)||'Unknown';var username=u&&u.username?'@'+u.username:'';var avatar=(u&&u.avatar)||(p&&p.avatar)||'';var color=safeColor((u&&u.bannerColor)||(p&&p.roleColor),'#dbdee1');var bot=(u&&u.bot)||(p&&p.bot);var verifiedBot=u&&u.verifiedBot;var roles=u&&Array.isArray(u.roles)&&u.roles.length?u.roles.map(rolePill).join(''):'';var flags=u&&Array.isArray(u.flags)&&u.flags.length?u.flags.filter(function(f){return f!=='VerifiedBot';}).map(flagBadgeHtml).join(''):'';var joinedAt=u&&u.joinedAt?fmt(u.joinedAt):'';var createdAt=u&&u.createdAt?fmt(u.createdAt):'';var labels=DATA().i18n&&(DATA().i18n[DATA().lang]||DATA().i18n.en)||{};var botBadge=bot?(' <span class="dht-popup-badge">'+esc(t('botBadge'))+(verifiedBot?' ✓':'')+'</span>'):'';return ''+'<div class="dht-popup-head">'+(avatar?'<img class="dht-popup-avatar" src="'+esc(avatar)+'" alt="">':'<div class="dht-popup-avatar dht-popup-avatar--ph"></div>')+'<div class="dht-popup-headtext"><div class="dht-popup-name" style="color:'+color+';">'+esc(name)+botBadge+'</div>'+(username?'<div class="dht-popup-sub">'+esc(username)+'</div>':'')+'</div></div>'+(flags?'<div class="dht-popup-section"><div class="dht-popup-label">'+esc(t('flags'))+'</div><div class="dht-popup-flags">'+flags+'</div></div>':'')+(roles?'<div class="dht-popup-section"><div class="dht-popup-label">Roles</div><div class="dht-popup-roles">'+roles+'</div></div>':'')+(joinedAt?'<div class="dht-popup-row"><span class="dht-popup-label">Server</span><span>'+esc(joinedAt)+'</span></div>':'')+(createdAt?'<div class="dht-popup-row"><span class="dht-popup-label">Account</span><span>'+esc(createdAt)+'</span></div>':'')+'<div class="dht-popup-id">ID: <code>'+esc(id)+'</code></div>';}
213
- function roleHtml(id){var r=(DATA().roles||{})[id];if(!r)return '<i>?</i><div class="dht-popup-id">ID: <code>'+esc(id)+'</code></div>';var color=safeColor(r.color,'#dbdee1');return '<div class="dht-popup-head"><div class="dht-popup-rolemark" style="background:'+color+';"></div><div class="dht-popup-headtext"><div class="dht-popup-name" style="color:'+color+';">@'+esc(r.name)+'</div>'+(r.color?'<div class="dht-popup-sub"><code>'+esc(r.color)+'</code></div>':'')+'</div></div>'+(r.memberCount!=null?'<div class="dht-popup-row"><span class="dht-popup-label">Members</span><span>'+esc(r.memberCount)+'</span></div>':'')+(r.position!=null?'<div class="dht-popup-row"><span class="dht-popup-label">Position</span><span>'+esc(r.position)+'</span></div>':'')+(r.hoist?'<div class="dht-popup-row"><span class="dht-popup-label">Hoisted</span><span>Yes</span></div>':'')+(r.mentionable?'<div class="dht-popup-row"><span class="dht-popup-label">Mentionable</span><span>Yes</span></div>':'')+(r.managed?'<div class="dht-popup-row"><span class="dht-popup-label">Managed</span><span>Bot/Integration</span></div>':'')+'<div class="dht-popup-id">ID: <code>'+esc(id)+'</code></div>';}
214
- function channelHtml(id){var c=(DATA().channels||{})[id];if(!c)return '<i>Channel</i><div class="dht-popup-id">ID: <code>'+esc(id)+'</code></div>';return '<div class="dht-popup-head"><div class="dht-popup-rolemark" style="background:#5865F2;">#</div><div class="dht-popup-headtext"><div class="dht-popup-name">#'+esc(c.name)+'</div>'+(c.parent?'<div class="dht-popup-sub">in '+esc(c.parent)+'</div>':'')+'</div></div>'+(c.topic?'<div class="dht-popup-section"><div class="dht-popup-label">Topic</div><div>'+esc(c.topic)+'</div></div>':'')+(c.nsfw?'<div class="dht-popup-row"><span class="dht-popup-label">NSFW</span><span>Yes</span></div>':'')+'<div class="dht-popup-id">ID: <code>'+esc(id)+'</code></div>';}
215
- document.addEventListener('click',function(e){if(!e||e.defaultPrevented)return;
243
+ function userHtml(id){
244
+ var d=DATA();
245
+ var u=(d.users&&d.users[id])||null;
246
+ var p=(d.profiles&&d.profiles[id])||null;
247
+ if(!u&&!p)return '<i>?</i><div class="dht-popup-id">ID: <code>'+esc(id)+'</code>'+copyBtn(id,'user ID')+'</div>';
248
+ var name=(u&&(u.displayName||u.username))||(p&&p.author)||'Unknown';
249
+ var username=u&&u.username?'@'+u.username:'';
250
+ var avatar=(u&&u.avatar)||(p&&p.avatar)||'';
251
+ var color=safeColor((u&&u.bannerColor)||(p&&p.roleColor),'#dbdee1');
252
+ var bot=(u&&u.bot)||(p&&p.bot);
253
+ var verifiedBot=u&&u.verifiedBot;
254
+ var roles=u&&Array.isArray(u.roles)&&u.roles.length?u.roles.map(rolePill).join(''):'';
255
+ var flags=u&&Array.isArray(u.flags)&&u.flags.length?u.flags.filter(function(f){return f!=='VerifiedBot';}).map(flagBadgeHtml).join(''):'';
256
+ var joinedAt=u&&u.joinedAt?fmt(u.joinedAt):'';
257
+ var createdAt=u&&u.createdAt?fmt(u.createdAt):'';
258
+ var botBadge=bot?(' <span class="dht-popup-badge">'+esc(t('botBadge'))+(verifiedBot?' ✓':'')+'</span>'):'';
259
+ return ''
260
+ +'<div class="dht-popup-head">'
261
+ +(avatar?'<img class="dht-popup-avatar" src="'+esc(avatar)+'" alt="">':'<div class="dht-popup-avatar dht-popup-avatar--ph"></div>')
262
+ +'<div class="dht-popup-headtext">'
263
+ +'<div class="dht-popup-name" style="color:'+color+';">'+esc(name)+botBadge+copyBtn(name,'name')+'</div>'
264
+ +(username?'<div class="dht-popup-sub">'+esc(username)+copyBtn(username,'username')+'</div>':'')
265
+ +'</div>'
266
+ +'</div>'
267
+ +(flags?'<div class="dht-popup-section"><div class="dht-popup-label">'+esc(t('flags'))+'</div><div class="dht-popup-flags">'+flags+'</div></div>':'')
268
+ +(roles?'<div class="dht-popup-section"><div class="dht-popup-label">Roles</div><div class="dht-popup-roles">'+roles+'</div></div>':'')
269
+ +(joinedAt?'<div class="dht-popup-row"><span class="dht-popup-label">Server</span><span>'+esc(joinedAt)+'</span></div>':'')
270
+ +(createdAt?'<div class="dht-popup-row"><span class="dht-popup-label">Account</span><span>'+esc(createdAt)+'</span></div>':'')
271
+ +'<div class="dht-popup-id">ID: <code>'+esc(id)+'</code>'+copyBtn(id,'user ID')+'</div>';
272
+ }
273
+ function roleHtml(id){
274
+ var r=(DATA().roles||{})[id];
275
+ if(!r)return '<i>?</i><div class="dht-popup-id">ID: <code>'+esc(id)+'</code>'+copyBtn(id,'role ID')+'</div>';
276
+ var color=safeColor(r.color,'#dbdee1');
277
+ return '<div class="dht-popup-head"><div class="dht-popup-rolemark" style="background:'+color+';"></div><div class="dht-popup-headtext"><div class="dht-popup-name" style="color:'+color+';">@'+esc(r.name)+copyBtn('@'+r.name,'role name')+'</div>'+(r.color?'<div class="dht-popup-sub"><code>'+esc(r.color)+'</code>'+copyBtn(r.color,'color')+'</div>':'')+'</div></div>'
278
+ +(r.memberCount!=null?'<div class="dht-popup-row"><span class="dht-popup-label">Members</span><span>'+esc(r.memberCount)+'</span></div>':'')
279
+ +(r.position!=null?'<div class="dht-popup-row"><span class="dht-popup-label">Position</span><span>'+esc(r.position)+'</span></div>':'')
280
+ +(r.hoist?'<div class="dht-popup-row"><span class="dht-popup-label">Hoisted</span><span>Yes</span></div>':'')
281
+ +(r.mentionable?'<div class="dht-popup-row"><span class="dht-popup-label">Mentionable</span><span>Yes</span></div>':'')
282
+ +(r.managed?'<div class="dht-popup-row"><span class="dht-popup-label">Managed</span><span>Bot/Integration</span></div>':'')
283
+ +'<div class="dht-popup-id">ID: <code>'+esc(id)+'</code>'+copyBtn(id,'role ID')+'</div>';
284
+ }
285
+ function channelHtml(id){
286
+ var c=(DATA().channels||{})[id];
287
+ if(!c)return '<i>Channel</i><div class="dht-popup-id">ID: <code>'+esc(id)+'</code>'+copyBtn(id,'channel ID')+'</div>';
288
+ return '<div class="dht-popup-head"><div class="dht-popup-rolemark" style="background:#5865F2;">#</div><div class="dht-popup-headtext"><div class="dht-popup-name">#'+esc(c.name)+copyBtn('#'+c.name,'channel')+'</div>'+(c.parent?'<div class="dht-popup-sub">in '+esc(c.parent)+'</div>':'')+'</div></div>'
289
+ +(c.topic?'<div class="dht-popup-section"><div class="dht-popup-label">Topic</div><div>'+esc(c.topic)+'</div></div>':'')
290
+ +(c.nsfw?'<div class="dht-popup-row"><span class="dht-popup-label">NSFW</span><span>Yes</span></div>':'')
291
+ +'<div class="dht-popup-id">ID: <code>'+esc(id)+'</code>'+copyBtn(id,'channel ID')+'</div>';
292
+ }
293
+ function slashHtml(el){
294
+ var cmd=el.getAttribute('data-slash-cmd')||el.getAttribute('command')||'/?';
295
+ var by=el.getAttribute('data-slash-by')||'';
296
+ var byId=el.getAttribute('data-slash-by-id')||el.getAttribute('profile')||'';
297
+ var optsJson=el.getAttribute('data-slash-options')||'[]';
298
+ var opts;
299
+ try{opts=JSON.parse(optsJson);}catch(_){opts=[];}
300
+ if(!Array.isArray(opts))opts=[];
301
+ var paramsHtml=opts.length
302
+ ? '<div class="dht-popup-section"><div class="dht-popup-label">Parameters</div><div class="dht-slash-params">'
303
+ +opts.map(function(o){return '<div class="dht-slash-param"><span class="dht-slash-param-name">'+esc(o.name)+'</span><span class="dht-slash-param-val">'+esc(o.value)+'</span>'+copyBtn(o.value,o.name)+'</div>';}).join('')
304
+ +'</div></div>'
305
+ : '<div class="dht-popup-sub" style="margin-top:8px">No parameters</div>';
306
+ return '<div class="dht-popup-head">'
307
+ +'<div class="dht-popup-rolemark" style="background:#5865F2;font-family:Consolas,Menlo,monospace;font-size:18px;">/</div>'
308
+ +'<div class="dht-popup-headtext">'
309
+ +'<div class="dht-popup-name" style="font-family:Consolas,Menlo,monospace;color:#5865F2">'+esc(cmd)+copyBtn(cmd,'command')+'</div>'
310
+ +(by?'<div class="dht-popup-sub">used by '+esc(by)+'</div>':'')
311
+ +'</div>'
312
+ +'</div>'
313
+ +paramsHtml
314
+ +(byId?'<div class="dht-popup-id">User ID: <code>'+esc(byId)+'</code>'+copyBtn(byId,'user ID')+'</div>':'');
315
+ }
316
+ function findAncestor(path,pred){for(var i=0;i<path.length;i++){if(pred(path[i]))return path[i];}return null;}
317
+ function isAuthorPath(path){
318
+ for(var i=0;i<path.length;i++){
319
+ var x=path[i];if(!x||!x.classList)continue;
320
+ if(x.classList.contains('discord-author-info')||x.classList.contains('discord-author-username')||x.classList.contains('discord-author-avatar')||x.classList.contains('discord-application-tag'))return true;
321
+ }
322
+ return false;
323
+ }
324
+ // Copy-button delegated handler (any .dht-popup-copy click — inside popup or anywhere else)
325
+ document.addEventListener('click',function(e){
326
+ var t=e.target;
327
+ if(!t)return;
328
+ var btn=null;
329
+ if(t.closest)btn=t.closest('.dht-popup-copy');
330
+ if(!btn)return;
331
+ var text=btn.getAttribute('data-copy')||'';
332
+ if(!text)return;
333
+ e.preventDefault();e.stopPropagation();
334
+ function done(){btn.classList.add('dht-popup-copy--ok');setTimeout(function(){btn.classList.remove('dht-popup-copy--ok');},900);}
335
+ try{
336
+ if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(text).then(done,function(){fallback();});}
337
+ else fallback();
338
+ }catch(_){fallback();}
339
+ function fallback(){try{var ta=document.createElement('textarea');ta.value=text;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);done();}catch(_2){}}
340
+ },true);
341
+ // Main trigger handler
342
+ document.addEventListener('click',function(e){
343
+ if(!e||e.defaultPrevented)return;
216
344
  var path=(typeof e.composedPath==='function')?e.composedPath():[];
217
345
  if(!path.length){var n=e.target;while(n){path.push(n);n=n.parentNode||(n.host);}}
218
- var el=null;
219
- for(var i=0;i<path.length;i++){var x=path[i];if(x&&x.getAttribute&&x.getAttribute('data-mention-id')){el=x;break;}}
220
- if(!el)return;
221
- var tp=el.getAttribute('data-mention-type');var id=el.getAttribute('data-mention-id');
222
- if(!tp||!id)return;
223
- e.preventDefault();e.stopPropagation();
224
- var html='';if(tp==='user')html=userHtml(id);else if(tp==='role')html=roleHtml(id);else if(tp==='channel')html=channelHtml(id);else return;
225
- showAt(el,html);
346
+ // 0) skip if click is on a copy button (handled in capture phase above)
347
+ for(var z=0;z<path.length;z++){var zz=path[z];if(zz&&zz.classList&&zz.classList.contains('dht-popup-copy'))return;}
348
+ // 1) explicit mention pills with data-mention-id (users/roles/channels via markdown)
349
+ var pill=findAncestor(path,function(x){return x&&x.getAttribute&&x.getAttribute('data-mention-id');});
350
+ if(pill){
351
+ var tp=pill.getAttribute('data-mention-type');var id=pill.getAttribute('data-mention-id');
352
+ if(!tp||!id)return;
353
+ e.preventDefault();e.stopPropagation();
354
+ var html='';
355
+ if(tp==='user')html=userHtml(id);
356
+ else if(tp==='role')html=roleHtml(id);
357
+ else if(tp==='channel')html=channelHtml(id);
358
+ else return;
359
+ showAt(pill,html);return;
360
+ }
361
+ // 2) slash command pill — discord-command web component
362
+ var cmdEl=findAncestor(path,function(x){return x&&x.tagName&&typeof x.tagName==='string'&&x.tagName.toLowerCase()==='discord-command';});
363
+ if(cmdEl){
364
+ e.preventDefault();e.stopPropagation();
365
+ showAt(cmdEl,slashHtml(cmdEl));return;
366
+ }
367
+ // 3) author area inside <discord-message> shadow DOM (avatar, name, BOT/APP tag)
368
+ if(isAuthorPath(path)){
369
+ var msgEl=findAncestor(path,function(x){return x&&x.tagName&&typeof x.tagName==='string'&&x.tagName.toLowerCase()==='discord-message';});
370
+ if(msgEl){
371
+ var uid=msgEl.getAttribute('profile')||msgEl.getAttribute('data-author-id');
372
+ if(uid){e.preventDefault();e.stopPropagation();showAt(msgEl,userHtml(uid));return;}
373
+ }
374
+ }
226
375
  });
227
376
  })();`;
228
377
 
@@ -272,8 +421,35 @@ exports.mentionPopupStyles = `
272
421
  .dht-popup-label{color:#949ba4;font-size:11px;text-transform:uppercase;letter-spacing:.04em;font-weight:700}
273
422
  .dht-popup-roles{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
274
423
  .dht-role-pill{display:inline-flex;align-items:center;background:color-mix(in srgb,var(--dht-role) 18%,transparent);color:var(--dht-role);border:1px solid color-mix(in srgb,var(--dht-role) 40%,transparent);padding:1px 8px;border-radius:999px;font-size:11px;line-height:1.5}
275
- .dht-popup-id{margin-top:10px;padding-top:8px;border-top:1px solid #2b2d31;font-size:11px;color:#6e727a}
424
+ .dht-popup-id{margin-top:10px;padding-top:8px;border-top:1px solid #2b2d31;font-size:11px;color:#6e727a;display:flex;align-items:center;gap:6px}
276
425
  .dht-popup-id code{background:rgba(255,255,255,.05);padding:1px 4px;border-radius:3px}
426
+ /* Copy buttons inside the popup */
427
+ .dht-popup-copy{display:inline-flex;align-items:center;justify-content:center;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.06);color:#b5bac1;border-radius:4px;width:22px;height:22px;padding:0;margin-left:6px;cursor:pointer;vertical-align:middle;transition:background .15s ease,color .15s ease,border-color .15s ease;flex:none}
428
+ .dht-popup-copy:hover{background:rgba(255,255,255,.1);color:#fff}
429
+ .dht-popup-copy:focus-visible{outline:2px solid #5865F2;outline-offset:2px}
430
+ .dht-popup-copy--ok{background:#3ba55d!important;color:#fff!important;border-color:#3ba55d!important}
431
+ .dht-popup-copy svg{display:block}
432
+ .dht-popup-name{display:flex;align-items:center;gap:0;flex-wrap:wrap}
433
+ .dht-popup-sub{display:flex;align-items:center;gap:0;flex-wrap:wrap}
434
+ /* Slash command popup — parameter list */
435
+ .dht-slash-params{display:flex;flex-direction:column;gap:4px;margin-top:4px}
436
+ .dht-slash-param{display:flex;align-items:center;gap:6px;background:rgba(255,255,255,.04);padding:4px 8px;border-radius:4px;font-size:12px}
437
+ .dht-slash-param-name{color:#949ba4;font-family:Consolas,Menlo,monospace}
438
+ .dht-slash-param-name::after{content:":"}
439
+ .dht-slash-param-val{color:#dbdee1;font-family:Consolas,Menlo,monospace;word-break:break-all;flex:1}
440
+ /* Make message author area look clickable (skyra shadow DOM elements bubble into the host) */
441
+ discord-message .discord-author-username,
442
+ discord-message .discord-author-info,
443
+ discord-message [slot="author-image"],
444
+ discord-message .discord-author-avatar,
445
+ discord-message .discord-application-tag{cursor:pointer}
446
+ discord-message .discord-author-username:hover,
447
+ discord-message .discord-author-info:hover{text-decoration:underline}
448
+ /* Slash command pill — clickable affordance */
449
+ discord-command.dht-slash-clickable,
450
+ discord-command{cursor:pointer;transition:filter .15s ease}
451
+ discord-command.dht-slash-clickable:hover,
452
+ discord-command:hover{filter:brightness(1.2)}
277
453
  `;
278
454
 
279
455
  // ===========================================================================
@@ -283,39 +459,13 @@ exports.backToTop = `(function(){var btn=document.createElement('button');btn.cl
283
459
  exports.transcriptUtils = `.dht-back-top{position:fixed;bottom:24px;right:24px;width:44px;height:44px;border-radius:50%;border:none;background:#5865F2;color:#fff;font-size:16px;cursor:pointer;box-shadow:0 6px 18px rgba(0,0,0,.45);opacity:0;pointer-events:none;transition:opacity .2s ease,transform .2s ease;z-index:9999}.dht-back-top:hover{transform:translateY(-2px)}`;
284
460
 
285
461
  // ===========================================================================
286
- // Search bar — Ctrl+F replacement: live highlight, x of n matches, prev/next nav
462
+ // Search — handled inside the unified TOC panel (see exports.toc).
463
+ // These exports stay for backwards compatibility with anyone wiring them
464
+ // individually; both are inert (no DOM injection). Search-hit styles still
465
+ // live here because the TOC IIFE creates <mark class="dht-hit"> nodes.
287
466
  // ===========================================================================
288
- exports.searchBar = `(function(){
289
- var DATA=globalThis.$discordMessage||{};
290
- var dict=(DATA.i18n&&(DATA.i18n[DATA.lang]||DATA.i18n.en))||{};
291
- var bar=document.createElement('div');
292
- bar.className='dht-search';
293
- function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c];});}
294
- bar.innerHTML='<input type="search" class="dht-search-input" placeholder="'+esc(dict.searchPlaceholder||'Search…')+'"><span class="dht-search-counter">0</span><button class="dht-search-btn" data-act="prev" title="Previous">&#x25B2;</button><button class="dht-search-btn" data-act="next" title="Next">&#x25BC;</button><button class="dht-search-btn" data-act="close" title="Close">&times;</button>';
295
- document.body.appendChild(bar);
296
- var input=bar.querySelector('.dht-search-input');
297
- var counter=bar.querySelector('.dht-search-counter');
298
- var matches=[],idx=-1;
299
- function clear(){matches.forEach(function(m){var p=m.parentNode;if(!p)return;var tn=document.createTextNode(m.textContent);p.replaceChild(tn,m);p.normalize();});matches=[];idx=-1;counter.textContent='0';}
300
- function walk(node,re,acc){if(node.nodeType===3){var text=node.nodeValue;if(!re.test(text))return;re.lastIndex=0;var frag=document.createDocumentFragment();var last=0;var m;while((m=re.exec(text))!==null){if(m.index>last)frag.appendChild(document.createTextNode(text.slice(last,m.index)));var span=document.createElement('mark');span.className='dht-hit';span.textContent=m[0];frag.appendChild(span);acc.push(span);last=re.lastIndex;}if(last<text.length)frag.appendChild(document.createTextNode(text.slice(last)));node.parentNode.replaceChild(frag,node);}else if(node.nodeType===1){if(node.classList&&(node.classList.contains('dht-search')||node.tagName==='SCRIPT'||node.tagName==='STYLE'))return;for(var i=node.childNodes.length-1;i>=0;i--)walk(node.childNodes[i],re,acc);}}
301
- function search(q){clear();if(!q||q.length<2)return;var re=new RegExp(q.replace(/[.*+?^\${}()|[\\]\\\\]/g,function(c){return '\\\\'+c;}),'gi');walk(document.body,re,matches);if(matches.length){idx=0;goTo();}counter.textContent=matches.length?(idx+1)+' '+(dict.ofMatches||'of')+' '+matches.length:(dict.noMatches||'no matches');}
302
- function goTo(){matches.forEach(function(m,i){m.classList.toggle('dht-hit--active',i===idx);});if(idx>=0&&matches[idx]){matches[idx].scrollIntoView({behavior:'smooth',block:'center'});counter.textContent=(idx+1)+' '+(dict.ofMatches||'of')+' '+matches.length;}}
303
- input.addEventListener('input',function(){search(input.value);});
304
- bar.addEventListener('click',function(e){var t=e.target;var act=t.getAttribute&&t.getAttribute('data-act');if(act==='next'){if(matches.length){idx=(idx+1)%matches.length;goTo();}}else if(act==='prev'){if(matches.length){idx=(idx-1+matches.length)%matches.length;goTo();}}else if(act==='close'){bar.classList.remove('dht-search--open');input.value='';clear();}});
305
- document.addEventListener('keydown',function(e){
306
- if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='f'){
307
- // Skip if user is currently typing in the filter panel
308
- var ae=document.activeElement;
309
- if(ae && ae.closest && ae.closest('.dht-filter'))return;
310
- e.preventDefault();bar.classList.add('dht-search--open');input.focus();input.select();
311
- }else if(e.key==='Escape'&&bar.classList.contains('dht-search--open')){
312
- bar.classList.remove('dht-search--open');input.value='';clear();
313
- }else if(e.key==='Enter'&&document.activeElement===input){
314
- if(matches.length){idx=(idx+(e.shiftKey?-1:1)+matches.length)%matches.length;goTo();}
315
- }
316
- });
317
- })();`;
318
- exports.searchBarStyles = `.dht-search{position:fixed;top:16px;right:16px;background:#1e1f22;border:1px solid #2b2d31;border-radius:8px;padding:6px 8px;display:none;align-items:center;gap:6px;z-index:99998;box-shadow:0 6px 18px rgba(0,0,0,.5);font-family:"gg sans",sans-serif;color:#dbdee1}.dht-search--open{display:flex}.dht-search-input{background:#2b2d31;color:#dbdee1;border:1px solid #2b2d31;border-radius:4px;padding:4px 8px;font-size:13px;width:220px;outline:none}.dht-search-input:focus{border-color:#5865F2}.dht-search-counter{color:#949ba4;font-size:12px;min-width:60px}.dht-search-btn{background:none;border:none;color:#dbdee1;cursor:pointer;padding:4px 6px;border-radius:4px;font-size:12px}.dht-search-btn:hover{background:rgba(255,255,255,.06)}.dht-hit{background:rgba(255,228,0,.35);color:inherit;border-radius:2px}.dht-hit--active{background:rgba(255,140,0,.7);color:#000}`;
467
+ exports.searchBar = '/* searchBar: merged into TOC */';
468
+ exports.searchBarStyles = `.dht-hit{background:rgba(255,228,0,.35);color:inherit;border-radius:2px}.dht-hit--active{background:rgba(255,140,0,.7);color:#000}`;
319
469
 
320
470
  // ===========================================================================
321
471
  // Image lightbox — click images to fullscreen, arrow keys to navigate
@@ -341,24 +491,166 @@ exports.toc = `(function(){
341
491
  var DATA=globalThis.$discordMessage||{};
342
492
  var dict=(DATA.i18n&&(DATA.i18n[DATA.lang]||DATA.i18n.en))||{};
343
493
  var users=DATA.users||DATA.profiles||{};
344
- var ids=Object.keys(users).filter(function(x){return /^\\d+$/.test(x);});
345
- if(ids.length===0)return;
494
+ var roles=DATA.roles||{};
495
+ var userIds=Object.keys(users).filter(function(x){return /^\\d+$/.test(x);}).sort(function(a,b){var na=(users[a].displayName||users[a].author||users[a].username||'').toLowerCase();var nb=(users[b].displayName||users[b].author||users[b].username||'').toLowerCase();return na.localeCompare(nb);});
496
+ var roleIds=Object.keys(roles).filter(function(x){return /^\\d+$/.test(x);});
497
+
346
498
  function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c];});}
347
499
  function safeColor(c,fb){fb=fb||'#dbdee1';return typeof c==='string' && /^#[0-9a-fA-F]{3,8}$/.test(c) ? c : fb;}
348
500
  function safeUrl(u){if(typeof u!=='string'||!u)return '';if(/^https?:\\/\\//i.test(u))return u;return '';}
349
501
  function firstMessageOf(uid){var m=document.querySelector('discord-message[profile="'+uid+'"],[data-profile="'+uid+'"]');return m?m.id:null;}
502
+ function parseId(v){if(!v)return null;var t=v.trim();if(/^\\d{15,21}$/.test(t))return t;var m=t.match(/\\((\\d{15,21})\\)/);return m?m[1]:null;}
503
+ function authorHasRole(authorId,roleId){var u=users[authorId];if(!u||!Array.isArray(u.roles))return false;for(var i=0;i<u.roles.length;i++){if(u.roles[i].id===roleId)return true;}return false;}
504
+
350
505
  var btn=document.createElement('button');btn.className='dht-toc-toggle';btn.type='button';btn.title=dict.tocTitle||'Participants';btn.setAttribute('aria-label',dict.tocTitle||'Participants');btn.innerHTML='&#x2630;';document.body.appendChild(btn);
506
+
351
507
  var panel=document.createElement('aside');panel.className='dht-toc';panel.setAttribute('aria-hidden','true');
352
- var html='<header><h3>'+esc(dict.tocTitle||'Participants')+'</h3><button class="dht-toc-close" aria-label="Close">&times;</button></header><ul role="list">';
353
- ids.sort(function(a,b){var ua=users[a],ub=users[b];var na=(ua.displayName||ua.author||ua.username||'').toLowerCase();var nb=(ub.displayName||ub.author||ub.username||'').toLowerCase();return na.localeCompare(nb);}).forEach(function(id){var u=users[id];var name=u.displayName||u.author||u.username||'Unknown';var avatar=safeUrl(u.avatar||'');var color=safeColor(u.bannerColor||u.roleColor,'#dbdee1');html+='<li data-uid="'+esc(id)+'" tabindex="0" role="button">'+(avatar?'<img src="'+esc(avatar)+'" alt="" loading="lazy">':'<span class="dht-toc-ph"></span>')+'<span class="dht-toc-name" style="color:'+color+'">'+esc(name)+'</span></li>';});
354
- html+='</ul>';panel.innerHTML=html;document.body.appendChild(panel);
508
+
509
+ // Header
510
+ var headerHtml='<header><h3>'+esc(dict.tocTitle||'Participants')+'</h3><button class="dht-toc-close" type="button" aria-label="Close">&times;</button></header>';
511
+ // Search field — keyword highlight + participant name filter
512
+ var searchHtml='<div class="dht-toc-search-wrap"><span class="dht-toc-search-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg></span><input type="search" class="dht-toc-search" placeholder="'+esc(dict.searchPlaceholder||'Search…')+'"><span class="dht-toc-counter" aria-live="polite"></span><button class="dht-toc-srch-btn" type="button" data-act="prev" title="Previous" aria-label="Previous match">&#x25B2;</button><button class="dht-toc-srch-btn" type="button" data-act="next" title="Next" aria-label="Next match">&#x25BC;</button></div>';
513
+ // Participants list — collapsible, open by default
514
+ var listHtml='<details class="dht-toc-section" open><summary>'+esc(dict.tocTitle||'Participants')+'</summary><ul class="dht-toc-list" role="list">';
515
+ if(userIds.length>0){
516
+ userIds.forEach(function(id){
517
+ var u=users[id];var name=u.displayName||u.author||u.username||'Unknown';var avatar=safeUrl(u.avatar||'');var color=safeColor(u.bannerColor||u.roleColor,'#dbdee1');
518
+ listHtml+='<li data-uid="'+esc(id)+'" data-name="'+esc(String(name).toLowerCase())+'" tabindex="0" role="button">'+(avatar?'<img src="'+esc(avatar)+'" alt="" loading="lazy">':'<span class="dht-toc-ph"></span>')+'<span class="dht-toc-name" style="color:'+color+'">'+esc(name)+'</span></li>';
519
+ });
520
+ }
521
+ listHtml+='</ul></details>';
522
+ // Filter form — replaces the standalone bottom-right magnifying glass. Open by default.
523
+ var filterHtml='<details class="dht-toc-section dht-toc-filter-details" open><summary>'+esc(dict.filterPanel||'Filter')+'</summary><div class="dht-toc-filter-body">';
524
+ filterHtml+='<label class="dht-f-field">'+esc(dict.filterAuthor||'Author')+'<input list="dht-f-authors" class="dht-f-author" placeholder="@user / ID / name"></label>';
525
+ filterHtml+='<datalist id="dht-f-authors">'+userIds.map(function(id){var u=users[id];return '<option value="'+esc(u.displayName||u.author||u.username||'')+' ('+esc(id)+')">';}).join('')+'</datalist>';
526
+ filterHtml+='<label class="dht-f-field">'+esc(dict.filterRole||'Role')+'<input list="dht-f-roles" class="dht-f-role" placeholder="@role / ID / name"></label>';
527
+ filterHtml+='<datalist id="dht-f-roles">'+roleIds.map(function(id){var r=roles[id];return '<option value="'+esc(r.name||'')+' ('+esc(id)+')">';}).join('')+'</datalist>';
528
+ filterHtml+='<div class="dht-f-grid"><label class="dht-f-field">'+esc(dict.filterFrom||'From')+'<input type="datetime-local" class="dht-f-from"></label><label class="dht-f-field">'+esc(dict.filterTo||'To')+'<input type="datetime-local" class="dht-f-to"></label></div>';
529
+ filterHtml+='<div class="dht-f-checks">';
530
+ filterHtml+='<label><input type="checkbox" class="dht-f-pinned"> '+esc(dict.filterPinned||'Pinned only')+'</label>';
531
+ filterHtml+='<label><input type="checkbox" class="dht-f-image"> '+esc(dict.filterHasImage||'Has image')+'</label>';
532
+ filterHtml+='<label><input type="checkbox" class="dht-f-embed"> '+esc(dict.filterHasEmbed||'Has embed')+'</label>';
533
+ filterHtml+='<label><input type="checkbox" class="dht-f-attach"> '+esc(dict.filterHasAttachment||'Has attachment')+'</label>';
534
+ filterHtml+='<label><input type="checkbox" class="dht-f-v2"> '+esc(dict.filterHasComponentV2||'Has container')+'</label>';
535
+ filterHtml+='</div>';
536
+ filterHtml+='<div class="dht-f-actions"><button type="button" class="dht-f-apply">'+esc(dict.filterApply||'Apply')+'</button><button type="button" class="dht-f-reset">'+esc(dict.filterReset||'Reset')+'</button></div>';
537
+ filterHtml+='</div></details>';
538
+
539
+ panel.innerHTML=headerHtml+searchHtml+listHtml+filterHtml;document.body.appendChild(panel);
540
+
355
541
  function syncToggleVisibility(open){btn.style.opacity=open?'0':'1';btn.style.pointerEvents=open?'none':'auto';btn.setAttribute('aria-expanded',open?'true':'false');}
356
- btn.addEventListener('click',function(){var open=panel.classList.toggle('dht-toc--open');panel.setAttribute('aria-hidden',open?'false':'true');syncToggleVisibility(open);});
357
- panel.querySelector('.dht-toc-close').addEventListener('click',function(){panel.classList.remove('dht-toc--open');panel.setAttribute('aria-hidden','true');syncToggleVisibility(false);});
542
+ function openPanel(){panel.classList.add('dht-toc--open');panel.setAttribute('aria-hidden','false');syncToggleVisibility(true);}
543
+ function closePanel(){panel.classList.remove('dht-toc--open');panel.setAttribute('aria-hidden','true');syncToggleVisibility(false);}
544
+ btn.addEventListener('click',function(){if(panel.classList.contains('dht-toc--open'))closePanel();else openPanel();});
545
+ panel.querySelector('.dht-toc-close').addEventListener('click',closePanel);
546
+
358
547
  function activate(li){var uid=li.getAttribute('data-uid');if(!uid||!/^\\d+$/.test(uid))return;var mid=firstMessageOf(uid);if(mid){var el=document.getElementById(mid);if(el){el.scrollIntoView({behavior:'smooth',block:'center'});var prev=el.style.backgroundColor;el.style.backgroundColor='rgba(148,156,247,0.18)';setTimeout(function(){el.style.backgroundColor=prev||'transparent';},1500);}}}
359
- panel.querySelectorAll('li').forEach(function(li){li.addEventListener('click',function(){activate(li);});li.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key===' '){e.preventDefault();activate(li);}});});
548
+ panel.querySelectorAll('.dht-toc-list li').forEach(function(li){li.addEventListener('click',function(){activate(li);});li.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key===' '){e.preventDefault();activate(li);}});});
549
+
550
+ // Keyword highlight + participant filter (driven by the top search input)
551
+ var input=panel.querySelector('.dht-toc-search');
552
+ var counter=panel.querySelector('.dht-toc-counter');
553
+ var matches=[],idx=-1;
554
+ function clearMatches(){matches.forEach(function(m){var p=m.parentNode;if(!p)return;var tn=document.createTextNode(m.textContent);p.replaceChild(tn,m);p.normalize();});matches=[];idx=-1;counter.textContent='';}
555
+ function walk(node,re,acc){if(node.nodeType===3){var text=node.nodeValue;if(!re.test(text))return;re.lastIndex=0;var frag=document.createDocumentFragment();var last=0;var m;while((m=re.exec(text))!==null){if(m.index>last)frag.appendChild(document.createTextNode(text.slice(last,m.index)));var span=document.createElement('mark');span.className='dht-hit';span.textContent=m[0];frag.appendChild(span);acc.push(span);last=re.lastIndex;}if(last<text.length)frag.appendChild(document.createTextNode(text.slice(last)));node.parentNode.replaceChild(frag,node);}else if(node.nodeType===1){if(node.classList&&(node.classList.contains('dht-toc')||node.classList.contains('dht-popup')||node.tagName==='SCRIPT'||node.tagName==='STYLE'))return;for(var i=node.childNodes.length-1;i>=0;i--)walk(node.childNodes[i],re,acc);}}
556
+ function goTo(){matches.forEach(function(m,i){m.classList.toggle('dht-hit--active',i===idx);});if(idx>=0&&matches[idx]){matches[idx].scrollIntoView({behavior:'smooth',block:'center'});counter.textContent=(idx+1)+' '+(dict.ofMatches||'of')+' '+matches.length;}}
557
+ function applySearch(q){
558
+ clearMatches();
559
+ var lowerQ=(q||'').trim().toLowerCase();
560
+ panel.querySelectorAll('.dht-toc-list li').forEach(function(li){var n=li.getAttribute('data-name')||'';li.style.display=(!lowerQ||n.indexOf(lowerQ)!==-1)?'':'none';});
561
+ if(!lowerQ||lowerQ.length<2){counter.textContent='';return;}
562
+ var re=new RegExp(lowerQ.replace(/[.*+?^\${}()|[\\]\\\\]/g,function(c){return '\\\\'+c;}),'gi');
563
+ walk(document.body,re,matches);
564
+ if(matches.length){idx=0;goTo();}else{counter.textContent=dict.noMatches||'no matches';}
565
+ }
566
+ input.addEventListener('input',function(){applySearch(input.value);});
567
+ panel.querySelectorAll('.dht-toc-srch-btn').forEach(function(b){b.addEventListener('click',function(){var act=b.getAttribute('data-act');if(!matches.length)return;if(act==='next')idx=(idx+1)%matches.length;else if(act==='prev')idx=(idx-1+matches.length)%matches.length;goTo();});});
568
+ input.addEventListener('keydown',function(e){if(e.key==='Enter'){if(matches.length){idx=(idx+(e.shiftKey?-1:1)+matches.length)%matches.length;goTo();}e.preventDefault();}});
569
+
570
+ // Filter form — author / role / date / has-flags. Replaces the old standalone .dht-filter aside.
571
+ function applyFilter(){
572
+ var authorIdRaw=parseId(panel.querySelector('.dht-f-author').value);
573
+ var authorTextRaw=(panel.querySelector('.dht-f-author').value||'').trim().toLowerCase();
574
+ var roleIdRaw=parseId(panel.querySelector('.dht-f-role').value);
575
+ var roleTextRaw=(panel.querySelector('.dht-f-role').value||'').trim().toLowerCase();
576
+ var fromVal=panel.querySelector('.dht-f-from').value;
577
+ var toVal=panel.querySelector('.dht-f-to').value;
578
+ var fromMs=fromVal?new Date(fromVal).getTime():null;
579
+ var toMs=toVal?new Date(toVal).getTime():null;
580
+ var needPinned=panel.querySelector('.dht-f-pinned').checked;
581
+ var needImage=panel.querySelector('.dht-f-image').checked;
582
+ var needEmbed=panel.querySelector('.dht-f-embed').checked;
583
+ var needAttach=panel.querySelector('.dht-f-attach').checked;
584
+ var needV2=panel.querySelector('.dht-f-v2').checked;
585
+ var matchedRoleId=null;
586
+ if(roleIdRaw){matchedRoleId=roleIdRaw;}
587
+ else if(roleTextRaw){var exact=null,sub=null;for(var rid in roles){if(Object.prototype.hasOwnProperty.call(roles,rid)){var rn=(roles[rid].name||'').toLowerCase();if(rn===roleTextRaw){exact=rid;break;}else if(!sub && rn.indexOf(roleTextRaw)!==-1){sub=rid;}}}matchedRoleId=exact||sub;}
588
+ var matchedAuthorId=authorIdRaw||null;
589
+ var msgs=document.querySelectorAll('discord-message, discord-system-message');
590
+ for(var i=0;i<msgs.length;i++){
591
+ var m=msgs[i];var ok=true;
592
+ var authorId=m.getAttribute('data-author-id')||'';
593
+ var authorName=(m.getAttribute('data-author-name')||'').toLowerCase();
594
+ var rolesAttr=m.getAttribute('data-roles')||'';
595
+ var tsStr=m.getAttribute('data-timestamp');
596
+ var ts=tsStr?parseInt(tsStr,10):0;
597
+ var pinned=m.getAttribute('data-pinned')==='true';
598
+ var hasImg=m.getAttribute('data-has-image')==='true';
599
+ var hasEmb=m.getAttribute('data-has-embed')==='true';
600
+ var hasAtt=m.getAttribute('data-has-attachment')==='true';
601
+ var hasV2=m.getAttribute('data-has-component-v2')==='true';
602
+ if(matchedAuthorId && authorId!==matchedAuthorId)ok=false;
603
+ if(ok && !matchedAuthorId && !authorIdRaw && authorTextRaw && authorName.indexOf(authorTextRaw)===-1)ok=false;
604
+ if(ok && matchedRoleId){var rIds=rolesAttr.split(',');if(rIds.indexOf(matchedRoleId)===-1 && !authorHasRole(authorId,matchedRoleId))ok=false;}
605
+ if(ok && fromMs){if(!ts || ts<fromMs)ok=false;}
606
+ if(ok && toMs){if(!ts || ts>toMs)ok=false;}
607
+ if(ok && needPinned && !pinned)ok=false;
608
+ if(ok && needImage && !hasImg)ok=false;
609
+ if(ok && needEmbed && !hasEmb)ok=false;
610
+ if(ok && needAttach && !hasAtt)ok=false;
611
+ if(ok && needV2 && !hasV2)ok=false;
612
+ m.style.display=ok?'':'none';
613
+ }
614
+ var seps=document.querySelectorAll('.dht-date-sep');
615
+ for(var s=0;s<seps.length;s++){var sep=seps[s];var next=sep.nextElementSibling;var hasVis=false;while(next && !next.classList.contains('dht-date-sep')){if(next.style.display!=='none' && (next.tagName==='DISCORD-MESSAGE' || next.tagName==='DISCORD-SYSTEM-MESSAGE')){hasVis=true;break;}next=next.nextElementSibling;}sep.style.display=hasVis?'':'none';}
616
+ }
617
+ function resetFilter(){panel.querySelectorAll('.dht-toc-filter-body input').forEach(function(i){if(i.type==='checkbox')i.checked=false;else i.value='';});document.querySelectorAll('discord-message, discord-system-message, .dht-date-sep').forEach(function(m){m.style.display='';});}
618
+ panel.querySelector('.dht-f-apply').addEventListener('click',applyFilter);
619
+ panel.querySelector('.dht-f-reset').addEventListener('click',resetFilter);
620
+ panel.querySelector('.dht-toc-filter-body').addEventListener('keydown',function(e){if(e.key==='Enter'){e.preventDefault();applyFilter();}});
621
+ try{globalThis.__DHT_FLAG_BADGES__=${JSON.stringify(FLAG_BADGES)};}catch(_){}
622
+
623
+ // Ctrl+F / Cmd+F opens the panel and focuses the search input
624
+ document.addEventListener('keydown',function(e){
625
+ if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='f'){
626
+ e.preventDefault();openPanel();setTimeout(function(){input.focus();input.select();},20);
627
+ }else if(e.key==='Escape'&&panel.classList.contains('dht-toc--open')){
628
+ if(document.activeElement===input&&input.value){input.value='';applySearch('');}
629
+ else closePanel();
630
+ }
631
+ });
360
632
  })();`;
361
- exports.tocStyles = `.dht-toc-toggle{position:fixed;top:16px;left:16px;width:40px;height:40px;border-radius:50%;border:none;background:#2b2d31;color:#fff;font-size:16px;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.4);z-index:9998}.dht-toc-toggle:hover{background:#5865F2}.dht-toc{position:fixed;top:0;left:-320px;width:300px;height:100vh;background:#1e1f22;border-right:1px solid #2b2d31;transition:left .25s ease;z-index:9997;overflow-y:auto;font-family:"gg sans",sans-serif;color:#dbdee1;padding:16px}.dht-toc--open{left:0}.dht-toc header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid #2b2d31}.dht-toc h3{margin:0;font-size:14px;text-transform:uppercase;letter-spacing:.04em;color:#949ba4}.dht-toc-close{background:none;border:none;color:#949ba4;cursor:pointer;font-size:22px;line-height:1}.dht-toc ul{list-style:none;padding:0;margin:0}.dht-toc li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;cursor:pointer;font-size:14px}.dht-toc li:hover{background:rgba(255,255,255,.04)}.dht-toc li img,.dht-toc-ph{width:28px;height:28px;border-radius:50%;flex:none}.dht-toc-ph{background:#313338}.dht-toc-name{flex:1;font-weight:500}`;
633
+ exports.tocStyles = `.dht-toc-toggle{position:fixed;top:16px;right:16px;width:40px;height:40px;border-radius:50%;border:none;background:#2b2d31;color:#fff;font-size:16px;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.4);z-index:9998}.dht-toc-toggle:hover{background:#5865F2}
634
+ .dht-toc{position:fixed;top:0;right:-340px;left:auto;width:320px;height:100vh;background:#1e1f22;border-left:1px solid #2b2d31;transition:right .25s ease;z-index:9997;overflow-y:auto;font-family:"gg sans",sans-serif;color:#dbdee1;padding:16px;box-sizing:border-box}
635
+ .dht-toc--open{right:0}
636
+ .dht-toc header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid #2b2d31}
637
+ .dht-toc h3{margin:0;font-size:14px;text-transform:uppercase;letter-spacing:.04em;color:#949ba4}
638
+ .dht-toc-close{background:none;border:none;color:#949ba4;cursor:pointer;font-size:22px;line-height:1}
639
+ .dht-toc-search-wrap{display:flex;align-items:center;gap:6px;margin-bottom:14px;background:#2b2d31;border:1px solid #2b2d31;border-radius:6px;padding:4px 8px;transition:border-color .15s ease}
640
+ .dht-toc-search-wrap:focus-within{border-color:#5865F2}
641
+ .dht-toc-search-icon{color:#949ba4;display:inline-flex;align-items:center;flex:none}
642
+ .dht-toc-search{flex:1;min-width:0;background:transparent;color:#dbdee1;border:none;font-size:13px;outline:none;font-family:inherit;padding:4px 0}
643
+ .dht-toc-counter{font-size:11px;color:#949ba4;min-width:46px;text-align:right;white-space:nowrap}
644
+ .dht-toc-srch-btn{background:none;border:none;color:#dbdee1;cursor:pointer;padding:2px 4px;border-radius:3px;font-size:11px;line-height:1;flex:none}
645
+ .dht-toc-srch-btn:hover{background:rgba(255,255,255,.06)}
646
+ .dht-toc-section-label{font-size:11px;color:#949ba4;text-transform:uppercase;letter-spacing:.04em;font-weight:700;margin:0 0 6px}
647
+ .dht-toc-list{list-style:none;padding:0;margin:0}
648
+ .dht-toc-list li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;cursor:pointer;font-size:14px}
649
+ .dht-toc-list li:hover{background:rgba(255,255,255,.04)}
650
+ .dht-toc-list li img,.dht-toc-ph{width:28px;height:28px;border-radius:50%;flex:none}
651
+ .dht-toc-ph{background:#313338}
652
+ .dht-toc-name{flex:1;font-weight:500;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
653
+ @media (max-width:640px){.dht-toc{width:85vw;right:-85vw}.dht-toc--open{right:0}.dht-toc-toggle{top:12px;right:12px}}`;
362
654
 
363
655
  // ===========================================================================
364
656
  // Language switcher
@@ -472,7 +764,18 @@ exports.markdownStyles = `
472
764
  .dht-hit--active{background:rgba(255,140,0,.85);color:#000}
473
765
  /* Body fallback when web components fail to load from CDN */
474
766
  body{background:#313338;color:#dbdee1;font-family:"gg sans","Helvetica Neue",Helvetica,Arial,sans-serif}
475
- discord-messages{background:#313338}
767
+ discord-messages{background:#313338;display:block}
768
+ /* CRITICAL: ensure regular message hosts are block-level. Without this, two
769
+ consecutive messages collapse onto the same line in some browser states.
770
+ discord-system-message is intentionally NOT forced to block — skyra's
771
+ shadow DOM uses display:grid to lay out icon / content / timestamp; if we
772
+ override it, the pin icon ends up floating to the far right instead of
773
+ sitting to the left of the author. */
774
+ discord-message{display:block;position:relative}
775
+ discord-system-message{position:relative}
776
+ /* Fallback layout when @skyra Web Components fail to register (offline viewer). */
777
+ discord-message:not(:defined),discord-system-message:not(:defined){padding:6px 16px 6px 16px;min-height:32px;color:#dbdee1;font-size:14px;border-top:1px solid rgba(255,255,255,0.04);margin-top:0}
778
+ discord-message:not(:defined)::before{content:attr(profile);display:block;font-weight:600;color:#fff;font-size:13px;margin-bottom:2px;opacity:.7}
476
779
  /* Date separator divider — more visible */
477
780
  .dht-date-sep::before,.dht-date-sep::after{background:rgba(255,255,255,.1)}
478
781
  /* Reduce motion */
@@ -542,124 +845,21 @@ exports.markdownStyles = `
542
845
  // Advanced filter panel — keyword + author + role + date range + has-image/embed/attachment + pinned-only
543
846
  // Hides non-matching messages, respects date separators
544
847
  // ===========================================================================
545
- exports.filterPanel = `(function(){
546
- var DATA=globalThis.$discordMessage||{};
547
- var dict=(DATA.i18n&&(DATA.i18n[DATA.lang]||DATA.i18n.en))||{};
548
- var users=DATA.users||DATA.profiles||{};
549
- var roles=DATA.roles||{};
550
- function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c];});}
551
- function parseId(v){if(!v)return null;var t=v.trim();if(/^\\d{15,21}$/.test(t))return t;var m=t.match(/\\((\\d{15,21})\\)/);return m?m[1]:null;}
552
- function authorHasRole(authorId,roleId){var u=users[authorId];if(!u||!Array.isArray(u.roles))return false;for(var i=0;i<u.roles.length;i++){if(u.roles[i].id===roleId)return true;}return false;}
553
- var btn=document.createElement('button');btn.className='dht-filter-toggle';btn.type='button';btn.title=dict.filterPanel||'Filter';btn.setAttribute('aria-label',dict.filterPanel||'Filter');btn.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>';document.body.appendChild(btn);
554
- var panel=document.createElement('aside');panel.className='dht-filter';panel.setAttribute('aria-hidden','true');panel.setAttribute('role','region');
555
- var userIds=Object.keys(users).filter(function(x){return /^\\d+$/.test(x);}).sort(function(a,b){var na=(users[a].displayName||users[a].author||'').toLowerCase();var nb=(users[b].displayName||users[b].author||'').toLowerCase();return na.localeCompare(nb);});
556
- var roleIds=Object.keys(roles).filter(function(x){return /^\\d+$/.test(x);});
557
- var html='<header><h3>'+esc(dict.filterPanel||'Filter')+'</h3><button class="dht-filter-close" type="button" aria-label="Close">&times;</button></header><div class="dht-filter-body">';
558
- html+='<label class="dht-f-field">'+esc(dict.filterKeyword||'Keyword')+'<input type="search" class="dht-f-keyword" placeholder="'+esc(dict.searchPlaceholder||'Search…')+'"></label>';
559
- html+='<label class="dht-f-field">'+esc(dict.filterAuthor||'Author')+'<input list="dht-f-authors" class="dht-f-author" placeholder="@user / ID / name"></label>';
560
- html+='<datalist id="dht-f-authors">'+userIds.map(function(id){var u=users[id];return '<option value="'+esc(u.displayName||u.author||u.username||'')+' ('+esc(id)+')">';}).join('')+'</datalist>';
561
- html+='<label class="dht-f-field">'+esc(dict.filterRole||'Role')+'<input list="dht-f-roles" class="dht-f-role" placeholder="@role / ID / name"></label>';
562
- html+='<datalist id="dht-f-roles">'+roleIds.map(function(id){var r=roles[id];return '<option value="'+esc(r.name||'')+' ('+esc(id)+')">';}).join('')+'</datalist>';
563
- html+='<div class="dht-f-grid"><label class="dht-f-field">'+esc(dict.filterFrom||'From')+'<input type="datetime-local" class="dht-f-from"></label><label class="dht-f-field">'+esc(dict.filterTo||'To')+'<input type="datetime-local" class="dht-f-to"></label></div>';
564
- html+='<div class="dht-f-checks">';
565
- html+='<label><input type="checkbox" class="dht-f-pinned"> '+esc(dict.filterPinned||'Pinned only')+'</label>';
566
- html+='<label><input type="checkbox" class="dht-f-image"> '+esc(dict.filterHasImage||'Has image')+'</label>';
567
- html+='<label><input type="checkbox" class="dht-f-embed"> '+esc(dict.filterHasEmbed||'Has embed')+'</label>';
568
- html+='<label><input type="checkbox" class="dht-f-attach"> '+esc(dict.filterHasAttachment||'Has attachment')+'</label>';
569
- html+='</div>';
570
- html+='<div class="dht-f-actions"><button type="button" class="dht-f-apply">'+esc(dict.filterApply||'Apply')+'</button><button type="button" class="dht-f-reset">'+esc(dict.filterReset||'Reset')+'</button></div>';
571
- html+='<div class="dht-f-counter" aria-live="polite"></div>';
572
- html+='</div>';
573
- panel.innerHTML=html;document.body.appendChild(panel);
574
- function close(){panel.classList.remove('dht-filter--open');panel.setAttribute('aria-hidden','true');}
575
- function open(){panel.classList.add('dht-filter--open');panel.setAttribute('aria-hidden','false');var k=panel.querySelector('.dht-f-keyword');if(k)k.focus();}
576
- btn.addEventListener('click',open);
577
- panel.querySelector('.dht-filter-close').addEventListener('click',close);
578
- function apply(){
579
- var keyword=(panel.querySelector('.dht-f-keyword').value||'').trim().toLowerCase();
580
- var authorIdRaw=parseId(panel.querySelector('.dht-f-author').value);
581
- var authorTextRaw=(panel.querySelector('.dht-f-author').value||'').trim().toLowerCase();
582
- var roleIdRaw=parseId(panel.querySelector('.dht-f-role').value);
583
- var roleTextRaw=(panel.querySelector('.dht-f-role').value||'').trim().toLowerCase();
584
- var fromVal=panel.querySelector('.dht-f-from').value;
585
- var toVal=panel.querySelector('.dht-f-to').value;
586
- var fromMs=fromVal?new Date(fromVal).getTime():null;
587
- var toMs=toVal?new Date(toVal).getTime():null;
588
- var needPinned=panel.querySelector('.dht-f-pinned').checked;
589
- var needImage=panel.querySelector('.dht-f-image').checked;
590
- var needEmbed=panel.querySelector('.dht-f-embed').checked;
591
- var needAttach=panel.querySelector('.dht-f-attach').checked;
592
- var matchedRoleId=null;
593
- if(roleIdRaw){matchedRoleId=roleIdRaw;}
594
- else if(roleTextRaw){
595
- // exact-match wins over substring
596
- var exact=null,sub=null;
597
- for(var rid in roles){if(Object.prototype.hasOwnProperty.call(roles,rid)){var rn=(roles[rid].name||'').toLowerCase();if(rn===roleTextRaw){exact=rid;break;}else if(!sub && rn.indexOf(roleTextRaw)!==-1){sub=rid;}}}
598
- matchedRoleId=exact||sub;
599
- }
600
- var matchedAuthorId=null;
601
- if(authorIdRaw){matchedAuthorId=authorIdRaw;}
602
- var msgs=document.querySelectorAll('discord-message, discord-system-message');
603
- var visible=0;
604
- for(var i=0;i<msgs.length;i++){
605
- var m=msgs[i];
606
- var ok=true;
607
- var text=(m.getAttribute('data-text')||'').toLowerCase();
608
- var authorId=m.getAttribute('data-author-id')||'';
609
- var authorName=(m.getAttribute('data-author-name')||'').toLowerCase();
610
- var rolesAttr=m.getAttribute('data-roles')||'';
611
- var tsStr=m.getAttribute('data-timestamp');
612
- var ts=tsStr?parseInt(tsStr,10):0;
613
- var pinned=m.getAttribute('data-pinned')==='true';
614
- var hasImg=m.getAttribute('data-has-image')==='true';
615
- var hasEmb=m.getAttribute('data-has-embed')==='true';
616
- var hasAtt=m.getAttribute('data-has-attachment')==='true';
617
- if(keyword && text.indexOf(keyword)===-1 && authorName.indexOf(keyword)===-1)ok=false;
618
- if(ok && matchedAuthorId && authorId!==matchedAuthorId)ok=false;
619
- if(ok && !matchedAuthorId && !authorIdRaw && authorTextRaw && authorName.indexOf(authorTextRaw)===-1)ok=false;
620
- if(ok && matchedRoleId){var rIds=rolesAttr.split(',');if(rIds.indexOf(matchedRoleId)===-1 && !authorHasRole(authorId,matchedRoleId))ok=false;}
621
- // If date filter active but message has no timestamp, exclude it (safer than always-passing).
622
- if(ok && fromMs){if(!ts || ts<fromMs)ok=false;}
623
- if(ok && toMs){if(!ts || ts>toMs)ok=false;}
624
- if(ok && needPinned && !pinned)ok=false;
625
- if(ok && needImage && !hasImg)ok=false;
626
- if(ok && needEmbed && !hasEmb)ok=false;
627
- if(ok && needAttach && !hasAtt)ok=false;
628
- m.style.display=ok?'':'none';
629
- if(ok)visible++;
630
- }
631
- var seps=document.querySelectorAll('.dht-date-sep');
632
- for(var s=0;s<seps.length;s++){
633
- var sep=seps[s];var next=sep.nextElementSibling;var hasVis=false;
634
- while(next && !next.classList.contains('dht-date-sep')){
635
- if(next.style.display!=='none' && (next.tagName==='DISCORD-MESSAGE' || next.tagName==='DISCORD-SYSTEM-MESSAGE')){hasVis=true;break;}
636
- next=next.nextElementSibling;
637
- }
638
- sep.style.display=hasVis?'':'none';
639
- }
640
- panel.querySelector('.dht-f-counter').textContent=(dict.filterShowing||'Showing {visible} of {total}').replace('{visible}',visible).replace('{total}',msgs.length);
641
- }
642
- function reset(){
643
- panel.querySelectorAll('input').forEach(function(i){if(i.type==='checkbox')i.checked=false;else i.value='';});
644
- document.querySelectorAll('discord-message, discord-system-message, .dht-date-sep').forEach(function(m){m.style.display='';});
645
- panel.querySelector('.dht-f-counter').textContent='';
646
- }
647
- panel.querySelector('.dht-f-apply').addEventListener('click',apply);
648
- panel.querySelector('.dht-f-reset').addEventListener('click',reset);
649
- panel.addEventListener('keydown',function(e){if(e.key==='Enter'){e.preventDefault();apply();}else if(e.key==='Escape'){close();}});
650
- // Bind flag badges dict for popup access
651
- try{globalThis.__DHT_FLAG_BADGES__=${JSON.stringify(FLAG_BADGES)};}catch(_){}
652
- })();`;
848
+ exports.filterPanel = '/* filterPanel merged into TOC; see exports.toc */';
653
849
 
654
850
  exports.filterPanelStyles = `
655
- .dht-filter-toggle{position:fixed;bottom:76px;right:24px;width:44px;height:44px;border-radius:50%;border:none;background:#2b2d31;color:#fff;font-size:16px;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.4);z-index:9998}
656
- .dht-filter-toggle:hover{background:#5865F2}
657
- .dht-filter{position:fixed;top:0;right:-360px;width:340px;height:100vh;background:#1e1f22;border-left:1px solid #2b2d31;transition:right .25s ease;z-index:9997;overflow-y:auto;font-family:"gg sans",sans-serif;color:#dbdee1;padding:16px;box-sizing:border-box}
658
- .dht-filter--open{right:0}
659
- .dht-filter header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid #2b2d31}
660
- .dht-filter h3{margin:0;font-size:14px;text-transform:uppercase;letter-spacing:.04em;color:#949ba4}
661
- .dht-filter-close{background:none;border:none;color:#949ba4;cursor:pointer;font-size:22px;line-height:1}
662
- .dht-filter-body{display:flex;flex-direction:column;gap:10px}
851
+ /* Filter form lives inside the TOC panel — only the form-field styles remain.
852
+ The old floating .dht-filter-toggle (magnifying-glass button) and standalone
853
+ .dht-filter aside are gone. */
854
+ /* Collapsible sections inside the TOC (participants + filter) */
855
+ .dht-toc-section{margin-top:12px;border-top:1px solid #2b2d31;padding-top:10px}
856
+ .dht-toc-section:first-of-type{margin-top:0;border-top:none;padding-top:0}
857
+ .dht-toc-section>summary{cursor:pointer;font-size:11px;color:#949ba4;text-transform:uppercase;letter-spacing:.04em;font-weight:700;padding:4px 0;list-style:none;display:flex;align-items:center;justify-content:space-between}
858
+ .dht-toc-section>summary::-webkit-details-marker{display:none}
859
+ .dht-toc-section>summary::after{content:'\\25BC';font-size:9px;color:#6e727a;transition:transform .15s ease}
860
+ .dht-toc-section[open]>summary::after{transform:rotate(180deg)}
861
+ .dht-toc-section>summary:hover{color:#dbdee1}
862
+ .dht-toc-filter-body{display:flex;flex-direction:column;gap:10px;margin-top:8px}
663
863
  .dht-f-field{display:flex;flex-direction:column;gap:4px;font-size:11px;color:#b5bac1;text-transform:uppercase;letter-spacing:.04em;font-weight:600}
664
864
  .dht-f-field input{background:#2b2d31;color:#dbdee1;border:1px solid #2b2d31;border-radius:4px;padding:6px 8px;font-size:13px;outline:none;font-family:inherit;text-transform:none;letter-spacing:0;font-weight:400}
665
865
  .dht-f-field input:focus{border-color:#5865F2}
@@ -670,8 +870,6 @@ exports.filterPanelStyles = `
670
870
  .dht-f-actions button{flex:1;padding:8px;border:none;border-radius:4px;cursor:pointer;font-weight:600;font-size:13px}
671
871
  .dht-f-apply{background:#5865F2;color:#fff}.dht-f-apply:hover{background:#4752c4}
672
872
  .dht-f-reset{background:#3f4248;color:#dbdee1}.dht-f-reset:hover{background:#4a4d54}
673
- .dht-f-counter{margin-top:8px;font-size:12px;color:#b5bac1;text-align:center}
674
- @media (max-width:640px){.dht-filter{width:85vw;right:-85vw}.dht-filter-toggle{bottom:70px;right:16px;width:40px;height:40px}}
675
873
  .dht-flag{display:inline-flex;align-items:center;background:rgba(255,255,255,.06);border-radius:4px;padding:2px 6px;font-size:12px;margin:2px 2px 0 0}
676
874
  .dht-popup-flags{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
677
875
  /* Pinned background — Discord-style amber tint */