From a2dea8a17dc0a90631bb4ef81a1f57a2d9b34a33 Mon Sep 17 00:00:00 2001 From: Idrees Hassan Date: Wed, 11 Mar 2026 15:24:55 -0700 Subject: [PATCH] Add editor to this repository --- dist/extension.zip | Bin 155399 -> 155390 bytes dist/extension/birb.js | 117 +++++++------ dist/extension/manifest.json | 2 +- dist/obsidian/main.js | 119 +++++++------ dist/obsidian/manifest.json | 2 +- dist/userscript/birb.user.js | 119 +++++++------ dist/web/birb.embed.js | 117 +++++++------ dist/web/birb.js | 117 +++++++------ editor/editor.js | 321 +++++++++++++++++++++++++++++++++++ editor/index.html | 24 +++ editor/stylesheet.css | 151 ++++++++++++++++ src/animation/sprites.js | 56 ++++++ src/application.js | 59 +------ 13 files changed, 848 insertions(+), 356 deletions(-) create mode 100644 editor/editor.js create mode 100644 editor/index.html create mode 100644 editor/stylesheet.css diff --git a/dist/extension.zip b/dist/extension.zip index 3c0418dfdb427a4f3c82f3dbb7a15ee5d0771ca4..1a4283531263feeea95dc894733cfeecc8127cda 100644 GIT binary patch delta 33345 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 P18sePpq*6!ALRcJ6l9Jo delta 33215 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 diff --git a/dist/extension/birb.js b/dist/extension/birb.js index 08ef0b2..a14bea5 100644 --- a/dist/extension/birb.js +++ b/dist/extension/birb.js @@ -471,6 +471,62 @@ } } + /** + * Load a sprite sheet image and convert it to a 2D array of palette color names + * @param {string} src - URL or data URI of the sprite sheet image + * @param {boolean} [templateColors] - Whether to map pixel colors to palette names + * @returns {Promise} + */ + function loadSpriteSheetPixels(src, templateColors = true) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = src; + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Failed to get canvas context')); + return; + } + ctx.drawImage(img, 0, 0); + const imageData = ctx.getImageData(0, 0, img.width, img.height); + const pixels = imageData.data; + const hexArray = []; + for (let y = 0; y < img.height; y++) { + const row = []; + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const r = pixels[index]; + const g = pixels[index + 1]; + const b = pixels[index + 2]; + const a = pixels[index + 3]; + if (a === 0) { + row.push(PALETTE.TRANSPARENT); + continue; + } + const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; + if (!templateColors) { + row.push(hex); + continue; + } + if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { + row.push(hex); + continue; + } + row.push(SPRITE_SHEET_COLOR_MAP[hex]); + } + hexArray.push(row); + } + resolve(hexArray); + }; + img.onerror = (err) => { + reject(err); + }; + }); + } + /** @type {Record} */ const SPECIES = Object.fromEntries( Object.entries(species).map(([id, data]) => [ @@ -2100,7 +2156,7 @@ outline: none !important; box-shadow: none !important; }`; - const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABElJREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBb6ASIWaXJXpRS0UAhYCIrkAwRJISgIBq20sjrTWJnKxjTpLIKtd4ocWOSENOa1uJ11bm7n9u683ZnJ/R44Mtnb23f2dt5nZ/bfAYQQQgghZL5QvitAwkdExPWeUoptiEQLG28E+BSQjt2s1wEArXZ7oFxFHQghc4qkNJJEGkkyVB4lx1nFbySJfF5eFtnYGCqXHZ+QMlnwXQFSTLNex+NaDa1mc6hcJfufPuFxrZaVCYkdCjAifAmo1W7jSbc7MO1Jt5sNgQkhpBTMIfDn5eXsVdUQOK8OVcYmhASA5FB1bJ8CMutA+REyZ4hI/+C/8bfq+L4F5EP+hJTJGd8ViI39K1e8xFVKKRERn5ee8HIXQuYU3fPRvT/2ggiJH+7RJ8CUHntD1WPvdLgNyP/CBkSiQMtPO09EKhcgBTyn5J0B5RCQeEB22g0B4O0EUBo76PbPPB2fwguhzT2v+ULBPaqzhAImALDTbuiilxNASqmsDqH1/uy88JGnMTLyLLD5ZRqND3qaiJjzlNIg7KGPMR0iIqE1RFIed+vvvW5rnwIeBzMV1rc72TT6z41TgKZ4DtfvAA+A7Ruvs/d32s2B4zFlyCgEAROi8S3gEYhSKpPeaefCopLvf2Qm28K5EBGRhbPJwLTbzzr4cG8Bqy9PAAC9X98BAO8eJQN7mVnIaEjAGBTwtdUm7tbf63lnFpeQyBCX+L5+O8LB88teThiVxYVFJasvT/Dh3gJmIcGRAkQqoDuv8rvQWoBIJQhDRrnBJtgIvgVMwmZ9uyPvHiXzvp0L5RfqcD0UnENgfeeB631TfrCOOeQxzTD55HdnSMBafnZsezjuWqdxY5NwON5dkaW1PaXLALC05ld+vgW8vt3xemDPFT/tCEWTZyMrmgoLx7sr2Pj5EQDw4vwNAMCtw7djBfj67QgApuqK6/h5PVBbwBrdE81ZFkAJRss/8e1Vvv3yBVx9PUxcAjLzrWQROXufMUlwbAHaLK3t4er9Q1y6eC73s9aGmEpAvgVMiCYU8WmOd1fEzAE73yoQkLjyfy4ECEOCedjiwxS9L98CJiRUjndXpPelh40zLWBw5FNV+3YKEBFJsFCASI/r2RLqfekBAGqb+67P/gsypXR8C5iQkOluXc8a980fb7LpB88ve5egeWIy5LwrrFiehLT88qht7s9sxX0LmJDQMSWIfj5U3dZFd0JsEWoJhpx/YwkQqYS6W9dHzjtL+ZnxfQmYEDIWAgC2CGMQYOEDUfXlMOmKOCVYpnh0bPT3eM75KD9CvKDQH3r3k89xWCpqzKdhtJ4+zJ6KoctlPqDAutE7N37ZdSCEFGM/MSf0fJzoZzGVUmg9fZj9b5ZRYq9LL3dUfPb8CAmD9GYEIIJ8nOg3QcyhqF2ugqL4oX/ZhJx2YsvBia/Lcy6oghX3HZ8Qcrr4C5ZW3dG6vIHpAAAAAElFTkSuQmCC"; + const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABGxJREFUeJztnb9vE0kYht9xkA4RIZ1ObrKmPUyVSDRcH0RBAXbBmSZCOk46pEgUSFiIPwAhFwgiIRGRBq7yNSZ3UjoqmqRJRxGlxQ5IFlckRZCOfFfYs4w3+8Mm3p1Z+32kKJP12t/YO9+z3+yuNwAhhBBCCJkulO0OEPcREYl6TCnFMURyCwdvDrApIB27WioBAFrt9kA7iz4QQqYU6VPxPKl43rF2nBzHFb/iebI5Py+yvHysnXZ8QtKkYLsDJJlqqYQHxSJa1eqxdpZsvXuHB8Wi3yYk71CAOcKWgFrtNh53uwPLHne7/hSYEEJSwZwCb87P+z9ZTYHD+pBlbEKIA0gIWce2KSCzD5QfIVOGiPQO/hu/s45vW0A25E9Impyy3YG8sbWwYCWuUkqJiNi89ISXuxAypejKR1d/rIIIyT/co4+AKT1WQ9kT3OlwG5CTwgFEcoGWn3aeiGQuQAp4Sgk7A8opILGArLYrAsDaCaB+bKfHP/N0eBIvhDb3vOYPEr6jOk4oYAIAq+2Kblo5AaSU8vvgWvUXzAsbeZpHYs8Cmx+mMfigl4mIuU4qAyI49TGWQ0TEtYFI0uOP0hur29qmgIfBTIVao+Mvo/+iiRSgKZ6d2m3gHnD+6MdvK7QrA8dj0pCRCwImRGNbwDGIUsqX3qQzN6Nk76uMZVvEVoCFWQ/qzBwu/LMBADhaegpv7SauvjgCNoBaYw9ISUZhAm5cXvMfX21XUxcwITlAosS3+2E/886kzdyMkqsvjrBxpzAWCUa+gCmg2y/DS+iDf/f8drPu6edFBxtBUCIihVlvYNlvzzrYuFPoCdiI36x7A3Epwsmn1uhIs+5N+3aOld/2ShmuTtddIfbD6VdVkE838PvffwEA1q79CgC4+epZ6HO0CENeqxdwSDnZFjAhcdgWcK3RiT2wl7YAo+L38zA3eTaUAA/XF/HDLz8BAL5sfgYA3Nr5c6gAugzfXimPfO2WTQET4jJRAjLzLWURRVafeZLg0N8F1uLTNOseLt7dwc/nzoauH9gQJzoT9WXzsy++YD+CBDeK7gfPhpFJ4lV5aaAICeZbFgLa/bAfmf954btvhnC4vojT18vA3Z3Qx8PEd5Lqy6aACXGR5/9VsXyqBWQsviRqjQ6adU9c6EsSiVNg9Kunw/XFgccO3h8AAIoPt6Ke+y3Id4rPnIKHcfr6W1zMSMCEuEb30SV/cF/5+Npfvr1SzmqcS1QRYp6YdDnvEjsWJiEtvzCKD7fG9sZtC5gQ1zEliF4+ZD3WRRchQRFqCbqcf0MJEH0JdR9dil13nPIz49sSMCFkKAQAgiLMgwATjwHqG3H230ikBNMUj46N3h4vcj3KjxArKPSm3r3kizgslWvMu2G0ntz374qh22neoCDwRe/Q+Gn3gRCSTPCOOa7n40j/FlMphdaT+/7fZhspVl36dePis/IjxA36X00FcpCPI10GY05Fg+0sSIrv+odNyKSTtxwcqbNx5WwWb9x2fELIZPE/Any0lmed9+8AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAAAMCAYAAACjpxUSAAAAAXNSR0IArs4c6QAAA29JREFUWIXtl11oW2UYx3+v3ZCuc1ktIpvowrCBjmkdtl6ouBthWMeGTkQxuA9QKCiym20OBfVG3YUVOobYIszLyXY16HYxvOicns3NRoR267ZG04aOdc3Jx8lJsujjRZOzeE5y+raETjR/CJyc9/98vv/zvOdAAw000EADDWhB3e0E6gURkfK1Uuo/U9dS4567GFs0OHqOymJIKud/JeoV5/8AjyBaV7bJhrWdEmxtl3pumgv195tUEO1wRAGwdzg4F6whCm14BHFvU/NCfYjrp4WWF05TV2EEBIKjfFN4CUpi6Fy3mf0/hfys3Hnr1rDQenW4futLJmiPIO5vaSNrZ3XtZebJ5/k51M13wY3IhQA6hXf1GgQCqxaTb1U47wwBYc8DJ/h0YhOd6zaz65GjtK18yNf2Un/Yyavv1Wd0wkksFsMwDAzDQKfeQmSQxNkvAETMqnwZvvgrNYTpXvPYLuaBrIVl7htlMUQT4wt2drinFbkAqjspfi+sZ187SPvXn+u6dRdZ1a9SSpWPhoPrR4ARdslR9m38ft4Al/rDZFImFyOJeXOJxWLE43HGx7X6I4XIIFY6RcacRkwg2oGYo6JW1+yPHO47gplI8sEn788vttFDpNIWtpXEti1CPQM1ex9sbZcVzSugtM8Ve+zwPYI4/9R6bs3Okk88CsATV6/qFO5AdSd9C+jqNdj0bROUjg1raIufeCR84EcArv8+TTGf4vyJnb5iU0px8r1tjI1dRimFiFT76nAafeXUhHNzjXe90k6Mjh1ce/ZtJuNTACRu5+gnxLtcqZaTAJzbdryCH+IdcxS12pv3WHSKgeND5P+YwEwkSc6m2f36Wzz9ysuMRaeq9gZgePsxUnkb888imcl05VrNHvmdAI5RWT1ZO0smlwJgxr5RletOys+vm9/Va2AVmvjlzf20fbYPa2iLL/+5HX1YmQT54nIAfjvzoR9fF1JxVJBJmc51eUrsPfaDO44zHcooT4lwOOzhFiKDAFjp1J045rRz/fDWQx6bj/q+Ij85STH/FzO3bpLNZVnz+GN8+fEBr/9rg1CYnZsOudvY6ZtzKyVWqGfAbcOL7W9IJmuRy88JIkcGgMjMOYe31N/rugKab1QuOm8RkdLEWGgMLf6/0L8APHjfWpqXtVB5ZNhFixvp+D+M/gZZI68eaJ1OpQAAAABJRU5ErkJggg=="; @@ -2236,7 +2292,7 @@ }), new Separator(), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), - new MenuItem("2026.3.8", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.8"); }, false), + new MenuItem("2026.3.11", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.11"); }, false), ]; /** @type {Birb} */ @@ -3205,63 +3261,6 @@ draw(); } - /** - * Load the sprite sheet and return the pixel-map template - * @param {string} dataUri - * @param {boolean} [templateColors] - * @returns {Promise} - */ - function loadSpriteSheetPixels(dataUri, templateColors = true) { - return new Promise((resolve, reject) => { - const img = new Image(); - img.src = dataUri; - img.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - if (!ctx) { - reject(new Error('Failed to get canvas context')); - return; - } - ctx.drawImage(img, 0, 0); - const imageData = ctx.getImageData(0, 0, img.width, img.height); - const pixels = imageData.data; - const hexArray = []; - for (let y = 0; y < img.height; y++) { - const row = []; - for (let x = 0; x < img.width; x++) { - const index = (y * img.width + x) * 4; - const r = pixels[index]; - const g = pixels[index + 1]; - const b = pixels[index + 2]; - const a = pixels[index + 3]; - if (a === 0) { - row.push(PALETTE.TRANSPARENT); - continue; - } - const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; - if (!templateColors) { - row.push(hex); - continue; - } - if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - // Return the color as-is if not found in the map - row.push(hex); - continue; - } - row.push(SPRITE_SHEET_COLOR_MAP[hex]); - } - hexArray.push(row); - } - resolve(hexArray); - }; - img.onerror = (err) => { - reject(err); - }; - }); - } - initializeApplication(new BrowserExtensionContext()); })(); diff --git a/dist/extension/manifest.json b/dist/extension/manifest.json index c857fb9..c1c94d8 100644 --- a/dist/extension/manifest.json +++ b/dist/extension/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "Pocket Bird", "description": "It's a pet bird in your browser, what more could you want?", - "version": "2026.3.8", + "version": "2026.3.11", "homepage_url": "https://idreesinc.com", "icons": { "48": "images/icons/transparent/48x48x1.png", diff --git a/dist/obsidian/main.js b/dist/obsidian/main.js index 799da3f..42fd14c 100644 --- a/dist/obsidian/main.js +++ b/dist/obsidian/main.js @@ -1,7 +1,7 @@ const { Plugin, Notice } = require('obsidian'); module.exports = class PocketBird extends Plugin { onload() { - console.log("Loading Pocket Bird version 2026.3.8..."); + console.log("Loading Pocket Bird version 2026.3.11..."); const OBSIDIAN_PLUGIN = this; (function () { 'use strict'; @@ -476,6 +476,62 @@ module.exports = class PocketBird extends Plugin { } } + /** + * Load a sprite sheet image and convert it to a 2D array of palette color names + * @param {string} src - URL or data URI of the sprite sheet image + * @param {boolean} [templateColors] - Whether to map pixel colors to palette names + * @returns {Promise} + */ + function loadSpriteSheetPixels(src, templateColors = true) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = src; + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Failed to get canvas context')); + return; + } + ctx.drawImage(img, 0, 0); + const imageData = ctx.getImageData(0, 0, img.width, img.height); + const pixels = imageData.data; + const hexArray = []; + for (let y = 0; y < img.height; y++) { + const row = []; + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const r = pixels[index]; + const g = pixels[index + 1]; + const b = pixels[index + 2]; + const a = pixels[index + 3]; + if (a === 0) { + row.push(PALETTE.TRANSPARENT); + continue; + } + const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; + if (!templateColors) { + row.push(hex); + continue; + } + if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { + row.push(hex); + continue; + } + row.push(SPRITE_SHEET_COLOR_MAP[hex]); + } + hexArray.push(row); + } + resolve(hexArray); + }; + img.onerror = (err) => { + reject(err); + }; + }); + } + /** @type {Record} */ const SPECIES = Object.fromEntries( Object.entries(species).map(([id, data]) => [ @@ -2133,7 +2189,7 @@ module.exports = class PocketBird extends Plugin { outline: none !important; box-shadow: none !important; }`; - const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABElJREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBb6ASIWaXJXpRS0UAhYCIrkAwRJISgIBq20sjrTWJnKxjTpLIKtd4ocWOSENOa1uJ11bm7n9u683ZnJ/R44Mtnb23f2dt5nZ/bfAYQQQgghZL5QvitAwkdExPWeUoptiEQLG28E+BSQjt2s1wEArXZ7oFxFHQghc4qkNJJEGkkyVB4lx1nFbySJfF5eFtnYGCqXHZ+QMlnwXQFSTLNex+NaDa1mc6hcJfufPuFxrZaVCYkdCjAifAmo1W7jSbc7MO1Jt5sNgQkhpBTMIfDn5eXsVdUQOK8OVcYmhASA5FB1bJ8CMutA+REyZ4hI/+C/8bfq+L4F5EP+hJTJGd8ViI39K1e8xFVKKRERn5ee8HIXQuYU3fPRvT/2ggiJH+7RJ8CUHntD1WPvdLgNyP/CBkSiQMtPO09EKhcgBTyn5J0B5RCQeEB22g0B4O0EUBo76PbPPB2fwguhzT2v+ULBPaqzhAImALDTbuiilxNASqmsDqH1/uy88JGnMTLyLLD5ZRqND3qaiJjzlNIg7KGPMR0iIqE1RFIed+vvvW5rnwIeBzMV1rc72TT6z41TgKZ4DtfvAA+A7Ruvs/d32s2B4zFlyCgEAROi8S3gEYhSKpPeaefCopLvf2Qm28K5EBGRhbPJwLTbzzr4cG8Bqy9PAAC9X98BAO8eJQN7mVnIaEjAGBTwtdUm7tbf63lnFpeQyBCX+L5+O8LB88teThiVxYVFJasvT/Dh3gJmIcGRAkQqoDuv8rvQWoBIJQhDRrnBJtgIvgVMwmZ9uyPvHiXzvp0L5RfqcD0UnENgfeeB631TfrCOOeQxzTD55HdnSMBafnZsezjuWqdxY5NwON5dkaW1PaXLALC05ld+vgW8vt3xemDPFT/tCEWTZyMrmgoLx7sr2Pj5EQDw4vwNAMCtw7djBfj67QgApuqK6/h5PVBbwBrdE81ZFkAJRss/8e1Vvv3yBVx9PUxcAjLzrWQROXufMUlwbAHaLK3t4er9Q1y6eC73s9aGmEpAvgVMiCYU8WmOd1fEzAE73yoQkLjyfy4ECEOCedjiwxS9L98CJiRUjndXpPelh40zLWBw5FNV+3YKEBFJsFCASI/r2RLqfekBAGqb+67P/gsypXR8C5iQkOluXc8a980fb7LpB88ve5egeWIy5LwrrFiehLT88qht7s9sxX0LmJDQMSWIfj5U3dZFd0JsEWoJhpx/YwkQqYS6W9dHzjtL+ZnxfQmYEDIWAgC2CGMQYOEDUfXlMOmKOCVYpnh0bPT3eM75KD9CvKDQH3r3k89xWCpqzKdhtJ4+zJ6KoctlPqDAutE7N37ZdSCEFGM/MSf0fJzoZzGVUmg9fZj9b5ZRYq9LL3dUfPb8CAmD9GYEIIJ8nOg3QcyhqF2ugqL4oX/ZhJx2YsvBia/Lcy6oghX3HZ8Qcrr4C5ZW3dG6vIHpAAAAAElFTkSuQmCC"; + const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABGxJREFUeJztnb9vE0kYht9xkA4RIZ1ObrKmPUyVSDRcH0RBAXbBmSZCOk46pEgUSFiIPwAhFwgiIRGRBq7yNSZ3UjoqmqRJRxGlxQ5IFlckRZCOfFfYs4w3+8Mm3p1Z+32kKJP12t/YO9+z3+yuNwAhhBBCCJkulO0OEPcREYl6TCnFMURyCwdvDrApIB27WioBAFrt9kA7iz4QQqYU6VPxPKl43rF2nBzHFb/iebI5Py+yvHysnXZ8QtKkYLsDJJlqqYQHxSJa1eqxdpZsvXuHB8Wi3yYk71CAOcKWgFrtNh53uwPLHne7/hSYEEJSwZwCb87P+z9ZTYHD+pBlbEKIA0gIWce2KSCzD5QfIVOGiPQO/hu/s45vW0A25E9Impyy3YG8sbWwYCWuUkqJiNi89ISXuxAypejKR1d/rIIIyT/co4+AKT1WQ9kT3OlwG5CTwgFEcoGWn3aeiGQuQAp4Sgk7A8opILGArLYrAsDaCaB+bKfHP/N0eBIvhDb3vOYPEr6jOk4oYAIAq+2Kblo5AaSU8vvgWvUXzAsbeZpHYs8Cmx+mMfigl4mIuU4qAyI49TGWQ0TEtYFI0uOP0hur29qmgIfBTIVao+Mvo/+iiRSgKZ6d2m3gHnD+6MdvK7QrA8dj0pCRCwImRGNbwDGIUsqX3qQzN6Nk76uMZVvEVoCFWQ/qzBwu/LMBADhaegpv7SauvjgCNoBaYw9ISUZhAm5cXvMfX21XUxcwITlAosS3+2E/886kzdyMkqsvjrBxpzAWCUa+gCmg2y/DS+iDf/f8drPu6edFBxtBUCIihVlvYNlvzzrYuFPoCdiI36x7A3Epwsmn1uhIs+5N+3aOld/2ShmuTtddIfbD6VdVkE838PvffwEA1q79CgC4+epZ6HO0CENeqxdwSDnZFjAhcdgWcK3RiT2wl7YAo+L38zA3eTaUAA/XF/HDLz8BAL5sfgYA3Nr5c6gAugzfXimPfO2WTQET4jJRAjLzLWURRVafeZLg0N8F1uLTNOseLt7dwc/nzoauH9gQJzoT9WXzsy++YD+CBDeK7gfPhpFJ4lV5aaAICeZbFgLa/bAfmf954btvhnC4vojT18vA3Z3Qx8PEd5Lqy6aACXGR5/9VsXyqBWQsviRqjQ6adU9c6EsSiVNg9Kunw/XFgccO3h8AAIoPt6Ke+y3Id4rPnIKHcfr6W1zMSMCEuEb30SV/cF/5+Npfvr1SzmqcS1QRYp6YdDnvEjsWJiEtvzCKD7fG9sZtC5gQ1zEliF4+ZD3WRRchQRFqCbqcf0MJEH0JdR9dil13nPIz49sSMCFkKAQAgiLMgwATjwHqG3H230ikBNMUj46N3h4vcj3KjxArKPSm3r3kizgslWvMu2G0ntz374qh22neoCDwRe/Q+Gn3gRCSTPCOOa7n40j/FlMphdaT+/7fZhspVl36dePis/IjxA36X00FcpCPI10GY05Fg+0sSIrv+odNyKSTtxwcqbNx5WwWb9x2fELIZPE/Any0lmed9+8AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAAAMCAYAAACjpxUSAAAAAXNSR0IArs4c6QAAA29JREFUWIXtl11oW2UYx3+v3ZCuc1ktIpvowrCBjmkdtl6ouBthWMeGTkQxuA9QKCiym20OBfVG3YUVOobYIszLyXY16HYxvOicns3NRoR267ZG04aOdc3Jx8lJsujjRZOzeE5y+raETjR/CJyc9/98vv/zvOdAAw000EADDWhB3e0E6gURkfK1Uuo/U9dS4567GFs0OHqOymJIKud/JeoV5/8AjyBaV7bJhrWdEmxtl3pumgv195tUEO1wRAGwdzg4F6whCm14BHFvU/NCfYjrp4WWF05TV2EEBIKjfFN4CUpi6Fy3mf0/hfys3Hnr1rDQenW4futLJmiPIO5vaSNrZ3XtZebJ5/k51M13wY3IhQA6hXf1GgQCqxaTb1U47wwBYc8DJ/h0YhOd6zaz65GjtK18yNf2Un/Yyavv1Wd0wkksFsMwDAzDQKfeQmSQxNkvAETMqnwZvvgrNYTpXvPYLuaBrIVl7htlMUQT4wt2drinFbkAqjspfi+sZ187SPvXn+u6dRdZ1a9SSpWPhoPrR4ARdslR9m38ft4Al/rDZFImFyOJeXOJxWLE43HGx7X6I4XIIFY6RcacRkwg2oGYo6JW1+yPHO47gplI8sEn788vttFDpNIWtpXEti1CPQM1ex9sbZcVzSugtM8Ve+zwPYI4/9R6bs3Okk88CsATV6/qFO5AdSd9C+jqNdj0bROUjg1raIufeCR84EcArv8+TTGf4vyJnb5iU0px8r1tjI1dRimFiFT76nAafeXUhHNzjXe90k6Mjh1ce/ZtJuNTACRu5+gnxLtcqZaTAJzbdryCH+IdcxS12pv3WHSKgeND5P+YwEwkSc6m2f36Wzz9ysuMRaeq9gZgePsxUnkb888imcl05VrNHvmdAI5RWT1ZO0smlwJgxr5RletOys+vm9/Va2AVmvjlzf20fbYPa2iLL/+5HX1YmQT54nIAfjvzoR9fF1JxVJBJmc51eUrsPfaDO44zHcooT4lwOOzhFiKDAFjp1J045rRz/fDWQx6bj/q+Ij85STH/FzO3bpLNZVnz+GN8+fEBr/9rg1CYnZsOudvY6ZtzKyVWqGfAbcOL7W9IJmuRy88JIkcGgMjMOYe31N/rugKab1QuOm8RkdLEWGgMLf6/0L8APHjfWpqXtVB5ZNhFixvp+D+M/gZZI68eaJ1OpQAAAABJRU5ErkJggg=="; @@ -2269,7 +2325,7 @@ module.exports = class PocketBird extends Plugin { }), new Separator(), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), - new MenuItem("2026.3.8", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.8"); }, false), + new MenuItem("2026.3.11", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.11"); }, false), ]; /** @type {Birb} */ @@ -3238,63 +3294,6 @@ module.exports = class PocketBird extends Plugin { draw(); } - /** - * Load the sprite sheet and return the pixel-map template - * @param {string} dataUri - * @param {boolean} [templateColors] - * @returns {Promise} - */ - function loadSpriteSheetPixels(dataUri, templateColors = true) { - return new Promise((resolve, reject) => { - const img = new Image(); - img.src = dataUri; - img.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - if (!ctx) { - reject(new Error('Failed to get canvas context')); - return; - } - ctx.drawImage(img, 0, 0); - const imageData = ctx.getImageData(0, 0, img.width, img.height); - const pixels = imageData.data; - const hexArray = []; - for (let y = 0; y < img.height; y++) { - const row = []; - for (let x = 0; x < img.width; x++) { - const index = (y * img.width + x) * 4; - const r = pixels[index]; - const g = pixels[index + 1]; - const b = pixels[index + 2]; - const a = pixels[index + 3]; - if (a === 0) { - row.push(PALETTE.TRANSPARENT); - continue; - } - const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; - if (!templateColors) { - row.push(hex); - continue; - } - if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - // Return the color as-is if not found in the map - row.push(hex); - continue; - } - row.push(SPRITE_SHEET_COLOR_MAP[hex]); - } - hexArray.push(row); - } - resolve(hexArray); - }; - img.onerror = (err) => { - reject(err); - }; - }); - } - initializeApplication(new ObsidianContext()); })(); diff --git a/dist/obsidian/manifest.json b/dist/obsidian/manifest.json index c8fca71..40deb07 100644 --- a/dist/obsidian/manifest.json +++ b/dist/obsidian/manifest.json @@ -1,7 +1,7 @@ { "id": "pocket-bird", "name": "Pocket Bird", - "version": "2026.3.8", + "version": "2026.3.11", "minAppVersion": "0.15.0", "description": "Add a pet bird to fly around your notes and keep you company!", "author": "Idrees Hassan", diff --git a/dist/userscript/birb.user.js b/dist/userscript/birb.user.js index 65e8e1c..c3db689 100644 --- a/dist/userscript/birb.user.js +++ b/dist/userscript/birb.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Pocket Bird // @namespace https://idreesinc.com -// @version 2026.3.8 +// @version 2026.3.11 // @description It's a pet bird in your browser, what more could you want? // @author Idrees // @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/userscript/birb.user.js @@ -485,6 +485,62 @@ } } + /** + * Load a sprite sheet image and convert it to a 2D array of palette color names + * @param {string} src - URL or data URI of the sprite sheet image + * @param {boolean} [templateColors] - Whether to map pixel colors to palette names + * @returns {Promise} + */ + function loadSpriteSheetPixels(src, templateColors = true) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = src; + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Failed to get canvas context')); + return; + } + ctx.drawImage(img, 0, 0); + const imageData = ctx.getImageData(0, 0, img.width, img.height); + const pixels = imageData.data; + const hexArray = []; + for (let y = 0; y < img.height; y++) { + const row = []; + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const r = pixels[index]; + const g = pixels[index + 1]; + const b = pixels[index + 2]; + const a = pixels[index + 3]; + if (a === 0) { + row.push(PALETTE.TRANSPARENT); + continue; + } + const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; + if (!templateColors) { + row.push(hex); + continue; + } + if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { + row.push(hex); + continue; + } + row.push(SPRITE_SHEET_COLOR_MAP[hex]); + } + hexArray.push(row); + } + resolve(hexArray); + }; + img.onerror = (err) => { + reject(err); + }; + }); + } + /** @type {Record} */ const SPECIES = Object.fromEntries( Object.entries(species).map(([id, data]) => [ @@ -2095,7 +2151,7 @@ outline: none !important; box-shadow: none !important; }`; - const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABElJREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBb6ASIWaXJXpRS0UAhYCIrkAwRJISgIBq20sjrTWJnKxjTpLIKtd4ocWOSENOa1uJ11bm7n9u683ZnJ/R44Mtnb23f2dt5nZ/bfAYQQQgghZL5QvitAwkdExPWeUoptiEQLG28E+BSQjt2s1wEArXZ7oFxFHQghc4qkNJJEGkkyVB4lx1nFbySJfF5eFtnYGCqXHZ+QMlnwXQFSTLNex+NaDa1mc6hcJfufPuFxrZaVCYkdCjAifAmo1W7jSbc7MO1Jt5sNgQkhpBTMIfDn5eXsVdUQOK8OVcYmhASA5FB1bJ8CMutA+REyZ4hI/+C/8bfq+L4F5EP+hJTJGd8ViI39K1e8xFVKKRERn5ee8HIXQuYU3fPRvT/2ggiJH+7RJ8CUHntD1WPvdLgNyP/CBkSiQMtPO09EKhcgBTyn5J0B5RCQeEB22g0B4O0EUBo76PbPPB2fwguhzT2v+ULBPaqzhAImALDTbuiilxNASqmsDqH1/uy88JGnMTLyLLD5ZRqND3qaiJjzlNIg7KGPMR0iIqE1RFIed+vvvW5rnwIeBzMV1rc72TT6z41TgKZ4DtfvAA+A7Ruvs/d32s2B4zFlyCgEAROi8S3gEYhSKpPeaefCopLvf2Qm28K5EBGRhbPJwLTbzzr4cG8Bqy9PAAC9X98BAO8eJQN7mVnIaEjAGBTwtdUm7tbf63lnFpeQyBCX+L5+O8LB88teThiVxYVFJasvT/Dh3gJmIcGRAkQqoDuv8rvQWoBIJQhDRrnBJtgIvgVMwmZ9uyPvHiXzvp0L5RfqcD0UnENgfeeB631TfrCOOeQxzTD55HdnSMBafnZsezjuWqdxY5NwON5dkaW1PaXLALC05ld+vgW8vt3xemDPFT/tCEWTZyMrmgoLx7sr2Pj5EQDw4vwNAMCtw7djBfj67QgApuqK6/h5PVBbwBrdE81ZFkAJRss/8e1Vvv3yBVx9PUxcAjLzrWQROXufMUlwbAHaLK3t4er9Q1y6eC73s9aGmEpAvgVMiCYU8WmOd1fEzAE73yoQkLjyfy4ECEOCedjiwxS9L98CJiRUjndXpPelh40zLWBw5FNV+3YKEBFJsFCASI/r2RLqfekBAGqb+67P/gsypXR8C5iQkOluXc8a980fb7LpB88ve5egeWIy5LwrrFiehLT88qht7s9sxX0LmJDQMSWIfj5U3dZFd0JsEWoJhpx/YwkQqYS6W9dHzjtL+ZnxfQmYEDIWAgC2CGMQYOEDUfXlMOmKOCVYpnh0bPT3eM75KD9CvKDQH3r3k89xWCpqzKdhtJ4+zJ6KoctlPqDAutE7N37ZdSCEFGM/MSf0fJzoZzGVUmg9fZj9b5ZRYq9LL3dUfPb8CAmD9GYEIIJ8nOg3QcyhqF2ugqL4oX/ZhJx2YsvBia/Lcy6oghX3HZ8Qcrr4C5ZW3dG6vIHpAAAAAElFTkSuQmCC"; + const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABGxJREFUeJztnb9vE0kYht9xkA4RIZ1ObrKmPUyVSDRcH0RBAXbBmSZCOk46pEgUSFiIPwAhFwgiIRGRBq7yNSZ3UjoqmqRJRxGlxQ5IFlckRZCOfFfYs4w3+8Mm3p1Z+32kKJP12t/YO9+z3+yuNwAhhBBCCJkulO0OEPcREYl6TCnFMURyCwdvDrApIB27WioBAFrt9kA7iz4QQqYU6VPxPKl43rF2nBzHFb/iebI5Py+yvHysnXZ8QtKkYLsDJJlqqYQHxSJa1eqxdpZsvXuHB8Wi3yYk71CAOcKWgFrtNh53uwPLHne7/hSYEEJSwZwCb87P+z9ZTYHD+pBlbEKIA0gIWce2KSCzD5QfIVOGiPQO/hu/s45vW0A25E9Impyy3YG8sbWwYCWuUkqJiNi89ISXuxAypejKR1d/rIIIyT/co4+AKT1WQ9kT3OlwG5CTwgFEcoGWn3aeiGQuQAp4Sgk7A8opILGArLYrAsDaCaB+bKfHP/N0eBIvhDb3vOYPEr6jOk4oYAIAq+2Kblo5AaSU8vvgWvUXzAsbeZpHYs8Cmx+mMfigl4mIuU4qAyI49TGWQ0TEtYFI0uOP0hur29qmgIfBTIVao+Mvo/+iiRSgKZ6d2m3gHnD+6MdvK7QrA8dj0pCRCwImRGNbwDGIUsqX3qQzN6Nk76uMZVvEVoCFWQ/qzBwu/LMBADhaegpv7SauvjgCNoBaYw9ISUZhAm5cXvMfX21XUxcwITlAosS3+2E/886kzdyMkqsvjrBxpzAWCUa+gCmg2y/DS+iDf/f8drPu6edFBxtBUCIihVlvYNlvzzrYuFPoCdiI36x7A3Epwsmn1uhIs+5N+3aOld/2ShmuTtddIfbD6VdVkE838PvffwEA1q79CgC4+epZ6HO0CENeqxdwSDnZFjAhcdgWcK3RiT2wl7YAo+L38zA3eTaUAA/XF/HDLz8BAL5sfgYA3Nr5c6gAugzfXimPfO2WTQET4jJRAjLzLWURRVafeZLg0N8F1uLTNOseLt7dwc/nzoauH9gQJzoT9WXzsy++YD+CBDeK7gfPhpFJ4lV5aaAICeZbFgLa/bAfmf954btvhnC4vojT18vA3Z3Qx8PEd5Lqy6aACXGR5/9VsXyqBWQsviRqjQ6adU9c6EsSiVNg9Kunw/XFgccO3h8AAIoPt6Ke+y3Id4rPnIKHcfr6W1zMSMCEuEb30SV/cF/5+Npfvr1SzmqcS1QRYp6YdDnvEjsWJiEtvzCKD7fG9sZtC5gQ1zEliF4+ZD3WRRchQRFqCbqcf0MJEH0JdR9dil13nPIz49sSMCFkKAQAgiLMgwATjwHqG3H230ikBNMUj46N3h4vcj3KjxArKPSm3r3kizgslWvMu2G0ntz374qh22neoCDwRe/Q+Gn3gRCSTPCOOa7n40j/FlMphdaT+/7fZhspVl36dePis/IjxA36X00FcpCPI10GY05Fg+0sSIrv+odNyKSTtxwcqbNx5WwWb9x2fELIZPE/Any0lmed9+8AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAAAMCAYAAACjpxUSAAAAAXNSR0IArs4c6QAAA29JREFUWIXtl11oW2UYx3+v3ZCuc1ktIpvowrCBjmkdtl6ouBthWMeGTkQxuA9QKCiym20OBfVG3YUVOobYIszLyXY16HYxvOicns3NRoR267ZG04aOdc3Jx8lJsujjRZOzeE5y+raETjR/CJyc9/98vv/zvOdAAw000EADDWhB3e0E6gURkfK1Uuo/U9dS4567GFs0OHqOymJIKud/JeoV5/8AjyBaV7bJhrWdEmxtl3pumgv195tUEO1wRAGwdzg4F6whCm14BHFvU/NCfYjrp4WWF05TV2EEBIKjfFN4CUpi6Fy3mf0/hfys3Hnr1rDQenW4futLJmiPIO5vaSNrZ3XtZebJ5/k51M13wY3IhQA6hXf1GgQCqxaTb1U47wwBYc8DJ/h0YhOd6zaz65GjtK18yNf2Un/Yyavv1Wd0wkksFsMwDAzDQKfeQmSQxNkvAETMqnwZvvgrNYTpXvPYLuaBrIVl7htlMUQT4wt2drinFbkAqjspfi+sZ187SPvXn+u6dRdZ1a9SSpWPhoPrR4ARdslR9m38ft4Al/rDZFImFyOJeXOJxWLE43HGx7X6I4XIIFY6RcacRkwg2oGYo6JW1+yPHO47gplI8sEn788vttFDpNIWtpXEti1CPQM1ex9sbZcVzSugtM8Ve+zwPYI4/9R6bs3Okk88CsATV6/qFO5AdSd9C+jqNdj0bROUjg1raIufeCR84EcArv8+TTGf4vyJnb5iU0px8r1tjI1dRimFiFT76nAafeXUhHNzjXe90k6Mjh1ce/ZtJuNTACRu5+gnxLtcqZaTAJzbdryCH+IdcxS12pv3WHSKgeND5P+YwEwkSc6m2f36Wzz9ysuMRaeq9gZgePsxUnkb888imcl05VrNHvmdAI5RWT1ZO0smlwJgxr5RletOys+vm9/Va2AVmvjlzf20fbYPa2iLL/+5HX1YmQT54nIAfjvzoR9fF1JxVJBJmc51eUrsPfaDO44zHcooT4lwOOzhFiKDAFjp1J045rRz/fDWQx6bj/q+Ij85STH/FzO3bpLNZVnz+GN8+fEBr/9rg1CYnZsOudvY6ZtzKyVWqGfAbcOL7W9IJmuRy88JIkcGgMjMOYe31N/rugKab1QuOm8RkdLEWGgMLf6/0L8APHjfWpqXtVB5ZNhFixvp+D+M/gZZI68eaJ1OpQAAAABJRU5ErkJggg=="; @@ -2231,7 +2287,7 @@ }), new Separator(), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), - new MenuItem("2026.3.8", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.8"); }, false), + new MenuItem("2026.3.11", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.11"); }, false), ]; /** @type {Birb} */ @@ -3200,63 +3256,6 @@ draw(); } - /** - * Load the sprite sheet and return the pixel-map template - * @param {string} dataUri - * @param {boolean} [templateColors] - * @returns {Promise} - */ - function loadSpriteSheetPixels(dataUri, templateColors = true) { - return new Promise((resolve, reject) => { - const img = new Image(); - img.src = dataUri; - img.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - if (!ctx) { - reject(new Error('Failed to get canvas context')); - return; - } - ctx.drawImage(img, 0, 0); - const imageData = ctx.getImageData(0, 0, img.width, img.height); - const pixels = imageData.data; - const hexArray = []; - for (let y = 0; y < img.height; y++) { - const row = []; - for (let x = 0; x < img.width; x++) { - const index = (y * img.width + x) * 4; - const r = pixels[index]; - const g = pixels[index + 1]; - const b = pixels[index + 2]; - const a = pixels[index + 3]; - if (a === 0) { - row.push(PALETTE.TRANSPARENT); - continue; - } - const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; - if (!templateColors) { - row.push(hex); - continue; - } - if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - // Return the color as-is if not found in the map - row.push(hex); - continue; - } - row.push(SPRITE_SHEET_COLOR_MAP[hex]); - } - hexArray.push(row); - } - resolve(hexArray); - }; - img.onerror = (err) => { - reject(err); - }; - }); - } - initializeApplication(new UserScriptContext()); })(); diff --git a/dist/web/birb.embed.js b/dist/web/birb.embed.js index b994cba..ed74e5e 100644 --- a/dist/web/birb.embed.js +++ b/dist/web/birb.embed.js @@ -471,6 +471,62 @@ } } + /** + * Load a sprite sheet image and convert it to a 2D array of palette color names + * @param {string} src - URL or data URI of the sprite sheet image + * @param {boolean} [templateColors] - Whether to map pixel colors to palette names + * @returns {Promise} + */ + function loadSpriteSheetPixels(src, templateColors = true) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = src; + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Failed to get canvas context')); + return; + } + ctx.drawImage(img, 0, 0); + const imageData = ctx.getImageData(0, 0, img.width, img.height); + const pixels = imageData.data; + const hexArray = []; + for (let y = 0; y < img.height; y++) { + const row = []; + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const r = pixels[index]; + const g = pixels[index + 1]; + const b = pixels[index + 2]; + const a = pixels[index + 3]; + if (a === 0) { + row.push(PALETTE.TRANSPARENT); + continue; + } + const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; + if (!templateColors) { + row.push(hex); + continue; + } + if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { + row.push(hex); + continue; + } + row.push(SPRITE_SHEET_COLOR_MAP[hex]); + } + hexArray.push(row); + } + resolve(hexArray); + }; + img.onerror = (err) => { + reject(err); + }; + }); + } + /** @type {Record} */ const SPECIES = Object.fromEntries( Object.entries(species).map(([id, data]) => [ @@ -2075,7 +2131,7 @@ outline: none !important; box-shadow: none !important; }`; - const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABElJREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBb6ASIWaXJXpRS0UAhYCIrkAwRJISgIBq20sjrTWJnKxjTpLIKtd4ocWOSENOa1uJ11bm7n9u683ZnJ/R44Mtnb23f2dt5nZ/bfAYQQQgghZL5QvitAwkdExPWeUoptiEQLG28E+BSQjt2s1wEArXZ7oFxFHQghc4qkNJJEGkkyVB4lx1nFbySJfF5eFtnYGCqXHZ+QMlnwXQFSTLNex+NaDa1mc6hcJfufPuFxrZaVCYkdCjAifAmo1W7jSbc7MO1Jt5sNgQkhpBTMIfDn5eXsVdUQOK8OVcYmhASA5FB1bJ8CMutA+REyZ4hI/+C/8bfq+L4F5EP+hJTJGd8ViI39K1e8xFVKKRERn5ee8HIXQuYU3fPRvT/2ggiJH+7RJ8CUHntD1WPvdLgNyP/CBkSiQMtPO09EKhcgBTyn5J0B5RCQeEB22g0B4O0EUBo76PbPPB2fwguhzT2v+ULBPaqzhAImALDTbuiilxNASqmsDqH1/uy88JGnMTLyLLD5ZRqND3qaiJjzlNIg7KGPMR0iIqE1RFIed+vvvW5rnwIeBzMV1rc72TT6z41TgKZ4DtfvAA+A7Ruvs/d32s2B4zFlyCgEAROi8S3gEYhSKpPeaefCopLvf2Qm28K5EBGRhbPJwLTbzzr4cG8Bqy9PAAC9X98BAO8eJQN7mVnIaEjAGBTwtdUm7tbf63lnFpeQyBCX+L5+O8LB88teThiVxYVFJasvT/Dh3gJmIcGRAkQqoDuv8rvQWoBIJQhDRrnBJtgIvgVMwmZ9uyPvHiXzvp0L5RfqcD0UnENgfeeB631TfrCOOeQxzTD55HdnSMBafnZsezjuWqdxY5NwON5dkaW1PaXLALC05ld+vgW8vt3xemDPFT/tCEWTZyMrmgoLx7sr2Pj5EQDw4vwNAMCtw7djBfj67QgApuqK6/h5PVBbwBrdE81ZFkAJRss/8e1Vvv3yBVx9PUxcAjLzrWQROXufMUlwbAHaLK3t4er9Q1y6eC73s9aGmEpAvgVMiCYU8WmOd1fEzAE73yoQkLjyfy4ECEOCedjiwxS9L98CJiRUjndXpPelh40zLWBw5FNV+3YKEBFJsFCASI/r2RLqfekBAGqb+67P/gsypXR8C5iQkOluXc8a980fb7LpB88ve5egeWIy5LwrrFiehLT88qht7s9sxX0LmJDQMSWIfj5U3dZFd0JsEWoJhpx/YwkQqYS6W9dHzjtL+ZnxfQmYEDIWAgC2CGMQYOEDUfXlMOmKOCVYpnh0bPT3eM75KD9CvKDQH3r3k89xWCpqzKdhtJ4+zJ6KoctlPqDAutE7N37ZdSCEFGM/MSf0fJzoZzGVUmg9fZj9b5ZRYq9LL3dUfPb8CAmD9GYEIIJ8nOg3QcyhqF2ugqL4oX/ZhJx2YsvBia/Lcy6oghX3HZ8Qcrr4C5ZW3dG6vIHpAAAAAElFTkSuQmCC"; + const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABGxJREFUeJztnb9vE0kYht9xkA4RIZ1ObrKmPUyVSDRcH0RBAXbBmSZCOk46pEgUSFiIPwAhFwgiIRGRBq7yNSZ3UjoqmqRJRxGlxQ5IFlckRZCOfFfYs4w3+8Mm3p1Z+32kKJP12t/YO9+z3+yuNwAhhBBCCJkulO0OEPcREYl6TCnFMURyCwdvDrApIB27WioBAFrt9kA7iz4QQqYU6VPxPKl43rF2nBzHFb/iebI5Py+yvHysnXZ8QtKkYLsDJJlqqYQHxSJa1eqxdpZsvXuHB8Wi3yYk71CAOcKWgFrtNh53uwPLHne7/hSYEEJSwZwCb87P+z9ZTYHD+pBlbEKIA0gIWce2KSCzD5QfIVOGiPQO/hu/s45vW0A25E9Impyy3YG8sbWwYCWuUkqJiNi89ISXuxAypejKR1d/rIIIyT/co4+AKT1WQ9kT3OlwG5CTwgFEcoGWn3aeiGQuQAp4Sgk7A8opILGArLYrAsDaCaB+bKfHP/N0eBIvhDb3vOYPEr6jOk4oYAIAq+2Kblo5AaSU8vvgWvUXzAsbeZpHYs8Cmx+mMfigl4mIuU4qAyI49TGWQ0TEtYFI0uOP0hur29qmgIfBTIVao+Mvo/+iiRSgKZ6d2m3gHnD+6MdvK7QrA8dj0pCRCwImRGNbwDGIUsqX3qQzN6Nk76uMZVvEVoCFWQ/qzBwu/LMBADhaegpv7SauvjgCNoBaYw9ISUZhAm5cXvMfX21XUxcwITlAosS3+2E/886kzdyMkqsvjrBxpzAWCUa+gCmg2y/DS+iDf/f8drPu6edFBxtBUCIihVlvYNlvzzrYuFPoCdiI36x7A3Epwsmn1uhIs+5N+3aOld/2ShmuTtddIfbD6VdVkE838PvffwEA1q79CgC4+epZ6HO0CENeqxdwSDnZFjAhcdgWcK3RiT2wl7YAo+L38zA3eTaUAA/XF/HDLz8BAL5sfgYA3Nr5c6gAugzfXimPfO2WTQET4jJRAjLzLWURRVafeZLg0N8F1uLTNOseLt7dwc/nzoauH9gQJzoT9WXzsy++YD+CBDeK7gfPhpFJ4lV5aaAICeZbFgLa/bAfmf954btvhnC4vojT18vA3Z3Qx8PEd5Lqy6aACXGR5/9VsXyqBWQsviRqjQ6adU9c6EsSiVNg9Kunw/XFgccO3h8AAIoPt6Ke+y3Id4rPnIKHcfr6W1zMSMCEuEb30SV/cF/5+Npfvr1SzmqcS1QRYp6YdDnvEjsWJiEtvzCKD7fG9sZtC5gQ1zEliF4+ZD3WRRchQRFqCbqcf0MJEH0JdR9dil13nPIz49sSMCFkKAQAgiLMgwATjwHqG3H230ikBNMUj46N3h4vcj3KjxArKPSm3r3kizgslWvMu2G0ntz374qh22neoCDwRe/Q+Gn3gRCSTPCOOa7n40j/FlMphdaT+/7fZhspVl36dePis/IjxA36X00FcpCPI10GY05Fg+0sSIrv+odNyKSTtxwcqbNx5WwWb9x2fELIZPE/Any0lmed9+8AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAAAMCAYAAACjpxUSAAAAAXNSR0IArs4c6QAAA29JREFUWIXtl11oW2UYx3+v3ZCuc1ktIpvowrCBjmkdtl6ouBthWMeGTkQxuA9QKCiym20OBfVG3YUVOobYIszLyXY16HYxvOicns3NRoR267ZG04aOdc3Jx8lJsujjRZOzeE5y+raETjR/CJyc9/98vv/zvOdAAw000EADDWhB3e0E6gURkfK1Uuo/U9dS4567GFs0OHqOymJIKud/JeoV5/8AjyBaV7bJhrWdEmxtl3pumgv195tUEO1wRAGwdzg4F6whCm14BHFvU/NCfYjrp4WWF05TV2EEBIKjfFN4CUpi6Fy3mf0/hfys3Hnr1rDQenW4futLJmiPIO5vaSNrZ3XtZebJ5/k51M13wY3IhQA6hXf1GgQCqxaTb1U47wwBYc8DJ/h0YhOd6zaz65GjtK18yNf2Un/Yyavv1Wd0wkksFsMwDAzDQKfeQmSQxNkvAETMqnwZvvgrNYTpXvPYLuaBrIVl7htlMUQT4wt2drinFbkAqjspfi+sZ187SPvXn+u6dRdZ1a9SSpWPhoPrR4ARdslR9m38ft4Al/rDZFImFyOJeXOJxWLE43HGx7X6I4XIIFY6RcacRkwg2oGYo6JW1+yPHO47gplI8sEn788vttFDpNIWtpXEti1CPQM1ex9sbZcVzSugtM8Ve+zwPYI4/9R6bs3Okk88CsATV6/qFO5AdSd9C+jqNdj0bROUjg1raIufeCR84EcArv8+TTGf4vyJnb5iU0px8r1tjI1dRimFiFT76nAafeXUhHNzjXe90k6Mjh1ce/ZtJuNTACRu5+gnxLtcqZaTAJzbdryCH+IdcxS12pv3WHSKgeND5P+YwEwkSc6m2f36Wzz9ysuMRaeq9gZgePsxUnkb888imcl05VrNHvmdAI5RWT1ZO0smlwJgxr5RletOys+vm9/Va2AVmvjlzf20fbYPa2iLL/+5HX1YmQT54nIAfjvzoR9fF1JxVJBJmc51eUrsPfaDO44zHcooT4lwOOzhFiKDAFjp1J045rRz/fDWQx6bj/q+Ij85STH/FzO3bpLNZVnz+GN8+fEBr/9rg1CYnZsOudvY6ZtzKyVWqGfAbcOL7W9IJmuRy88JIkcGgMjMOYe31N/rugKab1QuOm8RkdLEWGgMLf6/0L8APHjfWpqXtVB5ZNhFixvp+D+M/gZZI68eaJ1OpQAAAABJRU5ErkJggg=="; @@ -2211,7 +2267,7 @@ }), new Separator(), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), - new MenuItem("2026.3.8", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.8"); }, false), + new MenuItem("2026.3.11", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.11"); }, false), ]; /** @type {Birb} */ @@ -3180,63 +3236,6 @@ draw(); } - /** - * Load the sprite sheet and return the pixel-map template - * @param {string} dataUri - * @param {boolean} [templateColors] - * @returns {Promise} - */ - function loadSpriteSheetPixels(dataUri, templateColors = true) { - return new Promise((resolve, reject) => { - const img = new Image(); - img.src = dataUri; - img.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - if (!ctx) { - reject(new Error('Failed to get canvas context')); - return; - } - ctx.drawImage(img, 0, 0); - const imageData = ctx.getImageData(0, 0, img.width, img.height); - const pixels = imageData.data; - const hexArray = []; - for (let y = 0; y < img.height; y++) { - const row = []; - for (let x = 0; x < img.width; x++) { - const index = (y * img.width + x) * 4; - const r = pixels[index]; - const g = pixels[index + 1]; - const b = pixels[index + 2]; - const a = pixels[index + 3]; - if (a === 0) { - row.push(PALETTE.TRANSPARENT); - continue; - } - const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; - if (!templateColors) { - row.push(hex); - continue; - } - if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - // Return the color as-is if not found in the map - row.push(hex); - continue; - } - row.push(SPRITE_SHEET_COLOR_MAP[hex]); - } - hexArray.push(row); - } - resolve(hexArray); - }; - img.onerror = (err) => { - reject(err); - }; - }); - } - initializeApplication(new LocalContext()); })(); diff --git a/dist/web/birb.js b/dist/web/birb.js index b994cba..ed74e5e 100644 --- a/dist/web/birb.js +++ b/dist/web/birb.js @@ -471,6 +471,62 @@ } } + /** + * Load a sprite sheet image and convert it to a 2D array of palette color names + * @param {string} src - URL or data URI of the sprite sheet image + * @param {boolean} [templateColors] - Whether to map pixel colors to palette names + * @returns {Promise} + */ + function loadSpriteSheetPixels(src, templateColors = true) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = src; + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Failed to get canvas context')); + return; + } + ctx.drawImage(img, 0, 0); + const imageData = ctx.getImageData(0, 0, img.width, img.height); + const pixels = imageData.data; + const hexArray = []; + for (let y = 0; y < img.height; y++) { + const row = []; + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const r = pixels[index]; + const g = pixels[index + 1]; + const b = pixels[index + 2]; + const a = pixels[index + 3]; + if (a === 0) { + row.push(PALETTE.TRANSPARENT); + continue; + } + const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; + if (!templateColors) { + row.push(hex); + continue; + } + if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { + row.push(hex); + continue; + } + row.push(SPRITE_SHEET_COLOR_MAP[hex]); + } + hexArray.push(row); + } + resolve(hexArray); + }; + img.onerror = (err) => { + reject(err); + }; + }); + } + /** @type {Record} */ const SPECIES = Object.fromEntries( Object.entries(species).map(([id, data]) => [ @@ -2075,7 +2131,7 @@ outline: none !important; box-shadow: none !important; }`; - const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABElJREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBb6ASIWaXJXpRS0UAhYCIrkAwRJISgIBq20sjrTWJnKxjTpLIKtd4ocWOSENOa1uJ11bm7n9u683ZnJ/R44Mtnb23f2dt5nZ/bfAYQQQgghZL5QvitAwkdExPWeUoptiEQLG28E+BSQjt2s1wEArXZ7oFxFHQghc4qkNJJEGkkyVB4lx1nFbySJfF5eFtnYGCqXHZ+QMlnwXQFSTLNex+NaDa1mc6hcJfufPuFxrZaVCYkdCjAifAmo1W7jSbc7MO1Jt5sNgQkhpBTMIfDn5eXsVdUQOK8OVcYmhASA5FB1bJ8CMutA+REyZ4hI/+C/8bfq+L4F5EP+hJTJGd8ViI39K1e8xFVKKRERn5ee8HIXQuYU3fPRvT/2ggiJH+7RJ8CUHntD1WPvdLgNyP/CBkSiQMtPO09EKhcgBTyn5J0B5RCQeEB22g0B4O0EUBo76PbPPB2fwguhzT2v+ULBPaqzhAImALDTbuiilxNASqmsDqH1/uy88JGnMTLyLLD5ZRqND3qaiJjzlNIg7KGPMR0iIqE1RFIed+vvvW5rnwIeBzMV1rc72TT6z41TgKZ4DtfvAA+A7Ruvs/d32s2B4zFlyCgEAROi8S3gEYhSKpPeaefCopLvf2Qm28K5EBGRhbPJwLTbzzr4cG8Bqy9PAAC9X98BAO8eJQN7mVnIaEjAGBTwtdUm7tbf63lnFpeQyBCX+L5+O8LB88teThiVxYVFJasvT/Dh3gJmIcGRAkQqoDuv8rvQWoBIJQhDRrnBJtgIvgVMwmZ9uyPvHiXzvp0L5RfqcD0UnENgfeeB631TfrCOOeQxzTD55HdnSMBafnZsezjuWqdxY5NwON5dkaW1PaXLALC05ld+vgW8vt3xemDPFT/tCEWTZyMrmgoLx7sr2Pj5EQDw4vwNAMCtw7djBfj67QgApuqK6/h5PVBbwBrdE81ZFkAJRss/8e1Vvv3yBVx9PUxcAjLzrWQROXufMUlwbAHaLK3t4er9Q1y6eC73s9aGmEpAvgVMiCYU8WmOd1fEzAE73yoQkLjyfy4ECEOCedjiwxS9L98CJiRUjndXpPelh40zLWBw5FNV+3YKEBFJsFCASI/r2RLqfekBAGqb+67P/gsypXR8C5iQkOluXc8a980fb7LpB88ve5egeWIy5LwrrFiehLT88qht7s9sxX0LmJDQMSWIfj5U3dZFd0JsEWoJhpx/YwkQqYS6W9dHzjtL+ZnxfQmYEDIWAgC2CGMQYOEDUfXlMOmKOCVYpnh0bPT3eM75KD9CvKDQH3r3k89xWCpqzKdhtJ4+zJ6KoctlPqDAutE7N37ZdSCEFGM/MSf0fJzoZzGVUmg9fZj9b5ZRYq9LL3dUfPb8CAmD9GYEIIJ8nOg3QcyhqF2ugqL4oX/ZhJx2YsvBia/Lcy6oghX3HZ8Qcrr4C5ZW3dG6vIHpAAAAAElFTkSuQmCC"; + const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABGxJREFUeJztnb9vE0kYht9xkA4RIZ1ObrKmPUyVSDRcH0RBAXbBmSZCOk46pEgUSFiIPwAhFwgiIRGRBq7yNSZ3UjoqmqRJRxGlxQ5IFlckRZCOfFfYs4w3+8Mm3p1Z+32kKJP12t/YO9+z3+yuNwAhhBBCCJkulO0OEPcREYl6TCnFMURyCwdvDrApIB27WioBAFrt9kA7iz4QQqYU6VPxPKl43rF2nBzHFb/iebI5Py+yvHysnXZ8QtKkYLsDJJlqqYQHxSJa1eqxdpZsvXuHB8Wi3yYk71CAOcKWgFrtNh53uwPLHne7/hSYEEJSwZwCb87P+z9ZTYHD+pBlbEKIA0gIWce2KSCzD5QfIVOGiPQO/hu/s45vW0A25E9Impyy3YG8sbWwYCWuUkqJiNi89ISXuxAypejKR1d/rIIIyT/co4+AKT1WQ9kT3OlwG5CTwgFEcoGWn3aeiGQuQAp4Sgk7A8opILGArLYrAsDaCaB+bKfHP/N0eBIvhDb3vOYPEr6jOk4oYAIAq+2Kblo5AaSU8vvgWvUXzAsbeZpHYs8Cmx+mMfigl4mIuU4qAyI49TGWQ0TEtYFI0uOP0hur29qmgIfBTIVao+Mvo/+iiRSgKZ6d2m3gHnD+6MdvK7QrA8dj0pCRCwImRGNbwDGIUsqX3qQzN6Nk76uMZVvEVoCFWQ/qzBwu/LMBADhaegpv7SauvjgCNoBaYw9ISUZhAm5cXvMfX21XUxcwITlAosS3+2E/886kzdyMkqsvjrBxpzAWCUa+gCmg2y/DS+iDf/f8drPu6edFBxtBUCIihVlvYNlvzzrYuFPoCdiI36x7A3Epwsmn1uhIs+5N+3aOld/2ShmuTtddIfbD6VdVkE838PvffwEA1q79CgC4+epZ6HO0CENeqxdwSDnZFjAhcdgWcK3RiT2wl7YAo+L38zA3eTaUAA/XF/HDLz8BAL5sfgYA3Nr5c6gAugzfXimPfO2WTQET4jJRAjLzLWURRVafeZLg0N8F1uLTNOseLt7dwc/nzoauH9gQJzoT9WXzsy++YD+CBDeK7gfPhpFJ4lV5aaAICeZbFgLa/bAfmf954btvhnC4vojT18vA3Z3Qx8PEd5Lqy6aACXGR5/9VsXyqBWQsviRqjQ6adU9c6EsSiVNg9Kunw/XFgccO3h8AAIoPt6Ke+y3Id4rPnIKHcfr6W1zMSMCEuEb30SV/cF/5+Npfvr1SzmqcS1QRYp6YdDnvEjsWJiEtvzCKD7fG9sZtC5gQ1zEliF4+ZD3WRRchQRFqCbqcf0MJEH0JdR9dil13nPIz49sSMCFkKAQAgiLMgwATjwHqG3H230ikBNMUj46N3h4vcj3KjxArKPSm3r3kizgslWvMu2G0ntz374qh22neoCDwRe/Q+Gn3gRCSTPCOOa7n40j/FlMphdaT+/7fZhspVl36dePis/IjxA36X00FcpCPI10GY05Fg+0sSIrv+odNyKSTtxwcqbNx5WwWb9x2fELIZPE/Any0lmed9+8AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAAAMCAYAAACjpxUSAAAAAXNSR0IArs4c6QAAA29JREFUWIXtl11oW2UYx3+v3ZCuc1ktIpvowrCBjmkdtl6ouBthWMeGTkQxuA9QKCiym20OBfVG3YUVOobYIszLyXY16HYxvOicns3NRoR267ZG04aOdc3Jx8lJsujjRZOzeE5y+raETjR/CJyc9/98vv/zvOdAAw000EADDWhB3e0E6gURkfK1Uuo/U9dS4567GFs0OHqOymJIKud/JeoV5/8AjyBaV7bJhrWdEmxtl3pumgv195tUEO1wRAGwdzg4F6whCm14BHFvU/NCfYjrp4WWF05TV2EEBIKjfFN4CUpi6Fy3mf0/hfys3Hnr1rDQenW4futLJmiPIO5vaSNrZ3XtZebJ5/k51M13wY3IhQA6hXf1GgQCqxaTb1U47wwBYc8DJ/h0YhOd6zaz65GjtK18yNf2Un/Yyavv1Wd0wkksFsMwDAzDQKfeQmSQxNkvAETMqnwZvvgrNYTpXvPYLuaBrIVl7htlMUQT4wt2drinFbkAqjspfi+sZ187SPvXn+u6dRdZ1a9SSpWPhoPrR4ARdslR9m38ft4Al/rDZFImFyOJeXOJxWLE43HGx7X6I4XIIFY6RcacRkwg2oGYo6JW1+yPHO47gplI8sEn788vttFDpNIWtpXEti1CPQM1ex9sbZcVzSugtM8Ve+zwPYI4/9R6bs3Okk88CsATV6/qFO5AdSd9C+jqNdj0bROUjg1raIufeCR84EcArv8+TTGf4vyJnb5iU0px8r1tjI1dRimFiFT76nAafeXUhHNzjXe90k6Mjh1ce/ZtJuNTACRu5+gnxLtcqZaTAJzbdryCH+IdcxS12pv3WHSKgeND5P+YwEwkSc6m2f36Wzz9ysuMRaeq9gZgePsxUnkb888imcl05VrNHvmdAI5RWT1ZO0smlwJgxr5RletOys+vm9/Va2AVmvjlzf20fbYPa2iLL/+5HX1YmQT54nIAfjvzoR9fF1JxVJBJmc51eUrsPfaDO44zHcooT4lwOOzhFiKDAFjp1J045rRz/fDWQx6bj/q+Ij85STH/FzO3bpLNZVnz+GN8+fEBr/9rg1CYnZsOudvY6ZtzKyVWqGfAbcOL7W9IJmuRy88JIkcGgMjMOYe31N/rugKab1QuOm8RkdLEWGgMLf6/0L8APHjfWpqXtVB5ZNhFixvp+D+M/gZZI68eaJ1OpQAAAABJRU5ErkJggg=="; @@ -2211,7 +2267,7 @@ }), new Separator(), new MenuItem(() => `Source Code ${isPetBoostActive() ? " ❤" : ""}`, () => { window.open("https://github.com/IdreesInc/Pocket-Bird"); }), - new MenuItem("2026.3.8", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.8"); }, false), + new MenuItem("2026.3.11", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.3.11"); }, false), ]; /** @type {Birb} */ @@ -3180,63 +3236,6 @@ draw(); } - /** - * Load the sprite sheet and return the pixel-map template - * @param {string} dataUri - * @param {boolean} [templateColors] - * @returns {Promise} - */ - function loadSpriteSheetPixels(dataUri, templateColors = true) { - return new Promise((resolve, reject) => { - const img = new Image(); - img.src = dataUri; - img.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - if (!ctx) { - reject(new Error('Failed to get canvas context')); - return; - } - ctx.drawImage(img, 0, 0); - const imageData = ctx.getImageData(0, 0, img.width, img.height); - const pixels = imageData.data; - const hexArray = []; - for (let y = 0; y < img.height; y++) { - const row = []; - for (let x = 0; x < img.width; x++) { - const index = (y * img.width + x) * 4; - const r = pixels[index]; - const g = pixels[index + 1]; - const b = pixels[index + 2]; - const a = pixels[index + 3]; - if (a === 0) { - row.push(PALETTE.TRANSPARENT); - continue; - } - const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; - if (!templateColors) { - row.push(hex); - continue; - } - if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - // Return the color as-is if not found in the map - row.push(hex); - continue; - } - row.push(SPRITE_SHEET_COLOR_MAP[hex]); - } - hexArray.push(row); - } - resolve(hexArray); - }; - img.onerror = (err) => { - reject(err); - }; - }); - } - initializeApplication(new LocalContext()); })(); diff --git a/editor/editor.js b/editor/editor.js new file mode 100644 index 0000000..4a2c870 --- /dev/null +++ b/editor/editor.js @@ -0,0 +1,321 @@ +// @ts-check +import { SPRITE_SHEET_COLOR_MAP, PALETTE, loadSpriteSheetPixels } from '../src/animation/sprites.js'; +import Layer, { TAG } from '../src/animation/layer.js'; +import Frame from '../src/animation/frame.js'; +import { Directions, getLayerPixels } from '../src/shared.js'; +import species from '../src/species.js'; + +const COLOR_MAP = SPRITE_SHEET_COLOR_MAP; +const SPRITE_PATH = "../sprites/birb.png"; +const SPRITE_SIZE = 32; +/** @type {Array<{tag: string, label: string}>} */ +const AVAILABLE_TAGS = [ + { tag: TAG.TUFT, label: "Tuft" }, +]; + +/** @type {Record} */ +const DEFAULT_OVERRIDES = { + "hood": "face", + "nose": "face" +}; +const IGNORED_PARTS = new Set( + ["transparent", "border", "heart", "heart-border", "heart-shine", "feather-spine"] +); + +/** @type {HTMLCanvasElement} */ +// @ts-ignore +const canvas = document.getElementById("preview"); +/** @type {CanvasRenderingContext2D} */ +// @ts-ignore +const ctx = canvas.getContext("2d"); +/** @type {HTMLElement} */ +// @ts-ignore +const editor = document.getElementById("editor"); +const colorPickerInput = document.createElement("input"); +/** @type {HTMLElement} */ +// @ts-ignore +const jsonElement = document.getElementById("json"); + +/** @type {string|null} */ +let selectedPart = null; +/** @type {HTMLElement|null} */ +let selectedColorElement = null; +/** @type {Record} */ +const colorElements = {}; + + +/** @type {Record} */ +let currentSpecies = { ...species.bluebird.colors }; +let colorHistory = [{ ...currentSpecies }]; +let historyIndex = 0; + + +/** @type {Set} */ +const currentTags = new Set(); + +/** @type {Frame|null} */ +let baseFrame = null; +const spriteCanvas = document.createElement('canvas'); +spriteCanvas.width = canvas.width; +spriteCanvas.height = canvas.height; +/** @type {CanvasRenderingContext2D} */ +// @ts-ignore +const spriteCtx = spriteCanvas.getContext('2d'); + +function drawBackground() { + const patternSize = 2; + const colors = ["#edf0f4", "#dadbe0"]; + for (let y = 0; y < canvas.height; y += patternSize) { + for (let x = 0; x < canvas.width; x += patternSize) { + ctx.fillStyle = ((x / patternSize + y / patternSize) % 2 === 0) ? colors[0] : colors[1]; + ctx.fillRect(x, y, patternSize, patternSize); + } + } +} + +/** + * Build the full palette color scheme from the current species settings + * @returns {Record} + */ +function buildColorScheme() { + /** @type {Record} */ + const scheme = {}; + for (const paletteName of Object.values(PALETTE)) { + scheme[paletteName] = getColor(paletteName); + } + return scheme; +} + +function draw() { + if (!baseFrame) { + return; + } + drawBackground(); + baseFrame.draw(spriteCtx, Directions.RIGHT, 1, buildColorScheme(), [...currentTags]); + ctx.drawImage(spriteCanvas, 0, 0); +} + +function updateColors() { + const lastColors = colorHistory[historyIndex]; + let changed = false; + for (const part of Object.keys(currentSpecies)) { + if (currentSpecies[part] !== lastColors[part]) { + changed = true; + break; + } + } + if (!changed) { + for (const part of Object.keys(lastColors)) { + if (!(part in currentSpecies)) { + changed = true; + break; + } + } + } + if (changed) { + colorHistory = colorHistory.slice(0, historyIndex + 1); + colorHistory.push({ ...currentSpecies }); + historyIndex++; + } + updateJson(); + draw(); +} + +function loadEditor() { + for (const [color, part] of Object.entries(COLOR_MAP)) { + if (IGNORED_PARTS.has(part)) { + continue; + } + const item = createColorItem(part, getColor(part) || color); + editor.appendChild(item); + } + // for (const { tag, label } of AVAILABLE_TAGS) { + // editor.appendChild(createTagItem(tag, label)); + // } +} + +/** + * @param {string} part + * @return {string} + */ +function getColor(part) { + if (currentSpecies[part]) { + return currentSpecies[part]; + } + if (DEFAULT_OVERRIDES[part]) { + return getColor(DEFAULT_OVERRIDES[part]); + } + for (const [color, partName] of Object.entries(COLOR_MAP)) { + if (partName === part) { + return color; + } + } + return "transparent"; +} + +function createColorPicker() { + colorPickerInput.type = "text"; + colorPickerInput.id = "coloris-proxy"; + colorPickerInput.setAttribute("data-coloris", ""); + document.body.appendChild(colorPickerInput); + + colorPickerInput.addEventListener("input", () => { + if (selectedColorElement && selectedPart !== null) { + const newColor = colorPickerInput.value; + selectedColorElement.style.backgroundColor = newColor; + currentSpecies[selectedPart] = newColor; + draw(); + } + }); + + document.addEventListener("mouseup", () => { + if (selectedPart !== null && !jsonElement.contains(document.activeElement)) { + updateColors(); + } + }); +} + +/** + * @param {string} label + * @param {string} color + * @returns {HTMLDivElement} + */ +function createColorItem(label, color) { + const item = document.createElement("div"); + item.classList.add("editor-item"); + + const colorElement = document.createElement("div"); + colorElement.classList.add("color"); + colorElement.style.backgroundColor = color; + colorElements[label] = colorElement; + item.appendChild(colorElement); + if (color !== "transparent") { + colorElement.addEventListener("click", () => { + selectedPart = label; + selectedColorElement = colorElement; + const rect = colorElement.getBoundingClientRect(); + colorPickerInput.style.left = rect.left + "px"; + colorPickerInput.style.top = (rect.bottom + window.scrollY) + "px"; + + colorPickerInput.value = currentSpecies[label] || color; + colorPickerInput.dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + } else { + colorElement.classList.add("color--transparent"); + } + const labelElement = document.createElement("div"); + const labelText = label.replaceAll("-", " ").toUpperCase(); + labelElement.classList.add("label"); + labelElement.textContent = labelText; + labelElement.title = "Click to remove from species"; + labelElement.addEventListener("click", () => { + delete currentSpecies[label]; + colorElement.style.backgroundColor = getColor(label); + updateColors(); + refreshEditorColors(); + }); + item.appendChild(labelElement); + + return item; +} + +/** + * @param {string} tag + * @param {string} label + * @returns {HTMLDivElement} + */ +function createTagItem(tag, label) { + const item = document.createElement("div"); + item.classList.add("tag-item"); + + const toggle = document.createElement("button"); + toggle.classList.add("tag-toggle"); + toggle.textContent = "✓"; + toggle.addEventListener("click", () => { + if (currentTags.has(tag)) { + currentTags.delete(tag); + toggle.classList.remove("tag-toggle--active"); + } else { + currentTags.add(tag); + toggle.classList.add("tag-toggle--active"); + } + draw(); + }); + item.appendChild(toggle); + + const labelElement = document.createElement("div"); + labelElement.classList.add("label"); + labelElement.textContent = label.toUpperCase(); + item.appendChild(labelElement); + + return item; +} + +function refreshEditorColors() { + for (const [, part] of Object.entries(COLOR_MAP)) { + const el = colorElements[part]; + if (el && !el.classList.contains("color--transparent")) { + el.style.backgroundColor = getColor(part); + } + } + if (selectedColorElement && selectedPart !== null) { + colorPickerInput.value = currentSpecies[selectedPart] || ""; + } +} + +function updateJson() { + jsonElement.textContent = JSON.stringify(currentSpecies, null, 2); +} + +document.addEventListener("keydown", (e) => { + if (!(e.metaKey || e.ctrlKey)) { + return; + } + if (e.key === "z" && !e.shiftKey) { + if (historyIndex > 0) { + historyIndex--; + currentSpecies = { ...colorHistory[historyIndex] }; + refreshEditorColors(); + updateJson(); + draw(); + e.preventDefault(); + } + } else if ((e.key === "z" && e.shiftKey) || e.key === "y") { + if (historyIndex < colorHistory.length - 1) { + historyIndex++; + currentSpecies = { ...colorHistory[historyIndex] }; + refreshEditorColors(); + updateJson(); + draw(); + e.preventDefault(); + } + } +}); + +jsonElement.addEventListener("input", () => { + try { + const parsed = JSON.parse(jsonElement.textContent || ""); + if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) { + currentSpecies = parsed; + refreshEditorColors(); + draw(); + } + } catch (e) { + } +}); + +jsonElement.addEventListener("blur", () => { + updateColors(); +}); + +createColorPicker(); +loadEditor(); + +(async () => { + const pixels = await loadSpriteSheetPixels(SPRITE_PATH); + baseFrame = new Frame([ + new Layer(getLayerPixels(pixels, 0, SPRITE_SIZE)), + new Layer(getLayerPixels(pixels, 5, SPRITE_SIZE), TAG.TUFT), + ]); + updateColors(); +})(); \ No newline at end of file diff --git a/editor/index.html b/editor/index.html new file mode 100644 index 0000000..bfed0b5 --- /dev/null +++ b/editor/index.html @@ -0,0 +1,24 @@ + + + + + + + Birb Editor + + + + + + +
+
+ +
+

+		
+
+ + + + \ No newline at end of file diff --git a/editor/stylesheet.css b/editor/stylesheet.css new file mode 100644 index 0000000..9a015ac --- /dev/null +++ b/editor/stylesheet.css @@ -0,0 +1,151 @@ +@import url(https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css); + +body { + background: linear-gradient(to top, #D2DAE9, white); + height: 100%; + margin: 0; + background-repeat: no-repeat; + background-attachment: fixed; +} + +* { + box-sizing: border-box; +} + +.container { + width: 100vw; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.horizontal-container { + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 40px; +} + +#preview { + width: 480px; + height: 480px; + image-rendering: pixelated; + filter: drop-shadow(0px 0px 40px rgba(0, 0, 0, 0.1)); + box-shadow: 0px 0px 40px rgba(0, 0, 0, 0.1); + border-radius: 40px; +} + +#editor { + width: 460px; + height: 480px; + background: #ffffff; + border-radius: 16px; + box-shadow: 0px 0px 40px rgba(0, 0, 0, 0.1); + padding: 20px; + display: flex; + flex-direction: column; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 30px 20px; + column-count: 2; +} + +#json { + width: 200px; + height: 480px; + background: #ffffff; + border-radius: 16px; + box-shadow: 0px 0px 40px rgba(0, 0, 0, 0.1); + padding: 20px; + gap: 20px; + text-align: left; + overflow-x: hidden; + overflow-y: auto; + white-space: pre-wrap; + font-family: 'Fira Code', monospace; + font-size: 12px; +} + +.editor-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 20px; + height: 38px; +} + +.label { + font-size: 18px; + font-family: 'Fira Code', monospace; + width: 100px; +} + +.color { + width: 32px; + height: 32px; + border-radius: 4px; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); + background: red; + transition: transform 0.1s; + cursor: pointer; +} + +.color:hover { + transform: scale(1.15); + transition: 0.1s ease-in; +} + +.color--transparent { + pointer-events: none; + cursor: default; +} + +#coloris-proxy { + position: fixed; + width: 1px; + height: 1px; + opacity: 0; + border: none; + padding: 0; + pointer-events: none; +} + +.tag-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 20px; + height: 38px; + margin-top: 10px; +} + +.tag-toggle { + width: 32px; + height: 32px; + border-radius: 4px; + border: none; + background: #edf0f4; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); + cursor: pointer; + font-size: 16px; + color: transparent; + transition: background 0.15s, color 0.15s, transform 0.1s; +} + +.tag-toggle:hover { + transform: scale(1.15); + transition: 0.1s ease-in; +} + +.tag-toggle--active { + background: #639bff; + color: white; +} \ No newline at end of file diff --git a/src/animation/sprites.js b/src/animation/sprites.js index 13d49a8..560b002 100644 --- a/src/animation/sprites.js +++ b/src/animation/sprites.js @@ -79,6 +79,62 @@ export class BirdType { } } +/** + * Load a sprite sheet image and convert it to a 2D array of palette color names + * @param {string} src - URL or data URI of the sprite sheet image + * @param {boolean} [templateColors] - Whether to map pixel colors to palette names + * @returns {Promise} + */ +export function loadSpriteSheetPixels(src, templateColors = true) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = src; + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Failed to get canvas context')); + return; + } + ctx.drawImage(img, 0, 0); + const imageData = ctx.getImageData(0, 0, img.width, img.height); + const pixels = imageData.data; + const hexArray = []; + for (let y = 0; y < img.height; y++) { + const row = []; + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const r = pixels[index]; + const g = pixels[index + 1]; + const b = pixels[index + 2]; + const a = pixels[index + 3]; + if (a === 0) { + row.push(PALETTE.TRANSPARENT); + continue; + } + const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; + if (!templateColors) { + row.push(hex); + continue; + } + if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { + row.push(hex); + continue; + } + row.push(SPRITE_SHEET_COLOR_MAP[hex]); + } + hexArray.push(row); + } + resolve(hexArray); + }; + img.onerror = (err) => { + reject(err); + }; + }); +} + /** @type {Record} */ export const SPECIES = Object.fromEntries( Object.entries(species).map(([id, data]) => [ diff --git a/src/application.js b/src/application.js index 4e80b29..db03045 100644 --- a/src/application.js +++ b/src/application.js @@ -25,7 +25,8 @@ import { import { PALETTE, SPRITE_SHEET_COLOR_MAP, - SPECIES + SPECIES, + loadSpriteSheetPixels, } from './animation/sprites.js'; import { StickyNote, @@ -1202,59 +1203,3 @@ function startApplication(birbPixels, featherPixels, hatsPixels) { draw(); } -/** - * Load the sprite sheet and return the pixel-map template - * @param {string} dataUri - * @param {boolean} [templateColors] - * @returns {Promise} - */ -function loadSpriteSheetPixels(dataUri, templateColors = true) { - return new Promise((resolve, reject) => { - const img = new Image(); - img.src = dataUri; - img.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - if (!ctx) { - reject(new Error('Failed to get canvas context')); - return; - } - ctx.drawImage(img, 0, 0); - const imageData = ctx.getImageData(0, 0, img.width, img.height); - const pixels = imageData.data; - const hexArray = []; - for (let y = 0; y < img.height; y++) { - const row = []; - for (let x = 0; x < img.width; x++) { - const index = (y * img.width + x) * 4; - const r = pixels[index]; - const g = pixels[index + 1]; - const b = pixels[index + 2]; - const a = pixels[index + 3]; - if (a === 0) { - row.push(PALETTE.TRANSPARENT); - continue; - } - const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; - if (!templateColors) { - row.push(hex); - continue; - } - if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - // Return the color as-is if not found in the map - row.push(hex); - continue; - } - row.push(SPRITE_SHEET_COLOR_MAP[hex]); - } - hexArray.push(row); - } - resolve(hexArray); - }; - img.onerror = (err) => { - reject(err); - }; - }); -} \ No newline at end of file