From 39aff8c839d52d200bf7516f63e5b9ee228490e1 Mon Sep 17 00:00:00 2001 From: jessikitty Date: Tue, 26 May 2026 15:15:59 +1000 Subject: [PATCH] fix: re-upload README with correct encoding --- README.md | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe58dc4..72aeb79 100644 --- a/README.md +++ b/README.md @@ -1 +1,203 @@ -IyBGcmFtYmUKCjxwIGFsaWduPSJjZW50ZXIiPgogIDxpbWcgc3JjPSJwdWJsaWMvaW1nL2ljb24ucG5nIiBhbHQ9IkZyYW1iZSIgd2lkdGg9IjE4MCI+CjwvcD4KCkEgbGlnaHR3ZWlnaHQsIHNlbGYtY29udGFpbmVkIERvY2tlciB3ZWIgYXBwbGljYXRpb24gdGhhdCBjb25uZWN0cyB0byB5b3VyIFtJbW1pY2hdKGh0dHBzOi8vaW1taWNoLmFwcC8pIHNlcnZlciBhbmQgZGlzcGxheXMgcGhvdG9zIGluIGEgYmVhdXRpZnVsIGZ1bGwtc2NyZWVuIHNsaWRlc2hvdyDigJQgcGVyZmVjdCBmb3IgdHVybmluZyBvbGQgdGFibGV0cywgc3BhcmUgc2NyZWVucywgYW5kIFJhc3BiZXJyeSBQaXMgaW50byBkaWdpdGFsIHBob3RvIGZyYW1lcy4KCiMjIOKcqCBGZWF0dXJlcwoKLSAqKkltbWljaCBBUEkgSW50ZWdyYXRpb24qKiDigJQgQ29ubmVjdHMgc2VjdXJlbHkgdmlhIEFQSSBrZXkgKGtlcHQgc2VydmVyLXNpZGUpCi0gKipBbGJ1bSBCcm93c2VyKiog4oCUIFNlbGVjdCBhbnkgYWxidW0sIHJhbmRvbSBwaG90b3MsIG9yIGZhdm9yaXRlcyBvbmx5Ci0gKipQZXJzb24gLyBGYWNlIFN1cHBvcnQqKiDigJQgRGlzcGxheSBwaG90b3Mgb2YgYSBzcGVjaWZpYyBwZXJzb24gdmlhIEltbWljaCdzIGZhY2UgcmVjb2duaXRpb24KLSAqKkFkbWluIERhc2hib2FyZCoqIOKAlCBSZWFsLXRpbWUgV2ViU29ja2V0LWJhc2VkIGNvbnRyb2wgcGFuZWwgZm9yIGFsbCBjb25uZWN0ZWQgZnJhbWVzCi0gKipBZG1pbiBBdXRoZW50aWNhdGlvbioqIOKAlCBPcHRpb25hbCB1c2VybmFtZS9wYXNzd29yZCBsb2dpbiB0byBwcm90ZWN0IHRoZSBhZG1pbiBkYXNoYm9hcmQKLSAqKlJFU1QgQVBJIHdpdGggVG9rZW4gQXV0aCoqIOKAlCBDb250cm9sIGZyYW1lcyBmcm9tIEhvbWUgQXNzaXN0YW50LCBzY3JpcHRzLCBvciBleHRlcm5hbCB0b29scwotICoqVVJMLUJhc2VkIFplcm8tVG91Y2ggTGF1bmNoKiog4oCUIFNraXAgdGhlIHNldHVwIHNjcmVlbiBlbnRpcmVseSB3aXRoIHF1ZXJ5IHBhcmFtZXRlcnMKLSAqKkF1dG8tUmVmcmVzaCoqIOKAlCBQZXJpb2RpY2FsbHkgY2hlY2tzIGZvciBuZXcgcGhvdG9zIGFkZGVkIHRvIHRoZSBzb3VyY2UgYWxidW0vcGVyc29uCi0gKipTbW9vdGggQ3Jvc3NmYWRlKiog4oCUIERvdWJsZS1idWZmZXJlZCBpbWFnZSB0cmFuc2l0aW9ucyB3aXRoIGNvbmZpZ3VyYWJsZSBkdXJhdGlvbgotICoqQmFja2dyb3VuZCBCbHVyKiog4oCUIEJsdXJyZWQgYmFja2Ryb3AgZmlsbHMgdGhlIHNwYWNlIGJlaGluZCBub24tY292ZXJpbmcgaW1hZ2VzCi0gKipDbG9jayAmIERhdGUgT3ZlcmxheSoqIOKAlCBBbHdheXMga25vdyB0aGUgdGltZSBhdCBhIGdsYW5jZQotICoqRVhJRiBJbmZvKiog4oCUIFNob3dzIHBob3RvIGxvY2F0aW9uLCBkYXRlLCBhbmQgY2FtZXJhIGluZm8KLSAqKlByb2dyZXNzIEJhcioqIOKAlCBTdWJ0bGUgaW5kaWNhdG9yIG9mIHRpbWUgdW50aWwgbmV4dCBwaG90bwotICoqVG91Y2ggQ29udHJvbHMqKiDigJQgVGFwIGxlZnQvcmlnaHQgZWRnZXMgdG8gbmF2aWdhdGUsIGNlbnRyZSB0byB0b2dnbGUgb3ZlcmxheQotICoqS2V5Ym9hcmQgQ29udHJvbHMqKiDigJQgQXJyb3cga2V5cywgU3BhY2UsIEYgKGZ1bGxzY3JlZW4pLCBJIChpbmZvKSwgRXNjIChleGl0KQotICoqU2NyZWVuIFdha2UgTG9jayoqIOKAlCBQcmV2ZW50cyBzY3JlZW4gc2xlZXAgb24gc3VwcG9ydGVkIGRldmljZXMKLSAqKlJlc3BvbnNpdmUqKiDigJQgV29ya3Mgb24gYW55IHNjcmVlbiBzaXplIGZyb20gcGhvbmUgdG8gVFYKLSAqKk9sZGVyIERldmljZSBGcmllbmRseSoqIOKAlCBWYW5pbGxhIEhUTUwvQ1NTL0pTLCBubyBoZWF2eSBmcmFtZXdvcmtzCi0gKipEb2NrZXIgQ29udGFpbmVyaXNlZCoqIOKAlCBTaW5nbGUgY29udGFpbmVyLCBtaW5pbWFsIGZvb3RwcmludAoKIyMg8J+agCBRdWljayBTdGFydAoKIyMjIDEuIEdldCB5b3VyIEltbWljaCBBUEkgS2V5CgoxLiBPcGVuIHlvdXIgSW1taWNoIHdlYiBpbnRlcmZhY2UKMi4gQ2xpY2sgeW91ciBwcm9maWxlIHBpY3R1cmUg4oaSICoqQWNjb3VudCBTZXR0aW5ncyoqIOKGkiAqKkFQSSBLZXlzKioKMy4gQ3JlYXRlIGEgbmV3IGtleSB3aXRoIGBhc3NldC5yZWFkYCBhbmQgYGFsYnVtLnJlYWRgIHBlcm1pc3Npb25zCgojIyMgMi4gUnVuIHdpdGggRG9ja2VyIENvbXBvc2UKCmBgYGJhc2gKZ2l0IGNsb25lIGh0dHBzOi8vZ2l0ZWEuaGlkZWF3YXlnYW1pbmcuY29tLmF1L2plc3Npa2l0dHkvZnJhbWJlLmdpdApjZCBmcmFtYmUKYGBgCgpFZGl0IGBkb2NrZXItY29tcG9zZS55bWxgIGFuZCBzZXQgeW91ciBgSU1NSUNIX1VSTGAgYW5kIGBJTU1JQ0hfQVBJX0tFWWAsIHRoZW46CgpgYGBiYXNoCmRvY2tlciBjb21wb3NlIHVwIC1kCmBgYAoKT3BlbiBgaHR0cDovL3lvdXItc2VydmVyOjMwMzBgIGluIGEgYnJvd3NlciBvbiB5b3VyIHRhYmxldC9zY3JlZW4uCgojIyMgMy4gUnVuIHdpdGggRG9ja2VyIGRpcmVjdGx5CgpgYGBiYXNoCmRvY2tlciBidWlsZCAtdCBmcmFtYmUgLgpkb2NrZXIgcnVuIC1kIFwKICAtLW5hbWUgZnJhbWJlIFwKICAtcCAzMDMwOjMwMDAgXAogIC1lIElNTUlDSF9VUkw9aHR0cDovL3lvdXItaW1taWNoLXNlcnZlcjoyMjgzIFwKICAtZSBJTU1JQ0hfQVBJX0tFWT15b3VyLWFwaS1rZXkgXAogIC0tcmVzdGFydCB1bmxlc3Mtc3RvcHBlZCBcCiAgZnJhbWJlCmBgYAoKIyMg8J+UkSBBdXRoZW50aWNhdGlvbgoKIyMjIEFkbWluIERhc2hib2FyZCBMb2dpbgoKUHJvdGVjdCB0aGUgYWRtaW4gZGFzaGJvYXJkIHdpdGggYSB1c2VybmFtZSBhbmQgcGFzc3dvcmQ6CgpgYGB5YW1sCmVudmlyb25tZW50OgogIC0gQURNSU5fVVNFUk5BTUU9YWRtaW4KICAtIEFETUlOX1BBU1NXT1JEPXlvdXItc2VjdXJlLXBhc3N3b3JkCmBgYAoKV2hlbiBgQURNSU5fUEFTU1dPUkRgIGlzIHNldCwgYWNjZXNzaW5nIGAvYWRtaW5gIHJlcXVpcmVzIHNpZ25pbmcgaW4uIFdoZW4gbm90IHNldCwgdGhlIGRhc2hib2FyZCBpcyBvcGVuICh1c2VmdWwgZm9yIHRydXN0ZWQgbG9jYWwgbmV0d29ya3MpLgoKIyMjIEFQSSBUb2tlbiBmb3IgRXh0ZXJuYWwgQWNjZXNzCgpFbmFibGUgdG9rZW4tYXV0aGVudGljYXRlZCBSRVNUIEFQSSBhY2Nlc3MgZm9yIEhvbWUgQXNzaXN0YW50LCBzY3JpcHRzLCBvciBvdGhlciBleHRlcm5hbCB0b29sczoKCmBgYHlhbWwKZW52aXJvbm1lbnQ6CiAgLSBGUkFNQkVfQVBJX1RPS0VOPXlvdXItc2VjcmV0LXRva2VuLWhlcmUKYGBgCgojIyDwn5SMIFJFU1QgQVBJCgpXaGVuIGBGUkFNQkVfQVBJX1RPS0VOYCBpcyBjb25maWd1cmVkLCB0aGUgZm9sbG93aW5nIGVuZHBvaW50cyBhcmUgYXZhaWxhYmxlOgoKIyMjIExpc3QgQ29ubmVjdGVkIEZyYW1lcwoKYGBgCkdFVCAvYXBpL2NsaWVudHMKQXV0aG9yaXphdGlvbjogQmVhcmVyIHlvdXItc2VjcmV0LXRva2VuLWhlcmUKYGBgCgpSZXR1cm5zIGFsbCBjb25uZWN0ZWQgZnJhbWUgY2xpZW50cyB3aXRoIHRoZWlyIHN0YXR1cywgSVAsIG5hbWUsIGFuZCBjb25maWcuCgojIyMgU2VuZCBDb21tYW5kIHRvIGEgRnJhbWUKCmBgYApQT1NUIC9hcGkvY2xpZW50cy86aWQvY29tbWFuZApBdXRob3JpemF0aW9uOiBCZWFyZXIgeW91ci1zZWNyZXQtdG9rZW4taGVyZQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL2pzb24KCnsKICAiYWN0aW9uIjogIm5leHQiLAogICJwYXlsb2FkIjoge30KfQpgYGAKCkF2YWlsYWJsZSBhY3Rpb25zOiBgc3RhcnRgLCBgc3RvcGAsIGBuZXh0YCwgYHByZXZgLCBgc2xlZXBgLCBgd2FrZWAsIGByZWZyZXNoYCwgYHNldFNvdXJjZWAsIGBzZXRDb25maWdgCgojIyMgSG9tZSBBc3Npc3RhbnQgRXhhbXBsZQoKYGBgeWFtbApyZXN0X2NvbW1hbmQ6CiAgZnJhbWJlX25leHRfcGhvdG86CiAgICB1cmw6ICJodHRwOi8vZnJhbWJlLXNlcnZlcjozMDMwL2FwaS9jbGllbnRzL3t7IGNsaWVudF9pZCB9fS9jb21tYW5kIgogICAgbWV0aG9kOiBQT1NUCiAgICBoZWFkZXJzOgogICAgICBBdXRob3JpemF0aW9uOiAiQmVhcmVyIHlvdXItc2VjcmV0LXRva2VuLWhlcmUiCiAgICAgIENvbnRlbnQtVHlwZTogImFwcGxpY2F0aW9uL2pzb24iCiAgICBwYXlsb2FkOiAneyJhY3Rpb24iOiAibmV4dCJ9JwpgYGAKCkF1dGhlbnRpY2F0aW9uIGNhbiBhbHNvIGJlIHByb3ZpZGVkIHZpYSBgeC1hcGktdG9rZW5gIGhlYWRlciBvciBgP3Rva2VuPWAgcXVlcnkgcGFyYW1ldGVyLgoKIyMg8J+UlyBaZXJvLVRvdWNoIFVSTCBQYXJhbWV0ZXJzCgpTa2lwIHRoZSBzZXR1cCBzY3JlZW4gZW50aXJlbHkgYnkgcGFzc2luZyBxdWVyeSBwYXJhbWV0ZXJzLiBUaGlzIGlzIGlkZWFsIGZvciBkZWRpY2F0ZWQgZnJhbWVzIOKAlCBqdXN0IGJvb2ttYXJrIHRoZSBVUkwgb24gZWFjaCB0YWJsZXQ6Cgp8IFVSTCB8IFdoYXQgaXQgc2hvd3MgfAp8LS0tfC0tLXwKfCBgaHR0cDovL3NlcnZlcjozMDMwLz9hbGJ1bT1BTEJVTV9VVUlEYCB8IFBob3RvcyBmcm9tIGEgc3BlY2lmaWMgYWxidW0gfAp8IGBodHRwOi8vc2VydmVyOjMwMzAvP3BlcnNvbj1QRVJTT05fVVVJRGAgfCBQaG90b3Mgb2YgYSBzcGVjaWZpYyBwZXJzb24gKGZhY2UgcmVjb2duaXRpb24pIHwKfCBgaHR0cDovL3NlcnZlcjozMDMwLz9mYXZvcml0ZXNgIHwgRmF2b3JpdGUgcGhvdG9zIG9ubHkgfAp8IGBodHRwOi8vc2VydmVyOjMwMzAvP3JhbmRvbWAgfCBSYW5kb20gcGhvdG9zIGZyb20gdGhlIGxpYnJhcnkgfAoKWW91IGNhbiBmaW5kIGFsYnVtIGFuZCBwZXJzb24gVVVJRHMgaW4gSW1taWNoJ3Mgd2ViIGludGVyZmFjZSBVUkwgYmFyIHdoZW4gdmlld2luZyBhbiBhbGJ1bSBvciBwZXJzb24uCgojIyDimpnvuI8gQ29uZmlndXJhdGlvbgoKQWxsIHNldHRpbmdzIGFyZSB2aWEgZW52aXJvbm1lbnQgdmFyaWFibGVzOgoKfCBWYXJpYWJsZSB8IERlZmF1bHQgfCBEZXNjcmlwdGlvbiB8CnwtLS18LS0tfC0tLXwKfCBgSU1NSUNIX1VSTGAgfCAqKHJlcXVpcmVkKSogfCBZb3VyIEltbWljaCBzZXJ2ZXIgVVJMIHwKfCBgSU1NSUNIX0FQSV9LRVlgIHwgKihyZXF1aXJlZCkqIHwgSW1taWNoIEFQSSBrZXkgfAp8IGBTTElERVNIT1dfSU5URVJWQUxgIHwgYDMwYCB8IFNlY29uZHMgYmV0d2VlbiBwaG90b3MgfAp8IGBUUkFOU0lUSU9OX0RVUkFUSU9OYCB8IGAyYCB8IENyb3NzZmFkZSBkdXJhdGlvbiBpbiBzZWNvbmRzIHwKfCBgSU1BR0VfRklUYCB8IGBjb250YWluYCB8IGBjb250YWluYCBvciBgY292ZXJgIHwKfCBgU0hVRkZMRWAgfCBgdHJ1ZWAgfCBSYW5kb21pc2UgcGhvdG8gb3JkZXIgfAp8IGBCQUNLR1JPVU5EX0JMVVJgIHwgYHRydWVgIHwgU2hvdyBibHVycmVkIGJhY2tkcm9wIHwKfCBgU0hPV19DTE9DS2AgfCBgdHJ1ZWAgfCBEaXNwbGF5IGNsb2NrIG92ZXJsYXkgfAp8IGBTSE9XX0RBVEVgIHwgYHRydWVgIHwgRGlzcGxheSBkYXRlIG92ZXJsYXkgfAp8IGBTSE9XX0VYSUZgIHwgYHRydWVgIHwgRGlzcGxheSBwaG90byBtZXRhZGF0YSB8CnwgYFNIT1dfUFJPR1JFU1NgIHwgYHRydWVgIHwgRGlzcGxheSBwcm9ncmVzcyBiYXIgfAp8IGBSRUZSRVNIX0lOVEVSVkFMYCB8IGAzMDBgIHwgU2Vjb25kcyBiZXR3ZWVuIHNvdXJjZSByZWZyZXNoIGNoZWNrcyAobmV3IHBob3RvcykgfAp8IGBBTEJVTV9JRGAgfCAqKGVtcHR5KSogfCBBdXRvLXN0YXJ0IHdpdGggc3BlY2lmaWMgYWxidW0gKGVudi1iYXNlZCkgfAp8IGBTSE9XX0ZBVk9SSVRFU19PTkxZYCB8IGBmYWxzZWAgfCBBdXRvLXN0YXJ0IHdpdGggZmF2b3JpdGVzIChlbnYtYmFzZWQpIHwKfCBgQURNSU5fVVNFUk5BTUVgIHwgYGFkbWluYCB8IEFkbWluIGRhc2hib2FyZCBsb2dpbiB1c2VybmFtZSB8CnwgYEFETUlOX1BBU1NXT1JEYCB8ICooZW1wdHkpKiB8IEFkbWluIGRhc2hib2FyZCBwYXNzd29yZCAobGVhdmUgZW1wdHkgdG8gZGlzYWJsZSBhdXRoKSB8CnwgYEZSQU1CRV9BUElfVE9LRU5gIHwgKihlbXB0eSkqIHwgQVBJIHRva2VuIGZvciBSRVNUIGVuZHBvaW50IGFjY2VzcyAobGVhdmUgZW1wdHkgZm9yIG9wZW4gYWNjZXNzKSB8CnwgYFBPUlRgIHwgYDMwMDBgIHwgSW50ZXJuYWwgc2VydmVyIHBvcnQgKERvY2tlciBtYXBzIGV4dGVybmFsbHkgdmlhIGNvbXBvc2UpIHwKCiMjIPCfjq4gQ29udHJvbHMKCiMjIyBUb3VjaCAvIE1vdXNlCi0gKipMZWZ0IDIwJSoqIG9mIHNjcmVlbiDigJQgUHJldmlvdXMgcGhvdG8KLSAqKkNlbnRyZSA2MCUqKiDigJQgVG9nZ2xlIG92ZXJsYXkgKGNsb2NrLCBpbmZvLCBjbG9zZSBidXR0b24pCi0gKipSaWdodCAyMCUqKiDigJQgTmV4dCBwaG90bwoKIyMjIEtleWJvYXJkCi0gYOKGkGAgLyBg4oaSYCDigJQgUHJldmlvdXMgLyBOZXh0IHBob3RvCi0gYFNwYWNlYCDigJQgTmV4dCBwaG90bwotIGBGYCDigJQgVG9nZ2xlIGZ1bGxzY3JlZW4KLSBgSWAg4oCUIFRvZ2dsZSBpbmZvIG92ZXJsYXkKLSBgRXNjYCDigJQgRXhpdCB0byBhbGJ1bSBzZWxlY3Rpb24KCiMjIPCfk7EgVGFibGV0IFNldHVwIFRpcHMKCjEuIE9wZW4gdGhlIGZyYW1lIFVSTCBpbiB5b3VyIHRhYmxldCdzIGJyb3dzZXIgKHVzZSBhIGA/YWxidW09YCBvciBgP3BlcnNvbj1gIFVSTCBmb3IgemVyby10b3VjaCkKMi4gQWRkIHRvIEhvbWUgU2NyZWVuIGZvciBhIGZ1bGwtc2NyZWVuIGFwcCBleHBlcmllbmNlCjMuIEVuYWJsZSBraW9zayBtb2RlIG9yIGd1aWRlZCBhY2Nlc3MgdG8gbG9jayB0byB0aGUgYXBwCjQuIERpc2FibGUgc2NyZWVuIHRpbWVvdXQgaW4geW91ciBkZXZpY2Ugc2V0dGluZ3MKCiMjIPCfj5fvuI8gQXJjaGl0ZWN0dXJlCgpgYGAK4pSM4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSQICAgICAgICAg4pSM4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSQICAgICAgICAg4pSM4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSQCuKUgiAgIEJyb3dzZXIgICAg4pSCICBIVFRQICAg4pSCICAgIEZyYW1iZSAgICDilIIgICBBUEkgICDilIIgICAgSW1taWNoICAgICDilIIK4pSCICAoVGFibGV0KSAgICDilILil4TilIDilIDilIDilIDilIDilIDilIDilIDilrrilIIgIChOb2RlLmpzKSAg4pSC4peE4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pa64pSCICAgU2VydmVyICAgICDilIIK4pSU4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSYICA6MzAzMCAg4pSU4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSYICA6MjI4MyAg4pSU4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSYCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIOKWsgogICAgICAgICAgICAgICAgICAgIOKUjOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUtOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUkAogICAgICAgICAgICAgICAgICAgIOKUgiAgUkVTVCBBUEkgLyBXUyAgICDilIIKICAgICAgICAgICAgICAgICAgICDilIIgIChIb21lIEFzc2lzdGFudCkg4pSCCiAgICAgICAgICAgICAgICAgICAg4pSU4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSYCmBgYAoKVGhlIE5vZGUuanMgYmFja2VuZCBhY3RzIGFzIGEgc2VjdXJlIHByb3h5IOKAlCB5b3VyIEltbWljaCBBUEkga2V5IG5ldmVyIHJlYWNoZXMgdGhlIGJyb3dzZXIuIFRoZSBmcm9udGVuZCBwZXJpb2RpY2FsbHkgcG9sbHMgdGhlIGJhY2tlbmQgZm9yIG5ldyBwaG90b3Mgc28gYWxidW1zIHN0YXkgdXAgdG8gZGF0ZSB3aXRob3V0IHJlc3RhcnRpbmcuCgojIyDwn5OLIFZlcnNpb24gSGlzdG9yeQoKLSAqKjEuNC4wKiog4oCUIEFkbWluIGxvZ2luICh1c2VybmFtZS9wYXNzd29yZCksIEFQSSB0b2tlbiBhdXRoIGZvciBleHRlcm5hbCBhY2Nlc3MgKEhvbWUgQXNzaXN0YW50KSwgUkVTVCBlbmRwb2ludHMgKGBHRVQgL2FwaS9jbGllbnRzYCwgYFBPU1QgL2FwaS9jbGllbnRzLzppZC9jb21tYW5kYCkKLSAqKjEuMy4wKiog4oCUIEFkbWluIGRhc2hib2FyZCB3aXRoIHJlYWwtdGltZSBXZWJTb2NrZXQgZnJhbWUgbWFuYWdlbWVudCwgdmlkZW8gc3VwcG9ydCwgcGVyc29uL2ZhY2Ugc3VwcG9ydAotICoqMS4yLjEqKiDigJQgRml4IHBvcnQgbWFwcGluZyAoMzAzMDozMDAwIGV4dGVybmFsOmludGVybmFsKSwgZml4IFVSTCBwYXJhbSBhdXRvLWxhdW5jaCBub3Qgc3RhcnRpbmcgc2xpZGVzaG93Ci0gKioxLjIuMCoqIOKAlCBVUkwgcGFyYW1zIChgP2FsYnVtPWAsIGA/cGVyc29uPWAsIGA/ZmF2b3JpdGVzYCwgYD9yYW5kb21gKSwgcGVyc29uL2ZhY2Ugc3VwcG9ydCwgcGVyaW9kaWMgYXV0by1yZWZyZXNoLCBhcHAgaWNvbiwgZGVmYXVsdCBwb3J0IGNoYW5nZWQgdG8gMzAzMAotICoqMS4xLjAqKiDigJQgUmVicmFuZCB0byBGcmFtYmUKLSAqKjEuMC4wKiog4oCUIEluaXRpYWwgcmVsZWFzZTogYWxidW0gYnJvd3Nlciwgc2xpZGVzaG93IHdpdGggY3Jvc3NmYWRlLCBjbG9jay9kYXRlL0VYSUYgb3ZlcmxheXMsIHRvdWNoICYga2V5Ym9hcmQgY29udHJvbHMsIERvY2tlciBkZXBsb3ltZW50CgojIyDwn5OEIExpY2Vuc2UKCk1JVAo= \ No newline at end of file +# Frambe + +

+ Frambe +

+ +A lightweight, self-contained Docker web application that connects to your [Immich](https://immich.app/) server and displays photos in a beautiful full-screen slideshow — perfect for turning old tablets, spare screens, and Raspberry Pis into digital photo frames. + +## ✨ Features + +- **Immich API Integration** — Connects securely via API key (kept server-side) +- **Album Browser** — Select any album, random photos, or favorites only +- **Person / Face Support** — Display photos of a specific person via Immich's face recognition +- **Admin Dashboard** — Real-time WebSocket-based control panel for all connected frames +- **Admin Authentication** — Optional username/password login to protect the admin dashboard +- **REST API with Token Auth** — Control frames from Home Assistant, scripts, or external tools +- **URL-Based Zero-Touch Launch** — Skip the setup screen entirely with query parameters +- **Auto-Refresh** — Periodically checks for new photos added to the source album/person +- **Smooth Crossfade** — Double-buffered image transitions with configurable duration +- **Background Blur** — Blurred backdrop fills the space behind non-covering images +- **Clock & Date Overlay** — Always know the time at a glance +- **EXIF Info** — Shows photo location, date, and camera info +- **Progress Bar** — Subtle indicator of time until next photo +- **Touch Controls** — Tap left/right edges to navigate, centre to toggle overlay +- **Keyboard Controls** — Arrow keys, Space, F (fullscreen), I (info), Esc (exit) +- **Screen Wake Lock** — Prevents screen sleep on supported devices +- **Responsive** — Works on any screen size from phone to TV +- **Older Device Friendly** — Vanilla HTML/CSS/JS, no heavy frameworks +- **Docker Containerised** — Single container, minimal footprint + +## 🚀 Quick Start + +### 1. Get your Immich API Key + +1. Open your Immich web interface +2. Click your profile picture → **Account Settings** → **API Keys** +3. Create a new key with `asset.read` and `album.read` permissions + +### 2. Run with Docker Compose + +```bash +git clone https://gitea.hideawaygaming.com.au/jessikitty/frambe.git +cd frambe +``` + +Edit `docker-compose.yml` and set your `IMMICH_URL` and `IMMICH_API_KEY`, then: + +```bash +docker compose up -d +``` + +Open `http://your-server:3030` in a browser on your tablet/screen. + +### 3. Run with Docker directly + +```bash +docker build -t frambe . +docker run -d \ + --name frambe \ + -p 3030:3000 \ + -e IMMICH_URL=http://your-immich-server:2283 \ + -e IMMICH_API_KEY=your-api-key \ + --restart unless-stopped \ + frambe +``` + +## 🔑 Authentication + +### Admin Dashboard Login + +Protect the admin dashboard with a username and password: + +```yaml +environment: + - ADMIN_USERNAME=admin + - ADMIN_PASSWORD=your-secure-password +``` + +When `ADMIN_PASSWORD` is set, accessing `/admin` requires signing in. When not set, the dashboard is open (useful for trusted local networks). + +### API Token for External Access + +Enable token-authenticated REST API access for Home Assistant, scripts, or other external tools: + +```yaml +environment: + - FRAMBE_API_TOKEN=your-secret-token-here +``` + +## 🔌 REST API + +When `FRAMBE_API_TOKEN` is configured, the following endpoints are available: + +### List Connected Frames + +``` +GET /api/clients +Authorization: Bearer your-secret-token-here +``` + +Returns all connected frame clients with their status, IP, name, and config. + +### Send Command to a Frame + +``` +POST /api/clients/:id/command +Authorization: Bearer your-secret-token-here +Content-Type: application/json + +{ + "action": "next", + "payload": {} +} +``` + +Available actions: `start`, `stop`, `next`, `prev`, `sleep`, `wake`, `refresh`, `setSource`, `setConfig` + +### Home Assistant Example + +```yaml +rest_command: + frambe_next_photo: + url: "http://frambe-server:3030/api/clients/{{ client_id }}/command" + method: POST + headers: + Authorization: "Bearer your-secret-token-here" + Content-Type: "application/json" + payload: '{"action": "next"}' +``` + +Authentication can also be provided via `x-api-token` header or `?token=` query parameter. + +## 🔗 Zero-Touch URL Parameters + +Skip the setup screen entirely by passing query parameters. This is ideal for dedicated frames — just bookmark the URL on each tablet: + +| URL | What it shows | +|---|---| +| `http://server:3030/?album=ALBUM_UUID` | Photos from a specific album | +| `http://server:3030/?person=PERSON_UUID` | Photos of a specific person (face recognition) | +| `http://server:3030/?favorites` | Favorite photos only | +| `http://server:3030/?random` | Random photos from the library | + +You can find album and person UUIDs in Immich's web interface URL bar when viewing an album or person. + +## ⚙️ Configuration + +All settings are via environment variables: + +| Variable | Default | Description | +|---|---|---| +| `IMMICH_URL` | *(required)* | Your Immich server URL | +| `IMMICH_API_KEY` | *(required)* | Immich API key | +| `SLIDESHOW_INTERVAL` | `30` | Seconds between photos | +| `TRANSITION_DURATION` | `2` | Crossfade duration in seconds | +| `IMAGE_FIT` | `contain` | `contain` or `cover` | +| `SHUFFLE` | `true` | Randomise photo order | +| `BACKGROUND_BLUR` | `true` | Show blurred backdrop | +| `SHOW_CLOCK` | `true` | Display clock overlay | +| `SHOW_DATE` | `true` | Display date overlay | +| `SHOW_EXIF` | `true` | Display photo metadata | +| `SHOW_PROGRESS` | `true` | Display progress bar | +| `REFRESH_INTERVAL` | `300` | Seconds between source refresh checks (new photos) | +| `ALBUM_ID` | *(empty)* | Auto-start with specific album (env-based) | +| `SHOW_FAVORITES_ONLY` | `false` | Auto-start with favorites (env-based) | +| `ADMIN_USERNAME` | `admin` | Admin dashboard login username | +| `ADMIN_PASSWORD` | *(empty)* | Admin dashboard password (leave empty to disable auth) | +| `FRAMBE_API_TOKEN` | *(empty)* | API token for REST endpoint access (leave empty for open access) | +| `PORT` | `3000` | Internal server port (Docker maps externally via compose) | + +## 🎮 Controls + +### Touch / Mouse +- **Left 20%** of screen — Previous photo +- **Centre 60%** — Toggle overlay (clock, info, close button) +- **Right 20%** — Next photo + +### Keyboard +- `←` / `→` — Previous / Next photo +- `Space` — Next photo +- `F` — Toggle fullscreen +- `I` — Toggle info overlay +- `Esc` — Exit to album selection + +## 📱 Tablet Setup Tips + +1. Open the frame URL in your tablet's browser (use a `?album=` or `?person=` URL for zero-touch) +2. Add to Home Screen for a full-screen app experience +3. Enable kiosk mode or guided access to lock to the app +4. Disable screen timeout in your device settings + +## 📋 Version History + +- **1.4.0** — Admin login (username/password), API token auth for external access (Home Assistant), REST endpoints (`GET /api/clients`, `POST /api/clients/:id/command`) +- **1.3.0** — Admin dashboard with real-time WebSocket frame management, video support, person/face support +- **1.2.1** — Fix port mapping (3030:3000 external:internal), fix URL param auto-launch not starting slideshow +- **1.2.0** — URL params (`?album=`, `?person=`, `?favorites`, `?random`), person/face support, periodic auto-refresh, app icon, default port changed to 3030 +- **1.1.0** — Rebrand to Frambe +- **1.0.0** — Initial release: album browser, slideshow with crossfade, clock/date/EXIF overlays, touch & keyboard controls, Docker deployment + +## 📄 License + +MIT