From 7ddcdc933c267ca2661839d7a13a02a51d027867 Mon Sep 17 00:00:00 2001 From: Ricardo Graca Date: Fri, 15 May 2026 23:02:28 -0300 Subject: [PATCH] =?UTF-8?q?v2.0:=20rewrite=20as=20JS=20Frontend=20?= =?UTF-8?q?=E2=80=94=20deletion=20now=20persists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Attribute-Garbage-Collector.zip | Bin 14948 -> 0 bytes Attribute-Garbage-Collector/README.md | 120 +++--- Attribute-Garbage-Collector/attribute-gc.js | 353 ++++++++++++++++++ 3 files changed, 411 insertions(+), 62 deletions(-) delete mode 100644 Attribute-Garbage-Collector/Attribute-Garbage-Collector.zip create mode 100644 Attribute-Garbage-Collector/attribute-gc.js diff --git a/Attribute-Garbage-Collector/Attribute-Garbage-Collector.zip b/Attribute-Garbage-Collector/Attribute-Garbage-Collector.zip deleted file mode 100644 index 68fe9eb87bc8d66743d782a957708c37254b080d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14948 zcmajGb8si!w>2J5Y}>YziEZ1qZQHgc$;7t(iEZ1qlX>U4-#_m2-XHEc)zzo_)ap8W z_wK4*b=Fdl1_eU}g8bV(ylZuU{?%arj){nfY)xGZ>8+gY?Q&COl1maG(_r@fm1%1V`TlUihW-wB$Z@MzavM-i-}=-17&vu_J%1#sjz<1TjdnP zzZ-9k{RYS^`{12Rm5+k@mrKur;P5r1ikF-;*qSQBTvnntScv|C;tj)D-H^^Grm?^x zCYV)-Ip4+^AbJF=urjXP3a?VZCcV5TqTW(Fu^UQM*k-q0sAUGh^Y%`(Qd>lJugu z{-6$4cBWSGkSIMGnc&`Bn#?@e&dDjv*RvWL6X@oJvi4eW!6#NOh0g$i9B#d`#5ieB z=8A>DdadSu=P=h!8J<7H61nms*=du5~hK3xeJ^h=TMF z$b}1L^c`Rzpf_+JAcen$0sUX)fMxW57Y6R%yMit*PL@WlE~bRyhE7I?=B9+g_BJ-A z#xC|w4F4eiQ&RI@|2LBIe?eNf*xC@98`J-Dp7OnN(&lPd_2(Bw$bC&9Ng}o!OOsUW zX&{~PSV{9C^L(+hOrg8QO$hPyaDV`MVoFo~pg~Aj?`WlV^(RfOD82`3FCU+o%$`gx z#_r?#aT1Jrl)0w4;voYPD^@gT;lhEvd#xT$@#Dnb%O6ac4dDgVhqkmBnx(D%-tK<9p;mJEUgoaRnT0sN~l z+N{8Wy*hN5^9Rbpo{D~z*}FP+RqC{_sVS!T6dQTY{E(C(nHFEZS~A<4A6t*VFS~J* zxnS(WdyCFf32BpXZajuQR+7u#!%K6g4JsF~Y}~nn&2y(wVBYyiTa5>XhKZ6LCG0+A z66?yfU9x6=<_ea+6)s@8+&v`s^}!yoMOvE)C~bwQ$Wf_ERDHL+oH8j&$$X~m>hzb2 z1640u%>Q^eaill}%e@o8n6T5@+&Z^&&0pKsQxJH0$T8l>|D6A}g~S8^&eo=lcXxAZapNxW=uu#uMT>9s-pIX@3knHAA8WijrK$%E z41n;!>+#(1v;s`{m_z!E{{W(M={Al;6tFY*&3~GlwA-J-W3cw~devXqXYP9t?JKDB z_%V6Oec>tdmZ_2)Ac2&VxyUH+BsQ@pAAT_~YWry2}RugaG2QO7MlIlu|m?c9sl6ut4$E@hIHgm%O z9<9t-Q|KL>1bzcyCYJ728EFgJe0@jwK2jp;i3t!2^2@KzP+xig+tonn_!~SnsC$dI zu1FC~&6Q4(92GPo6b>k4xspm#f^gPdwcN)UGx4EO3Zel3`iL}Mjo|{iE7z@6`~*~> z-Ihv_nCsYRJ6sf3c@IUhvJ+L}J8QvCW3{qSPj(=j(6c7I84-B|&DpGCGtgZc6k)ZC zDIj!6a3SHn0s$%Bf{l(g3ZvbS4qR04jpY^0%ldLqdFtK;%HEoY&Jy;mDRJ}~`d9M+ zokSmc1A^ABF@2^H&-sCyFluKZ=W?|=fllIktFyWJUK@jn)1p^>0XC@}jZtj4TYCM1rrf)+1nO5Q_qbS56xT!tPWdRBB>LIb zeou3ut9eChK7d0lI<>f#6(>C>#HIfA$tg6w%m0R}K z2}9PFd9GRKgHhzB1{!>7l&_}$S45?klvfe|k-EyJd4jxLQ=B(l5Gk+%{Aoqa9Vz>8ghzjn*vH3L zEuSoSyl zFpU~9(hz}>3{FMG4ENxQGC)#p&w@F%Vi25-x|b8NFKtTXU%>I4R;QD^bUR|r#FF|s zj=p$4R{yY%*f#8DVn%XNxoUMLvdX2gyy5hWGtGdvMO_l^S<-7^&)~YH)@DI=P!Zjt zT0eLf!6T7u4Hqjw*VO!OKwDE7cCI4(s)6(=&&#~dw|jBFr(pw#3%CW#CN@S}Ghzadmv z4_-urzUoR+dH`ELK3tQXGlSa?nm%0h5aaXi87~W$zHeo2~ndnW$F75}+%lL(Gu(Q>Vk^)ru{-?pXpFz(0SEvWlKbB9{XMI$<(u( zF_5|n<#95j;bsoFpaxTNApvr^)^|#1;91+Ov?-Vj)K6SA6)&-kjj2QX&V{WS4HPhh zJbc`b%8UXw)++2o10fh3+i7V*H9lvJoT|oB@YUZx%t^V>Rm|+)Rlo5I6S!v- zMwg>J0d$@;zqsd5njT~ z$M@P;xtr_)er>k8VqEL`{SGX>?}@pvdmzhwz@p8)uQq#GlWW}As5s}*M4BkwwA5Q` zCo~1*R>#d#4q0QErkf(`0oVh9Xk|%X9 zew1BXi&inUCbeLvVwy3jD0ePcU%TA$3tKr-ZC=o*J|Bsc(M+C-j4vwVJ6JP=57IWmG71Jg5x_<~Jv7Ye;9CtOQ zX@41M3Uo8yqL+PaHa&$pMqV{)&8KNp18W}hU^E&_$qMtmxf<`RQS0$Gh3s>+kL~}` zCYLenzF)R&sJS#b<9wNSeEO)i=FVbTO6u7)wAgoDmfUfgS!rD!QSgM!BV(+NHSL() za0O2@P3#Pxjaa2=?OpnXW;=v|KCp1^XMWKEvB!oq*C8h^5vsvJi>Ec!HGN<3ps{5H z5qMxqwoZYzXsZ*{eMH@iZJFKBrR3M4>Uyoh?YPjH^F}G1q=uF*D zoX8~HL!CDhi>QMndLf_h3Q_^y&;MhqD zpdjQM7j~6tv%v88YwEOQQF7<7mAj4j(dyj^+zEmXYb}@F+$|pO(oTR(72a$~XyqLb z1Z~P4QAGpfR=TD0eRF->WNmFCL)@CG6JzBqXUA9q-Af{2E<&7tZD$0!wPn^aA;v0WwM1GKD947sJiY3JgbbWC9XMnn1SUdC*n-zrGxkW$sP;gN*m@E%cKlpS zxB07hjIX&n<`8zg(DNs>wRiYFx*P^l4sK^cXLgp$hiQ{SROu+w4#gNW6RBjrNUe$r zDE=GKgZW^VhKM;|k@R>uWyz^#ME3q`~O1TW2TAbl|sI@cPia}s=0VsoU1>- z#cB-c1erLaBMUyz)+7n}R-8lcW&G-+P|DEXc-Ki0XM>C&uD+`i8<^ExPO4bxaNQy? zJY`!-856U;$@xKPK3zd)Jf)!JSUF^Mt~B(=0_+TUw3$b>a5xLK9zCl$QOmMi#QN_H zC9)R!vIw2MDVv@r4NI{MH8+Jd$x)+Kj!)Vz(&zvk#WeGvHCN=EV~M%(lJ7%YdU)$v z^mz)92vtu0uOokhT5Ld_*gj_$+m}=~%%KPhQ-z|3*q2s^J2n(uP0pbW^P1w*m_bMB zX>d=sE?E+?F2s69oqEW7#+%NDcKt6Y?CXX?I}6i?X@Ys+`@?bqR$35}N zR_OO7u|TV_l6;v*?AZFPr)#iUw8PhxOMtP>4S zp#dix6)o;6b>{cSJ6h-&&#knguk`~Bzp7eZ4)3DPXS{=Lybij96MTCM1Wv|aFUimi zhJ9>Z6C*-okn4XUd#{tG%I2PiC;dQ8S`>u>1_yu=2TI5kkT-O9VyTzmZ3eH*{Hr@xnzpNoT^3*^ViNvXY_FDE~Du>RjS8DBjA#Z}bq0k%uVe8tvnO>g$@ zuw?Buc`^q)-MsSXLzNtFgp~OwU&Nlcd{&xm=Vd9AScuN3Hd5m#1+s&HIVkoq-M+c0 z3*E<$o|1iFD^K?q(*b}s>M&4W>GoTcDMs2t=+fu|%t=Bng3THFKgI4-W!9SPz!SQxqTX!htf6bU(9 zqC@E)7-ESQ2zIJtaEXC595mBDFPy<31lUj5tCt4xmv9vrO2}-QM5DgGphnIS*{c?m zvj22IQaIe1gBuo42zIm?7T$mLk(#1xXVlf!EbbAF+R?Yxmg+GRq7GKOTebF54#NvqQp*)?K7C>oL0zS9j`T~YPU1yCR`@(<%KM$@YH6!o@UGA23a@ ziK5~}fcRRw9WYmU9HYD*&3L1GG`M8XVPfo-X5g1J$t84`=~{~|7Pp+0(GJK>e4kB~ z@~h%&@XPT42#6&nGkW-b7-2}>RUp%IhYom|X_#d_1GII#aF4b*3!3k=19+$D<3|}N zblG#bRiGPWLuw9-bMEf6!(d>X^UgtxuS zB5DH^`_<(;QB)t|D4U_?O8fFt7tdm29ty6p+1O8CdUIGV!rSB5*HctK_xoeaZZ&FPc_YMR z_M%xf3BK>UjiLn-LR)eKONm&F#lWuv;RtBcot!)$4}{up6+%ckIfQt*`c1?}f4A8t zLN?xC*9NxphhM?2Ke+DwmmzoXyZPLKQ-QmP_dw9HsGWomtROS%YuoGL!|ST@_Pw1U zMhLchLX(vhxccmhMkk=fx=G5O{+2y8ZnjW?sG-N&YypPQL3;>M#PjG3_S)24GFoGa zV2;sOj$2jwmg168@cSBnkL_dgfLNeCj>=Vn7z&%HDLe|^gvEi-F$)6#3+7sx3L%o% zwbXQ>Ku{_P@U~_VbziY47OolOdIHp3Zz5=g(qH9kT|?Srp;}TocR63g)`uh}lM&-N zRAW&#A`p6eJBKJ2EQ(L)aKD*%2s zkWm^*(1w^-Q`TVz@1z&DKe6|3AS9mka>gQ8$>s9R{$!G}(QFi~uXO3^fglB@lI8C^ z5t+nIo?;!rPuf!^YC~dJ&r_xv3t{gcbV036JbCVkzc-0wt!&4K!B_rem)+K7D7TdI zBQ{6DLZC4(CylFLLg2o^u(OSrpYdCv-vNnHMdL6t6{rs^0y(bDR zU38M}7=5{|Rpi)VmrAovlmE8`CjOA|GMepMHEyp10@0rHf1AgyvYaQ3I~2PsT)Mp%fRH7M+BL_X{--vO-JO4*(rYlZ-B@ zbe2PZ$os%c#g8(g-QD4%ojqi6hHSJ?GMn3>?78yk_HASw01>*RatK^_c@$JzZn(D; z@=~T4VFA+a=#SgQj+&ismeRa~P z8l<3SM~BK(KsW6<_1~GM{Fb}QQ+@a^3N3kp`CN9Lr}rHYo7qN(#o`Puc}P?))+Z@P z3aFMXtF!Xi%#}Hk_{bWrfiQm2`A~;qoN3VGD5yp)e-{K4Lc@<74EXq|JheAvK*t&S zebnjDlqDzi?vB2OW~L_kpMeoy4QtDf*=fiVV-b2GS-(OI7qdrwMdO05Fx3NPAuXGQ zaHA1tG73wW(L?WH#td^x8w2wc+cwdLiZRY&j?fYtW~?5CB93K*MCL{-CJ7l_l=^>j z;LW|}An@RIZSnu~et4I^%?8hz#LXw@_WpCSK!Q1N>|b`ExRX23n?ZJ~^v9VZ;t^vm zWd}ScW@#&8@|tna2WcntGHi!R@bK~a`1trboWQS*gyfIiCrX~?d8ifP^);b- zAxY$l3NE7LMXXpD2ul_+uJq&3XgKdFX?|E(jo2|B#hW1%Hp*xnxe~OAa>Knqr|>CJ z_1cQ<<%pa(4wpT|p+c4NFofpex{+H@(MWYOMxBfhQ(nQH~p!R zA*Vl zu@SROuL*5*@e%jdY0f$!+o!0O)qU^{EG8oEyXJ=%5a@Q3xr+5 z^194FGXMwmx#BH7ilM^tX9}F~v{;Z60yo@9am1{_GFAV2odk}j|M46_T-;0#FTa;FERMtL z-dAm+oC22Bb{HyaWuFTSVy3k9@c}+&4 zspHvszHsmIR?vs3w=07Fp|h(nsjEH*!E``h;Swl>RlDc=yl zRJpV#b$-)H=K^-M=9)WGWU5#G>@5?JfNgpoq|2^jzi_8lw*YoLUmeLPkiU5N^4U_r zjnUN;=~mLUnZw#WRN`)yXuHdE4EdFT)Dg27Z>pr6Bt|7kKp>Fh9TQhcLM$60)=m;` z;JzvxJR1SuUM_y-6q)pUe$_jQ5cskN;-h0>d)>`9FaTBEn{+yOBZ|f9D(w&=_%dNp--?(M*q;(e?MREW<`so4(5RQTCdZ(;$ON3BsD;?ZIX~SFS@QMeY z^wxLAnsuFkEBgG*YZi9e)TypgEOg=XeQM*ONO?7&F?qrH70$__VS?s>+~9b!G)R== z8(`b4BQ`BXGpS4Y#_b-vyAW3CBpAZ~giUUiqHeKjngrH8R5y;=a}7ZpaEvA&jE*>`ci^5easnnGaOD_?m6LDmzOyhNpY<>Mzf2DTxdg;`(a-)~nTfHcK z%da#s(Jp11D=hom4YR`s$r$}A+hQ`h(Vk&-vWZXXE20B$#(^`3ZI`SmiTaD)_Bdz~ zLUb-@lbUD8|LOULNRdCd+nvTz3!N;I&k|1`C`-BYN9JX4$QB9 z2HJZW|0S!>+?Ro=k{2ahZh(PHwNzgsT6;Te^HxDr$k≧pW-|l-V>hpPXOm$FD_| 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 **⚠ Status (TriliumNext)**: Detection works fully — all 83 notes, 66+ attribute groups scanned via `froca`. Deletion in the UI succeeds (cache is updated, re-scan confirms), but **changes are lost on page reload** because TriliumNext's frontend froca cache doesn't sync writes back to the server in the Render Note sandbox. Classic Trilium (`api.runOnBackend`) path works end-to-end. See [Persistence issue](#-persistence-issue-triliumnext). +> **v2.0** — Rewritten as a JS Frontend note with full `api.runOnBackend()` access. Scan and delete both work end-to-end on TriliumNext. The previous HTML Render Note approach (v1.x) could only detect — deletion did not persist. See [Why this works](#why-this-works). ## Features -- **Full scan** — Reads all notes via `froca` (TriliumNext) or `api.runOnBackend` (classic Trilium). Classifies every label and relation by usage count and health. -- **Broken relation detection** — Finds relations pointing to deleted or missing target notes. -- **Rare attribute flagging** — Highlights attributes used ≤2 times, plus temp/draft/test patterns. -- **Semantic duplicate finder** — Uses Levenshtein distance to surface near-identical names (`pipeline` vs `pipelne`, `autor` vs `autores`). +- **Full scan** — SQL-backed via `api.sql.getRows`. Classifies every label and relation by usage count and health. +- **Broken relation detection** — Relations pointing to deleted or missing target notes. +- **Rare attribute flagging** — Attributes used ≤2 times, plus temp/draft/test patterns. +- **Semantic duplicate finder** — Levenshtein distance to surface near-identical names (`pipeline` vs `pipelne`, `autor` vs `autores`). - **Dry run mode** — Toggle on to preview what would be deleted without making changes. -- **Batch selection** — Auto-select all problematic attributes with one click. -- **Scoped CSS** — All styles are prefixed under `#attrgc-root` — nothing leaks into the Trilium UI. +- **Batch deletion** — Auto-select all problematic attributes, then delete in one click. +- **Protected attributes** — 50+ system/internal labels and relations are locked from deletion. +- **Theme-aware** — Uses Trilium CSS variables, matches your theme automatically. ## Installation -1. In TriliumNext, import the .zip file (the plugin) into any root note of your choice (e.g., Tools, Plugins, or Add-ons). - -2. The import contains two notes: a Render note and an HTML code note. Simply click on the Render note to view the panel. +1. Create a **JS Frontend** code note in TriliumNext +2. Paste the full content of `attribute-gc.js` +3. Choose your mode: + - **Right panel widget**: add label `#widget`, reload (Ctrl+R), open any note + - **Full-page Render Note**: create a Render Note, add relation `~renderNote` → JS Frontend, open the Render Note ## Usage -1. Click **Escanear** — the tool reads all notes and shows a dashboard of attribute stats. -2. Use the filter tabs (**Quebrados**, **Raros**, **Sem uso**, **Sistema**) and search bar to narrow down. -3. Click **Auto-selecionar problemáticos** to check all non-system problematic attributes, or tick individual checkboxes. -4. **Toggle Dry Run OFF** (the yellow banner disappears). -5. Click **Executar limpeza** → confirm. The tool attempts to delete and runs a re-scan. +1. Click **Escanear** — stats dashboard appears with attribute counts +2. Filter by status (**Quebrados**, **Raros**, **Sem uso**, **Sistema**, **Saudáveis**) or search by name +3. Click **Auto-selecionar** to check all non-system problematic attributes, or tick individual rows +4. **Toggle Dry Run OFF** (yellow banner disappears) +5. Click **Executar limpeza** → confirm — attributes are permanently removed -> In classic Trilium, deletion persists to the database. In TriliumNext, the re-scan will show the attributes gone from the cache, but they reappear on reload (see below). +The individual **remover** button on each row works the same way (skips the batch dialog). ## Compatibility -| Environment | Scan | Delete | Notes | -|---|---|---|---| -| **Classic Trilium** | `api.sql.getRows` | `note.removeLabel` / `note.removeRelation` | Full end-to-end. | -| **TriliumNext** | `froca.notes` | `attr.update({ isDeleted: true })` | Detects everything. Delete works in UI but **not persisted**. | -| **Browser (demo)** | Mock data | Simulated (noop) | For testing outside Trilium. | +| Environment | Scan | Delete | +|---|---|---| +| **TriliumNext** | `api.sql.getRows` | `getNotesWithLabel` / `removeLabel` | +| **Classic Trilium** | `api.sql.getRows` | `getNotesWithLabel` / `removeLabel` | -## ⚠ Persistence issue (TriliumNext) +Both use the same backend path — `api.runOnBackend()` with the standard entity API. Becca stays in sync, changes persist. -### What works +## Why this works -- The froca path scans all 83 notes and 66+ attribute groups correctly. -- `note.getOwnedAttributes(type, name)` returns proper `FAttribute` objects. -- `attr.update({ isDeleted: true })` marks attributes deleted in the frontend cache. -- The re-scan confirms the deletions (groups count drops). -- `_clw.confirmSaveRelations()` exists and returns a Promise (talks to backend), but is scope-limited to the relations panel and doesn't propagate general attribute deletions. +v1.x used an **HTML Render Note** with inline `