Merge branch 'espruino:master' into modclk-updates

master
RKBoss6 2025-07-27 21:58:42 -04:00 committed by GitHub
commit 0ea7ea5a8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 178 additions and 26 deletions

View File

@ -0,0 +1 @@
0.01: initial import

View File

@ -0,0 +1,5 @@
# Elite clock ![](app.png)
Simple binary clock for leet haxorz.
Written by: [Pavel Machek](https://github.com/pavelmachek)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgIspiEPgEeAoU/4F/wGAiEAsEA4AFImAHBAolj/gRD4YFEC4UPwEfgAFC4EfF5IpHAp4dC4EQv/A/+AHYJlDnjY/AH4AJ"))

BIN
apps/eliteclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -0,0 +1,86 @@
// Bangle.js 2 - Binary Leet ClockZ
/*
bangle.js2: create binary 'leet clock' where the time is shown as text
"leet clockz" with binary 0 being normal character and binary one
being leet translation. Be careful to only update time on minute
boundaries.
So yeah 1337 c10ckZ = certified leetspeak 😎
ChatGPT said:
Alright, heres a hardcore hacker variant of elite clock in full-on aggressive leetspeak:
£|173 ¢|_0¢|<Z
Breakdown:
E £ or 3
L | or £
I 1 or !
T 7 or +
C ¢ or (
O 0
K |< or X
Other extreme variants:
3|173 (|_0(<Z
|!73 ©|0xX
3L1+3 (L0XXZ
*/
const TEXT = "leet\nclockz";
const LEET = {
"l": "1",
"e": "3",
"t": "7",
" ": " ",
"c": "(",
"o": "0",
"k": "X",
"z": "2"
};
// Convert hour (12h) and minute to binary mask
function getBinaryFromTime(d) {
let h = d.getHours() % 12;
if (h === 0) h = 12; // 12-hour format: 0 becomes 12
const m = d.getMinutes();
const bin = ((h << 7) | m).toString(2).padStart(11, '0');
return bin;
}
// Map normal characters to leet if binary mask says so
function getDisplayText(binMask) {
return TEXT.split("").map((ch, i) =>
binMask[i] === '1' ? (LEET[ch] || ch) : ch
).join("");
}
function draw() {
g.reset().clear();
const now = new Date();
const bin = getBinaryFromTime(now);
const txt = getDisplayText(bin);
const w = 0;
g.setFont("Vector", 47).setFontAlign(0,0);
g.drawString(txt, (g.getWidth() - w) / 2, (g.getHeight() - 0) / 2);
}
function scheduleNextDraw() {
const now = new Date();
const msToNextMin = 60000 - (now.getSeconds() * 1000 + now.getMilliseconds());
setTimeout(() => {
draw();
scheduleNextDraw();
}, msToNextMin);
}
// Init
draw();
scheduleNextDraw();
//Bangle.loadWidgets();
//Bangle.drawWidgets();

View File

@ -0,0 +1,14 @@
{ "id": "eliteclock",
"name": "Elite clock",
"version": "0.01",
"description": "Simple binary clock for leet haxorz",
"icon": "app.png",
"readme": "README.md",
"supports" : ["BANGLEJS2"],
"type": "clock",
"tags": "clock",
"storage": [
{"name":"eliteclock.app.js","url":"eliteclock.app.js"},
{"name":"eliteclock.img","url":"app-icon.js","evaluate":true}
]
}

View File

@ -16,5 +16,4 @@ It is also possible to load existing icon into editor, using
"load_icon("");" command. At the end of iconbits.app.js file there are "load_icon("");" command. At the end of iconbits.app.js file there are
more utility functions. more utility functions.
Create 48x48 icon in gimp.

BIN
apps/iconbits/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -16,3 +16,4 @@
0.12: Fix bug where settings would behave as if all were set to false 0.12: Fix bug where settings would behave as if all were set to false
0.13: Update to new touch-event handling 0.13: Update to new touch-event handling
0.14: Fix bug in handling changes to `Bangle.appRect` 0.14: Fix bug in handling changes to `Bangle.appRect`
0.15: Workaround bug in 2v26/2v27 firmware

View File

@ -2,7 +2,11 @@ var _a, _b;
var prosettings = (require("Storage").readJSON("promenu.settings.json", true) || {}); var prosettings = (require("Storage").readJSON("promenu.settings.json", true) || {});
(_a = prosettings.naturalScroll) !== null && _a !== void 0 ? _a : (prosettings.naturalScroll = false); (_a = prosettings.naturalScroll) !== null && _a !== void 0 ? _a : (prosettings.naturalScroll = false);
(_b = prosettings.wrapAround) !== null && _b !== void 0 ? _b : (prosettings.wrapAround = true); (_b = prosettings.wrapAround) !== null && _b !== void 0 ? _b : (prosettings.wrapAround = true);
E.showMenu = function (items) { E.showMenu = (function (items) {
if (items == null) {
g.clearRect(Bangle.appRect);
return Bangle.setUI();
}
var RectRnd = function (x1, y1, x2, y2, r) { var RectRnd = function (x1, y1, x2, y2, r) {
var pp = []; var pp = [];
pp.push.apply(pp, g.quadraticBezier([x2 - r, y1, x2, y1, x2, y1 + r])); pp.push.apply(pp, g.quadraticBezier([x2 - r, y1, x2, y1, x2, y1 + r]));
@ -122,7 +126,6 @@ E.showMenu = function (items) {
nameScroll_1 = 0; nameScroll_1 = 0;
}, 300, name, v, item, idx, x, iy); }, 300, name, v, item, idx, x, iy);
} }
g.setColor(g.theme.fg);
iy += fontHeight; iy += fontHeight;
idx++; idx++;
}; };
@ -204,7 +207,10 @@ E.showMenu = function (items) {
else else
l.select(evt); l.select(evt);
}; };
Bangle.setUI({ var touchcb = (function (_button, xy) {
cb(void 0, xy);
});
var uiopts = {
mode: "updown", mode: "updown",
back: back, back: back,
remove: function () { remove: function () {
@ -212,11 +218,20 @@ E.showMenu = function (items) {
if (nameScroller) if (nameScroller)
clearInterval(nameScroller); clearInterval(nameScroller);
Bangle.removeListener("swipe", onSwipe); Bangle.removeListener("swipe", onSwipe);
if (setUITouch)
Bangle.removeListener("touch", touchcb);
(_a = options.remove) === null || _a === void 0 ? void 0 : _a.call(options); (_a = options.remove) === null || _a === void 0 ? void 0 : _a.call(options);
}, },
touch: (function (_button, xy) { };
cb(void 0, xy); var setUITouch = process.env.VERSION >= "2v26";
}), if (!setUITouch) {
}, cb); uiopts.touch = touchcb;
}
Bangle.setUI(uiopts, cb);
if (setUITouch) {
Bangle.removeListener("touch", Bangle.touchHandler);
delete Bangle.touchHandler;
Bangle.on("touch", touchcb);
}
return l; return l;
}; });

View File

@ -13,7 +13,12 @@ const prosettings = (require("Storage").readJSON("promenu.settings.json", true)
prosettings.naturalScroll ??= false; prosettings.naturalScroll ??= false;
prosettings.wrapAround ??= true; prosettings.wrapAround ??= true;
E.showMenu = (items?: Menu): MenuInstance => { E.showMenu = ((items?: Menu): MenuInstance | void => {
if(items == null){
g.clearRect(Bangle.appRect);
return Bangle.setUI();
}
const RectRnd = (x1: number, y1: number, x2: number, y2: number, r: number) => { const RectRnd = (x1: number, y1: number, x2: number, y2: number, r: number) => {
const pp = []; const pp = [];
pp.push(...g.quadraticBezier([x2 - r, y1, x2, y1, x2, y1 + r])); pp.push(...g.quadraticBezier([x2 - r, y1, x2, y1, x2, y1 + r]));
@ -167,7 +172,6 @@ E.showMenu = (items?: Menu): MenuInstance => {
}, 300, name, v, item, idx, x, iy); }, 300, name, v, item, idx, x, iy);
} }
g.setColor(g.theme.fg);
iy += fontHeight; iy += fontHeight;
idx++; idx++;
} }
@ -252,21 +256,47 @@ E.showMenu = (items?: Menu): MenuInstance => {
else l.select(evt); else l.select(evt);
}; };
Bangle.setUI({ const touchcb = ((_button, xy) => {
mode: "updown",
back,
remove: () => {
if (nameScroller) clearInterval(nameScroller);
Bangle.removeListener("swipe", onSwipe);
options.remove?.();
},
touch: ((_button, xy) => {
// since we've specified options.touch, // since we've specified options.touch,
// we need to pass through all taps since the default // we need to pass through all taps since the default
// touchHandler isn't installed in setUI // touchHandler isn't installed in setUI
cb(void 0, xy); cb(void 0, xy);
}) satisfies TouchCallback, }) satisfies TouchCallback;
} as SetUIArg<"updown">, cb);
const uiopts = {
mode: "updown",
back: back as () => void,
remove: () => {
if (nameScroller) clearInterval(nameScroller);
Bangle.removeListener("swipe", onSwipe);
if(setUITouch)
Bangle.removeListener("touch", touchcb);
options.remove?.();
},
} satisfies SetUIArg<"updown">;
// does setUI install its own touch handler?
const setUITouch = process.env.VERSION >= "2v26";
if (!setUITouch) {
// old firmware, we can use its touch handler - no need for workaround
(uiopts as any).touch = touchcb;
}
Bangle.setUI(uiopts, cb);
if(setUITouch){
// new firmware, remove setUI's touch handler and use just our own to
// avoid `cb` drawing the menu (as part of setUI's touch handler)
// followed by us drawing the menu (as part of our touch handler)
//
// work around details:
// - https://github.com/espruino/Espruino/issues/2648
// - https://github.com/orgs/espruino/discussions/7697#discussioncomment-13782299
Bangle.removeListener("touch", (Bangle as any).touchHandler);
delete (Bangle as any).touchHandler;
Bangle.on("touch", touchcb);
}
return l; return l;
}; }) as typeof E.showMenu;

View File

@ -1,7 +1,7 @@
{ {
"id": "promenu", "id": "promenu",
"name": "Pro Menu", "name": "Pro Menu",
"version": "0.14", "version": "0.15",
"description": "Replace the built in menu function. Supports Bangle.js 1 and Bangle.js 2.", "description": "Replace the built in menu function. Supports Bangle.js 1 and Bangle.js 2.",
"icon": "icon.png", "icon": "icon.png",
"type": "bootloader", "type": "bootloader",

View File

@ -88,4 +88,4 @@ of 'Select Clock'
0.77: Save altitude calibration when user exits via reset 0.77: Save altitude calibration when user exits via reset
0.78: Fix menu scroll restore on BangleJS1 0.78: Fix menu scroll restore on BangleJS1
0.79: Ensure that tapping on pressure/altitude doesn't cause a menu to display temporarily 0.79: Ensure that tapping on pressure/altitude doesn't cause a menu to display temporarily
0.80: Add option to set LCD brightness to 0, default brightness is now 0 as well. 0.80: Add option to set LCD brightness to 0, keep default brightness at 1.