BTHLABS-82: Spring 2026 Refresh

Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
This commit is contained in:
2026-03-16 19:09:38 +00:00
committed by Tomek Wójcik
parent c842657766
commit 3c71464663
22 changed files with 531 additions and 234 deletions

View File

@@ -2,6 +2,7 @@ run:
echo: true
pty: true
files_to_version:
- "src/content/options.html"
- "src/content/preauth.html"
- "src/manifest/common.json"
- "package.json"

View File

@@ -18,7 +18,10 @@
"watch:chrome": "HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js -w",
"build:firefox": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js",
"dev:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js",
"watch:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js -w"
"watch:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js -w",
"build:opera": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=opera npx rollup -c rollup.config.js",
"dev:opera": "HOTPOCKET_EXTENSION_TARGET=opera npx rollup -c rollup.config.js",
"watch:opera": "HOTPOCKET_EXTENSION_TARGET=opera npx rollup -c rollup.config.js -w"
},
"devDependencies": {
"@eslint/js": "9.33.0",

View File

@@ -6,6 +6,7 @@ import packageJSON from './package.json' with {type: 'json'};
import manifestChrome from './src/manifest/chrome.json' with {type: 'json'};
import manifestCommon from './src/manifest/common.json' with {type: 'json'};
import manifestFirefox from './src/manifest/firefox.json' with {type: 'json'};
import manifestOpera from './src/manifest/opera.json' with {type: 'json'};
import manifestSafari from './src/manifest/safari.json' with {type: 'json'};
const BANNER = `/*!
@@ -66,6 +67,11 @@ const manifestJsonOutputPlugin = () => {
...result,
...manifestFirefox,
};
} else if (TARGET == 'opera') {
result = {
...result,
...manifestOpera,
};
}
result.version = packageJSON.version;
@@ -110,6 +116,8 @@ export default [
},
{
src: [
'src/content/options.html',
'src/content/options.js',
'src/content/preauth.html',
'src/content/preauth.js',
],

View File

@@ -8,13 +8,21 @@ let authSessionToken = null;
let accountsRPCURL = null;
let rpcURL = null;
const updateRpcURLs = () => {
const updateRpcURLs = (options) => {
options = {
clear: false,
...(options || {}),
};
accountsRPCURL = null;
rpcURL = null;
if (HotPocketExtension.base_url !== null) {
rpcURL = (new URL(RPC_PATH, HotPocketExtension.base_url)).toString();
accountsRPCURL = (new URL(ACCOUNTS_RPC_PATH, HotPocketExtension.base_url)).toString();
} else if (options.clear === true) {
rpcURL = null;
accountsRPCURL = null;
}
HotPocketExtension.LOGGER.debug(
@@ -355,6 +363,12 @@ const onBrowserActionClicked = async (tab) => {
return await doSendTabMessage(tab, message);
};
const doLogOut = async () => {
// eslint-disable-next-line no-unused-vars
let storageResult = await HotPocketExtension.api.storage.local.clear();
updateRpcURLs({clear: true});
};
const onMessage = (message, sender, sendResponse) => {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.onMessage()', message, sender, sendResponse,
@@ -368,6 +382,10 @@ const onMessage = (message, sender, sendResponse) => {
});
} else if (message.type === 'HotPocket:Extension:setBaseURL') {
doUpdateBaseURL(message.result);
} else if (message.type === 'HotPocket:ExtensionOptions:logOut') {
doLogOut();
} else if (message.type === 'HotPocket:ExtensionOptions:logIn') {
doSetupRPC();
}
} catch (exception) {
HotPocketExtension.LOGGER.error(

View File

@@ -0,0 +1,6 @@
import main from './main';
main({
platform: 'Opera',
api: chrome,
});

View File

@@ -0,0 +1,6 @@
import main from './main';
main({
platform: 'Opera',
api: window.chrome,
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,118 @@
/*!
* HotPocket by BTHLabs (https://hotpocket.app/)
* Copyright 2025-present BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(() => {
'use strict';
document.addEventListener('DOMContentLoaded', (event) => {
const form = document.getElementById('OptionsForm');
const fieldBaseURL = document.getElementById('div_id_base_url');
const fieldNeedsSetup = document.getElementById('div_id_needs_setup');
const inputBaseURL = document.getElementById('id_base_url');
const inputSubmit = document.getElementById('id_submit');
let formLoader = form.querySelector('.loader');
let api = window.browser || null;
if (api === null && window.chrome) {
api = window.chrome;
}
const updateForm = () => {
if (inputBaseURL.value) {
fieldBaseURL.classList.remove('d-none');
fieldNeedsSetup.classList.add('d-none');
inputSubmit.classList.remove('btn-primary');
inputSubmit.classList.add('btn-secondary');
inputSubmit.value = 'Log out';
} else {
fieldBaseURL.classList.add('d-none');
fieldNeedsSetup.classList.remove('d-none');
inputSubmit.classList.remove('btn-secondary');
inputSubmit.classList.add('btn-primary');
inputSubmit.value = 'Log in';
}
};
const loadBaseURL = async (options) => {
options = {
initial: false,
...(options || {}),
};
formLoader.classList.remove('d-none');
const storageResult = await api.storage.local.get(['baseURL']);
if (storageResult.baseURL) {
inputBaseURL.value = storageResult.baseURL;
}
if (options.initial === true) {
formLoader.classList.add('d-none');
}
updateForm();
return storageResult.baseURL || null;
};
const startPollingBaseURL = async () => {
let timeout = null;
formLoader.classList.remove('d-none');
window.setTimeout(
async () => {
window.clearTimeout(timeout);
const result = await loadBaseURL();
if (result === null) {
startPollingBaseURL();
} else {
formLoader.classList.add('d-none');
}
},
5000,
);
};
form.addEventListener('submit', (event) => {
event.stopPropagation();
event.preventDefault();
if (inputBaseURL.value) {
api.runtime.sendMessage({
type: 'HotPocket:ExtensionOptions:logOut',
});
inputBaseURL.value = '';
} else {
api.runtime.sendMessage({
type: 'HotPocket:ExtensionOptions:logIn',
});
startPollingBaseURL();
}
updateForm();
return false;
});
loadBaseURL({initial: true});
});
})();

View File

@@ -86,6 +86,6 @@ body, html {
</div>
</div>
</div>
<script type="text/javascript" src="/preauth.js"></script>
<script type="text/javascript" src="/preauth.js"></script>
</body>
</html>

View File

@@ -28,5 +28,9 @@
"storage",
"activeTab",
"tabs"
]
],
"options_ui": {
"open_in_tab": true,
"page": "options.html"
}
}

View File

@@ -0,0 +1,13 @@
{
"action": {
"default_title": "__MSG_extension_name__",
"default_icon": {
"16": "images/toolbar-icon-16.png",
"32": "images/toolbar-icon-32.png"
}
},
"background": {
"service_worker": "background-bundle.js",
"type": "module"
}
}

View File

@@ -255,7 +255,7 @@ def build_firefox_source(ctx: Context):
@task
def build_opera_source(ctx: Context):
# AMO requires source bundle to be uploaded alongside the built version.
# Opera requires source bundle to be uploaded alongside the built version.
ctx.run('rm -rf dist/opera-source')
ctx.run('mkdir -p dist/opera-source dist/opera-source/assets dist/opera-source/src')