Porównaj commity

...

15 Commity

Autor SHA1 Wiadomość Data
Lartsch 22e3be13cb
Update README.md 2023-05-15 16:56:29 +02:00
Lartsch 97f9c10872
Update README.md 2023-05-15 16:56:19 +02:00
Lartsch 053af951bc
Merge pull request #65 from wlonkly/firefox-animations
namespace animations to avoid conflicts
2023-04-06 08:51:51 +02:00
Lartsch e0fbc804cc
Update README.md 2023-04-06 08:50:56 +02:00
Lartsch 6a9028afe0
Update README.md 2023-04-06 08:50:20 +02:00
Rich Lafferty 2289637543
minify 2023-01-22 00:02:19 -04:00
Rich Lafferty 8766852c9d
namespace animations 2023-01-22 00:02:19 -04:00
Lartsch 75cd8bb25a
Merge pull request #60 from ZeroEcks/main
Fix title of menu item to prevent confusion about the name of the plugin
2023-01-14 12:35:13 +00:00
Melody 7050e7b735
Fix title of menu item to prevent confusion about the name of the plugin 2023-01-09 13:17:23 +10:00
Lartsch f42eb8fd28
Merge pull request #53 from Freeplayg/main
Add https:// hint to Home Server Domain input
2023-01-02 20:33:08 +01:00
Freeplayg 15d59f96ea 5px in outline property wasn't needed 2023-01-02 14:31:23 -05:00
Freeplayg 6889c46fc0 Add https:// hint to Home Server Domain input 2023-01-02 12:25:44 -05:00
lartsch 2522f3f05c move in if 2022-12-29 11:12:33 -05:00
lartsch 0e20ff2f98 fix toot buttons in feed views when returning from redirect in "_self" mode, bump version 2022-12-29 10:47:09 -05:00
lartsch f23e7be502 improve 429 prevention (async limit), add ua 2022-12-29 10:10:59 -05:00
12 zmienionych plików z 105 dodań i 46 usunięć

Wyświetl plik

@ -1,3 +1,7 @@
> **Note**
> LOOKING FOR AN ACTIVE MAINTAINER TO TAKE CARE OF THIS PROJECT!
> FediAct requires some major updates/rewriting to resolve issues, increase performance/reliability and to be easier to maintain, for which I do not have time currently. Please leave a message/issue.
# FediAct (v0.9.8)
A Chrome/Firefox extension that simplifies follow and post interactions on Mastodon servers other than your own.

Wyświetl plik

@ -1,6 +1,6 @@
{
"name": "FediAct",
"version": "0.9.8.6",
"version": "0.9.8.7",
"description": "Simplifies interactions on other Mastodon instances than your own. Visit https://github.com/lartsch/FediAct for more.",
"manifest_version": 2,
"content_scripts": [
@ -37,7 +37,7 @@
"browser_action": {
"default_popup": "src/popup.html",
"default_icon": "src/icon/48.png",
"default_title": "FediFollow settings"
"default_title": "FediAct settings"
},
"icons": {
"48": "src/icon/48.png"

Wyświetl plik

@ -1,6 +1,6 @@
{
"name": "FediAct",
"version": "0.9.8.6",
"version": "0.9.8.7",
"description": "Simplifies interactions on other Mastodon instances than your own. Visit https://github.com/lartsch/FediAct for more.",
"manifest_version": 3,
"content_scripts": [

Wyświetl plik

@ -53,14 +53,19 @@ async function generalRequest(data) {
controller.abort()
}, timeout)
if (data[3]) {
// json body provided, post as body to target
data[2]["User-Agent"] = "FediAct Service"
data[2]["Content-Type"] = "application/json"
var res = await fetch(data[1], {
method: data[0],
signal: controller.signal,
// if json body is provided, there is also header data
headers: data[2],
body: JSON.stringify(data[3])
})
} else if (data[2]) {
// header data provided
data[2]["User-Agent"] = "FediAct Service"
var res = await fetch(data[1], {
method: data[0],
signal: controller.signal,
@ -69,7 +74,8 @@ async function generalRequest(data) {
} else {
var res = await fetch(data[1], {
method: data[0],
signal: controller.signal
signal: controller.signal,
headers: {"User-Agent": "FediAct Service"}
})
}
clearTimeout(timeoutId)

Wyświetl plik

@ -1 +1 @@
var browser,chrome,c;const a=!1,n="[FediAct]",t=1,i="/api/v1/mutes",o="/api/v1/blocks",s="/api/v1/domain_blocks",r=15e3,d=/"access_token":".*?",/gm,u={fediact_homeinstance:null,fediact_token:null};function h(t){a&&console.log(n+" "+t)}async function f(i){return new Promise(async function(e){try{const n=new AbortController;var t=setTimeout(()=>{h("Timed out"),n.abort()},r),a=await fetch(i,{method:"HEAD",signal:n.signal});clearTimeout(t),a.redirected?e(a.url):e(!1)}catch(t){h(t),e(!1)}})}async function l(c){return new Promise(async function(e){try{const i=new AbortController;var t,a,n=setTimeout(()=>{h("Timed out"),i.abort()},r);t=c[3]?(c[2]["Content-Type"]="application/json",await fetch(c[1],{method:c[0],signal:i.signal,headers:c[2],body:JSON.stringify(c[3])})):c[2]?await fetch(c[1],{method:c[0],signal:i.signal,headers:c[2]}):await fetch(c[1],{method:c[0],signal:i.signal}),clearTimeout(n),200<=t.status&&t.status<300&&(a=t.headers.get("content-type"))&&-1!==a.indexOf("application/json")?e(await t.text()):e(!1)}catch(t){h(t),e(!1)}})}async function m(){return new Promise(async function(e){var t="https://"+c.fediact_homeinstance;try{var a=await(await fetch(t)).text()}catch(t){return h(t),void e(!1)}if(a){t=a.match(d);if(t){var a=t[0].search(/"access_token":"/),n=t[0].search(/",/);if(-1<a&&-1<n){t=t[0].substring(a+=16,n);if(16<t.length)return c.fediact_token=t,void e(!0)}}}c.fediact_token=null,h("Token could not be found."),e(!1)})}function g(){return new Promise(async function(t){try{[c.fediact_mutes,c.fediact_blocks,c.fediact_domainblocks]=[[],[],[]];var[e,a,n]=await Promise.all([fetch("https://"+c.fediact_homeinstance+i,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json()),fetch("https://"+c.fediact_homeinstance+o,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json()),fetch("https://"+c.fediact_homeinstance+s,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json())]);e.length&&c.fediact_mutes.push(...e.map(t=>t.acct)),a.length&&c.fediact_blocks.push(...a.map(t=>t.acct)),n.length&&(c.fediact_domainblocks=n),t(!0)}catch{t(!1)}})}async function y(n,i){return new Promise(async function(t){var a=!1;try{if((c=await(browser||chrome).storage.local.get(u)).fediact_homeinstance){if(n||i){!n&&c.fediact_token||await m(),i&&await g();try{await(browser||chrome).storage.local.set(c),a=!0}catch{h(e)}}}else h("Home instance not set")}catch(t){h(t)}t(a)})}async function p(){chrome.tabs.query({},async function(t){for(var e=0;e<t.length;++e)try{chrome.tabs.sendMessage(t[e].id,{updatedfedisettings:!0})}catch(t){continue}})}chrome.runtime.onInstalled.addListener(function(){y(!0,!0)}),chrome.alarms.create("refresh",{periodInMinutes:t}),chrome.alarms.onAlarm.addListener(function(){y(!0,!0)}),chrome.runtime.onMessage.addListener((t,n,e)=>t.externaltoot?(f(t.externaltoot).then(e),!0):t.requestdata?(l(t.requestdata).then(e),!0):t.updatedsettings?(y(!0,!0).then(p),!0):t.updatemutedblocked?(y(!1,!0).then(e),!0):void(t.running&&chrome.tabs.onUpdated.addListener(async function(t,e,a){if(t===n.tab.id&&e.url)try{await chrome.tabs.sendMessage(t,{urlchanged:e.url})}catch(t){h(t)}})));
var browser,chrome,c;const a=!1,n="[FediAct]",t=1,i="/api/v1/mutes",s="/api/v1/blocks",o="/api/v1/domain_blocks",r=15e3,d=/"access_token":".*?",/gm,u={fediact_homeinstance:null,fediact_token:null};function h(t){a&&console.log(n+" "+t)}async function f(i){return new Promise(async function(e){try{const n=new AbortController;var t=setTimeout(()=>{h("Timed out"),n.abort()},r),a=await fetch(i,{method:"HEAD",signal:n.signal});clearTimeout(t),a.redirected?e(a.url):e(!1)}catch(t){h(t),e(!1)}})}async function l(c){return new Promise(async function(e){try{const i=new AbortController;var t,a,n=setTimeout(()=>{h("Timed out"),i.abort()},r);t=c[3]?(c[2]["User-Agent"]="FediAct Service",c[2]["Content-Type"]="application/json",await fetch(c[1],{method:c[0],signal:i.signal,headers:c[2],body:JSON.stringify(c[3])})):c[2]?(c[2]["User-Agent"]="FediAct Service",await fetch(c[1],{method:c[0],signal:i.signal,headers:c[2]})):await fetch(c[1],{method:c[0],signal:i.signal,headers:{"User-Agent":"FediAct Service"}}),clearTimeout(n),200<=t.status&&t.status<300&&(a=t.headers.get("content-type"))&&-1!==a.indexOf("application/json")?e(await t.text()):e(!1)}catch(t){h(t),e(!1)}})}async function m(){return new Promise(async function(e){var t="https://"+c.fediact_homeinstance;try{var a=await(await fetch(t)).text()}catch(t){return h(t),void e(!1)}if(a){t=a.match(d);if(t){var a=t[0].search(/"access_token":"/),n=t[0].search(/",/);if(-1<a&&-1<n){t=t[0].substring(a+=16,n);if(16<t.length)return c.fediact_token=t,void e(!0)}}}c.fediact_token=null,h("Token could not be found."),e(!1)})}function g(){return new Promise(async function(t){try{[c.fediact_mutes,c.fediact_blocks,c.fediact_domainblocks]=[[],[],[]];var[e,a,n]=await Promise.all([fetch("https://"+c.fediact_homeinstance+i,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json()),fetch("https://"+c.fediact_homeinstance+s,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json()),fetch("https://"+c.fediact_homeinstance+o,{headers:{Authorization:"Bearer "+c.fediact_token}}).then(t=>t.json())]);e.length&&c.fediact_mutes.push(...e.map(t=>t.acct)),a.length&&c.fediact_blocks.push(...a.map(t=>t.acct)),n.length&&(c.fediact_domainblocks=n),t(!0)}catch{t(!1)}})}async function y(n,i){return new Promise(async function(t){var a=!1;try{if((c=await(browser||chrome).storage.local.get(u)).fediact_homeinstance){if(n||i){!n&&c.fediact_token||await m(),i&&await g();try{await(browser||chrome).storage.local.set(c),a=!0}catch{h(e)}}}else h("Home instance not set")}catch(t){h(t)}t(a)})}async function p(){chrome.tabs.query({},async function(t){for(var e=0;e<t.length;++e)try{chrome.tabs.sendMessage(t[e].id,{updatedfedisettings:!0})}catch(t){continue}})}chrome.runtime.onInstalled.addListener(function(){y(!0,!0)}),chrome.alarms.create("refresh",{periodInMinutes:t}),chrome.alarms.onAlarm.addListener(function(){y(!0,!0)}),chrome.runtime.onMessage.addListener((t,n,e)=>t.externaltoot?(f(t.externaltoot).then(e),!0):t.requestdata?(l(t.requestdata).then(e),!0):t.updatedsettings?(y(!0,!0).then(p),!0):t.updatemutedblocked?(y(!1,!0).then(e),!0):void(t.running&&chrome.tabs.onUpdated.addListener(async function(t,e,a){if(t===n.tab.id&&e.url)try{await chrome.tabs.sendMessage(t,{urlchanged:e.url})}catch(t){h(t)}})));

Wyświetl plik

@ -34,8 +34,8 @@
display: -ms-flexbox;
display: flex;
font-size: 1rem;
-webkit-animation: fadeIn .2s;
animation: fadeIn .2s;
-webkit-animation: fa_fadeIn .2s;
animation: fa_fadeIn .2s;
}
.fediactmodalinner {
@ -46,8 +46,8 @@
margin: auto;
padding: .4em;
border-radius: 8px;
-webkit-animation: scaleInSmall .2s;
animation: scaleInSmall .2s;
-webkit-animation: fa_scaleInSmall .2s;
animation: fa_scaleInSmall .2s;
}
.fediactmodalitem {
position: relative;
@ -93,8 +93,8 @@
border-radius: 4px;
background-color: white;
color: var(--confirmation);
-webkit-animation: scaleInFadeSmall .2s;
animation: scaleInFadeSmall .2s;
-webkit-animation: fa_scaleInFadeSmall .2s;
animation: fa_scaleInFadeSmall .2s;
}
.fediactmodallink.activated {
-webkit-box-shadow: inset 300px 0 0 var(--confirmation);
@ -178,45 +178,45 @@
color: blue !important;
}
/* Keyframes */
/* keyframes fa_*/
@-webkit-keyframes fadeIn {
@-webkit-keyframes fa_fadeIn {
from {
-webkit-filter: opacity(0);
filter: opacity(0);
}
}
@keyframes fadeIn {
@keyframes fa_fadeIn {
from {
-webkit-filter: opacity(0);
filter: opacity(0);
}
}
@-webkit-keyframes fadeOut {
@-webkit-keyframes fa_fadeOut {
to {
-webkit-filter: opacity(0);
filter: opacity(0);
}
}
@keyframes fadeOut {
@keyframes fa_fadeOut {
to {
-webkit-filter: opacity(0);
filter: opacity(0);
}
}
@-webkit-keyframes scaleInSmall {
@-webkit-keyframes fa_scaleInSmall {
from {
-webkit-transform: scale(.98);
transform: scale(.98);
}
}
@keyframes scaleInSmall {
@keyframes fa_scaleInSmall {
from {
-webkit-transform: scale(.98);
transform: scale(.98);
}
}
@-webkit-keyframes scaleInFadeSmall {
@-webkit-keyframes fa_scaleInFadeSmall {
from {
-webkit-transform: scale(.98);
transform: scale(.98);
@ -224,7 +224,7 @@
filter: opacity(0);
}
}
@keyframes scaleInFadeSmall {
@keyframes fa_scaleInFadeSmall {
from {
-webkit-transform: scale(.98);
transform: scale(.98);
@ -234,41 +234,41 @@
}
/*
/*
We insert these styles for the DOMNodeAppeared function as separate stylesheet (see manifest) to fix
Firefox blocking script-inserted <style> elements due to the site's CSP (long time bug)
*/
@-moz-keyframes nodeInserted {
@-moz-keyframes fa_nodeInserted {
from {
opacity: 1;
} to {
opacity: 1;
}
}
@-webkit-keyframes nodeInserted {
@-webkit-keyframes fa_nodeInserted {
from {
opacity: 1;
} to {
opacity: 1;
}
}
@-ms-keyframes nodeInserted {
@-ms-keyframes fa_nodeInserted {
from {
opacity: 1;
} to {
opacity: 1;
}
}
@-o-keyframes nodeInserted {
@-o-keyframes fa_nodeInserted {
from {
opacity: 1;
} to {
opacity: 1;
}
}
@keyframes nodeInserted {
from {
@keyframes fa_nodeInserted {
from {
opacity: 1;
} to {
opacity: 1;
@ -285,15 +285,15 @@ div.account-card a.logo-button,
div.directory-card a.icon-button,
div.detailed-status a.logo-button,
button.remote-button,
script#initial-state {
-webkit-animation-name: nodeInserted !important;
script#initial-state {
-webkit-animation-name: fa_nodeInserted !important;
-webkit-animation-duration: 0.001s !important;
-ms-animation-name: nodeInserted !important;
-ms-animation-name: fa_nodeInserted !important;
-ms-animation-duration: 0.001s !important;
-moz-animation-name: nodeInserted !important;
-moz-animation-name: fa_nodeInserted !important;
-moz-animation-duration: 0.001s !important;
-o-animation-name: nodeInserted !important;
-o-animation-name: fa_nodeInserted !important;
-o-animation-duration: 0.001s !important;
animation-name: nodeInserted !important;
animation-name: fa_nodeInserted !important;
animation-duration: 0.001s !important;
}

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -17,9 +17,10 @@ const mutesApi = "/api/v1/mutes"
const blocksApi = "/api/v1/blocks"
const domainBlocksApi = "/api/v1/domain_blocks"
const pollsApi = "/api/v1/polls"
const apiDelay = 650
const apiDelay = 600
const maxTootCache = 200
const modalHtml = '<div class="fediactmodal"><div class="fediactmodalinner"><ul class="fediactmodallist"></ul></div></div>'
const maxAsyncRequests = 10
// settings keys with defauls
var settings = {}
@ -78,7 +79,7 @@ function log(text) {
// catch all animationstart events
$(document).on('animationstart webkitAnimationStart oanimationstart MSAnimationStart', function(e){
// check if the animatonname equals our animation and if the element is one of our selectors
if (e.originalEvent.animationName == 'nodeInserted' && $(e.target).is(selector)) {
if (e.originalEvent.animationName == 'fa_nodeInserted' && $(e.target).is(selector)) {
if (typeof callback == 'function') {
// return the complete object in the callback
callback(e)
@ -124,6 +125,20 @@ var getUrlParameter = function getUrlParameter(sParam) {
return false
}
const asyncLimit = (fn, n) => {
let pendingPromises = []
return async function (...args) {
while (pendingPromises.length >= n) {
await Promise.race(pendingPromises).catch(() => {})
}
const p = fn.apply(this, args)
pendingPromises.push(p)
await p.catch(() => {})
pendingPromises = pendingPromises.filter(pending => pending !== p)
return p
}
}
// promisified xhr for api calls
function makeRequest(method, url, extraheaders, jsonbody) {
return new Promise(async function(resolve) {
@ -142,7 +157,6 @@ function makeRequest(method, url, extraheaders, jsonbody) {
}, apiDelay-difference)
})
}
// TODO: move this to the top? or get new Date.now() here?
tmpSettings.lasthomerequest = Date.now()
}
try {
@ -162,6 +176,9 @@ function makeRequest(method, url, extraheaders, jsonbody) {
})
}
// wrap so there are never more than 10 concurrent requests
const requestAsyncLimited = asyncLimit(makeRequest, maxAsyncRequests)
// Escape characters used for regex
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
@ -176,6 +193,16 @@ function replaceAll(str, find, replace) {
function redirectTo(url) {
// check if redirects are enabled at all
if (settings.fediact_redirects) {
if (settings.fediact_target == "_self") {
/* If browser back button was used, flush cache */
(function () {
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
})()
}
// check if alert before redirect is enabled and show the prompt if so
if (settings.fediact_alert) {
if (!confirm("Redirecting to " + url)) {
@ -283,7 +310,7 @@ async function executeAction(data, action, polldata) {
return
}
if (requestUrl) {
var response = await makeRequest(method, requestUrl, tmpSettings.tokenheader, jsonbody)
var response = await requestAsyncLimited(method, requestUrl, tmpSettings.tokenheader, jsonbody)
if (response) {
// convert to json object
response = JSON.parse(response)
@ -307,7 +334,7 @@ async function isFollowingHomeInstance(ids) {
requestUrl += "id[]=" + id.toString() + "&"
}
// make the request
var responseFollowing = await makeRequest("GET", requestUrl, tmpSettings.tokenheader, null)
var responseFollowing = await requestAsyncLimited("GET", requestUrl, tmpSettings.tokenheader, null)
// fill response array according to id amount with false
const follows = Array(ids.length).fill(false)
// parse the response
@ -379,7 +406,7 @@ function checkAllMutedBlocked(handle) {
// Return the user id on the users home instance
async function resolveHandleToHome(handle) {
var requestUrl = 'https://' + settings.fediact_homeinstance + accountsApi + "/search?q=" + handle + "&resolve=true&limit=1&exclude_unreviewed=false"
var searchResponse = await makeRequest("GET", requestUrl, tmpSettings.tokenheader, null)
var searchResponse = await requestAsyncLimited("GET", requestUrl, tmpSettings.tokenheader, null)
if (searchResponse) {
searchResponse = JSON.parse(searchResponse)
if (searchResponse[0].id) {
@ -393,7 +420,7 @@ async function resolveHandleToHome(handle) {
// resolve a toot to the users home instance
async function resolveTootToHome(searchstring) {
var requestUrl = 'https://' + settings.fediact_homeinstance + searchApi + "/?q=" + searchstring + "&resolve=true&limit=1&exclude_unreviewed=false"
var response = await makeRequest("GET", requestUrl, tmpSettings.tokenheader, null)
var response = await requestAsyncLimited("GET", requestUrl, tmpSettings.tokenheader, null)
if (response) {
response = JSON.parse(response)
// do we have a status as result?
@ -860,6 +887,9 @@ async function processToots() {
var actionExecuted = await executeAction(id, action, null)
if (actionExecuted) {
if (cacheIndex) {
console.log(cacheIndex)
console.log(tmpSettings.processed[cacheIndex])
console.log(tmpSettings.processed)
// set interacted to true
tmpSettings.processed[cacheIndex][11] = true
}
@ -1471,7 +1501,7 @@ async function checkSite() {
// last check - and probably the most accurate to determine if it actually is mastadon
var requestUrl = location.protocol + '//' + location.hostname + instanceApi
// call instance api to confirm its mastodon and get normalized handle uri
var response = await makeRequest("GET", requestUrl, null, null)
var response = await requestAsyncLimited("GET", requestUrl, null, null)
// todo: add basic check for "mastodon" string in response
if (response) {
var uri = JSON.parse(response).uri

2
src/inject.min.js vendored

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -21,7 +21,10 @@
Make sure you are logged in to your home server
</span>
</label>
<input type="text" id="homeinstance" name="homeinstance" placeholder="example.social">
<div class="text-input-container">
<span>https://</span>
<input type="text" id="homeinstance" name="homeinstance" placeholder="example.social">
</div>
</div>
</fieldset>
<fieldset>

Wyświetl plik

@ -118,8 +118,24 @@ label span {
opacity: .7;
margin-top: .2em;
}
input[type="text"] {
.text-input-container {
width: 100%;
display: flex;
border: 1px solid var(--border);
border-radius: calc(var(--radius) / 2);
overflow: hidden;
}
.text-input-container span {
padding: .7em;
background-color: var(--highlight);
}
.text-input-container input {
border: 0 !important;
border-radius: 0 !important;
outline: 0;
}
.text-input-container:focus-within {
outline: auto; /* This is the browser's default focus outline */
}
input, textarea, select {
border: 1px solid var(--border);

Wyświetl plik

@ -1 +1 @@
html,body{margin:0;height:var(--popup-height);min-width:300px;max-height:900px}html{--highlight:rgba(130,130,150,0.1);--confirmation:#268500;--hover:rgba(120,120,130,0.15);--border:rgba(120,120,130,0.3);--border-2:rgba(130,130,150,0.3);--radius:12px;--popup-height:450px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%}body{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;min-height:var(--popup-height)}.hide{display:none}h1{font-size:1em;margin-block:.2em}h2{font-size:1em}#mhi-wrapper,#mhi-containers{display:contents}form{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%}.scroller{height:0;overflow-y:auto;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;padding:10px;-webkit-box-sizing:border-box;box-sizing:border-box}fieldset{all:unset;display:block}legend{all:unset}fieldset>legend,summary{margin-inline:.6em;padding-top:.6em;margin-bottom:.4em;font-weight:700}.row{background-color:var(--highlight);padding:.8em;-webkit-box-sizing:border-box;box-sizing:border-box;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-bottom:2px;position:relative;gap:.4em;-webkit-transition:background-color .1s;-o-transition:background-color .1s;transition:background-color .1s}.row:hover,.row:focus-within{background:var(--hover)}.row:first-of-type{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.row:last-of-type{border-bottom-left-radius:var(--radius);border-bottom-right-radius:var(--radius)}label{width:0;-webkit-box-flex:9999;-ms-flex-positive:9999;flex-grow:9999}label::before{content:"";position:absolute;inset:0}label span{display:block;font-size:.9em;opacity:.7;margin-top:.2em}input[type="text"]{width:100%}input,textarea,select{border:1px solid var(--border);padding:.7em;border-radius:calc(var(--radius) / 2);font-size:inherit;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;z-index:2}.textarea-container{width:calc(100% - .14em);border-radius:inherit}textarea{resize:vertical;width:100%}select{margin-block:-.8em;padding-block:.4em;border:0;background:0;text-align:right}.footer{padding:10px;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;border-top:1px dashed var(--border-2);position:relative;background-color:var(--highlight);-webkit-box-shadow:0 0 24px rgba(0,0,0,0.1);box-shadow:0 0 24px rgba(0,0,0,0.1)}[type="submit"]{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;padding-inline:2em}[type="submit"]:not(:hover):not(:focus){background:0}.footer span{width:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:right;padding-inline:1em}.footer a{opacity:.7;text-underline-offset:.2em;color:inherit}#indicator{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1;width:calc(100% + 20px);font-weight:600;padding:.7em;margin:-10px;margin-bottom:8px;background-color:var(--confirmation);text-align:center;color:white}@media(prefers-color-scheme:dark){body{color:white;background-color:#1c1b22}}
html,body{margin:0;height:var(--popup-height);min-width:300px;max-height:900px}html{--highlight:rgba(130,130,150,0.1);--confirmation:#268500;--hover:rgba(120,120,130,0.15);--border:rgba(120,120,130,0.3);--border-2:rgba(130,130,150,0.3);--radius:12px;--popup-height:450px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%}body{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;min-height:var(--popup-height)}.hide{display:none}h1{font-size:1em;margin-block:.2em}h2{font-size:1em}#mhi-wrapper,#mhi-containers{display:contents}form{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%}.scroller{height:0;overflow-y:auto;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;padding:10px;-webkit-box-sizing:border-box;box-sizing:border-box}fieldset{all:unset;display:block}legend{all:unset}fieldset>legend,summary{margin-inline:.6em;padding-top:.6em;margin-bottom:.4em;font-weight:700}.row{background-color:var(--highlight);padding:.8em;-webkit-box-sizing:border-box;box-sizing:border-box;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-bottom:2px;position:relative;gap:.4em;-webkit-transition:background-color .1s;-o-transition:background-color .1s;transition:background-color .1s}.row:hover,.row:focus-within{background:var(--hover)}.row:first-of-type{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.row:last-of-type{border-bottom-left-radius:var(--radius);border-bottom-right-radius:var(--radius)}label{width:0;-webkit-box-flex:9999;-ms-flex-positive:9999;flex-grow:9999}label::before{content:"";position:absolute;inset:0}label span{display:block;font-size:.9em;opacity:.7;margin-top:.2em}.text-input-container{width:100%;display:flex;border:1px solid var(--border);border-radius:calc(var(--radius) / 2);overflow:hidden}.text-input-container span{padding:.7em;background-color:var(--highlight)}.text-input-container input{border:0 !important;border-radius:0 !important;outline:0}.text-input-container:focus-within{outline:auto}input,textarea,select{border:1px solid var(--border);padding:.7em;border-radius:calc(var(--radius) / 2);font-size:inherit;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;z-index:2}.textarea-container{width:calc(100% - .14em);border-radius:inherit}textarea{resize:vertical;width:100%}select{margin-block:-.8em;padding-block:.4em;border:0;background:0;text-align:right}.footer{padding:10px;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;border-top:1px dashed var(--border-2);position:relative;background-color:var(--highlight);-webkit-box-shadow:0 0 24px rgba(0,0,0,0.1);box-shadow:0 0 24px rgba(0,0,0,0.1)}[type="submit"]{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;padding-inline:2em}[type="submit"]:not(:hover):not(:focus){background:0}.footer span{width:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:right;padding-inline:1em}.footer a{opacity:.7;text-underline-offset:.2em;color:inherit}#indicator{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1;width:calc(100% + 20px);font-weight:600;padding:.7em;margin:-10px;margin-bottom:8px;background-color:var(--confirmation);text-align:center;color:white}@media(prefers-color-scheme:dark){body{color:white;background-color:#1c1b22}}