Update colors and add icons

This commit is contained in:
Idrees Hassan
2026-04-03 19:06:28 -07:00
parent 2f3d7958ea
commit 85ade65a57
12 changed files with 652 additions and 142 deletions

BIN
dist/extension.zip vendored

Binary file not shown.

129
dist/extension/birb.js vendored
View File

@@ -622,8 +622,8 @@
"url": "https://en.wikipedia.org/wiki/Cuban_tody", "url": "https://en.wikipedia.org/wiki/Cuban_tody",
"colors": { "colors": {
"beak": "#f16f54", "beak": "#f16f54",
"face": "#5fdf44", "face": "#5ad63e",
"chin": "#f12d3e", "chin": "#e8273b",
"collar": "#f12d3e", "collar": "#f12d3e",
"belly": "#f6f5e4", "belly": "#f6f5e4",
"collar-scruff": "#a3ebff", "collar-scruff": "#a3ebff",
@@ -644,11 +644,14 @@
"colors": { "colors": {
"face": "#9c3af2", "face": "#9c3af2",
"wing": "#8f37ed", "wing": "#8f37ed",
"wing-edge": "#7029b8", "wing-edge": "#5b20c2",
"belly": "#ffffff", "belly": "#ffffff",
"underbelly": "#f2f2f2", "underbelly": "#f2f2f2",
"foot": "#736a66", "foot": "#736a66",
"collar": "#aa60e6" "collar": "#b760e6",
"nose": "#7a2ec7",
"cheek": "#7a2ec7",
"nose-tip": "#7a2ec7"
}, },
"rarity": "uncommon" "rarity": "uncommon"
} }
@@ -1945,11 +1948,13 @@
/** /**
* @param {string|(() => string)} text * @param {string|(() => string)} text
* @param {() => void} action * @param {() => void} action
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, icon, removeMenu = true) {
this.text = text; this.text = text;
this.action = action; this.action = action;
this.icon = icon;
this.removeMenu = removeMenu; this.removeMenu = removeMenu;
} }
} }
@@ -1959,10 +1964,11 @@
* @param {string} text * @param {string} text
* @param {() => void} action * @param {() => void} action
* @param {() => boolean} condition * @param {() => boolean} condition
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, condition, removeMenu = true) { constructor(text, action, condition, icon, removeMenu = true) {
super(text, action, removeMenu); super(text, action, icon, removeMenu);
this.condition = condition; this.condition = condition;
} }
} }
@@ -1973,7 +1979,7 @@
* @param {() => void} action * @param {() => void} action
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, removeMenu = true) {
super(text, action, () => isDebug(), removeMenu); super(text, action, () => isDebug(), undefined, removeMenu);
} }
} }
@@ -1988,11 +1994,29 @@
* @param {() => void} removeMenuCallback * @param {() => void} removeMenuCallback
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
function makeMenuItem(item, removeMenuCallback) { function createMenuItem(item, removeMenuCallback) {
if (item instanceof Separator) { if (item instanceof Separator) {
return makeElement("birb-window-separator"); return makeElement("birb-window-separator");
} }
let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text); let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text);
if (item.icon) {
const iconCanvas = document.createElement("canvas");
iconCanvas.width = 7;
iconCanvas.height = 6;
iconCanvas.classList.add("birb-menu-item-icon");
const ctx = iconCanvas.getContext("2d");
if (ctx) {
for (let row = 0; row < item.icon.length; row++) {
for (let col = 0; col < item.icon[row].length; col++) {
if (item.icon[row][col]) {
ctx.fillStyle = "black";
ctx.fillRect(col, row, 1, 1);
}
}
}
}
menuItem.prepend(iconCanvas);
}
onClick(menuItem, () => { onClick(menuItem, () => {
if (item.removeMenu) { if (item.removeMenu) {
removeMenuCallback(); removeMenuCallback();
@@ -2020,7 +2044,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
menu.appendChild(header); menu.appendChild(header);
@@ -2077,7 +2101,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
updateLocationCallback(menu); updateLocationCallback(menu);
@@ -2328,15 +2352,17 @@
font-size: 14px; font-size: 14px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: 10px; padding-left: 2px;
padding-right: 10px; padding-right: 10px;
box-sizing: border-box; box-sizing: border-box;
opacity: 0.7 !important; opacity: 0.7 !important;
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: space-between; justify-content: left;
align-items: center;
cursor: pointer; cursor: pointer;
color: black !important; color: black !important;
transition: background 0.1s, color 0.1s;
} }
.birb-menu-item:hover { .birb-menu-item:hover {
@@ -2348,6 +2374,21 @@
var(--birb-neg-border-size) 0 var(--birb-highlight), var(--birb-neg-border-size) 0 var(--birb-highlight),
0 var(--birb-neg-border-size) var(--birb-highlight), 0 var(--birb-neg-border-size) var(--birb-highlight),
0 var(--birb-border-size) var(--birb-highlight); 0 var(--birb-border-size) var(--birb-highlight);
transition: none;
}
.birb-menu-item-icon {
width: calc(7 * var(--birb-border-size));
height: calc(6 * var(--birb-border-size));
padding-right: calc(5 * var(--birb-border-size));
flex-shrink: 0;
image-rendering: pixelated;
color: var(--birb-highlight);
opacity: 0.9;
}
.birb-menu-item:hover > .birb-menu-item-icon {
filter: invert(1);
} }
.birb-menu-item-arrow { .birb-menu-item-arrow {
@@ -2398,10 +2439,12 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
transition: border-color 0.1s;
} }
.birb-grid-item:hover { .birb-grid-item:hover {
border-color: var(--birb-highlight); border-color: var(--birb-highlight);
transition: none;
} }
.birb-grid-item canvas { .birb-grid-item canvas {
@@ -2608,12 +2651,47 @@
}; };
const menuItems = [ const menuItems = [
new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem(`Pet ${birdBirb()}`, pet, [
new MenuItem("Field Guide", insertFieldGuide), [0, 1, 1, 0, 1, 1, 0],
new MenuItem("Wardrobe", insertWardrobe), [1, 0, 0, 1, 0, 0, 1],
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), [1, 0, 0, 0, 0, 0, 1],
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), [0, 1, 0, 0, 0, 1, 0],
new DebugMenuItem("Freeze/Unfreeze", () => { [0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
]),
new MenuItem("Field Guide", insertFieldGuide, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 1, 0, 1, 1, 1],
]),
new MenuItem("Wardrobe", insertWardrobe, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 0],
]),
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled(), [
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 0],
]),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false), [
[0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 0, 0],
]),
new DebugMenuItem("Freeze", () => {
frozen = !frozen; frozen = !frozen;
}), }),
new DebugMenuItem("Reset Data", resetSaveData), new DebugMenuItem("Reset Data", resetSaveData),
@@ -2632,11 +2710,18 @@
setDebug(false); setDebug(false);
}), }),
new Separator(), new Separator(),
new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), false), new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), [
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
], false),
]; ];
const settingsItems = [ const settingsItems = [
new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), false), new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), undefined, false),
new Separator(), new Separator(),
new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => { new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => {
userSettings.soundEnabled = !settings().soundEnabled; userSettings.soundEnabled = !settings().soundEnabled;
@@ -2656,7 +2741,7 @@
}), }),
new Separator(), new Separator(),
new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }),
new MenuItem("2026.3.30", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.30"); }, false), new MenuItem("Build 2026.4.3", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.4.3"); }, undefined, false),
]; ];
/** @type {Birb} */ /** @type {Birb} */

View File

@@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "Pocket Bird", "name": "Pocket Bird",
"description": "It's a pet bird in your browser, what more could you want?", "description": "It's a pet bird in your browser, what more could you want?",
"version": "2026.3.30", "version": "2026.4.3",
"homepage_url": "https://idreesinc.com", "homepage_url": "https://idreesinc.com",
"icons": { "icons": {
"48": "images/icons/transparent/48x48x1.png", "48": "images/icons/transparent/48x48x1.png",

131
dist/obsidian/main.js vendored
View File

@@ -1,7 +1,7 @@
const { Plugin, Notice } = require('obsidian'); const { Plugin, Notice } = require('obsidian');
module.exports = class PocketBird extends Plugin { module.exports = class PocketBird extends Plugin {
onload() { onload() {
console.log("Loading Pocket Bird version 2026.3.30..."); console.log("Loading Pocket Bird version 2026.4.3...");
const OBSIDIAN_PLUGIN = this; const OBSIDIAN_PLUGIN = this;
(function () { (function () {
'use strict'; 'use strict';
@@ -627,8 +627,8 @@ module.exports = class PocketBird extends Plugin {
"url": "https://en.wikipedia.org/wiki/Cuban_tody", "url": "https://en.wikipedia.org/wiki/Cuban_tody",
"colors": { "colors": {
"beak": "#f16f54", "beak": "#f16f54",
"face": "#5fdf44", "face": "#5ad63e",
"chin": "#f12d3e", "chin": "#e8273b",
"collar": "#f12d3e", "collar": "#f12d3e",
"belly": "#f6f5e4", "belly": "#f6f5e4",
"collar-scruff": "#a3ebff", "collar-scruff": "#a3ebff",
@@ -649,11 +649,14 @@ module.exports = class PocketBird extends Plugin {
"colors": { "colors": {
"face": "#9c3af2", "face": "#9c3af2",
"wing": "#8f37ed", "wing": "#8f37ed",
"wing-edge": "#7029b8", "wing-edge": "#5b20c2",
"belly": "#ffffff", "belly": "#ffffff",
"underbelly": "#f2f2f2", "underbelly": "#f2f2f2",
"foot": "#736a66", "foot": "#736a66",
"collar": "#aa60e6" "collar": "#b760e6",
"nose": "#7a2ec7",
"cheek": "#7a2ec7",
"nose-tip": "#7a2ec7"
}, },
"rarity": "uncommon" "rarity": "uncommon"
} }
@@ -1978,11 +1981,13 @@ module.exports = class PocketBird extends Plugin {
/** /**
* @param {string|(() => string)} text * @param {string|(() => string)} text
* @param {() => void} action * @param {() => void} action
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, icon, removeMenu = true) {
this.text = text; this.text = text;
this.action = action; this.action = action;
this.icon = icon;
this.removeMenu = removeMenu; this.removeMenu = removeMenu;
} }
} }
@@ -1992,10 +1997,11 @@ module.exports = class PocketBird extends Plugin {
* @param {string} text * @param {string} text
* @param {() => void} action * @param {() => void} action
* @param {() => boolean} condition * @param {() => boolean} condition
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, condition, removeMenu = true) { constructor(text, action, condition, icon, removeMenu = true) {
super(text, action, removeMenu); super(text, action, icon, removeMenu);
this.condition = condition; this.condition = condition;
} }
} }
@@ -2006,7 +2012,7 @@ module.exports = class PocketBird extends Plugin {
* @param {() => void} action * @param {() => void} action
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, removeMenu = true) {
super(text, action, () => isDebug(), removeMenu); super(text, action, () => isDebug(), undefined, removeMenu);
} }
} }
@@ -2021,11 +2027,29 @@ module.exports = class PocketBird extends Plugin {
* @param {() => void} removeMenuCallback * @param {() => void} removeMenuCallback
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
function makeMenuItem(item, removeMenuCallback) { function createMenuItem(item, removeMenuCallback) {
if (item instanceof Separator) { if (item instanceof Separator) {
return makeElement("birb-window-separator"); return makeElement("birb-window-separator");
} }
let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text); let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text);
if (item.icon) {
const iconCanvas = document.createElement("canvas");
iconCanvas.width = 7;
iconCanvas.height = 6;
iconCanvas.classList.add("birb-menu-item-icon");
const ctx = iconCanvas.getContext("2d");
if (ctx) {
for (let row = 0; row < item.icon.length; row++) {
for (let col = 0; col < item.icon[row].length; col++) {
if (item.icon[row][col]) {
ctx.fillStyle = "black";
ctx.fillRect(col, row, 1, 1);
}
}
}
}
menuItem.prepend(iconCanvas);
}
onClick(menuItem, () => { onClick(menuItem, () => {
if (item.removeMenu) { if (item.removeMenu) {
removeMenuCallback(); removeMenuCallback();
@@ -2053,7 +2077,7 @@ module.exports = class PocketBird extends Plugin {
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
menu.appendChild(header); menu.appendChild(header);
@@ -2110,7 +2134,7 @@ module.exports = class PocketBird extends Plugin {
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
updateLocationCallback(menu); updateLocationCallback(menu);
@@ -2361,15 +2385,17 @@ module.exports = class PocketBird extends Plugin {
font-size: 14px; font-size: 14px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: 10px; padding-left: 2px;
padding-right: 10px; padding-right: 10px;
box-sizing: border-box; box-sizing: border-box;
opacity: 0.7 !important; opacity: 0.7 !important;
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: space-between; justify-content: left;
align-items: center;
cursor: pointer; cursor: pointer;
color: black !important; color: black !important;
transition: background 0.1s, color 0.1s;
} }
.birb-menu-item:hover { .birb-menu-item:hover {
@@ -2381,6 +2407,21 @@ module.exports = class PocketBird extends Plugin {
var(--birb-neg-border-size) 0 var(--birb-highlight), var(--birb-neg-border-size) 0 var(--birb-highlight),
0 var(--birb-neg-border-size) var(--birb-highlight), 0 var(--birb-neg-border-size) var(--birb-highlight),
0 var(--birb-border-size) var(--birb-highlight); 0 var(--birb-border-size) var(--birb-highlight);
transition: none;
}
.birb-menu-item-icon {
width: calc(7 * var(--birb-border-size));
height: calc(6 * var(--birb-border-size));
padding-right: calc(5 * var(--birb-border-size));
flex-shrink: 0;
image-rendering: pixelated;
color: var(--birb-highlight);
opacity: 0.9;
}
.birb-menu-item:hover > .birb-menu-item-icon {
filter: invert(1);
} }
.birb-menu-item-arrow { .birb-menu-item-arrow {
@@ -2431,10 +2472,12 @@ module.exports = class PocketBird extends Plugin {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
transition: border-color 0.1s;
} }
.birb-grid-item:hover { .birb-grid-item:hover {
border-color: var(--birb-highlight); border-color: var(--birb-highlight);
transition: none;
} }
.birb-grid-item canvas { .birb-grid-item canvas {
@@ -2641,12 +2684,47 @@ module.exports = class PocketBird extends Plugin {
}; };
const menuItems = [ const menuItems = [
new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem(`Pet ${birdBirb()}`, pet, [
new MenuItem("Field Guide", insertFieldGuide), [0, 1, 1, 0, 1, 1, 0],
new MenuItem("Wardrobe", insertWardrobe), [1, 0, 0, 1, 0, 0, 1],
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), [1, 0, 0, 0, 0, 0, 1],
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), [0, 1, 0, 0, 0, 1, 0],
new DebugMenuItem("Freeze/Unfreeze", () => { [0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
]),
new MenuItem("Field Guide", insertFieldGuide, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 1, 0, 1, 1, 1],
]),
new MenuItem("Wardrobe", insertWardrobe, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 0],
]),
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled(), [
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 0],
]),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false), [
[0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 0, 0],
]),
new DebugMenuItem("Freeze", () => {
frozen = !frozen; frozen = !frozen;
}), }),
new DebugMenuItem("Reset Data", resetSaveData), new DebugMenuItem("Reset Data", resetSaveData),
@@ -2665,11 +2743,18 @@ module.exports = class PocketBird extends Plugin {
setDebug(false); setDebug(false);
}), }),
new Separator(), new Separator(),
new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), false), new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), [
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
], false),
]; ];
const settingsItems = [ const settingsItems = [
new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), false), new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), undefined, false),
new Separator(), new Separator(),
new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => { new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => {
userSettings.soundEnabled = !settings().soundEnabled; userSettings.soundEnabled = !settings().soundEnabled;
@@ -2689,7 +2774,7 @@ module.exports = class PocketBird extends Plugin {
}), }),
new Separator(), new Separator(),
new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }),
new MenuItem("2026.3.30", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.30"); }, false), new MenuItem("Build 2026.4.3", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.4.3"); }, undefined, false),
]; ];
/** @type {Birb} */ /** @type {Birb} */

View File

@@ -1,7 +1,7 @@
{ {
"id": "pocket-bird", "id": "pocket-bird",
"name": "Pocket Bird", "name": "Pocket Bird",
"version": "2026.3.30", "version": "2026.4.3",
"minAppVersion": "0.15.0", "minAppVersion": "0.15.0",
"description": "Add a pet bird to fly around your notes and keep you company!", "description": "Add a pet bird to fly around your notes and keep you company!",
"author": "Idrees Hassan", "author": "Idrees Hassan",

View File

@@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name Pocket Bird // @name Pocket Bird
// @namespace https://idreesinc.com // @namespace https://idreesinc.com
// @version 2026.3.30 // @version 2026.4.3
// @description It's a pet bird in your browser, what more could you want? // @description It's a pet bird in your browser, what more could you want?
// @author Idrees // @author Idrees
// @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/userscript/birb.user.js // @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/userscript/birb.user.js
@@ -636,8 +636,8 @@
"url": "https://en.wikipedia.org/wiki/Cuban_tody", "url": "https://en.wikipedia.org/wiki/Cuban_tody",
"colors": { "colors": {
"beak": "#f16f54", "beak": "#f16f54",
"face": "#5fdf44", "face": "#5ad63e",
"chin": "#f12d3e", "chin": "#e8273b",
"collar": "#f12d3e", "collar": "#f12d3e",
"belly": "#f6f5e4", "belly": "#f6f5e4",
"collar-scruff": "#a3ebff", "collar-scruff": "#a3ebff",
@@ -658,11 +658,14 @@
"colors": { "colors": {
"face": "#9c3af2", "face": "#9c3af2",
"wing": "#8f37ed", "wing": "#8f37ed",
"wing-edge": "#7029b8", "wing-edge": "#5b20c2",
"belly": "#ffffff", "belly": "#ffffff",
"underbelly": "#f2f2f2", "underbelly": "#f2f2f2",
"foot": "#736a66", "foot": "#736a66",
"collar": "#aa60e6" "collar": "#b760e6",
"nose": "#7a2ec7",
"cheek": "#7a2ec7",
"nose-tip": "#7a2ec7"
}, },
"rarity": "uncommon" "rarity": "uncommon"
} }
@@ -1940,11 +1943,13 @@
/** /**
* @param {string|(() => string)} text * @param {string|(() => string)} text
* @param {() => void} action * @param {() => void} action
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, icon, removeMenu = true) {
this.text = text; this.text = text;
this.action = action; this.action = action;
this.icon = icon;
this.removeMenu = removeMenu; this.removeMenu = removeMenu;
} }
} }
@@ -1954,10 +1959,11 @@
* @param {string} text * @param {string} text
* @param {() => void} action * @param {() => void} action
* @param {() => boolean} condition * @param {() => boolean} condition
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, condition, removeMenu = true) { constructor(text, action, condition, icon, removeMenu = true) {
super(text, action, removeMenu); super(text, action, icon, removeMenu);
this.condition = condition; this.condition = condition;
} }
} }
@@ -1968,7 +1974,7 @@
* @param {() => void} action * @param {() => void} action
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, removeMenu = true) {
super(text, action, () => isDebug(), removeMenu); super(text, action, () => isDebug(), undefined, removeMenu);
} }
} }
@@ -1983,11 +1989,29 @@
* @param {() => void} removeMenuCallback * @param {() => void} removeMenuCallback
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
function makeMenuItem(item, removeMenuCallback) { function createMenuItem(item, removeMenuCallback) {
if (item instanceof Separator) { if (item instanceof Separator) {
return makeElement("birb-window-separator"); return makeElement("birb-window-separator");
} }
let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text); let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text);
if (item.icon) {
const iconCanvas = document.createElement("canvas");
iconCanvas.width = 7;
iconCanvas.height = 6;
iconCanvas.classList.add("birb-menu-item-icon");
const ctx = iconCanvas.getContext("2d");
if (ctx) {
for (let row = 0; row < item.icon.length; row++) {
for (let col = 0; col < item.icon[row].length; col++) {
if (item.icon[row][col]) {
ctx.fillStyle = "black";
ctx.fillRect(col, row, 1, 1);
}
}
}
}
menuItem.prepend(iconCanvas);
}
onClick(menuItem, () => { onClick(menuItem, () => {
if (item.removeMenu) { if (item.removeMenu) {
removeMenuCallback(); removeMenuCallback();
@@ -2015,7 +2039,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
menu.appendChild(header); menu.appendChild(header);
@@ -2072,7 +2096,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
updateLocationCallback(menu); updateLocationCallback(menu);
@@ -2323,15 +2347,17 @@
font-size: 14px; font-size: 14px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: 10px; padding-left: 2px;
padding-right: 10px; padding-right: 10px;
box-sizing: border-box; box-sizing: border-box;
opacity: 0.7 !important; opacity: 0.7 !important;
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: space-between; justify-content: left;
align-items: center;
cursor: pointer; cursor: pointer;
color: black !important; color: black !important;
transition: background 0.1s, color 0.1s;
} }
.birb-menu-item:hover { .birb-menu-item:hover {
@@ -2343,6 +2369,21 @@
var(--birb-neg-border-size) 0 var(--birb-highlight), var(--birb-neg-border-size) 0 var(--birb-highlight),
0 var(--birb-neg-border-size) var(--birb-highlight), 0 var(--birb-neg-border-size) var(--birb-highlight),
0 var(--birb-border-size) var(--birb-highlight); 0 var(--birb-border-size) var(--birb-highlight);
transition: none;
}
.birb-menu-item-icon {
width: calc(7 * var(--birb-border-size));
height: calc(6 * var(--birb-border-size));
padding-right: calc(5 * var(--birb-border-size));
flex-shrink: 0;
image-rendering: pixelated;
color: var(--birb-highlight);
opacity: 0.9;
}
.birb-menu-item:hover > .birb-menu-item-icon {
filter: invert(1);
} }
.birb-menu-item-arrow { .birb-menu-item-arrow {
@@ -2393,10 +2434,12 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
transition: border-color 0.1s;
} }
.birb-grid-item:hover { .birb-grid-item:hover {
border-color: var(--birb-highlight); border-color: var(--birb-highlight);
transition: none;
} }
.birb-grid-item canvas { .birb-grid-item canvas {
@@ -2603,12 +2646,47 @@
}; };
const menuItems = [ const menuItems = [
new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem(`Pet ${birdBirb()}`, pet, [
new MenuItem("Field Guide", insertFieldGuide), [0, 1, 1, 0, 1, 1, 0],
new MenuItem("Wardrobe", insertWardrobe), [1, 0, 0, 1, 0, 0, 1],
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), [1, 0, 0, 0, 0, 0, 1],
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), [0, 1, 0, 0, 0, 1, 0],
new DebugMenuItem("Freeze/Unfreeze", () => { [0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
]),
new MenuItem("Field Guide", insertFieldGuide, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 1, 0, 1, 1, 1],
]),
new MenuItem("Wardrobe", insertWardrobe, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 0],
]),
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled(), [
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 0],
]),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false), [
[0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 0, 0],
]),
new DebugMenuItem("Freeze", () => {
frozen = !frozen; frozen = !frozen;
}), }),
new DebugMenuItem("Reset Data", resetSaveData), new DebugMenuItem("Reset Data", resetSaveData),
@@ -2627,11 +2705,18 @@
setDebug(false); setDebug(false);
}), }),
new Separator(), new Separator(),
new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), false), new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), [
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
], false),
]; ];
const settingsItems = [ const settingsItems = [
new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), false), new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), undefined, false),
new Separator(), new Separator(),
new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => { new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => {
userSettings.soundEnabled = !settings().soundEnabled; userSettings.soundEnabled = !settings().soundEnabled;
@@ -2651,7 +2736,7 @@
}), }),
new Separator(), new Separator(),
new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }),
new MenuItem("2026.3.30", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.30"); }, false), new MenuItem("Build 2026.4.3", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.4.3"); }, undefined, false),
]; ];
/** @type {Birb} */ /** @type {Birb} */

129
dist/web/birb.embed.js vendored
View File

@@ -622,8 +622,8 @@
"url": "https://en.wikipedia.org/wiki/Cuban_tody", "url": "https://en.wikipedia.org/wiki/Cuban_tody",
"colors": { "colors": {
"beak": "#f16f54", "beak": "#f16f54",
"face": "#5fdf44", "face": "#5ad63e",
"chin": "#f12d3e", "chin": "#e8273b",
"collar": "#f12d3e", "collar": "#f12d3e",
"belly": "#f6f5e4", "belly": "#f6f5e4",
"collar-scruff": "#a3ebff", "collar-scruff": "#a3ebff",
@@ -644,11 +644,14 @@
"colors": { "colors": {
"face": "#9c3af2", "face": "#9c3af2",
"wing": "#8f37ed", "wing": "#8f37ed",
"wing-edge": "#7029b8", "wing-edge": "#5b20c2",
"belly": "#ffffff", "belly": "#ffffff",
"underbelly": "#f2f2f2", "underbelly": "#f2f2f2",
"foot": "#736a66", "foot": "#736a66",
"collar": "#aa60e6" "collar": "#b760e6",
"nose": "#7a2ec7",
"cheek": "#7a2ec7",
"nose-tip": "#7a2ec7"
}, },
"rarity": "uncommon" "rarity": "uncommon"
} }
@@ -1920,11 +1923,13 @@
/** /**
* @param {string|(() => string)} text * @param {string|(() => string)} text
* @param {() => void} action * @param {() => void} action
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, icon, removeMenu = true) {
this.text = text; this.text = text;
this.action = action; this.action = action;
this.icon = icon;
this.removeMenu = removeMenu; this.removeMenu = removeMenu;
} }
} }
@@ -1934,10 +1939,11 @@
* @param {string} text * @param {string} text
* @param {() => void} action * @param {() => void} action
* @param {() => boolean} condition * @param {() => boolean} condition
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, condition, removeMenu = true) { constructor(text, action, condition, icon, removeMenu = true) {
super(text, action, removeMenu); super(text, action, icon, removeMenu);
this.condition = condition; this.condition = condition;
} }
} }
@@ -1948,7 +1954,7 @@
* @param {() => void} action * @param {() => void} action
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, removeMenu = true) {
super(text, action, () => isDebug(), removeMenu); super(text, action, () => isDebug(), undefined, removeMenu);
} }
} }
@@ -1963,11 +1969,29 @@
* @param {() => void} removeMenuCallback * @param {() => void} removeMenuCallback
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
function makeMenuItem(item, removeMenuCallback) { function createMenuItem(item, removeMenuCallback) {
if (item instanceof Separator) { if (item instanceof Separator) {
return makeElement("birb-window-separator"); return makeElement("birb-window-separator");
} }
let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text); let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text);
if (item.icon) {
const iconCanvas = document.createElement("canvas");
iconCanvas.width = 7;
iconCanvas.height = 6;
iconCanvas.classList.add("birb-menu-item-icon");
const ctx = iconCanvas.getContext("2d");
if (ctx) {
for (let row = 0; row < item.icon.length; row++) {
for (let col = 0; col < item.icon[row].length; col++) {
if (item.icon[row][col]) {
ctx.fillStyle = "black";
ctx.fillRect(col, row, 1, 1);
}
}
}
}
menuItem.prepend(iconCanvas);
}
onClick(menuItem, () => { onClick(menuItem, () => {
if (item.removeMenu) { if (item.removeMenu) {
removeMenuCallback(); removeMenuCallback();
@@ -1995,7 +2019,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
menu.appendChild(header); menu.appendChild(header);
@@ -2052,7 +2076,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
updateLocationCallback(menu); updateLocationCallback(menu);
@@ -2303,15 +2327,17 @@
font-size: 14px; font-size: 14px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: 10px; padding-left: 2px;
padding-right: 10px; padding-right: 10px;
box-sizing: border-box; box-sizing: border-box;
opacity: 0.7 !important; opacity: 0.7 !important;
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: space-between; justify-content: left;
align-items: center;
cursor: pointer; cursor: pointer;
color: black !important; color: black !important;
transition: background 0.1s, color 0.1s;
} }
.birb-menu-item:hover { .birb-menu-item:hover {
@@ -2323,6 +2349,21 @@
var(--birb-neg-border-size) 0 var(--birb-highlight), var(--birb-neg-border-size) 0 var(--birb-highlight),
0 var(--birb-neg-border-size) var(--birb-highlight), 0 var(--birb-neg-border-size) var(--birb-highlight),
0 var(--birb-border-size) var(--birb-highlight); 0 var(--birb-border-size) var(--birb-highlight);
transition: none;
}
.birb-menu-item-icon {
width: calc(7 * var(--birb-border-size));
height: calc(6 * var(--birb-border-size));
padding-right: calc(5 * var(--birb-border-size));
flex-shrink: 0;
image-rendering: pixelated;
color: var(--birb-highlight);
opacity: 0.9;
}
.birb-menu-item:hover > .birb-menu-item-icon {
filter: invert(1);
} }
.birb-menu-item-arrow { .birb-menu-item-arrow {
@@ -2373,10 +2414,12 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
transition: border-color 0.1s;
} }
.birb-grid-item:hover { .birb-grid-item:hover {
border-color: var(--birb-highlight); border-color: var(--birb-highlight);
transition: none;
} }
.birb-grid-item canvas { .birb-grid-item canvas {
@@ -2583,12 +2626,47 @@
}; };
const menuItems = [ const menuItems = [
new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem(`Pet ${birdBirb()}`, pet, [
new MenuItem("Field Guide", insertFieldGuide), [0, 1, 1, 0, 1, 1, 0],
new MenuItem("Wardrobe", insertWardrobe), [1, 0, 0, 1, 0, 0, 1],
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), [1, 0, 0, 0, 0, 0, 1],
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), [0, 1, 0, 0, 0, 1, 0],
new DebugMenuItem("Freeze/Unfreeze", () => { [0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
]),
new MenuItem("Field Guide", insertFieldGuide, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 1, 0, 1, 1, 1],
]),
new MenuItem("Wardrobe", insertWardrobe, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 0],
]),
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled(), [
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 0],
]),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false), [
[0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 0, 0],
]),
new DebugMenuItem("Freeze", () => {
frozen = !frozen; frozen = !frozen;
}), }),
new DebugMenuItem("Reset Data", resetSaveData), new DebugMenuItem("Reset Data", resetSaveData),
@@ -2607,11 +2685,18 @@
setDebug(false); setDebug(false);
}), }),
new Separator(), new Separator(),
new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), false), new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), [
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
], false),
]; ];
const settingsItems = [ const settingsItems = [
new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), false), new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), undefined, false),
new Separator(), new Separator(),
new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => { new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => {
userSettings.soundEnabled = !settings().soundEnabled; userSettings.soundEnabled = !settings().soundEnabled;
@@ -2631,7 +2716,7 @@
}), }),
new Separator(), new Separator(),
new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }),
new MenuItem("2026.3.30", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.30"); }, false), new MenuItem("Build 2026.4.3", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.4.3"); }, undefined, false),
]; ];
/** @type {Birb} */ /** @type {Birb} */

129
dist/web/birb.js vendored
View File

@@ -622,8 +622,8 @@
"url": "https://en.wikipedia.org/wiki/Cuban_tody", "url": "https://en.wikipedia.org/wiki/Cuban_tody",
"colors": { "colors": {
"beak": "#f16f54", "beak": "#f16f54",
"face": "#5fdf44", "face": "#5ad63e",
"chin": "#f12d3e", "chin": "#e8273b",
"collar": "#f12d3e", "collar": "#f12d3e",
"belly": "#f6f5e4", "belly": "#f6f5e4",
"collar-scruff": "#a3ebff", "collar-scruff": "#a3ebff",
@@ -644,11 +644,14 @@
"colors": { "colors": {
"face": "#9c3af2", "face": "#9c3af2",
"wing": "#8f37ed", "wing": "#8f37ed",
"wing-edge": "#7029b8", "wing-edge": "#5b20c2",
"belly": "#ffffff", "belly": "#ffffff",
"underbelly": "#f2f2f2", "underbelly": "#f2f2f2",
"foot": "#736a66", "foot": "#736a66",
"collar": "#aa60e6" "collar": "#b760e6",
"nose": "#7a2ec7",
"cheek": "#7a2ec7",
"nose-tip": "#7a2ec7"
}, },
"rarity": "uncommon" "rarity": "uncommon"
} }
@@ -1920,11 +1923,13 @@
/** /**
* @param {string|(() => string)} text * @param {string|(() => string)} text
* @param {() => void} action * @param {() => void} action
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, icon, removeMenu = true) {
this.text = text; this.text = text;
this.action = action; this.action = action;
this.icon = icon;
this.removeMenu = removeMenu; this.removeMenu = removeMenu;
} }
} }
@@ -1934,10 +1939,11 @@
* @param {string} text * @param {string} text
* @param {() => void} action * @param {() => void} action
* @param {() => boolean} condition * @param {() => boolean} condition
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, condition, removeMenu = true) { constructor(text, action, condition, icon, removeMenu = true) {
super(text, action, removeMenu); super(text, action, icon, removeMenu);
this.condition = condition; this.condition = condition;
} }
} }
@@ -1948,7 +1954,7 @@
* @param {() => void} action * @param {() => void} action
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, removeMenu = true) {
super(text, action, () => isDebug(), removeMenu); super(text, action, () => isDebug(), undefined, removeMenu);
} }
} }
@@ -1963,11 +1969,29 @@
* @param {() => void} removeMenuCallback * @param {() => void} removeMenuCallback
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
function makeMenuItem(item, removeMenuCallback) { function createMenuItem(item, removeMenuCallback) {
if (item instanceof Separator) { if (item instanceof Separator) {
return makeElement("birb-window-separator"); return makeElement("birb-window-separator");
} }
let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text); let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text);
if (item.icon) {
const iconCanvas = document.createElement("canvas");
iconCanvas.width = 7;
iconCanvas.height = 6;
iconCanvas.classList.add("birb-menu-item-icon");
const ctx = iconCanvas.getContext("2d");
if (ctx) {
for (let row = 0; row < item.icon.length; row++) {
for (let col = 0; col < item.icon[row].length; col++) {
if (item.icon[row][col]) {
ctx.fillStyle = "black";
ctx.fillRect(col, row, 1, 1);
}
}
}
}
menuItem.prepend(iconCanvas);
}
onClick(menuItem, () => { onClick(menuItem, () => {
if (item.removeMenu) { if (item.removeMenu) {
removeMenuCallback(); removeMenuCallback();
@@ -1995,7 +2019,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
menu.appendChild(header); menu.appendChild(header);
@@ -2052,7 +2076,7 @@
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
updateLocationCallback(menu); updateLocationCallback(menu);
@@ -2303,15 +2327,17 @@
font-size: 14px; font-size: 14px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: 10px; padding-left: 2px;
padding-right: 10px; padding-right: 10px;
box-sizing: border-box; box-sizing: border-box;
opacity: 0.7 !important; opacity: 0.7 !important;
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: space-between; justify-content: left;
align-items: center;
cursor: pointer; cursor: pointer;
color: black !important; color: black !important;
transition: background 0.1s, color 0.1s;
} }
.birb-menu-item:hover { .birb-menu-item:hover {
@@ -2323,6 +2349,21 @@
var(--birb-neg-border-size) 0 var(--birb-highlight), var(--birb-neg-border-size) 0 var(--birb-highlight),
0 var(--birb-neg-border-size) var(--birb-highlight), 0 var(--birb-neg-border-size) var(--birb-highlight),
0 var(--birb-border-size) var(--birb-highlight); 0 var(--birb-border-size) var(--birb-highlight);
transition: none;
}
.birb-menu-item-icon {
width: calc(7 * var(--birb-border-size));
height: calc(6 * var(--birb-border-size));
padding-right: calc(5 * var(--birb-border-size));
flex-shrink: 0;
image-rendering: pixelated;
color: var(--birb-highlight);
opacity: 0.9;
}
.birb-menu-item:hover > .birb-menu-item-icon {
filter: invert(1);
} }
.birb-menu-item-arrow { .birb-menu-item-arrow {
@@ -2373,10 +2414,12 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
transition: border-color 0.1s;
} }
.birb-grid-item:hover { .birb-grid-item:hover {
border-color: var(--birb-highlight); border-color: var(--birb-highlight);
transition: none;
} }
.birb-grid-item canvas { .birb-grid-item canvas {
@@ -2583,12 +2626,47 @@
}; };
const menuItems = [ const menuItems = [
new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem(`Pet ${birdBirb()}`, pet, [
new MenuItem("Field Guide", insertFieldGuide), [0, 1, 1, 0, 1, 1, 0],
new MenuItem("Wardrobe", insertWardrobe), [1, 0, 0, 1, 0, 0, 1],
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), [1, 0, 0, 0, 0, 0, 1],
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), [0, 1, 0, 0, 0, 1, 0],
new DebugMenuItem("Freeze/Unfreeze", () => { [0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
]),
new MenuItem("Field Guide", insertFieldGuide, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 1, 0, 1, 1, 1],
]),
new MenuItem("Wardrobe", insertWardrobe, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 0],
]),
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled(), [
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 0],
]),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false), [
[0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 0, 0],
]),
new DebugMenuItem("Freeze", () => {
frozen = !frozen; frozen = !frozen;
}), }),
new DebugMenuItem("Reset Data", resetSaveData), new DebugMenuItem("Reset Data", resetSaveData),
@@ -2607,11 +2685,18 @@
setDebug(false); setDebug(false);
}), }),
new Separator(), new Separator(),
new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), false), new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), [
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
], false),
]; ];
const settingsItems = [ const settingsItems = [
new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), false), new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), undefined, false),
new Separator(), new Separator(),
new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => { new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => {
userSettings.soundEnabled = !settings().soundEnabled; userSettings.soundEnabled = !settings().soundEnabled;
@@ -2631,7 +2716,7 @@
}), }),
new Separator(), new Separator(),
new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }),
new MenuItem("2026.3.30", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.30"); }, false), new MenuItem("Build 2026.4.3", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.4.3"); }, undefined, false),
]; ];
/** @type {Birb} */ /** @type {Birb} */

View File

@@ -169,12 +169,47 @@ function startApplication(birbPixels, featherPixels, hatsPixels) {
}; };
const menuItems = [ const menuItems = [
new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem(`Pet ${birdBirb()}`, pet, [
new MenuItem("Field Guide", insertFieldGuide), [0, 1, 1, 0, 1, 1, 0],
new MenuItem("Wardrobe", insertWardrobe), [1, 0, 0, 1, 0, 0, 1],
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), [1, 0, 0, 0, 0, 0, 1],
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), [0, 1, 0, 0, 0, 1, 0],
new DebugMenuItem("Freeze/Unfreeze", () => { [0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
]),
new MenuItem("Field Guide", insertFieldGuide, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 1, 0, 1, 1, 1],
]),
new MenuItem("Wardrobe", insertWardrobe, [
[0, 1, 1, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 0],
]),
new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled(), [
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 0],
]),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false), [
[0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 0, 0],
]),
new DebugMenuItem("Freeze", () => {
frozen = !frozen; frozen = !frozen;
}), }),
new DebugMenuItem("Reset Data", resetSaveData), new DebugMenuItem("Reset Data", resetSaveData),
@@ -193,11 +228,18 @@ function startApplication(birbPixels, featherPixels, hatsPixels) {
setDebug(false); setDebug(false);
}), }),
new Separator(), new Separator(),
new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), false), new MenuItem("Settings", () => switchMenuItems(settingsItems, updateMenuLocation), [
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0],
], false),
]; ];
const settingsItems = [ const settingsItems = [
new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), false), new MenuItem("Go Back", () => switchMenuItems(menuItems, updateMenuLocation), undefined, false),
new Separator(), new Separator(),
new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => { new MenuItem(() => `${settings().soundEnabled ? "Disable" : "Enable"} Sound`, () => {
userSettings.soundEnabled = !settings().soundEnabled; userSettings.soundEnabled = !settings().soundEnabled;
@@ -217,7 +259,7 @@ function startApplication(birbPixels, featherPixels, hatsPixels) {
}), }),
new Separator(), new Separator(),
new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }),
new MenuItem("__VERSION__", () => { alert("Thank you for using Pocket Bird! You are on version: __VERSION__") }, false), new MenuItem("Build __VERSION__", () => { alert("Thank you for using Pocket Bird! You are on version: __VERSION__") }, undefined, false),
]; ];
/** @type {Birb} */ /** @type {Birb} */

View File

@@ -14,11 +14,13 @@ export class MenuItem {
/** /**
* @param {string|(() => string)} text * @param {string|(() => string)} text
* @param {() => void} action * @param {() => void} action
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, icon, removeMenu = true) {
this.text = text; this.text = text;
this.action = action; this.action = action;
this.icon = icon;
this.removeMenu = removeMenu; this.removeMenu = removeMenu;
} }
} }
@@ -28,10 +30,11 @@ export class ConditionalMenuItem extends MenuItem {
* @param {string} text * @param {string} text
* @param {() => void} action * @param {() => void} action
* @param {() => boolean} condition * @param {() => boolean} condition
* @param {number[][]} [icon]
* @param {boolean} [removeMenu] * @param {boolean} [removeMenu]
*/ */
constructor(text, action, condition, removeMenu = true) { constructor(text, action, condition, icon, removeMenu = true) {
super(text, action, removeMenu); super(text, action, icon, removeMenu);
this.condition = condition; this.condition = condition;
} }
} }
@@ -42,7 +45,7 @@ export class DebugMenuItem extends ConditionalMenuItem {
* @param {() => void} action * @param {() => void} action
*/ */
constructor(text, action, removeMenu = true) { constructor(text, action, removeMenu = true) {
super(text, action, () => isDebug(), removeMenu); super(text, action, () => isDebug(), undefined, removeMenu);
} }
} }
@@ -57,11 +60,29 @@ export class Separator extends MenuItem {
* @param {() => void} removeMenuCallback * @param {() => void} removeMenuCallback
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
function makeMenuItem(item, removeMenuCallback) { function createMenuItem(item, removeMenuCallback) {
if (item instanceof Separator) { if (item instanceof Separator) {
return makeElement("birb-window-separator"); return makeElement("birb-window-separator");
} }
let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text); let menuItem = makeElement("birb-menu-item", typeof item.text === "function" ? item.text() : item.text);
if (item.icon) {
const iconCanvas = document.createElement("canvas");
iconCanvas.width = 7;
iconCanvas.height = 6;
iconCanvas.classList.add("birb-menu-item-icon");
const ctx = iconCanvas.getContext("2d");
if (ctx) {
for (let row = 0; row < item.icon.length; row++) {
for (let col = 0; col < item.icon[row].length; col++) {
if (item.icon[row][col]) {
ctx.fillStyle = "black";
ctx.fillRect(col, row, 1, 1);
}
}
}
}
menuItem.prepend(iconCanvas);
}
onClick(menuItem, () => { onClick(menuItem, () => {
if (item.removeMenu) { if (item.removeMenu) {
removeMenuCallback(); removeMenuCallback();
@@ -89,7 +110,7 @@ export function insertMenu(menuItems, title, updateLocationCallback) {
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
menu.appendChild(header); menu.appendChild(header);
@@ -146,7 +167,7 @@ export function switchMenuItems(menuItems, updateLocationCallback) {
const removeCallback = () => removeMenu(); const removeCallback = () => removeMenu();
for (const item of menuItems) { for (const item of menuItems) {
if (!(item instanceof ConditionalMenuItem) || item.condition()) { if (!(item instanceof ConditionalMenuItem) || item.condition()) {
content.appendChild(makeMenuItem(item, removeCallback)); content.appendChild(createMenuItem(item, removeCallback));
} }
} }
updateLocationCallback(menu); updateLocationCallback(menu);

View File

@@ -394,8 +394,8 @@ export default {
"url": "https://en.wikipedia.org/wiki/Cuban_tody", "url": "https://en.wikipedia.org/wiki/Cuban_tody",
"colors": { "colors": {
"beak": "#f16f54", "beak": "#f16f54",
"face": "#5fdf44", "face": "#5ad63e",
"chin": "#f12d3e", "chin": "#e8273b",
"collar": "#f12d3e", "collar": "#f12d3e",
"belly": "#f6f5e4", "belly": "#f6f5e4",
"collar-scruff": "#a3ebff", "collar-scruff": "#a3ebff",
@@ -416,11 +416,14 @@ export default {
"colors": { "colors": {
"face": "#9c3af2", "face": "#9c3af2",
"wing": "#8f37ed", "wing": "#8f37ed",
"wing-edge": "#7029b8", "wing-edge": "#5b20c2",
"belly": "#ffffff", "belly": "#ffffff",
"underbelly": "#f2f2f2", "underbelly": "#f2f2f2",
"foot": "#736a66", "foot": "#736a66",
"collar": "#aa60e6" "collar": "#b760e6",
"nose": "#7a2ec7",
"cheek": "#7a2ec7",
"nose-tip": "#7a2ec7"
}, },
"rarity": "uncommon" "rarity": "uncommon"
} }

View File

@@ -211,15 +211,17 @@
font-size: 14px; font-size: 14px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: 10px; padding-left: 2px;
padding-right: 10px; padding-right: 10px;
box-sizing: border-box; box-sizing: border-box;
opacity: 0.7 !important; opacity: 0.7 !important;
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: space-between; justify-content: left;
align-items: center;
cursor: pointer; cursor: pointer;
color: black !important; color: black !important;
transition: background 0.1s, color 0.1s;
} }
.birb-menu-item:hover { .birb-menu-item:hover {
@@ -231,6 +233,21 @@
var(--birb-neg-border-size) 0 var(--birb-highlight), var(--birb-neg-border-size) 0 var(--birb-highlight),
0 var(--birb-neg-border-size) var(--birb-highlight), 0 var(--birb-neg-border-size) var(--birb-highlight),
0 var(--birb-border-size) var(--birb-highlight); 0 var(--birb-border-size) var(--birb-highlight);
transition: none;
}
.birb-menu-item-icon {
width: calc(7 * var(--birb-border-size));
height: calc(6 * var(--birb-border-size));
padding-right: calc(5 * var(--birb-border-size));
flex-shrink: 0;
image-rendering: pixelated;
color: var(--birb-highlight);
opacity: 0.9;
}
.birb-menu-item:hover > .birb-menu-item-icon {
filter: invert(1);
} }
.birb-menu-item-arrow { .birb-menu-item-arrow {
@@ -281,10 +298,12 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
transition: border-color 0.1s;
} }
.birb-grid-item:hover { .birb-grid-item:hover {
border-color: var(--birb-highlight); border-color: var(--birb-highlight);
transition: none;
} }
.birb-grid-item canvas { .birb-grid-item canvas {