Reading typography for individual FetLife writings
// ==UserScript==
// @name FL Tools Read
// @namespace https://gitlab.com/fl-tools-lite/fl-tools-read
// @version 0.1.1
// @description Reading typography for individual FetLife writings
// @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-read
// @supportURL https://gitlab.com/fl-tools-lite/fl-tools-read/-/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%20Read%20badge%3C%2Ftitle%3E%0A%20%20%3Cdesc%20id%3D%22desc%22%3EFL%20Tools%20Read%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%23B9A0F5%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%23B9A0F5%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%3EREAD%3C%2Ftext%3E%0A%3C%2Fsvg%3E%0A";
// read.js
// Copyright 2026 TypicalBits.
function normalizeReading(value) {
const v=value&&typeof value==='object'?value:{};
return {enabled:v.enabled===true,width:[640,760,880].includes(v.width)?v.width:760,size:[16,18,20].includes(v.size)?v.size:18,spacing:[1.5,1.7,1.9].includes(v.spacing)?v.spacing:1.7};
}
function findWriting(document,input) {
const url=new URL(input);
if(url.hostname!=='fetlife.com'||!/^\/(?:users\/)?[^/]+\/(?:posts|writings)\/[^/]+\/?$/.test(url.pathname))return null;
const candidates=[...document.querySelectorAll('main [itemprop="articleBody"],main [data-writing-body],main .writing-body,main .writing__body,main .writing-content,main article:has(h1) > .story__copy')].filter(x=>!x.closest('.comments,#comments_wrapper,[data-flt-lite-panel]'));
return candidates.length===1?candidates[0]:null;
}
function createReading(document) {
let target=null;const style=document.createElement('style');style.dataset.fltReadStyle='';style.dataset.fltLiteStyle='';
function clear(){target?.removeAttribute('data-flt-read-body');target=null;style.remove();}
return {
apply(settings,url){const value=normalizeReading(settings),next=findWriting(document,url);
if(!value.enabled||!next){clear();return next?'Original appearance.':'Open an individual writing to use Read.';}
if(target!==next){clear();target=next;target.setAttribute('data-flt-read-body','');}
const css=`[data-flt-read-body]{width:100%!important;max-width:${value.width}px!important;margin-inline:auto!important;font-size:${value.size}px!important;line-height:${value.spacing}!important;overflow-wrap:anywhere}[data-flt-read-body] :is(p,li,blockquote){font-size:inherit!important;line-height:inherit!important}`;
if(style.textContent!==css)style.textContent=css;if(!style.isConnected)document.head.append(style);return 'Reading appearance is active.';
},destroy:clear
};
}
// main.js
// Copyright 2026 TypicalBits.
function startRead({document,window,storage}) {
return bootstrapLite({document,window,storage,product:{id:'read',name:'Read',version:'0.1.1',icon:PRODUCT_ICON,accent:'#B9A0F5'},setup:async({ui})=>{
const store=createRecordStore(storage,'flt-read:settings:v1',normalizeReading,window.navigator.locks);let value=await store.load();value=normalizeReading(value);const reading=createReading(document);let message='';
const row=ui.e('div',{class:'row'});row.append(ui.e('label',{id:'read-enabled'},'Reading appearance'));const toggle=liteButton(ui,'',{'role':'switch','aria-labelledby':'read-enabled','aria-checked':'false',class:'switch'},()=>change({...value,enabled:!value.enabled}));row.append(toggle);ui.body.append(row);
const groups=[];for(const [key,label,choices]of [['width','Writing width',[640,760,880]],['size','Text size',[16,18,20]],['spacing','Line spacing',[1.5,1.7,1.9]]]){
const field=ui.e('fieldset');field.append(ui.e('legend',{},label));const group=ui.e('div',{class:'choices'});
for(const choice of choices)group.append(liteButton(ui,String(choice)+(key==='spacing'?'':' px'),{'aria-pressed':'false','data-setting':key,'data-value':choice},()=>change({...value,[key]:choice})));
field.append(group);ui.body.append(field);groups.push(group);
}
ui.body.append(liteButton(ui,'Original appearance',{},()=>change({...value,enabled:false})),ui.e('p',{class:'note'},'Applies only to an individual writing. Website colors and comments stay native.'));
ui.settings.append(liteButton(ui,'Restore defaults',{},()=>change(normalizeReading({}))));
function refresh(){toggle.setAttribute('aria-checked',String(value.enabled));for(const group of groups)for(const button of group.children)button.setAttribute('aria-pressed',String(+button.dataset.value===value[button.dataset.setting]));ui.setStatus([reading.apply(value,window.location.href),message||store.error].filter(Boolean).join(' '));}
async function change(next){value=normalizeReading(next);message='';refresh();try{await store.transact(()=>normalizeReading(next));}catch(error){message=error.message;}refresh();}
return {refresh,destroy(){reading.destroy();}};
}});
}
void startRead({document,window,storage:GM}).catch(()=>{});
})();