UserScript / UserStyle

Hi, I started to use adblocker AdGuard. It’s not a browser extension but a full app. And it’s really great.

However it doesn’t play 100% nicely with DEVONthink.

Network Filtering

AdGuard’s Network Filtering makes it possible to

  • browse in e.g. Safari
  • remove unwanted parts of a website
  • those removed parts are removed across all apps
    (that were added in AdGuard’s preferences Network > Automatically filter applications)

See this quite noisy example, top is Safari, bottom DEVONthink:

After removing everything I don’t need:

Userscripts

AdGuard can also be used as a UserScript agent. From AdGuard’s knowledgebase, Userscripts:

What is a userscript? Userscripts (we also call them ‘extensions’ sometimes) de-facto are mini-programs written in Javascript. They modify or extend functionality of one or more websites.

I managed to create a UserScript that injects CSS in stackoverflow.com sites to show code blocks in the code’s width which is quite useful:

However, the CSS isn’t applied in DEVONthink (meanwhile tested other apps and it seems it’s injected only in “real” browsers, e.g. Firefox and Vivaldi but not in e.g. CodeRunner or Dash).

Is injection something that simply isn’t possible or is it something DEVONthink intentionally doesn’t allow?

I seem too remember a settings option related to JavaScript. May be that has something to do with it.

There is one but it doesn’t change the result.

Now, there are two aspects to that.

  1. How do you “inject” your script in the page?
  2. What does the script do?

If I understand your description correctly, the anser to the 2nd question is “it manipulates the DOM and/or the CSS”. Since the DOM is something the browser (or rather a “user agent”) implements, other software like CodeRunner or Dash can’t do anything with your DOM methods. There are other objects that are browser specific, notably document and window. The latter is relevant if you inject your script via addEventListener - this method is defined for the browser’s global window object only.

Back to DT: Since DT renders HTML, it should instantiate a Webview (or whatever Apple’s incantation of a HTML interpreter is called). It should be able to do what you want. And I know that it can do it when rendering MD files because I fiddled around with JS in this context.

So maybe if you could show some code for your userscript, we could figure out what is going on. BTW, the Adguard online FAQ says that userscripts are currently not supported on MacOS. But maybe the FAQ is outdated.

I have too little knowledge of the whole topic, so I’m afraid I can only say: the UserScript adds CSS from a file.

UserScript:

// ==UserScript==
//
// @name				Stackoverflow Netzwerk — CSS
//
// @match				*://*askubuntu.com/*
// @match				*://*mathoverflow.net/*
// @match				*://*serverfault.com/*
// @match				*://*stackapps.com/*
// @match				*://*stackexchange.com/*
// @match				*://*stackoverflow.com/*
// @match				*://*superuser.com/*
//
// @grant				GM_log
// @grant				GM_getResourceText
// @grant				GM_addStyle
//
// @resource			theCSSFile file:///Users/USER/Library/Application%20Support/AdGuard/AdGuard%20-%20UserScript/Verlinkte%20CSS%20Dateien/Userscripts%20(Safari%20Extension)/Data/Documents/scripts/AdGuard%20-%20Stackoverflow%20Netzwerk%20-%20CSS.css
//
// ==/UserScript== 


(function () {
    //console.log("Start: CSS hinzufügen");
    var theCSSText  = GM_getResourceText("theCSSFile");
    //console.log(theCSSText);
    GM_addStyle (theCSSText);
    //console.log("Ende: CSS hinzufügen");
})();

UserStyle:

/* ==UserStyle==

@name				AdGuard - Stackoverflow Netzwerk - CSS

@match				*://*askubuntu.com/*
@match				*://*mathoverflow.net/*
@match				*://*serverfault.com/*
@match				*://*stackapps.com/*
@match				*://*stackexchange.com/*
@match				*://*stackoverflow.com/*
@match				*://*superuser.com/*

==/UserStyle== */

#mainbar, .mainbar {
	width:calc(100% - 80px);
	max-width: 80ch;					
}

pre, pre.s-code-block, .s-prose pre:not(.s-code-block) {
	max-height:fit-content;
	width:max-content; 
	min-width:100%;				
    background-color:#fffaef;			
	white-space: pre-wrap;
}

.snippet-code {
	width:fit-content;
	min-width: 100%;
}

Yes, that’s the reason I didn’t try it immediately after I started to use AdGuard. I must have overlooked the part that says “outdated” sometime later, tried it and it does work. So the info is outdated.

Sounds very good!

By the way, thanks a ton for posting a capture of Safari’s web inspector in another thread! I knew it was there but never used it.

One can open an URL and view the site inside CodeRunner (and manipulate it) and one can search e.g. stackoverflow and view the results inside Dash, so I guess these apps act like normal browsers in this situation (but again, I have little knowledge about theses things).

(In CodeRunner it’s menu View > Show console, in the “Inject into” dropdown select “Website”, paste an URL, hit Enter)

The little code that is there looks like it is using the GreaseMonkey API. That was a famous framework at its time permitting to inject JavaScript in web pages, and people used it for all kinds of stuff. Ok, enough of the past.

After having had a look ad the Adguard site, I noticed that they are deliberately vague about the technical background of their Userscripts. Conceivably, they add their GM API to the currently installed browsers via a browser extension, so it is available there. But presumably they don’t know anything of DT nor its usage of a Webview, so they’re not going to inject their GM API into it.

Digging around some more led me to Tampermonkey • Documentation, and that confirmed what I thought: All these GM_ methods are (re-)implementations of the old GM stuff in a browser extension (!). So: no browser, no extension, no GM_ methods. Just to make it cristal clear: the GM_ methods are not part of JavaScript per se or JavaScript in a browser, they are an API that is added to a browser.

So your way of injecting CSS into DT will most certainly not work. However, you could write a (Java)script that simply adds the whole script element to all HTML files that you download from Stackexchange. Since they’re alread fairly cleaned up when they arrive in your DT inbox, that shouldn’t be too hard. (If you like it harder, you can of course use AppleScript :wink:

2 Likes

That’s the epitome of cool. Thanks for letting me know. Apparently, they included a Webview in their product, so the JavaScript console has access to window and document globals. Why didn’t I know that before?

Still: The Webview does not provide any GM methods. So…

1 Like

Not sure I understand. I don’t have AdGuard extensions installed for Firefox and Vivaldi. But the CSS is applied there (I left Dark Reader enabled while taking the capture):

Do you mean AdGuard doesn’t add it to DEVONthink because AdGuard doesn’t know that DEVONthink got a built-in browser? And it adds it to Firefox and Vivaldi because it knows they are browsers?

No Adguard Assistant?

Something like that. Really, I don’t know. As I said, they’re fairly vague on their website. Overly, I’d say. But I do not see another way to get GM methods into a browser than by installing an extension. On the other hand, DT will certainly not allow them to install anything that remotely resembles a browser extension. Nor does Webview offer a browser extension API.

I’d have investigated further, but Adguard is by far too intrusive for my taste (and not really forthcoming with information). They even want to install a root certificate on my machine to be able to inspect HTTPS traffic! That goes completely against the idea of HTTPS. If I wanted someone to see my traffic, I’d use HTTP. And then they want to add a proxy configuration – seriously? Have my web traffic go over their proxy? Not in this part of the universe. So, no further insight into this, I’m afraid. You might want to try the ‘add CSS with a little JS in a smart rule’ approach :wink:

1 Like

Nope, not for Firefox and Vivaldi. Thanks!

A basic JavaScript to add your CSS to an HTML (any HTML, in fact) might look like this

(() => {
  const app = Application("DEVONthink 3");
  const records = app.selectedRecords();
  const myCSS = `put your CSS between backticks, linebreaks and all`;
  records.forEach(r => {
    const t = r.plainText();
    r.plainText = t.replace(/<\/head>/,`${myCSS}\n</head>`);
  })
})()

As usual, I didn’t test it and it comes without warranty. And of course you can’t yet use it in a smart rule.