From 17c413a2bc85d9d2c305a027ba9a709242797b61 Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Tue, 8 Sep 2026 06:05:37 +0500 Subject: [PATCH 1/5] icon and info for zcode --- apps/zcode | 10 ++++++++-- icons/zcode.png | Bin 0 -> 7668 bytes 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 icons/zcode.png diff --git a/apps/zcode b/apps/zcode index 2efd2399c..1a935f79a 100644 --- a/apps/zcode +++ b/apps/zcode @@ -1,6 +1,12 @@ # zcode === - ZCode is next-gen vibe coding for complex goals—with multiple agents and control from anywhere. +ZCode is an Agentic Development Environment (ADE) built to bring GLM-5.3 into real coding workflows. It helps turn GLM-5.3's long-context, long-horizon, and agentic coding capabilities into a stable desktop experience for planning, coding, reviewing, and iterating across complex development tasks. + +With GLM-5.3's stable 1M context and long-horizon task capability, ZCode Agent can keep goals, files, terminal results, browser context, execution modes, and Git state in the same task. This allows complex development work to move from planning to implementation and verification without losing continuity. + +ZCode has been deeply tuned and specifically optimized around GLM-5.3, with a focus on the first-party ZCode Agent. Model, tools, and execution workflow are more tightly integrated, so complex development tasks can move from planning all the way to verification within a single context. + +With natural-language instructions, you can ask ZCode Agent to handle coding, debugging, testing, project preview, and change review. Through the desktop workspace, Remote, and Bot Channel, you can keep checking progress and adding instructions while long-running work continues. === -# SCREENSHOTS: /contribute_ss.webp /contribute_ss.webp +# SCREENSHOTS: https://raw.githubusercontent.com/kazam0180/linux-app-shots/refs/heads/main/zcode/1.webp https://raw.githubusercontent.com/kazam0180/linux-app-shots/refs/heads/main/zcode/2.webp # SITES: https://zcode.z.ai diff --git a/icons/zcode.png b/icons/zcode.png new file mode 100644 index 0000000000000000000000000000000000000000..25fbb84be3b4db86e23a94509170d67c47d47788 GIT binary patch literal 7668 zcmZu$1yq~O(hgEwO3~t02wI94EmB;A7Yz_7P9b=4DNb>Bhhi;K+)8n2aJS;_8u-I^ z&%OWoB{{n@XJ?+>*?o5B-8TtSQ<25RBF6#%0J!pU(jbKNdny=ch`L)0JO}_FnOaFn zsacts0RX^Qm#?jg-D*TVs^y?Dp-?<3O71YJ2^f~^r;ltz@3}Oem20H=VA_3VWC495 z)M2@%(e2c8tPa(5{N(st8l7M8wo7E-PExGW zuY$UXroUc+U$QjHEWVfIv12{JoA=KCTr8=E>M1w7KA%-JNFL!#cFjW8@P!*rX7{I& z!MPM$yqJHy{Wvy(bWw1A3oHJWEE73gTI!PCXWgYTUp1oN3{4;uT zIHaO3aAqlcQYX2Ga#wu_OvpqQuBPsf`fSzremq({n!`!QM2#~BnSDT7o`CTCud}gS z@C)uw7!tc*e>(5^;8)!B+u0ovr`S1;yq=qrL=nu)jh~#Kb4Mj32iWf_@oCEL=3Z_( zb=+Zcr67kK0e^ruyXf4!d~vqDR`1Plj;WAc6VIv`Mh?h>v;EoN=%g&xE&CJ~h$RNl zn#qBb0RT@X1Peg`!0i(o+W>$I7XYwp0ssi5005+p8BOXU2!jtqSKdNd8NiCrF#yky z$N|p~8WN%?AW{5B2O$9gDF3u0BaCkVh#K%j?}sl|0Mvil0RR((eEP^C$`k${DPv`6 z2L=F;bCCb-hYZL;`EUIRK!3VDix6n0in7vx$ER;jYjHfH1=CSZ&lv!~e(_Y0V2eKY zh(>g%yz+bWT}%vs5R)`^pb=uQY)?&HC*(ZQVPy-Ug?czbX#d%6f>DVYdeIwP z8248!n;0pRZ=)OJoFB1qma@1ju0AZv#S(VBSO0q4Fm-I1@)eCN3YugPO+`=A4^y$kzPP;o*~MO=MX z;fY=2M#}L67Ksn&1gxjmBWLA28`-aaR*z%HLihi{CD8CP$yj#nYPdiL(@Ed(wGm8$%&_&G0u(5YGLN%@q-tf1cbvU zy%-Rzgn^BD8r+Ku9h@8nVA*^@qz95fDg|k2MI?Jq!-ueKJr+rbfvMtN{M%JjrwZol zMNVBRNJjCaw~d_Lp{k2rx46~zzR;XN8Y6-#;QvdNyPe}xX7C0n+V{}nEI5Z#`$tA8 zM@ymXv%1W#yD60=^CpHTW{a<%2=45y^R)I5$i;P+^HxoX>Nl)eQ#gH|2Y0_Rg(1x= z*^z&f6NXpqW6~C5hm2eSS+u@C|VEw8wWvr&^k82rM z-!Zd)lV5A8G*`j%cE7_-zQy;rg%5ajWKoK?Z5pC`lnrst(MWFuKa@Na!0CMh1aPN< zA>_LWei^QPv?q%h=KPhubdTu|6KH3ZBP=RS<<7xEQp>T28gnSSF9{@WC zb1Msv{|4|s0eE?Z_;~&Wkeq3_L;!&Q83Af#2XTgi5q$iwK?H?(1pkF$O+>CiU|u|7 zAnsNcLhliks)dC!1PXY|Uw!0NiLjFW$Es%K4zbmhM)Yg%Y~^4N;CsVe&-ruV|N7|t z4<9x7ft?_Le^LseWU3p&{7>MBgDzy|;OOCGW%&^bkovbqE8*yf z$OZEDW}NJ_h}m%et!>XhV91}ke8QMRoXwo99HCE9Nn1HP+JZf3)p%k&np+TF#7|y` zeJLdMwB`;@nhp-O{~B5X`d%0SAla6eme6#cJIM4%(CW$T6?a+APq!y|pZlq^#NG7k zwx5cOdT0qDX&m7%c{L|qf9c?#(gXw_$sOroyy;}KP_$&}iFjH@b;rmk=MaE35g*ES zm^2QMfgm)Ly;Z$oGv?0ds`+ZM@NoX{{oS7W-D24-hn7pZ&qcZMBr$4^R}3KmCSgJ~ zHSh(7CkiQm6v+~$7+LFGFRkYR5QE%bw8PZ|`oR!)k3Rbf8fO7CbS!>9^Clr zpnCjbCb+)(Z0MVSKJ~`vvR=C$FA(JuNy(9E>$h~Z-D(!GLX9m}JQ|_xMWm*yZOnI# zsi+b(!5>f;P&F;eh>7zyeG_s)9X%b>&(=8TST6%|zH2Ze+$YjX>rgZ=MErl_Va8zv zf#Ad>5jCC(t6E`&yFD zdXJsv;cZeK-{hZy_MF!x)BXoo5_x$V%+=)HB#uT4P@vYL%wotZ=|aCstP>H(Uvi?9 zK^uR=a9IDIfleOjB|A5A+AvN0QUM_-nfWB&s9s5tn*5Y}g~R2Ek}S=kF02sUiR{Vj zkCdZQQ+zWNDil&y`DQP5bU2YBPMrKkVqVo4O| z^Jzf?S%N;+k*{8P{1@+V-syB9N00=)`ivXD9*%b$Rh)q+6v&+A{z5jI@?@x3iBKbz z4oFHZiGfEc*b(vi!GBczkusLR+CGiRGSh{G)DQ|T;6^43>8vztE~zq>QdCsz+-5ps z27dvCf5(GFz7$PELP1NYjdSYB>xsBIIP$R+wlralrxx{86ZnzT8Hg^+K|jpFp_C<( zx|x&|fEM+tbNST8x#n8$$O`BJ9qOrSyJf{DVJ~Pbec8?R!9|`iZoz3#)$GqTpxv-B zt~lEVtx$6hm>4t22A{|H&br8cF=T03F)5I38^)asQz zQXo(Gw#a6>q~~vkf8fM$*6>vO!z7bs?$`4RN%JPT*5AH;t{%VJ;T|VF;>R;)G5JG7 zbXw}_>Ya=0zfV-RG6?HJ0U{@Z%(fO&$mu?8^aQ8?RU80k&# z<$-WXdAZqgn>T;Caca0t71c;x_ZP$x0Vs?-f`hS1XDc3ZZbyTr?=#dZF`{$a^Gi#^ zJn)CseFf~kPd%{6vFt3j)40V@$a;8qP*Y+BE;hSq=nKaiHoJn!dTfdcM)tF0zK#m1 zSlYH6r*91244-aXUbKOrds9)n^6`Qf6B=hZMr3j!KCi@Va9pY zoI8&7GDUP>Q;S4MKX?6T8Jx*mHP#ss&ls+>?Y5ctXl=b#n7rHQys!IE4|gb~n~c*^ z=wIn1b>8D>AaW_0?5Ve-QPUBKfZvHLMuF_B65LwnSL;k1yt10vX7V!t(Y2J|jqt?# zn*(uupL2sNE;ur{Ws?H^0G@rcb=>>u;J;}maM4S@}8`!_6kM%})yEGQ{|PEJ$Pkeul9qKKErd)=;=T-|7B^Nw{sE868b@ zcX?nYbD&gzG$TR(homsMs;YNKz$rgglQYKU3LOdygf0C1a>B85>cHofkmsB{qLd-% zU*&yZBaK@~6eDs<({jK3TK~)^o(Qjp34AN+7Lf}{;~TI@8)DWDgrBNU zuf}aj{tF&eD7moPp*bST%fm$*j&v7exFk*(i*)-j2Z7RfL0MUNT3UYw6eh18A?57M zgD=9B9YrA^v&(Y>f&zl+QELOcub0Bkyye@iX1IN=KY{@M&`<5e$Grfr@P zY&vQ^_iecM@o{2#B~{rJp6<|A`jPl0+ELpb=MRg~)v;WuQXpe7L?eczwI zx*>k65g-l}u;#Qf8c`G|?P`jIa+qtRor|>!#Fv-9-Apl)bYc-fB(!%2LqhcrE;dtR z%i6NDOTLTChv(l3VGWO@Oss;WE$^1bP>7p-nP!U|+@|%!28R;$8^phR_Ypo)DP)o0!O=H|^kefSDO6Ra^R-zeHiEdb(l@kUpq#oIcVm9~P(qd4<(62(3zzsoGSpdr`9V3rdW->9+ABX@;9$Qqq z7Q<^Rol>!~*FnL#+>xb{T@hKJOW$ruh4`gbF9az}{7BS1);61jhWK60Q5YvSRFC^#Ujq1q3Prf$fJE&)-;`ez+Tllxdc@)wm zdi9IGV?AC~42;NR`*Rncb_I35nlSZGyoAxLsRCT%x?-(FBxF40ccWA&A%Gf$t_FU$52(FUz-{t@6|WJ01wTs=f$L@#wG zOo>n{Doq-$KQVtebANWvhECOPBjZB5(nrwW&~{OEzkfMzMzC`7aCgL$U!MfOyDG+W zo@49N@?BtV%TZ;zos5u8(Qlc#glfIk|cyHQvWbQm?qJu$z@dg*jc%#KFd9w(zsr)OqJT zelY1||A!9}^}trLaoB=wzOBM#PdltH9p`hV;uz*amb}I(9K38f(HHA+`WlDdexYxn z(b>w{y4H}J*l0P&6Zi{REnDV1z6`q{;rw7eIjukbX)Zg(+1%c@@co$@1`<~}I(1#U zu1b={@2u~WISo?`5Rn36dLojmEr!!<^ItD}viz)Gyj z4f=g=*jtX}mE*AlCa-PKgPyz2na6t;2quHZPE_gwuj=UQgBPMNAa-g-TX zHX8-3B#MVd!*YsIAdg0V^H*Kmv39-|Mi6q!B9Qi!e{=8RaPS*M4z0I+IhgHD!f+wF zRLfD3(3S84bqeik1)9kDS z&CSh#W@~9Z505{5@u1U-Ht(B@DqrPnG4JqU&gW0pqrZLXEyu>MXEwdZd3WX;UXLF& z#|*x1&j=3>FM0oLrEc12eSY}{4;`PH{ijXS0Scd$`(ppL+eJeR1t>MV(y$4np+Ok& zTSuYh;~#pXGT(lZ6|h3QMw=5>1PQ14SdN5xgjHYy%>IRYX5F8Q9?0K6?M$3N=os^I ziF*Fe%`}_5N?E|}-kx6VM*@%n4!A(Flz3xfV}a)(Ylctk=Zvn89RZA4bK(xEAmM4I zC+kPi%6`40h%iOHE@xEEQ(%9+7}`kFDSnIOR-3kBhjzrsqIdGJ{^s48{lR>-Xb7GS@#K^Ip%`I<@a6KPlR{py`|6jwLE<=IpQC5@O;+P1Q8% z#|LCKMT0ReY{(ip9^$4IcoyUXQ zVyxHjT>^rULmbksKTE6QrkmOYV<_+w!z0cR{stxj;kec9YVdfXlp2{d=?-rVpk&ihPTk;%cn{f3|sYod*~qI=;=C%fk~ z4+EmjQq%bU({UjyC>o`8z>`ValSSS$u16{F+wR~H7_aBT=}r;{>Z1}o(Oz#0{YC5b zy0R<;23tA0)5h?t!@BSXvoZ#3rc`P)aSFoGMiVG|NH~Q=3Z-Z!NYw(?{d4NjdlYvJL3TfB`q=7`0;Wj(V2~lP z!T!c;)koY$hk>S>BA5FOALj9e?Y+LOXX3uC)9;Oq?prTK?k~9lF0s3|kGg!tif&MI zlxV?eOkG^hi$GZXx!c&gN))N=xRlDV(RHfDjf_LJ^%8JJ6!q}pUudK3T+8(1YMV%EDl6LrFaM|xk6O2Y*H}jAzXAF}(2|wD*v%pWZm4JH8!zYdt)-8iDdoH2fLXg)ChV zrh7=o9o~VJ8QtVdR)2{OM-wh>9Eo$C&-EzZhFfC%>N*^VzLAdQI3G&b*pId(%ScGR zb1(ro(mC=cRg481&_~S0Vw3wX>ef)|`{}%c%d?0*m+XTw*+90JF&}7d@^?rOV literal 0 HcmV?d00001 From 0de2d6fd275980412859c0880711ef9fcfc1de33 Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Tue, 8 Sep 2026 09:15:05 +0500 Subject: [PATCH 2/5] Update templates: fix paths, lang attr & tool version - Update pla-site-tool reference from 0.1.3 to 0.2.3 - Fix asset paths to absolute (/assets/...) across all templates - Change lang attribute from hardcoded 'en' to $LANG variable - Remove hardcoded architecture options from filters - Document pla-site.pot translation template in CONTRIBUTING.md --- CONTRIBUTING.md | 3 ++- app_page.in | 10 +++++----- apps.in | 11 ++++------- cat_page.in | 13 +++++-------- index.in | 15 ++++++--------- metapkg_page.in | 14 +++++++------- wiki.in | 8 ++++---- 7 files changed, 33 insertions(+), 41 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe620a7ab..af6b2a8c7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,6 +72,7 @@ The site supports multiple languages using gettext `.po` files. Translatable str **Directory structure:** ``` locales/ +├── pla-site.pot # Translation template ├── en/ │ └── pla-site.po # English translations (source language) ├── it/ @@ -122,7 +123,7 @@ The [static.yml](.github/workflows/static.yml) workflow builds and deploys the s ```sh # download the generator -wget -O pla-site-tool "https://github.com/kazam0180/portable-apps/releases/download/pla-site-tool-0.1.3/pla-site-tool_0.1.3-x86_64.AppImage" +wget -O pla-site-tool "https://github.com/kazam0180/portable-apps/releases/download/pla-site-tool-0.2.3/pla-site-tool_0.2.3-x86_64.AppImage" chmod +x pla-site-tool ./pla-site-tool # generates the site into public/ diff --git a/app_page.in b/app_page.in index 154ab581f..b89d226bc 100644 --- a/app_page.in +++ b/app_page.in @@ -18,12 +18,12 @@ - - + + - - - + + + diff --git a/apps.in b/apps.in index daf55d8a4..e581e64e7 100644 --- a/apps.in +++ b/apps.in @@ -1,5 +1,5 @@ - + @@ -16,10 +16,10 @@ - + - - + + @@ -46,9 +46,6 @@
diff --git a/cat_page.in b/cat_page.in index 71f3e356f..b9462ff80 100644 --- a/cat_page.in +++ b/cat_page.in @@ -1,5 +1,5 @@ - + @@ -17,11 +17,11 @@ - - + + - - + + @@ -48,9 +48,6 @@
diff --git a/index.in b/index.in index 8ec2b668c..7174b654a 100644 --- a/index.in +++ b/index.in @@ -1,5 +1,5 @@ - + @@ -17,11 +17,11 @@ - - - - - + + + + + @@ -59,9 +59,6 @@ +
diff --git a/assets/css/style.css b/assets/css/style.css index 8a35af4ef..2f0355389 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -192,6 +192,14 @@ nav a:focus { background: var(--color-surface); } +/* === Results Count === */ +.results-count { + color: var(--color-text-muted); + font-size: 0.875rem; + margin-left: 0.5rem; + white-space: nowrap; +} + @media (max-width: 640px) { .lang-selector { width: 100%; diff --git a/assets/js/script.js b/assets/js/script.js index 9bbd06edd..f934a1161 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -131,6 +131,9 @@ document.addEventListener('DOMContentLoaded', function() { if (visible) matched.push({ index: k, rank: terms.length ? matchRank(k, terms) : 0 }); } + var countEl = document.getElementById('results-count'); + if (countEl) countEl.textContent = matched.length + ' results'; + if (terms.length) { matched.sort(function(a, b) { return a.rank !== b.rank ? a.rank - b.rank : cache[a.index].name.localeCompare(cache[b.index].name); diff --git a/cat_page.in b/cat_page.in index b9462ff80..e1cf7944a 100644 --- a/cat_page.in +++ b/cat_page.in @@ -49,6 +49,7 @@ +
diff --git a/index.in b/index.in index 7174b654a..51f02617b 100644 --- a/index.in +++ b/index.in @@ -68,7 +68,12 @@ -
+ + +

_("Categories")

  • AppImages
  • @@ -344,6 +349,142 @@ only use **bold**, *italic*, ++underline++, lists markdown here diff --git a/pla-site.toml b/pla-site.toml index d6200b74e..133ddc6c1 100644 --- a/pla-site.toml +++ b/pla-site.toml @@ -1,8 +1,8 @@ am_repo = "https://raw.githubusercontent.com/ivan-hc/AM/main" -archs = ["x86_64", "aarch64", "i686"] +archs = ["x86_64", "aarch64", "i686", "armv7l"] meta_pkgs = ["kdegames", "kdeutils", "node", "platform-tools"] -out_dir = "public" languages = ["en", "it"] +out_dir = "public" [[categories]] name = "ai" @@ -178,9 +178,10 @@ name = "youtube" include = "youtube" [cat_comments] -appimage-on-the-fly = 'Here are listed $COUNT AppImages (including $ITEM_COUNT metapackage items) built on the fly from the official portable builds, showing how easily portable apps can be converted to AppImages to gain isolated dotfiles, sandboxing via AM and AppMan, and reduced disk usage, serving as a proof of concept to encourage upstream developers to distribute them directly in such packaging format. Powered by portable2appimage' -appimages = 'Here are listed the $COUNT unique Appimages managed by AM and AppMan' +portable-cli = "Here are listed $COUNT command line utilities: static and dynamic ELF binaries, or simple scripts" metapackages = """Here are listed $SUITE_COUNT "metapackages" (along with their $ITEM_COUNT items), those few scripts that allow the use of many more programs with one installation. Find which metapackage corresponds to the program you're interested in from this list""" -portable-desktop = 'Here are listed $COUNT desktop applications NOT in AppImage format. Launchers and icons are downloaded separately during the installation process (through AM and AppMan). Most of them already have all the necessary requirements to be easily exported into AppImage packages... or they are waiting to be moved to another category (see AppImages on-the-fly)' -portable = "Here are listed $COUNT command line utilities: static and dynamic ELF binaries, or simple scripts" +appimages = 'Here are listed the $COUNT unique Appimages managed by AM and AppMan' +portable = "Here are listed $COUNT portable programs NOT in AppImage format" +appimage-on-the-fly = 'Here are listed $COUNT AppImages (including $ITEM_COUNT metapackage items) built on the fly from the official portable builds, showing how easily portable apps can be converted to AppImages to gain isolated dotfiles, sandboxing via AM and AppMan, and reduced disk usage, serving as a proof of concept to encourage upstream developers to distribute them directly in such packaging format. Powered by portable2appimage' am-utils = 'Here are listed $COUNT static binaries built for AM and AppMan which can be used as a fallback in case the system does not have them' +portable-desktop = 'Here are listed $COUNT desktop applications NOT in AppImage format. Launchers and icons are downloaded separately during the installation process (through AM and AppMan). Most of them already have all the necessary requirements to be easily exported into AppImage packages... or they are waiting to be moved to another category (see AppImages on-the-fly)' From ca743721dc13b415ca0ef5f0029a70e6fff991ac Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Tue, 8 Sep 2026 20:58:36 +0500 Subject: [PATCH 4/5] optimizations --- .../workflows/{static.yml => deploy-site.yml} | 84 ++--- convert_screenshots | 300 ------------------ 2 files changed, 35 insertions(+), 349 deletions(-) rename .github/workflows/{static.yml => deploy-site.yml} (55%) delete mode 100644 convert_screenshots diff --git a/.github/workflows/static.yml b/.github/workflows/deploy-site.yml similarity index 55% rename from .github/workflows/static.yml rename to .github/workflows/deploy-site.yml index 4396a4133..325cf041a 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/deploy-site.yml @@ -1,5 +1,5 @@ # Simple workflow for deploying static content to GitHub Pages -name: Deploy static content to Pages +name: Deploy site to Pages on: # Runs on pushes targeting the default branch @@ -33,6 +33,8 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest + container: + image: registry.gitlab.com/kazam0180/pla-site-tool steps: - name: Checkout uses: actions/checkout@v7 @@ -42,40 +44,48 @@ jobs: uses: actions/cache@v6 with: path: .cache/icons - key: icons-${{ hashFiles('icons/**', 'no-icon.png', 'convert_icons') }} + key: icons-${{ hashFiles('icons/**', 'no-icon.png') }} - name: Cache screenshots id: screenshots-cache uses: actions/cache@v6 with: path: .cache/screenshots - key: screenshots-${{ hashFiles('convert_screenshots') }} + key: screenshots-v1 - - name: Build App Files + - name: Build site + shell: bash run: | - sudo apt install --no-install-recommends wget esbuild -y - wget -O pla-site-tool "$(curl -Ls https://api.github.com/repos/kazam0180/portable-apps/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*pla-site-tool.*mage$" | head -1)" - - if [ ! -d .cache/icons ]; then - wget -O magick "https://github.com/ImageMagick/ImageMagick/releases/download/7.1.2-29/ImageMagick-7.1.2-29-gcc-x86_64.AppImage" - chmod a+x magick - ./magick --appimage-extract - ls squashfs-root - sudo cp -r squashfs-root/usr/* /usr/ - sudo chmod a+x /usr/bin/magick - rm -rf magick squashfs-root - else - echo "icon cache hit, skipping ImageMagick download" | tee build-log.txt - fi + set -e + + pla-site-tool > pla-site-tool.log 2>&1 & + PLA_PID=$! + + imgtool -p . -o ./public convert-screenshots > screenshots.log 2>&1 & + SS_PID=$! - [ ! -x ./pla-site-tool ] && chmod a+x ./pla-site-tool - ./pla-site-tool 2>&1 | tee -a build-log.txt - test ${PIPESTATUS[0]} -eq 0 || { echo "pla-site-tool failed, aborting build" | tee -a build-log.txt; exit 1; } + wait "$PLA_PID"; PLA_STATUS=$? + wait "$SS_PID"; SS_STATUS=$? - rm -f sitemap.xml - [ ! -x ./gen_sitemap ] && chmod a+x ./gen_sitemap - ./gen_sitemap public https://portable-linux-apps.github.io/ 2>&1 | tee -a build-log.txt + cat pla-site-tool.log screenshots.log | tee -a build-log.txt + + [ $PLA_STATUS -eq 0 ] || { echo "pla-site-tool failed" | tee -a build-log.txt; exit 1; } + [ $SS_STATUS -eq 0 ] || { echo "imgtool convert-screenshots failed" | tee -a build-log.txt; exit 1; } + + [ ! -x ./minify ] && chmod a+x ./minify + ./minify public 2>&1 | tee -a build-log.txt + + cp -t public/ favicon.ico *.webp *.jpg + cp -r assets/fonts public/assets/ + cp -r casts public/ + cp -r am-gui public/ + cp -t public/ google*.html BingSiteAuth.xml + + echo "site generated" | tee -a build-log.txt + - name: Process icons + run: | + set -e if [ -d .cache/icons ]; then mkdir -p public/icons public/icons_48 cp -r .cache/icons/public/icons/. public/icons/ @@ -84,8 +94,7 @@ jobs: cp .cache/icons/public/no-icon_48.webp public/no-icon_48.webp echo "icons restored from cache" | tee -a build-log.txt else - [ ! -x ./convert_icons ] && chmod a+x ./convert_icons - ./convert_icons public 2>&1 | tee -a build-log.txt + imgtool -p . -o ./public convert-icons 2>&1 | tee -a build-log.txt mkdir -p .cache/icons/public cp -r public/icons .cache/icons/public/ cp -r public/icons_48 .cache/icons/public/ @@ -93,29 +102,6 @@ jobs: cp public/no-icon_48.webp .cache/icons/public/ fi - if ! command -v magick >/dev/null 2>&1; then - wget -O magick "https://github.com/ImageMagick/ImageMagick/releases/download/7.1.2-29/ImageMagick-7.1.2-29-gcc-x86_64.AppImage" - chmod a+x magick - ./magick --appimage-extract - sudo cp -r squashfs-root/usr/* /usr/ - sudo chmod a+x /usr/bin/magick - rm -rf magick squashfs-root - fi - - [ ! -x ./convert_screenshots ] && chmod a+x ./convert_screenshots - ./convert_screenshots public 2>&1 | tee -a build-log.txt - - [ ! -x ./minify ] && chmod a+x ./minify - ./minify public 2>&1 | tee -a build-log.txt - - cp -t public/ favicon.ico *.webp *.jpg - cp -r assets/fonts public/assets/ - cp -r casts public/ - cp -r am-gui public/ - cp -t public/ sitemap.xml google*.html BingSiteAuth.xml - - echo "site generated" | tee -a build-log.txt - - name: Upload Build Log uses: actions/upload-artifact@v7 with: diff --git a/convert_screenshots b/convert_screenshots deleted file mode 100644 index 263987de3..000000000 --- a/convert_screenshots +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env python3 - -import hashlib -import json -import os -import re -import shutil -import subprocess -import sys -import threading -import urllib.error -import urllib.request -from concurrent.futures import ThreadPoolExecutor -from urllib.parse import urlparse - -SITE_DIR = sys.argv[1] if len(sys.argv) > 1 else os.environ.get("SITE_DIR") -if not SITE_DIR: - print("Usage: convert_screenshots [repo_dir]", file=sys.stderr) - sys.exit(1) -REPO_DIR = sys.argv[2] if len(sys.argv) > 2 else os.environ.get("REPO_DIR", ".") -QUALITY = os.environ.get("WEBP_QUALITY", "100") - -APPS_DIR = os.path.join(REPO_DIR, "apps") -CACHE_DIR = os.path.join(REPO_DIR, ".cache", "screenshots") -DL_DIR = os.path.join(CACHE_DIR, "downloads") -WEBP_DIR = os.path.join(CACHE_DIR, "webp") -MANIFEST = os.path.join(CACHE_DIR, "manifest.json") -OUT_DIR = os.path.join(SITE_DIR, "screenshots") - -MAX_BYTES = 64 * 1024 * 1024 -USER_AGENT = "Mozilla/5.0 (convert_screenshots; +https://portable-linux-apps.github.io)" -EXTS = {".png": "png", ".jpg": "jpg", ".jpeg": "jpeg", ".gif": "gif", ".bmp": "bmp", ".webp": "webp"} - - -def magick_cmd(): - for cmd in ("magick", "magick-im7.q16"): - path = shutil.which(cmd) - if path: - return path - print("Error: magick not found (install it with: sudo apt install imagemagick)", file=sys.stderr) - sys.exit(1) - - -MAGICK = magick_cmd() - -LOG_LOCK = threading.Lock() - - -def log(msg): - with LOG_LOCK: - print(msg, file=sys.stderr, flush=True) - - -def stable_key(url): - return hashlib.sha256(url.encode("utf-8")).hexdigest()[:16] - - -def collect_urls(): - urls = {} - for fname in sorted(os.listdir(APPS_DIR)): - if fname.startswith(".") or fname.endswith("~"): - continue - path = os.path.join(APPS_DIR, fname) - if not os.path.isfile(path): - continue - try: - with open(path, "r", errors="replace") as fh: - for line in fh: - m = re.match(r"^#\s*SCREENSHOTS\s*:\s*(.+?)\s*$", line) - if not m: - continue - for token in m.group(1).split(): - if not token.startswith(("http://", "https://")): - continue - path_only = token.split("?", 1)[0].split("#", 1)[0] - if path_only.lower().endswith(".webp"): - continue - urls[token] = fname - except OSError as e: - print(f"warning: cannot read {path}: {e}") - return urls - - -def ext_for(url): - path_only = url.split("?", 1)[0].split("#", 1)[0] - ext = os.path.splitext(urlparse(path_only).path)[1].lower() - ext = ext.split("?")[0] - if ext in EXTS and ext != ".webp": - return EXTS[ext] - return "img" - - -def read_manifest(): - if os.path.exists(MANIFEST): - try: - with open(MANIFEST, "r") as fh: - return json.load(fh) - except (OSError, ValueError): - pass - return {} - - -def write_manifest(manifest): - os.makedirs(CACHE_DIR, exist_ok=True) - tmp = MANIFEST + ".tmp" - with open(tmp, "w") as fh: - json.dump(manifest, fh, indent=2, sort_keys=True) - os.rename(tmp, MANIFEST) - - -def fetch(url, etag=None): - req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT}) - if etag: - req.add_header("If-None-Match", etag) - try: - with urllib.request.urlopen(req, timeout=60) as resp: - if resp.status == 304: - return "unchanged", "", {} - headers = dict(resp.headers.items()) - buf = bytearray() - while True: - chunk = resp.read(65536) - if not chunk: - break - buf.extend(chunk) - if len(buf) > MAX_BYTES: - return "error", "", {} - return "ok", bytes(buf), headers - except urllib.error.HTTPError as e: - if e.code == 304: - return "unchanged", "", {} - if e.code == 404: - return "missing", "", {} - return "error", "", {} - except OSError: - return "error", "", "" - - -def is_image_body(body): - return (body[:8] == b"\x89PNG\r\n\x1a\n" - or body[:3] == b"\xff\xd8\xff" - or body[:6] in (b"GIF87a", b"GIF89a") - or (body[:4] == b"RIFF" and body[8:12] == b"WEBP")) - - -def convert_to_webp(src_path, dst_path): - tmp = dst_path + ".tmp" - try: - os.makedirs(os.path.dirname(dst_path), exist_ok=True) - subprocess.run([MAGICK, "-quality", QUALITY, src_path, tmp], - check=True, capture_output=True, timeout=120) - os.rename(tmp, dst_path) - return True - except (subprocess.CalledProcessError, subprocess.TimeoutExpired, OSError): - if os.path.exists(tmp): - os.unlink(tmp) - return False - - -def process_url(url, app, manifest, stats): - log(f"[{app}] processing {url}") - key = stable_key(url) - entry = manifest.get(url) - dl_file = os.path.join(DL_DIR, f"{key}.{ext_for(url)}") - webp_file = os.path.join(WEBP_DIR, f"{key}.webp") - - if entry and os.path.exists(webp_file): - if entry.get("etag"): - status, body, headers = fetch(url, etag=entry["etag"]) - if status == "unchanged": - stats["unchanged"] += 1 - return key - if status == "missing": - stats["missing"] += 1 - manifest.pop(url, None) - return None - if status != "ok": - stats["changed"] += 1 - manifest.pop(url, None) - return None - body = None - else: - stats["unchanged"] += 1 - return key - else: - status, body, headers = fetch(url) - if status == "missing": - stats["missing"] += 1 - return None - if status != "ok": - stats["failed"] += 1 - return None - manifest.pop(url, None) - stats["downloaded"] += 1 - if not is_image_body(body): - stats["failed"] += 1 - return None - - sha = hashlib.sha256(body if body is not None else b"").hexdigest() - if body is not None: - os.makedirs(DL_DIR, exist_ok=True) - tmp = dl_file + ".tmp" - with open(tmp, "wb") as fh: - fh.write(body) - os.rename(tmp, dl_file) - - if body is not None and body[:12] == b"RIFF" and b"WEBP" in body[:16]: - shutil.copy2(dl_file, webp_file) - else: - if not convert_to_webp(dl_file, webp_file): - stats["failed"] += 1 - return None - stats["converted"] += 1 - - manifest[url] = { - "file": os.path.basename(dl_file), - "webp": f"{key}.webp", - "sha256": sha, - "etag": headers.get("ETag", headers.get("etag")) if isinstance(headers, dict) else None, - } - return key - - -def replace_in_file(path, mapping, rel_prefix): - try: - with open(path, "r", encoding="utf-8", errors="replace") as fh: - content = fh.read() - except OSError: - return 0 - count = 0 - for url, key in mapping.items(): - new = f"/screenshots/{key}.webp" - n = content.count(url) - if n: - content = content.replace(url, new) - count += n - if count: - tmp = path + ".tmp" - with open(tmp, "w", encoding="utf-8") as fh: - fh.write(content) - os.rename(tmp, path) - return count - - -def main(): - urls = collect_urls() - if not urls: - print("no screenshot URLs found in app files") - return - - manifest = read_manifest() - if os.path.exists(MANIFEST): - log(f"cache: found ({len(manifest)} entries)") - else: - log("cache: not found (fresh run)") - stats = {"downloaded": 0, "converted": 0, "unchanged": 0, - "missing": 0, "failed": 0, "changed": 0} - results = {} - - with ThreadPoolExecutor(max_workers=8) as pool: - futures = {pool.submit(process_url, url, urls[url], manifest, stats): url for url in urls} - for fut in futures: - url = futures[fut] - key = fut.result() - if key: - results[url] = key - - for url in list(manifest): - if url not in urls: - del manifest[url] - - write_manifest(manifest) - - if os.path.isdir(OUT_DIR): - shutil.rmtree(OUT_DIR) - os.makedirs(OUT_DIR, exist_ok=True) - os.makedirs(WEBP_DIR, exist_ok=True) - copied = 0 - if os.path.isdir(WEBP_DIR): - for webp in os.listdir(WEBP_DIR): - if webp.endswith(".webp") and os.path.isfile(os.path.join(WEBP_DIR, webp)): - shutil.copy2(os.path.join(WEBP_DIR, webp), os.path.join(OUT_DIR, webp)) - copied += 1 - - replaced_total = 0 - for root, dirs, files in os.walk(SITE_DIR): - dirs[:] = [d for d in dirs if d not in (".cache",)] - for fname in files: - if not fname.endswith((".json", ".html")): - continue - replaced_total += replace_in_file(os.path.join(root, fname), results) - - print(f"screenshots: {stats['downloaded']} downloaded, {stats['converted']} converted, " - f"{stats['unchanged']} unchanged (cache), {stats['missing']} not found, " - f"{stats['failed']} failed, {copied} webp in {OUT_DIR}, " - f"{replaced_total} URL references replaced") - - -if __name__ == "__main__": - main() From 7bfe7462747746c35a9f2e1ffb01cc01c52c76e6 Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Wed, 9 Sep 2026 15:36:49 +0500 Subject: [PATCH 5/5] update pla-site.toml --- pla-site.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pla-site.toml b/pla-site.toml index 133ddc6c1..f69e11df9 100644 --- a/pla-site.toml +++ b/pla-site.toml @@ -1,4 +1,5 @@ am_repo = "https://raw.githubusercontent.com/ivan-hc/AM/main" +site_url = "https://portable-linux-apps.github.io" archs = ["x86_64", "aarch64", "i686", "armv7l"] meta_pkgs = ["kdegames", "kdeutils", "node", "platform-tools"] languages = ["en", "it"] @@ -178,10 +179,10 @@ name = "youtube" include = "youtube" [cat_comments] -portable-cli = "Here are listed $COUNT command line utilities: static and dynamic ELF binaries, or simple scripts" +am-utils = 'Here are listed $COUNT static binaries built for AM and AppMan which can be used as a fallback in case the system does not have them' metapackages = """Here are listed $SUITE_COUNT "metapackages" (along with their $ITEM_COUNT items), those few scripts that allow the use of many more programs with one installation. Find which metapackage corresponds to the program you're interested in from this list""" +portable-cli = "Here are listed $COUNT command line utilities: static and dynamic ELF binaries, or simple scripts" +portable-desktop = 'Here are listed $COUNT desktop applications NOT in AppImage format. Launchers and icons are downloaded separately during the installation process (through AM and AppMan). Most of them already have all the necessary requirements to be easily exported into AppImage packages... or they are waiting to be moved to another category (see AppImages on-the-fly)' appimages = 'Here are listed the $COUNT unique Appimages managed by AM and AppMan' -portable = "Here are listed $COUNT portable programs NOT in AppImage format" appimage-on-the-fly = 'Here are listed $COUNT AppImages (including $ITEM_COUNT metapackage items) built on the fly from the official portable builds, showing how easily portable apps can be converted to AppImages to gain isolated dotfiles, sandboxing via AM and AppMan, and reduced disk usage, serving as a proof of concept to encourage upstream developers to distribute them directly in such packaging format. Powered by portable2appimage' -am-utils = 'Here are listed $COUNT static binaries built for AM and AppMan which can be used as a fallback in case the system does not have them' -portable-desktop = 'Here are listed $COUNT desktop applications NOT in AppImage format. Launchers and icons are downloaded separately during the installation process (through AM and AppMan). Most of them already have all the necessary requirements to be easily exported into AppImage packages... or they are waiting to be moved to another category (see AppImages on-the-fly)' +portable = "Here are listed $COUNT portable programs NOT in AppImage format"