Skip to content

Commit 3b1be98

Browse files
committed
release: v1.0.1
### Added - Force re-download for skipped-archive jobs, which plain retry would re-skip. ### Changed - Renamed the extension to monsender. ### Fixed - Scans and right-click now send the largest available image, not the rendered thumbnail. - Scan cards show each image's real loaded size.
1 parent 9f570dc commit 3b1be98

20 files changed

Lines changed: 116 additions & 52 deletions

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,5 @@ jobs:
4141
name: ${{ github.ref_name }}
4242
body_path: .latest_changelog.md
4343
files: |
44-
dist/monloader-browser-chrome-*.zip
45-
dist/monloader-browser-firefox-*.xpi
44+
dist/monsender-chrome-*.zip
45+
dist/monsender-firefox-*.xpi

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## [v1.0.1] - 2026-06-21
4+
### Added
5+
- Force re-download for skipped-archive jobs, which plain retry would re-skip.
6+
### Changed
7+
- Renamed the extension to monsender.
8+
### Fixed
9+
- Scans and right-click now send the largest available image, not the rendered thumbnail.
10+
- Scan cards show each image's real loaded size.
11+
312
## [v1.0.0] - 2026-06-19
413
### Added
514
- Initial release.

PRIVACY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Privacy policy
22

3-
monloader-browser collects nothing and sends nothing to anyone except the
3+
monsender collects nothing and sends nothing to anyone except the
44
monloader server you configure.
55

66
- No analytics, no telemetry, no remote code. All code ships in the extension

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# monloader-browser
1+
# monsender
22

33
A small firefox or chrome extension for [monloader](https://github.com/leqwin/monloader).
44
It sends the page you are looking at, a right-clicked image or video, or images you pick from a page scan to your monloader download queue, which pushes them into monbooru.
@@ -36,7 +36,7 @@ npm run build # writes dist/chrome and dist/firefox
3636

3737
## Attribution and license
3838

39-
monloader-browser is licensed under **AGPL-3.0-or-later** (see `LICENSE`).
39+
monsender is licensed under **AGPL-3.0-or-later** (see `LICENSE`).
4040

4141
The in-page image detection uses code from [ushiro](https://github.com/gary-host-laptop/ushiro) by
4242
**gary-host-laptop**, and [behind!](https://github.com/kubuzetto/behind) by **kubuzetto**, originally

REPOSITORY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
https://github.com/leqwin/monloader-browser
1+
https://github.com/leqwin/monsender

VERSION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v1.0.0
1+
v1.0.1

build.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function buildManifest(target, version) {
4545
m.background = { scripts: BG_SCRIPTS };
4646
m.sidebar_action = {
4747
default_panel: 'panel/panel.html',
48-
default_title: 'monloader',
48+
default_title: 'monsender',
4949
default_icon: m.icons,
5050
// Do not pop the sidebar open on load; it opens only when the user scans
5151
// (Firefox defaults open_at_install to true).
@@ -56,7 +56,7 @@ function buildManifest(target, version) {
5656
// data_collection_permissions key (we declare no collection - PRIVACY.md)
5757
// needs >=140. web-ext lint is the source of these floors.
5858
gecko: {
59-
id: 'monloader-browser@leqwin',
59+
id: 'monsender@leqwin',
6060
strict_min_version: '140.0',
6161
data_collection_permissions: { required: ['none'], optional: [] }
6262
}
@@ -92,7 +92,7 @@ if (require.main === module) {
9292
for (const t of targets) {
9393
const dest = build(t, version);
9494
const ext = t === 'firefox' ? 'xpi' : 'zip';
95-
const out = path.join(DIST, `monloader-browser-${t}-v${version}.${ext}`);
95+
const out = path.join(DIST, `monsender-${t}-v${version}.${ext}`);
9696
const info = zipDir(dest, out);
9797
console.log(`built ${t} v${version} -> ${path.relative(ROOT, dest)} (${info.files} files, ${path.relative(ROOT, out)})`);
9898
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "Monloader browser extension",
3-
"version": "1.0.0",
2+
"name": "monsender",
3+
"version": "1.0.1",
44
"description": "Send the current page or its images to your monloader download queue.",
55
"private": true,
66
"type": "commonjs",
@@ -24,7 +24,7 @@
2424
"license": "AGPL-3.0-or-later",
2525
"repository": {
2626
"type": "git",
27-
"url": "https://github.com/leqwin/monloader-browser"
27+
"url": "https://github.com/leqwin/monsender"
2828
},
2929
"devDependencies": {
3030
"jsdom": "^24.0.0",

src/assets/logo.png

24.7 KB
Loading

src/bg.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,41 @@ async function resolveMenuTarget(cfg, info) {
138138
return info.srcUrl;
139139
}
140140

141-
browser.contextMenus.onClicked.addListener(async (info) => {
141+
// The context menu exposes only the rendered srcUrl, so a responsive image
142+
// yields its downscaled variant. Look up the clicked image's largest srcset
143+
// candidate in the page, under the gesture's activeTab grant; fall back to the
144+
// rendered URL on any miss.
145+
async function largestForSrc(tabId, frameId, srcUrl) {
146+
try {
147+
const [res] = await browser.scripting.executeScript({
148+
target: frameId != null ? { tabId, frameIds: [frameId] } : { tabId },
149+
args: [srcUrl],
150+
func: (target) => {
151+
const abs = (u) => { try { return new URL(u, document.baseURI).href; } catch (e) { return null; } };
152+
for (const img of document.querySelectorAll('img')) {
153+
if (img.currentSrc !== target && img.src !== target) continue;
154+
// a srcset entry wider than the displayed image, else the full src
155+
let best = null, bv = img.naturalWidth || 0;
156+
for (const part of (img.srcset || '').split(',')) {
157+
const seg = part.trim().split(/\s+/);
158+
const m = /^(\d+)w$/.exec(seg[1] || '');
159+
if (seg[0] && m && +m[1] > bv) { bv = +m[1]; best = seg[0]; }
160+
}
161+
const pick = best || img.src;
162+
return pick ? abs(pick) : null;
163+
}
164+
return null;
165+
}
166+
});
167+
const up = res && res.result;
168+
return (typeof up === 'string' && /^https?:/i.test(up)) ? up : srcUrl;
169+
} catch (e) { return srcUrl; }
170+
}
171+
172+
browser.contextMenus.onClicked.addListener(async (info, tab) => {
142173
if (info.menuItemId !== MENU.send) return;
143174
const s = await ML.storage.getSettings();
144-
await sendUrl(await resolveMenuTarget({ baseUrl: s.baseUrl, token: s.token }, info));
175+
let target = await resolveMenuTarget({ baseUrl: s.baseUrl, token: s.token }, info);
176+
if (s.baseUrl && tab && target === info.srcUrl) target = await largestForSrc(tab.id, info.frameId, info.srcUrl);
177+
await sendUrl(target);
145178
});

0 commit comments

Comments
 (0)