Sleazy Fork is available in English.
Manually saved local notes for FetLife pages
// ==UserScript==
// @name FL Tools Notes
// @namespace https://gitlab.com/fl-tools-lite/fl-tools-notes
// @version 0.1.1
// @description Manually saved local notes for FetLife pages
// @author TypicalBits
// @icon data:image/png;base64,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
// @icon64 data:image/png;base64,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
// @license PolyForm-Noncommercial-1.0.0
// @match https://fetlife.com/*
// @run-at document-end
// @noframes
// @grant GM.getValue
// @grant GM.setValue
// @homepageURL https://gitlab.com/fl-tools-lite/fl-tools-notes
// @supportURL https://gitlab.com/fl-tools-lite/fl-tools-notes/-/issues
// ==/UserScript==
// Copyright 2026 TypicalBits.
(function(){
'use strict';
// dock.js
// Copyright 2026 TypicalBits. Shared FL Tools Lite coordination.
const LITE_ORDER = ['view','read','calendar','notes','return'];
const LITE_CORNERS = ['bottom-left','bottom-right','top-left','top-right'];
const LITE_COLORS = {view:'#62A8DE',read:'#B9A0F5',calendar:'#65D8EB',notes:'#F39BC8',return:'#CCD4E0'};
const LITE_REPAIR = 'flt-lite:repair-v1';
function claimProduct(document,id) {
if (!LITE_ORDER.includes(id) || document.querySelector(`[data-flt-lite-claim="${id}"]`)) return null;
const claim=document.createElement('span');claim.hidden=true;claim.dataset.fltLiteClaim=id;
document.documentElement.append(claim);
return {ensure(){if(!claim.isConnected)document.documentElement.append(claim);},release(){claim.remove();}};
}
function setLiteCorner(document,host,corner) {
host.dataset.corner=corner; document.documentElement.dataset.fltLiteCorner=corner;
for(const panel of document.querySelectorAll('[data-flt-lite-panel]'))panel.dataset.fltLiteCorner=corner;
}
function ownLiteDock(document,host,storage,restoreSaved=true) {
const w=document.defaultView; const shadow=host.attachShadow({mode:'open'});
const style=document.createElement('style');style.textContent=`
:host{position:fixed;bottom:16px;left:16px;z-index:2147483001;color-scheme:dark;font:12px/1.4 system-ui,sans-serif}
:host([data-corner="bottom-right"]){left:auto;right:16px}
:host([data-corner="top-left"]){bottom:auto;top:16px}
:host([data-corner="top-right"]){left:auto;right:16px;bottom:auto;top:16px}
:host([data-fallback="true"]){transform:translateY(-58px)}:host([data-fallback="true"][data-corner^="top"]){transform:translateY(58px)}
.dock{display:flex;flex-wrap:wrap;gap:5px;max-width:calc(100vw - 32px)}
button{display:flex;align-items:center;justify-content:center;padding:3px;width:46px;height:46px;border:1px solid var(--accent);border-radius:8px;background:#17171a;cursor:pointer;color:var(--accent);font:inherit}
button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}button[aria-expanded="true"]{box-shadow:0 0 0 1px var(--accent)}img{width:38px;height:38px;display:block}
`;const bar=document.createElement('nav');bar.className='dock';bar.setAttribute('aria-label','FL Tools Lite');shadow.append(style,bar);
let cornerRevision=0;
let current=null;const buttons=new Map(); let disposed=false;let writes=Promise.resolve();
function ports(){return [...host.children].filter(x=>x.hasAttribute('data-flt-lite-port'));}
function close(port,focus=false){port?.dispatchEvent(new w.Event('flt-lite:close'));buttons.get(port)?.setAttribute('aria-expanded','false');if(current===port)current=null;if(focus)buttons.get(port)?.focus();}
function open(port){if(current===port){close(port,true);return;}if(current)close(current);current=port;buttons.get(port)?.setAttribute('aria-expanded','true');port.dispatchEvent(new w.Event('flt-lite:open'));}
function render(){
const active=ports().sort((a,b)=>LITE_ORDER.indexOf(a.dataset.product)-LITE_ORDER.indexOf(b.dataset.product));
for(const [port,button]of buttons)if(!active.includes(port)){if(current===port)close(port);button.remove();buttons.delete(port);}
for(const port of active){
let button=buttons.get(port);
if(!button){port.dispatchEvent(new w.Event('flt-lite:close'));button=document.createElement('button');button.type='button';button.dataset.product=port.dataset.product;button.setAttribute('aria-label','Open FL Tools '+port.dataset.label);button.setAttribute('aria-expanded','false');button.style.setProperty('--accent',LITE_COLORS[port.dataset.product]);
if(port.dataset.icon){const image=document.createElement('img');image.src=port.dataset.icon;image.alt='';button.append(image);}else button.textContent=port.dataset.label;
button.addEventListener('click',()=>open(port));buttons.set(port,button);
}
if(bar.children[active.indexOf(port)]!==button)bar.append(button);
}
setLiteCorner(document,host,host.dataset.corner||'bottom-left');
}
const route=event=>{const port=event.target;if(port.parentNode!==host||!port.hasAttribute('data-flt-lite-port'))return;
if(event.type==='flt-lite:register')render();
if(event.type==='flt-lite:toggle')open(port);
if(event.type==='flt-lite:hide')close(port,true);
if(event.type==='flt-lite:focus')buttons.get(port)?.focus();
if(event.type==='flt-lite:corner'&&LITE_CORNERS.includes(port.dataset.corner)){
cornerRevision++;const corner=port.dataset.corner;setLiteCorner(document,host,corner);
if(storage)writes=writes.then(()=>storage.setValue('flt-lite:dock:v1',corner)).catch(()=>{});
}
};
for(const name of ['register','toggle','hide','focus','corner'])host.addEventListener('flt-lite:'+name,route);
const escape=event=>{if(current&&event.key==='Escape'){close(current,true);event.stopPropagation();}};
document.addEventListener('keydown',escape);
const observer=new w.MutationObserver(()=>{if(!disposed)render();});observer.observe(host,{childList:true});render();
if(storage&&restoreSaved)Promise.resolve().then(()=>storage.getValue('flt-lite:dock:v1','bottom-left')).then(corner=>{if(!disposed&&cornerRevision===0&&host.isConnected&&LITE_CORNERS.includes(corner))setLiteCorner(document,host,corner);}).catch(()=>{});
return {destroy(){disposed=true;observer.disconnect();document.removeEventListener('keydown',escape);for(const name of ['register','toggle','hide','focus','corner'])host.removeEventListener('flt-lite:'+name,route);},render};
}
function registerDock(document,{id,label,icon,onOpen=()=>{},onClose=()=>{},storage}) {
const w=document.defaultView;const port=document.createElement('span');port.hidden=true;port.dataset.fltLitePort='';port.dataset.product=id;port.dataset.label=label;port.dataset.icon=icon;
port.addEventListener('flt-lite:open',onOpen);port.addEventListener('flt-lite:close',onClose);
let owner=null,ownedHost=null,destroyed=false,scheduled=false;
function ensure(){
if(destroyed||!document.body)return;
let host=document.querySelector('[data-flt-lite-dock="1"]');
if(!host){const restoreSaved=!document.documentElement.dataset.fltLiteCorner;owner?.destroy();host=document.createElement('div');host.dataset.fltLiteDock='1';host.dataset.corner=document.documentElement.dataset.fltLiteCorner||ownedHost?.dataset.corner||'bottom-left';host.dataset.fallback=String(!!document.querySelector('[data-flt-lite-dock]:not([data-flt-lite-dock="1"])'));document.body.append(host);ownedHost=host;owner=ownLiteDock(document,host,storage,restoreSaved);}
if(port.parentNode!==host){host.append(port);port.dispatchEvent(new w.Event('flt-lite:register',{bubbles:true}));}
owner?.render();
}
const schedule=()=>{if(scheduled||destroyed)return;scheduled=true;w.queueMicrotask(()=>{scheduled=false;ensure();});};
const observer=new w.MutationObserver(records=>{if(!port.isConnected||!document.querySelector('[data-flt-lite-dock="1"]'))schedule();});observer.observe(document.documentElement,{childList:true,subtree:true});
document.addEventListener(LITE_REPAIR,schedule);ensure();
const send=name=>{ensure();port.dispatchEvent(new w.Event('flt-lite:'+name,{bubbles:true}));};
return {
ensure,open(){send('toggle');},close(){send('hide');},focus(){send('focus');},
setCorner(corner){if(!LITE_CORNERS.includes(corner))return;port.dataset.corner=corner;send('corner');},
get corner(){return port.parentNode?.dataset.corner||'bottom-left';},
destroy(){if(destroyed)return;destroyed=true;observer.disconnect();document.removeEventListener(LITE_REPAIR,schedule);onClose();const host=port.parentNode;port.remove();
if(owner&&host===ownedHost){const survivors=[...host.children];owner.destroy();for(const survivor of survivors)document.body.append(survivor);host.remove();document.dispatchEvent(new w.Event(LITE_REPAIR));}
else if(host&&!host.querySelector('[data-flt-lite-port]'))host.remove();
port.removeEventListener('flt-lite:open',onOpen);port.removeEventListener('flt-lite:close',onClose);
}
};
}
// storage.js
// Copyright 2026 TypicalBits.
function createRecordStore(storage,key,normalize,locks=globalThis.navigator?.locks) {
let value={},loaded=false,error='',queue=Promise.resolve();
return {
get value(){return structuredClone(value);},get error(){return error;},
async load(){try{value=normalize(await storage.getValue(key,{}));loaded=true;error='';}catch{error='Saved data could not be read. Reload before saving.';}return structuredClone(value);},
transact(change){const job=queue.then(async()=>{
if(!loaded)throw Error(error||'Saved data has not loaded.');
if(!locks?.request){error='This browser cannot safely save across tabs. Use a browser with Web Locks support.';throw Error(error);}
const controller=new AbortController();const timeout=setTimeout(()=>controller.abort(),10000);
try{return await locks.request('flt-lite:storage:'+key,{mode:'exclusive',signal:controller.signal},async()=>{
let latest;try{latest=normalize(await storage.getValue(key,{}));}catch{error='Saved data could not be read. Nothing was overwritten.';throw Error(error);}
const next=normalize(change(structuredClone(latest)));
try{await storage.setValue(key,next);value=next;error='';return structuredClone(value);}catch{error='Could not save. Your previous saved data is unchanged.';throw Error(error);}
});}catch(problem){if(controller.signal.aborted){error='Another tab is busy saving. Please try again.';throw Error(error);}throw problem;}finally{clearTimeout(timeout);}
});queue=job.catch(()=>{});return job;}
};
}
function pageKey(input) {
const url=new URL(input);
if(url.protocol!=='https:'||url.hostname!=='fetlife.com'||url.username||url.password||url.port)throw Error('This is not a FetLife page.');
url.hash='';return url.href;
}
// panel.js
// Copyright 2026 TypicalBits.
function liteElement(document,tag,attributes={},text='') {
const element=document.createElement(tag);for(const [key,value]of Object.entries(attributes))element.setAttribute(key,String(value));if(text)element.textContent=text;return element;
}
function createLitePanel(document,{id,name,accent,icon,version,storage,onOpen=()=>{}}) {
const e=(tag,attrs,text)=>liteElement(document,tag,attrs,text);
const host=e('div',{'data-flt-lite-panel':id,'data-flt-lite-corner':'bottom-left'});const shadow=host.attachShadow({mode:'open'});
const style=e('style');style.textContent=`
:host{all:initial;position:fixed;left:16px;bottom:16px;z-index:2147483002;font:12px/1.5 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#eeeef4;color-scheme:dark;--accent:${accent}}
:host([data-flt-lite-corner$="right"]){left:auto;right:16px}:host([data-flt-lite-corner^="top"]){bottom:auto;top:16px}
*,*::before,*::after{box-sizing:border-box}[hidden]{display:none!important}button,input,textarea,select{font:inherit;color:inherit}button{cursor:pointer}button:disabled{opacity:.5;cursor:default}
button,input,textarea,select{background:#222228;border:1px solid #4b4b59;border-radius:6px;padding:6px 8px;min-width:0}button{min-height:32px}button:focus-visible,input:focus-visible,textarea:focus-visible,summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.panel{position:absolute;bottom:54px;left:0;width:min(320px,calc(100vw - 32px));max-height:calc(100dvh - 90px);overflow:auto;padding:12px;background:#131318;border:1px solid var(--accent);border-radius:10px;box-shadow:0 8px 26px #0008}
:host([data-flt-lite-corner$="right"]) .panel{left:auto;right:0}:host([data-flt-lite-corner^="top"]) .panel{bottom:auto;top:54px}
.heading{display:flex;align-items:center;gap:8px;margin-bottom:12px}.heading strong{flex:1;font-size:14px}.heading img{width:30px;height:30px}.version{font-size:10px;color:#b5b5c4}.close{width:30px;min-height:30px;padding:0}
p{margin:0 0 10px}fieldset{border:0;padding:0;margin:0 0 12px;min-width:0}legend,label{font-weight:600;padding:0;margin-bottom:6px;display:block}input,textarea,select{display:block;width:100%;margin-bottom:10px}textarea{min-height:160px;resize:vertical}
.choices,.actions{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}.choices button{flex:1}.choices button[aria-pressed=true],.primary{background:var(--accent);border-color:var(--accent);color:#101015;font-weight:600}
.row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}.row label{margin:0;min-width:0}.switch{position:relative;flex:none;width:34px;min-height:20px;height:20px;padding:0;border-radius:5px}.switch::after{content:"";position:absolute;top:3px;left:3px;width:12px;height:12px;background:#b5b5c4;border-radius:3px}.switch[aria-checked=true]{background:var(--accent)}.switch[aria-checked=true]::after{left:17px;background:#131318}
.note,.status,.page{font-size:11px;color:#b5b5c4;overflow-wrap:anywhere}.status{border-top:1px solid #3d3d46;padding-top:8px;margin:10px 0 0}.status:empty{display:none}details{border-top:1px solid #3d3d46;margin-top:10px;padding-top:8px}summary{cursor:pointer;color:var(--accent);font-weight:600;margin-bottom:8px}.record{border-top:1px solid #3d3d46;padding:8px 0;overflow-wrap:anywhere}.record strong{display:block;margin-bottom:4px}.confirmation{border:1px solid var(--accent);padding:8px;border-radius:6px;margin-bottom:10px}
`;
const panel=e('section',{class:'panel','aria-label':'FL Tools '+name});panel.hidden=true;
const heading=e('div',{class:'heading'});if(icon)heading.append(e('img',{src:icon,alt:''}));heading.append(e('strong',{},'FL Tools '+name),e('span',{class:'version'},version));const close=e('button',{class:'close',type:'button','aria-label':'Close '+name},'×');heading.append(close);
const body=e('div',{'data-content':''}),settings=e('details');settings.append(e('summary',{},'Settings'));const corners=e('div',{class:'choices','aria-label':'Dock position'});
let dock;for(const corner of ['bottom-left','bottom-right','top-left','top-right']){const button=e('button',{type:'button','aria-label':corner.replace('-',' '),'aria-pressed':'false'},corner.startsWith('top')?'↑ '+(corner.endsWith('left')?'Left':'Right'):'↓ '+(corner.endsWith('left')?'Left':'Right'));button.addEventListener('click',()=>{dock.setCorner(corner);for(const b of corners.children)b.setAttribute('aria-pressed',String(b===button));});corners.append(button);}settings.append(e('p',{class:'note'},'Dock position'),corners);
const status=e('p',{class:'status',role:'status','data-status':''});panel.append(heading,body,settings,status);shadow.append(style,panel);document.body.append(host);
dock=registerDock(document,{id,label:name,icon,storage,onOpen:()=>{panel.hidden=false;onOpen();close.focus();},onClose:()=>{panel.hidden=true;}});close.addEventListener('click',()=>dock.close());
return {host,shadow,panel,body,settings,dock,e,setStatus(text){if(status.textContent!==text)status.textContent=text;},ensure(){if(!host.isConnected)document.body.append(host);dock.ensure();},destroy(){dock.destroy();host.remove();}};
}
function watchLitePage(document,callback) {
const window=document.defaultView;let queued=false,destroyed=false;
const owned=node=>{const element=node.nodeType===1?node:node.parentElement;const host=element?.getRootNode()?.host;return !!element?.closest('[data-flt-lite-panel],[data-flt-lite-dock],[data-flt-lite-port],[data-flt-lite-claim],[data-flt-lite-style],[data-flt-view-root],[data-flt-view-style]')||(typeof host?.hasAttribute==='function'&&host.hasAttribute('data-flt-lite-panel'));};
const schedule=()=>{if(queued||destroyed)return;queued=true;window.queueMicrotask(()=>{queued=false;if(!destroyed)callback();});};
const observer=new window.MutationObserver(records=>{if(records.some(record=>!owned(record.target)&&[...record.addedNodes,...record.removedNodes].some(node=>!owned(node))))schedule();});observer.observe(document.documentElement,{childList:true,subtree:true});
const events=['popstate','pageshow','turbo:load','turbo:render','turbolinks:load'];for(const event of events)window.addEventListener(event,schedule);
return ()=>{destroyed=true;observer.disconnect();for(const event of events)window.removeEventListener(event,schedule);};
}
// bootstrap.js
// Copyright 2026 TypicalBits.
async function bootstrapLite({document,window,storage,product,setup}) {
const claim=claimProduct(document,product.id);if(!claim)return {duplicate:true,destroy(){}};
let feature,stop=()=>{},ui;
try {
const refresh=()=>{claim.ensure();ui.ensure();feature?.refresh?.();};
ui=createLitePanel(document,{...product,storage,onOpen:refresh});ui.setStatus('Loading saved settings…');
feature=await setup({ui,document,window,storage});stop=watchLitePage(document,refresh);refresh();
return {refresh,destroy(){stop();feature?.destroy?.();ui.destroy();claim.release();}};
}catch(error){stop();feature?.destroy?.();ui?.destroy();claim.release();throw error;}
}
function liteButton(ui,label,attributes={},handler=()=>{}) {
const button=ui.e('button',{type:'button',...attributes},label);button.addEventListener('click',handler);return button;
}
function liteField(ui,name,label,value='') {
const id='flt-'+ui.host.dataset.fltLitePanel+'-'+name;const node=ui.e('input',{id,type:'text','data-field':name});node.value=value;
ui.body.append(ui.e('label',{for:id},label),node);return node;
}
// icon.js
const PRODUCT_ICON = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%20role%3D%22img%22%20aria-labelledby%3D%22title%20desc%22%3E%0A%20%20%3Ctitle%20id%3D%22title%22%3EFL%20Tools%20Notes%20badge%3C%2Ftitle%3E%0A%20%20%3Cdesc%20id%3D%22desc%22%3EFL%20Tools%20Notes%20badge.%20Artwork%20is%20border-free%3B%20CSS%20supplies%20the%20launcher%20and%20menu%20frames.%3C%2Fdesc%3E%0A%20%20%3Cdefs%3E%0A%20%20%20%20%3CradialGradient%20id%3D%22surface%22%20cx%3D%2238%25%22%20cy%3D%2228%25%22%20r%3D%2285%25%22%3E%0A%20%20%20%20%20%20%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2325242a%22%2F%3E%0A%20%20%20%20%20%20%3Cstop%20offset%3D%22.58%22%20stop-color%3D%22%2317171a%22%2F%3E%0A%20%20%20%20%20%20%3Cstop%20offset%3D%221%22%20stop-color%3D%22%230e0e10%22%2F%3E%0A%20%20%20%20%3C%2FradialGradient%3E%0A%20%20%20%20%3Cfilter%20id%3D%22labelGlow%22%20x%3D%22-40%25%22%20y%3D%22-60%25%22%20width%3D%22180%25%22%20height%3D%22220%25%22%3E%0A%20%20%20%20%20%20%3CfeGaussianBlur%20in%3D%22SourceGraphic%22%20stdDeviation%3D%22.85%22%20result%3D%22blur%22%2F%3E%0A%20%20%20%20%20%20%3CfeFlood%20flood-color%3D%22%23F39BC8%22%20flood-opacity%3D%22.42%22%20result%3D%22glowColor%22%2F%3E%0A%20%20%20%20%20%20%3CfeComposite%20in%3D%22glowColor%22%20in2%3D%22blur%22%20operator%3D%22in%22%20result%3D%22softGlow%22%2F%3E%0A%20%20%20%20%20%20%3CfeMerge%3E%0A%20%20%20%20%20%20%20%20%3CfeMergeNode%20in%3D%22softGlow%22%2F%3E%0A%20%20%20%20%20%20%20%20%3CfeMergeNode%20in%3D%22SourceGraphic%22%2F%3E%0A%20%20%20%20%20%20%3C%2FfeMerge%3E%0A%20%20%20%20%3C%2Ffilter%3E%0A%20%20%3C%2Fdefs%3E%0A%20%20%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22url%28%23surface%29%22%2F%3E%0A%20%20%3Cimage%20href%3D%22data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAE90lEQVR4AcyZW4hWVRTH%2FzM5NamVGXQhrKjHLCqsJgeMLhBGQaCkZEqXyWns60JBRUJkD0UQZMVoWSFJt4dIKOqpHoIQqgepXkpKRcuKKCu0LK%2B%2F5ezvzNnn7HPxm%2FMdz8daZ%2B%2B199prrf86%2B%2By9z%2Fl6VfB7SZpfoNKV7tXSqa9IVxcZzwWAkYsx8BBcOxHYrgPS2lFpap5z9MLdB6WeHmmVpD64djpewr3OmiQ9rpxfb1Yft%2B9GLAzAJ2fpdLP9P6nt915mwplZvoIALPvwCjfoNFfWWuyXTncO%2B4%2BRHnH1VBEE8Kp0FZoXwUZTmEftbJhcF0dZJ5l3viCdGHIcBMDDsySuTAYujct11Jm6cZ%2BT%2BzNWwxSAl6XJBDgPjtPlcaGm%2BkDCz%2BKEfFhMAQD5bHqmwhHRdkMk1FAhiSfhxuKgGCOm0SDtltyxBndNAUAxlW3aLmPwBW5M1wsStggnrKRcx6mP6iWwRykA9KYA0GbUsku3%2BQlpEgkbCfkBWHJaqTegeE6gzZqGOFbMsko3%2BQzpbuzPhFMEsLOTjSEAJySVnNxLBlaztc8wZlk7zrVPuFgn2VJ9LpvnhRh7Es4ieza8vhCA4HrrRs1ia99mTPR7eC5%2BhNetkebZrXc6hcVaaRpjlsEfMv6vf6VdLNU%2FsHx%2FxeDMPYcEpmILATgWI2XJNpvF3Np3ufWbCaaVB4QNcjrTcOX%2F0nbGjMLX4ygVFG1Z1JfsCAH4I6lUUp6B3osA2eimAuI4EfgCjgebyOL9tHrLNHIpAnAqthCAbaWsZSvNZCp8zvSwQMV06efOPE%2Fg7zDkFLhjwsbW5OAUAJQ2J5U6kPvJ1koCX7VX%2Bpjx98ETJhKTis0DYIc2HFe5645gb3DCkTsDJPc2V40KDwArgW0g06Pe5lXmcFe9jdYDQLwL4abTLfEAIwA8bNPoOB%2FOoMY0e4e8CAAPmx0hIrkx4aYDOS%2FeFAXMGj0l3tHgureHRAB4gP9scNDx0HbGhQgAe%2FRPdLDUcm0wsSxvj4cXAbhdsjvwrRr%2BI%2BAN8RCR46Le96QGCkyRD%2BJheQDY6VhNhU5cpVH1Lb9In8Qj8gAslTbR%2BSbcVFrBcd1LsAfAot4nLaf8G24UMTu%2B4E3njWRQKQD38LKBUiWnR%2BxURbtJ7JKbJbYr32QKgHUPS69T8k2V69Gng2R%2FaJn0XSiUIACnaC8kR31VIviHeTbtZciF5ReZALgLHI80n41jvT%2BkVmk5wT%2Bb5zETgA0yEGRgAfX34FoJv4%2Fh%2F6kip7kAbDBG7E4sxOBrJtfAe%2FG1lMw%2FXcZXIQAzYiAwOIThB5C9dRi5StrJlJ2LLz5slDNbCkDbFIbt64L9a7m73VZh%2BT1r5BV8V%2FR22iL7RwTAjAFiPYPsrWiLyRXxp5yGZ2ctlXk%2BiCWvO9x3l%2FQ1D4b9g2KfTMJK5VvX%2FCxde4f0W%2Fkh45odAbDhLel3HM%2Bl%2FhzcCfFJVIt4voY537DRdmJC6hiAuTPHw9KD1O0PiT2UY1R83dEjXcnYt4pV8zUmBKBt2gIhoGuQf4WLaCPpHuBZ%2BrJIsUx%2FJQDMEQFtYH21f1C%2BMTnEgHyb5WvQHRhDKkfcVhkA8zwibeXb%2FBzqn8FJGt0h3cp8s7mf7OtYrhSAReHera8j2x%2BZbEz9GaZZi2eGm2Qt1XHlACw0gv2HHfUm6vYC0mJ6PUq9K3QIAAD%2F%2F1wKF9MAAAAGSURBVAMAjeXXP35naSYAAAAASUVORK5CYII%3D%22%20x%3D%2216%22%20y%3D%226%22%20width%3D%2232%22%20height%3D%2232%22%20preserveAspectRatio%3D%22xMidYMid%20meet%22%2F%3E%0A%20%20%3Ctext%20x%3D%2232%22%20y%3D%2254%22%20fill%3D%22%23F39BC8%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2212%22%20font-weight%3D%22800%22%20letter-spacing%3D%22.1%22%20text-anchor%3D%22middle%22%20filter%3D%22url%28%23labelGlow%29%22%3ENOTES%3C%2Ftext%3E%0A%3C%2Fsvg%3E%0A";
// notes.js
// Copyright 2026 TypicalBits.
function normalizeNotes(input) {
if(!input||typeof input!=='object'||Array.isArray(input)||Object.keys(input).length>500)throw Error('Invalid saved notes.');
const output={};for(const [url,record]of Object.entries(input)){
if(pageKey(url)!==url||!record||typeof record.text!=='string'||record.text.length>20000)throw Error('Invalid saved note.');
output[url]={text:record.text,updated:Number.isFinite(record.updated)?record.updated:0};
}return output;
}
function createNotes(store,input) {
let page=pageKey(input),draft=store.value[page]?.text||'';
return {
get page(){return page;},get draft(){return draft;},get dirty(){return draft!==(store.value[page]?.text||'');},
edit(text){if(typeof text!=='string'||text.length>20000)throw Error('Notes can contain up to 20,000 characters.');draft=text;},
discard(){draft=store.value[page]?.text||'';},
switchPage(url){const next=pageKey(url);if(next===page)return true;if(this.dirty)return false;page=next;draft=store.value[page]?.text||'';return true;},
async save(){const snapshot=draft,key=page;await store.transact(value=>{
if(!Object.hasOwn(value,key)&&Object.keys(value).length>=500)throw Error('The 500-note limit has been reached.');
return {...value,[key]:{text:snapshot,updated:Date.now()}};
});},
async remove(){const key=page;await store.transact(value=>{delete value[key];return value;});if(page===key)draft='';}
};
}
// main.js
// Copyright 2026 TypicalBits.
function accountId(document) {
const ids=new Set([...document.querySelectorAll('[data-nav--ama-toggle-user-id-value]')].map(node=>node.getAttribute('data-nav--ama-toggle-user-id-value')));
const id=[...ids][0];return ids.size===1&&/^\d+$/.test(id)?id:null;
}
function startNotes({document,window,storage,copy=text=>window.navigator.clipboard.writeText(text)}) {
return bootstrapLite({document,window,storage,product:{id:'notes',name:'Notes',version:'0.1.1',icon:PRODUCT_ICON,accent:'#F39BC8'},setup:async({ui})=>{
const olderStore=createRecordStore(storage,'flt-notes:records:v1',normalizeNotes,window.navigator.locks);await olderStore.load();
const accounts=new Map();let active=null,notes=null,store=null,busy=false,message='',destroyed=false;
const page=ui.e('p',{class:'page'});const label=ui.e('label',{for:'notes-text'},'Your note');const text=ui.e('textarea',{id:'notes-text',maxlength:'20000','aria-label':'Your note'});
text.addEventListener('input',()=>{if(!notes||active!==accountId(document))return;try{notes.edit(text.value);message='';}catch(error){message=error.message;}refresh();});
const actions=ui.e('div',{class:'actions'});const save=liteButton(ui,'Save',{class:'primary','data-save':''},()=>perform(async model=>{await model.save();return 'Note saved.';}));
const copyButton=liteButton(ui,'Copy',{'data-copy':''},()=>perform(async model=>{try{await copy(model.draft);return 'Note copied.';}catch{return 'Copy is unavailable. Select the note text and copy it manually.';}}));
const remove=liteButton(ui,'Delete',{},()=>{confirmDelete.hidden=false;});actions.append(save,copyButton,remove);
const confirmDelete=ui.e('div',{class:'confirmation'});confirmDelete.hidden=true;confirmDelete.append(ui.e('p',{},'Delete the saved note and discard this page’s draft?'),liteButton(ui,'Delete note',{},()=>perform(async model=>{await model.remove();confirmDelete.hidden=true;return 'Note deleted.';})),liteButton(ui,'Cancel',{},()=>confirmDelete.hidden=true));
const switching=ui.e('div',{class:'confirmation','data-switch':''});switching.hidden=true;switching.append(ui.e('p',{},'You have an unsaved draft for the previous page.'),liteButton(ui,'Save & switch',{'data-save-switch':''},()=>perform(async model=>{await model.save();model.switchPage(window.location.href);return 'Previous note saved.';})),liteButton(ui,'Discard & switch',{},()=>{if(!notes||active!==accountId(document))return;notes.discard();notes.switchPage(window.location.href);message='';refresh();}));
const scope=ui.e('p',{class:'note'}),older=ui.e('details',{'data-older-notes':''}),olderBody=ui.e('div');
older.append(ui.e('summary',{},'Older notes'),ui.e('p',{class:'note'},'Older notes were shared across accounts. Review before importing. Originals are preserved.'),olderBody);
older.addEventListener('toggle',renderOlder);
ui.body.append(scope,page,label,text,actions,confirmDelete,switching,older,ui.e('p',{class:'note'},'Only text you enter is stored. Unsaved drafts stay in memory for their account until this page closes.'));
async function activate(id){
active=id;notes=null;store=null;message='';text.value='';older.open=false;olderBody.replaceChildren();confirmDelete.hidden=true;
if(!id)return;
let entry=accounts.get(id);
if(!entry){
const guarded={getValue:(key,fallback)=>storage.getValue(key,fallback),setValue:(key,value)=>{
if(accountId(document)!==id)throw Error('The signed-in account changed. Nothing was saved.');return storage.setValue(key,value);
}};
entry={store:createRecordStore(guarded,`flt-notes:account:${id}:records:v1`,normalizeNotes,window.navigator.locks)};
accounts.set(id,entry);
entry.ready=entry.store.load().then(()=>{entry.readable=!entry.store.error;entry.notes=createNotes(entry.store,window.location.href);});
}
await entry.ready;if(destroyed||active!==id||accountId(document)!==id)return;
store=entry.store;notes=entry.notes;refresh();
}
async function perform(action){
if(busy||!notes||!accounts.get(active)?.readable||active!==accountId(document))return;
const model=notes,id=active;busy=true;refresh();
try{const result=await action(model);if(id===active&&id===accountId(document))message=result||'';}
catch(error){if(id===active)message=error.message;}
finally{busy=false;if(!destroyed)refresh();}
}
function renderOlder(){
olderBody.replaceChildren();if(!older.open||!notes||active!==accountId(document))return;
if(olderStore.error){olderBody.append(ui.e('p',{class:'note'},olderStore.error));return;}
const accountStore=store,id=active,records=Object.entries(olderStore.value);
if(!records.length)olderBody.append(ui.e('p',{class:'note'},'No older notes.'));
for(const [url,record] of records){
const row=ui.e('div',{class:'record'});row.append(ui.e('strong',{},new URL(url).pathname),ui.e('p',{},record.text));
const exists=Object.hasOwn(accountStore.value,url);
const button=liteButton(ui,exists?'Already saved for this account':'Import into this account',{'data-import-note':''},()=>perform(async model=>{
if(id!==active)throw Error('The signed-in account changed.');
if(model.page===url&&model.dirty)throw Error('Save or discard your current draft before importing.');
await accountStore.transact(value=>{
if(Object.hasOwn(value,url))throw Error('A note already exists for this account. Nothing was overwritten.');
if(Object.keys(value).length>=500)throw Error('The 500-note limit has been reached.');
return {...value,[url]:record};
});
if(model.page===url)model.discard();renderOlder();return 'Older note imported. Original preserved.';
}));button.disabled=busy||exists;row.append(button);olderBody.append(row);
}
}
function refresh(){
if(destroyed)return;const id=accountId(document);if(id!==active)void activate(id);
const available=!!notes&&accounts.get(active).readable;
scope.textContent=id?'Notes for the signed-in account':'Account not identified. Notes are hidden.';older.hidden=!available;
if(notes){const target=pageKey(window.location.href);if(target!==notes.page)notes.switchPage(target);switching.hidden=target===notes.page;page.textContent=new URL(notes.page).pathname;if(text.value!==notes.draft)text.value=notes.draft;}
else{page.textContent='';text.value='';switching.hidden=true;}
text.disabled=busy||!available;for(const b of actions.querySelectorAll('button'))b.disabled=busy||!available;
for(const b of switching.querySelectorAll('button'))b.disabled=busy||!available;
ui.setStatus(message||store?.error||(!id?'Identify the signed-in account before using Notes.':!notes?'Loading this account’s notes…':notes.dirty?'Unsaved draft.':'Enter a note and select Save.'));
}
const observer=new window.MutationObserver(refresh);observer.observe(document.documentElement,{attributes:true,attributeFilter:['data-nav--ama-toggle-user-id-value'],subtree:true});
await activate(accountId(document));return {refresh,destroy(){destroyed=true;observer.disconnect();accounts.clear();}};
}});
}
void startNotes({document,window,storage:GM}).catch(()=>{});
})();