- When using SingleFile as:
- an extension, enable the hidden option
userScriptEnabledby exporting the settings from the options page, editing the JSON file, replacinguserScriptEnabled: falsewithuserScriptEnabled: true, and importing the modified file in SingleFile. You will also need to install an extension to store and run user scripts (search "user script" on stores). - a CLI tool, use the option
--browser-scriptto pass script path(s) to SingleFile.
- Dispatch the custom event
single-file-user-script-initin the user script.
dispatchEvent(new CustomEvent("single-file-user-script-init"));
- Listen to the custom event
single-file-on-before-capture-requestin the user script. The listener function is called just before the page is saved.
addEventListener("single-file-on-before-capture-request", () => {
console.log("The page will be saved by SingleFile");
});
If the listener function has to be async, call event.preventDefault() and dispatch the custom event single-file-on-before-capture-response once the asynchronous tasks are terminated.
addEventListener("single-file-on-before-capture-request", async event => {
event.preventDefault();
try {
await ...
} finally {
dispatchEvent(new CustomEvent("single-file-on-before-capture-response"));
}
});
You can also get and set the options via the detail property of the single-file-on-before-capture-request and single-file-on-before-capture-response events. If the detail value is tainted (e.g. in Firefox), you should dispatch the single-file-user-script-init event with { detail: "jsonDetail" } as option. Then, you have to handle the detail value as a JSON string (instead of an object).
- Listen to the custom event
single-file-on-after-capture-requestin the user script to execute a script after the page is processed by SingleFile.
addEventListener("single-file-on-after-capture-request", () => {
console.log("The page has been processed by SingleFile");
});
Like single-file-on-before-capture-request, you can call event.preventDefault() and dispatch the custom event single-file-on-after-capture-response if the listener is an async function.
- Examples
- This script removes images from the page just before saving it and restores them after the page is processed.
// ==UserScript==
// @name Remove images
// @namespace https://github.com/gildas-lormeau/SingleFile
// @version 1.0
// @description [SingleFile] Remove all the images
// @author Gildas Lormeau
// @match *://*/*
// @grant none
// ==/UserScript==
(() => {
const elements = new Map();
const removedElementsSelector = "img";
dispatchEvent(new CustomEvent("single-file-user-script-init"));
addEventListener("single-file-on-before-capture-request", () => {
document.querySelectorAll(removedElementsSelector).forEach(element => {
const placeHolderElement = document.createElement(element.tagName);
elements.set(placeHolderElement, element);
element.parentElement.replaceChild(placeHolderElement, element);
});
});
addEventListener("single-file-on-after-capture-request", () => {
Array.from(elements).forEach(([placeHolderElement, element]) => {
placeHolderElement.parentElement.replaceChild(element, placeHolderElement);
});
elements.clear();
});
})();
- This script converts the title to kebab-case and restores it after the page is processed.
// ==UserScript==
// @name Convert title to kebab-case
// @namespace https://github.com/gildas-lormeau/SingleFile
// @version 1.0
// @description [SingleFile] Convert title to kebab-case
// @author Gildas Lormeau
// @match *://*/*
// @noframes
// @grant none
// ==/UserScript==
(() => {
let title;
dispatchEvent(new CustomEvent("single-file-user-script-init"));
addEventListener("single-file-on-before-capture-request", () => {
title = document.title;
document.title = document.title
.replace(/[^a-z0-9-\s]/gi, "")
.replace(/[-\s]+/g, "-")
.toLowerCase();
});
addEventListener("single-file-on-after-capture-request", () => {
document.title = title;
});
})();
-
This script displays the page in reader mode (with readability.js) just before saving it: https://gist.github.com/gildas-lormeau/963f363bce2a7bdb53b46ec74e11bf5c
-
This script expands the scrollable areas of the page so that conversations and long lists are saved entirely. It is useful on sites like ChatGPT or Perplexity, which remove the messages that are scrolled out of view: How to save AI chat conversations.