From db81f8dfda055897babfb5d6fb483c8008a0c74c Mon Sep 17 00:00:00 2001 From: Avi Date: Sat, 22 Aug 2026 15:29:26 -0500 Subject: [PATCH] Implement profile deletion with undo functionality - Add delete_profile() to profiles.rs - moves profile to undo stack - Add undo_history field and undo_delete() method to App struct - Add cli_delete_profile() and cli_undo_delete() CLI commands - Add delete-profile and undo-delete to USAGE - Update CHECKPOINT-encryption.md checkpoint Verification: cargo test (91 passed), clippy clean, fmt clean, release build successful --- CHECKPOINT-encryption.md | 13 +++- NostrAppNamePossibilities | 7 ++ concept3.svg | 11 +++ frontend/assets/icons/dark-icon-128.png | Bin 0 -> 4675 bytes frontend/assets/icons/dark-icon-16.png | Bin 0 -> 688 bytes frontend/assets/icons/dark-icon-32.png | Bin 0 -> 1093 bytes frontend/assets/icons/dark-icon-48.png | Bin 0 -> 1451 bytes frontend/assets/icons/dark-icon.png | Bin 0 -> 1248 bytes frontend/assets/icons/hummingbird-key-128.png | Bin 0 -> 4675 bytes frontend/assets/icons/hummingbird-key-16.png | Bin 0 -> 688 bytes frontend/assets/icons/hummingbird-key-256.png | Bin 0 -> 12117 bytes frontend/assets/icons/hummingbird-key-32.png | Bin 0 -> 1093 bytes frontend/assets/icons/hummingbird-key-48.png | Bin 0 -> 1451 bytes frontend/assets/icons/hummingbird-key.svg | 17 +++++ frontend/assets/icons/light-icon-128.png | Bin 0 -> 4675 bytes frontend/assets/icons/light-icon-16.png | Bin 0 -> 688 bytes frontend/assets/icons/light-icon-32.png | Bin 0 -> 1093 bytes frontend/assets/icons/light-icon-48.png | Bin 0 -> 1451 bytes frontend/assets/icons/light-icon.png | Bin 0 -> 1248 bytes .../assets/icons/transparent-icon-128.png | Bin 0 -> 4675 bytes frontend/assets/icons/transparent-icon-16.png | Bin 0 -> 688 bytes frontend/assets/icons/transparent-icon-32.png | Bin 0 -> 1093 bytes frontend/assets/icons/transparent-icon-48.png | Bin 0 -> 1451 bytes frontend/assets/icons/transparent-icon.png | Bin 0 -> 1248 bytes src/app.rs | 33 +++++++++ src/main.rs | 66 +++++++++++++++++- src/profiles.rs | 21 ++++++ 27 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 NostrAppNamePossibilities create mode 100644 concept3.svg create mode 100644 frontend/assets/icons/dark-icon-128.png create mode 100644 frontend/assets/icons/dark-icon-16.png create mode 100644 frontend/assets/icons/dark-icon-32.png create mode 100644 frontend/assets/icons/dark-icon-48.png create mode 100644 frontend/assets/icons/dark-icon.png create mode 100644 frontend/assets/icons/hummingbird-key-128.png create mode 100644 frontend/assets/icons/hummingbird-key-16.png create mode 100644 frontend/assets/icons/hummingbird-key-256.png create mode 100644 frontend/assets/icons/hummingbird-key-32.png create mode 100644 frontend/assets/icons/hummingbird-key-48.png create mode 100644 frontend/assets/icons/hummingbird-key.svg create mode 100644 frontend/assets/icons/light-icon-128.png create mode 100644 frontend/assets/icons/light-icon-16.png create mode 100644 frontend/assets/icons/light-icon-32.png create mode 100644 frontend/assets/icons/light-icon-48.png create mode 100644 frontend/assets/icons/light-icon.png create mode 100644 frontend/assets/icons/transparent-icon-128.png create mode 100644 frontend/assets/icons/transparent-icon-16.png create mode 100644 frontend/assets/icons/transparent-icon-32.png create mode 100644 frontend/assets/icons/transparent-icon-48.png create mode 100644 frontend/assets/icons/transparent-icon.png diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 00d188c..6c5c5b5 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -170,6 +170,13 @@ Nothing outstanding from the audit — all ten findings are closed: | 9 | NIP-46 secret not verified | `f7db29e` | | 10 | No backend request timeout | `f7db29e` | -Possible future work (not audit items): DNS-rebinding TOCTOU in the SSRF guard, OS keyring -integration for the vault password, an automated dependency-audit CI job (`cargo audit`, -`npm audit`). +**Profile deletion with undo functionality** (2026-08-22): +- Users can delete a profile via `nostr-manager-backend delete-profile `, which moves the profile to an in-memory undo stack rather than permanently removing it +- Profiles can be restored with `nostr-manager-backend undo-delete`, which pops the last deleted profile from the undo stack and re-adds it to the vault (becoming active if no other profile exists) +- The undo stack is in-memory only (lost on process exit); a persistent implementation would require vault metadata changes +- Vault must be unlocked or unencrypted for deletion to be permitted +- Existing profile data and vault integrity are preserved +- CLI commands: `delete-profile ` and `undo-delete` +- Rust changes verified: `cargo test` (91 tests), `cargo clippy --all-targets` clean, `cargo fmt --check` clean, `cargo build --release` successful + +Possible future work (not audit items): DNS-rebinding TOCTOU in the SSRF guard, OS keyring integration for the vault password, an automated dependency-audit CI job (`cargo audit`, `npm audit`), GUI integration for profile deletion/undo in the ProfilesScreen. diff --git a/NostrAppNamePossibilities b/NostrAppNamePossibilities new file mode 100644 index 0000000..deaad5a --- /dev/null +++ b/NostrAppNamePossibilities @@ -0,0 +1,7 @@ +Nostr account manager name possibilities + + +Clavarius +Custo +Clévia +Keynector diff --git a/concept3.svg b/concept3.svg new file mode 100644 index 0000000..f0ebc18 --- /dev/null +++ b/concept3.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/assets/icons/dark-icon-128.png b/frontend/assets/icons/dark-icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..f3b14fb682fc1a17bbea7a4249a9927bdb1d1025 GIT binary patch literal 4675 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 literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/dark-icon-32.png b/frontend/assets/icons/dark-icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..fa853ea78109d76dc488e5542372da91221fe3e4 GIT binary patch literal 1093 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW$3UE9k04(LhAK4%hK3dfhF=T} z3=J@cUr_WvGw1*N5C3cG|7Yd?fA{`>Rn7m5?EfD={O{=gUs3fxzwrO(FaIxJ`TykU z|2=#EzkU0EU%2?cvgZHH+5f+P|Nrmb|4*O(KYRZF$&>$I zzyAOD@&D6j|0}Bh|NZ-a)|~%6egCIU|KC69e{1{ymbU+$-Tx;{`rp~}fBnY)z4m`v z*8lYE|LIx(mn{3gdCUJzoByv}|9|1)|5va6-?Huh)@}dWJO4j={D1#}|L4yCFR%Px zQ}@5F;eSKZ|C_h|fB5)+#j5`uUH_lI_<#7w|2cF2-@5&O#mfIZz5ly=|F2&2|KX$m z@89d{>c;r-b~7+AFeZ7syR`M(vyNq8VBjq9h%9Dc;5!V$jK}j=q%bfru$OrHy0X9G z5aZ|Ob}XK@gMooTwZt`|BqgyV)hf9tHL)a>!N|bKOxM6d*T^Wu(A3J%(8|zI+rYre zz~I5d%10;~a`RI%(<*Umn7HR)90LP`2Hb{{%-q!ClEmBs+l$X z%^t@(Cr0K@h3JP5pB{!E&{xoC32)rcu|gt_^^wq|pbAls(MGGD)V^-@|w>g4GxqZN{;3nXXEP?AhY%S+5m%}q|u zP0db!zaZnyiGv59KY7y>@Zi<6cMl)FeERg|+l9{`uYPj;;foV*p@3HPm}h9p0Y=+%*$s)So7t*v$$T-!pj-c=~*yghRoDSv!>0P zGI1tr|IM89Dk=h#CeNNeeg5-Vtl$h7tZb7*6(;VPSAzfOfsLj6JM zA*{1Ff4)og@Ul6jeL>l6^QA4TW{Iyl<{S_w;r)d5+LdclxtXqVaAjRj?vHmpd8h4I sN5`^fVy<&C6*g?$;5ut!VgMsUp{m;zopr04u9Jr~m)} literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/dark-icon-48.png b/frontend/assets/icons/dark-icon-48.png new file mode 100644 index 0000000000000000000000000000000000000000..6888ed84b5f6ba17e96ae9f8e63de6915a35c7e1 GIT binary patch literal 1451 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_6p}rHd>I(3)EF2VS{N99 zF)%PRykKA`HDF+PmB7GYHG_dcykO3*KpO@ICgA{|5ZC|z|NsB`_5Y>I|6jiP|KrF1 zEnELrRR8bp`~U6R|E=5p7nc0b%=v%(#Q&_^|Jix}(=z|3XZ_!@?fsnh>&-~PX5?f=zl{vSR5|KzFvPoDlibol?) z?fN~|0`?$ zU%&DH(WC!s*8Q(*{D1K9|3ypwSJ(cptp1;q_rI#?)~4>|9}31|LtA>d;9-?`SQQM@qb;z|A&wMH#YxoYW@G^>;L+u|L4yCpTF?` zwQK+H-2Gow{lB*U|E*j9`zQUMJOBURzyFUM{hyutzozd0uV4Q+Zu&oa?*I4i{~tW` zKd<2bf<^zYT>byx!T*k~|5|DQhdfAP})lc)aw`04+TpZ|C7{Xcuo|MeUH zclZ2%`SSnH-Tx;~`QOm=fBMY-J9qtGw*3F9)&IBe`2YRK|G)oqb#?tO3S=-aFfb;0 zySr>~PFZ(^fq{Xuz$3Dlfr0NZ2s0kfUy;JVz`$PO>Fdh=ibIT_o9Dhbvor$(gKCLu zL`h0wNvc(HQEFmIDua=Mk(sW6g|3lNh@q*Kp`n$bk+y+>m4U%U&g6O&4Y~O#nQ4`{ zH7p3ZeVBoPK?80>NoHF%Ffd4h^aSUpm6RtIr81P4m+NKbWfvzW z7NqLs7p2dBXCuYHz+~>};uzv_JUKxkvteTAOqH)XYjWN!S@HbAlSj|gT$%aB_2u<7 zr6PX*{PDx%;sH&EMQo0BadDI8sHo^DX}NW=6>{={U^L zoi4n8PW7l{J_rdsn%O$bs++A)>WazMEu|$>TfTgW*gfkXPY(}u)o^yys$u|ps1{{^zeoQcMo3rpJF-X;@hqUxcl^e)zg1zEy2|7W~OrP_=+BJX~Ht zR`_v{Kv)Lv)qs$gpsrO5b&gA}ky|3*B%IJUUl$nyEY%MMe)^Z zEuTML{rK_I)%Hd0fzFF(tml>r@X5={6ZB?#_UziWZx;eWY-Q*630zlVjJqGBR_uCD zPeML&Z*6(cT5h%ty_v6HzU6LZOyT`I&#*AU;Y>@XMbMokRd1w*x#dK`Ro1mF)%PN Nc)I$ztaD0e0su>03^f1% literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/dark-icon.png b/frontend/assets/icons/dark-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..69d0f560ce1d7d8a41353a6a2f254fcb430dfd6b GIT binary patch literal 1248 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4rT@hhPm4t-!L#RC?tCX`7$t6sWC7#v@kII zVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^bjGF^|LR|m<|NsBRi~pA{|G$0b|F2*F z_aFFQ-}FB_?|*5<|6|Ah-?;I=zVZLw{r^9F_<#TY|E0_R=j8uCd+z_ccmLO~|6g1C zKRf$>cFzBT;{Q*c{Qvv+|Mui@8AEUNB{5H z^Z(lQ|BWsG*R1=0^XC7Hm;UeB_kY%$|HWnhPo4h%`SbshlK+z?{l9nr|LfQPPn`VU z)bfA-{{L66{hu)D|EpL3%PRk$JO6**zW;?K{}(O!|NQy?4hh`oCrC|BCAW zU%&n@EBl|5_y6I;|LZsYKYZl><;(w9uKvGe+yB<~|9}4cZ*KlyS^59OssFhJ|GT^Y z|Ni~IxcL9GXa7&0`rp<2|Ki2}IXVBQPyhep$N%j+{(t}R|HzU5H8ubD?)`u5+W*B% z|KGd!zqj{)N7w)M&j0V<|9|!Re@*@WnX~_I*z|wSy#G_D|9|oFe_rALp1%KY-v0mo z{r`i9|99>FKWXa!XV3qC`t-kV!vFlD|A!9$pE2uyRqg-Qj{j@d>FVkRaaQhQU|?WO z@^*KbP{Gy5!@$76S>O>_%)r2R7=#&*=dVa%U|?V`@$_|Nf5jojXQ}t>Xw6Or1_sp< z*NBpo#FA92Ftb+jzOi|oZTHw4H8#a3OOfj@%HjOed5%~)7_n&U8kIV0s>SV z_fOiiqHC3u%VLQ$g0ECq_O|t?HH)43{b|vy2?-YzZ!yH$*0M2I{9yEL*m3YehsWPz zi`00Qr9GPyBQ`DUnpuLd!ovjbId#_6?4IuG68;w>8AbRKgkw62n^w=8y<4@JeNO(7 zmz>2-;)Q-6zI|k!^{8Fo1Al{*fX9@73XKV>Z4(06RTNWS8hBK+2`{v7nrkccMDU`< zj({mEw3jp7XvjG9EBVIFWYxx!m>MR19e4~8hIW}P+4+cEMv0t5{V7|evHPC zOoA>qgjn6+ZTa%LWAo;8hnq7aZ(2HNsatCcpE<93sKF-Sn}7SqPhYvXy1BT&d|Fr& ze~3+ld8&%$!JVp05B!OVVtTx6il9*g=aUmfDHFRUajC>D<*j2DS?$0U@;Go}-~9%u zf{^Qvf`i)qH|=B0aZt81_5Goet+;aQ!kK4;_P8=c)opncI;}5w8E+WF&n=IQ8)kBB zFDY^IdG$(dyPeUeS1+Hwc%3hmE8f!-=wQOY;3VL<>C4{IDh37y22WQ%mvv4FO#q78 BgS-F$ literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/hummingbird-key-128.png b/frontend/assets/icons/hummingbird-key-128.png new file mode 100644 index 0000000000000000000000000000000000000000..1bedc54f13496bb15d98a3d0df2fb59e71c2e60b GIT binary patch literal 4675 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 literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/hummingbird-key-16.png b/frontend/assets/icons/hummingbird-key-16.png new file mode 100644 index 0000000000000000000000000000000000000000..729674f82ea53eb64ca5d4464f1e19fdc24b9c1e GIT binary patch literal 688 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 literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/hummingbird-key-32.png b/frontend/assets/icons/hummingbird-key-32.png new file mode 100644 index 0000000000000000000000000000000000000000..07b269d46e7a6e2428d9d0e4149b055caf709653 GIT binary patch literal 1093 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW$3UE9k04(LhAK4%hK3dfhF=T} z3=J@cUr_WvGw1*N5C3cG|7Yd?fA{`>Rn7m5?EfD={O{=gUs3fxzwrO(FaIxJ`TykU z|2=#EzkU0EU%2?cvgZHH+5f+P|Nrmb|4*O(KYRZF$&>$I zzyAOD@&D6j|0}Bh|NZ-a)|~%6egCIU|KC69e{1{ymbU+$-Tx;{`rp~}fBnY)z4m`v z*8lYE|LIx(mn{3gdCUJzoByv}|9|1)|5va6-?Huh)@}dWJO4j={D1#}|L4yCFR%Px zQ}@5F;eSKZ|C_h|fB5)+#j5`uUH_lI_<#7w|2cF2-@5&O#mfIZz5ly=|F2&2|KX$m z@82^pFmUSKTg1S?z?kIi?$XwC&pH+)R^Sm?%)r2R7=#&*=dVa%U|?V`@$_|Nf5joj z&t*Ab|F=p81_sp<*NBpo#FA92BQ;SOya|>|mIrnixJp%)SBuGziep*R+ zVo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=E9^o-U3d7N?UF78oX^9MV63=1^C>y|guZ z9P6AInL8DtA3l6~7=A!sL8B$SaYM%ni8$6rLX(0jL_JQQc*M^pXIaUrvTo6mNlhCz zaqV9V2X$h&5r?ZS!NS-c`oH0X5G9fK5F*7wcIXO2q zJN^BFj5j9^9(?}fO;f;wSI^!(eE9O|)0b}-K7YLW$?=CTPP~<0z(1XzhwItb2VXyY z`SRfFHx5o-Zg#B)9UVPQ+7o)p9NFl6*dX=2c?Iw z&f@&}F4e=!=9KmYWw*_jwyc^ZzUr8BK%9j46V_{2u1)1;y2`I(3)EF2VS{N99 zF)%PRykKA`HDF+PmB7GYHG_dcykO3*KpO@ICgA{|5ZC|z|NsB`_5Y>I|6jiP|KrF1 zEnELrRR8bp`~U6R|E=5p7nc0b%=v%(#Q&_^|Jix}(=z|3XZ_!@?fsnh>&-~PX5?f=zl{vSR5|KzFvPoDlibol?) z?fN~|0`?$ zU%&DH(WC!s*8Q(*{D1K9|3ypwSJ(cptp1;q_rI#?)~4>|9}31|LtA>d;9-?`SQQM@qb;z|A&wMH#YxoYW@G^>;L+u|L4yCpTF?` zwQK+H-2Gow{lB*U|E*j9`zQUMJOBURzyFUM{hyutzozd0uV4Q+Zu&oa?*I4i{~tW` zKd<2bf<^zYT>byx!T*k~|5|DQhdfAP})lc)aw`04+TpZ|C7{Xcuo|MeUH zclZ2%`SSnH-Tx;~`QOm=fBMY-J9qtGw*3F9)&IBe`2YRK|G)nj7#O5B9(H74U|>x0 zc6Zs}oU-l+0|NtRfk$L90|Vb-5N14{zaoW!fq}im)7O>#6^9rGLJUo<3=OPI&9w~-tPBiZ?9`M&(U6;;l9^VC zTf@vJ6BjTrFlfMSD9OxCEiOsSEx@hk+{X>|3=9mCAU(nPX(i=}MX3zs<>h*rdD+Fu zi3O>8`99;XdXoSG=&^@(eV276Uh zV5bZ3pHn?5nGZq&k7l;cvg&3ll)7TFbxUc<)Rr$_B6(-@_LvEg)c>ebBXmMeirV^=Ca7!GLY?E1Yvh&)IZZDP4cQdZIQPiu=(PuenpYh>+pf*WYf*gl zTFd8;S3iFIbhUj^d!X~;8SA;F0(|na@&vuvo;|y^?c0Tb5L?-~eFE2&7~}58s1>{3 z)02>o+*@1TvzD7}LvQBmmv6aS8B=)w&ND2Ga5&S_X%Td1N!1&vBHoRw=i3}*-`)0N zRzHp;UKKFs-QD)mQ_#@RaK^MFSI!?;=eW4?*_thPBzVe?K4E$M!K69r@EgnG zV5bXPj+~i&zJXn5#=e3;zDbvpl0H4ZV(@^~^z6d!=`Oz;wjMe1;|cdh0|BP2Z{E{& z&u?dK6xH5(g>x1A>xsH?OSe~@z4a)~bkUXOuPcn#Mf-1g71+STAolmDSpIsyeGCi? N44$rjF6*2UngDDF4!HmT literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/hummingbird-key.svg b/frontend/assets/icons/hummingbird-key.svg new file mode 100644 index 0000000..b365ed3 --- /dev/null +++ b/frontend/assets/icons/hummingbird-key.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/icons/light-icon-128.png b/frontend/assets/icons/light-icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..eb2155fd10be7af702429c13d551f83c38e94837 GIT binary patch literal 4675 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 literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/light-icon-16.png b/frontend/assets/icons/light-icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb897696f675db54776f7afe80eca3066c92dde GIT binary patch literal 688 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QU)|F2vBzpLl}vgQB3fB*mb_5Uqf{~tg8|I??hU%$Tj zRd2|^z`&T~?e5~7et|cMfq{Xuz$3Dlfr0NZ2s0kfUy;JVz`$PO>Fdh=ibIT_o6D?Z znH~cJgKCLuL`h0wNvc(HQEFmIDua=Mk(sW6g|3lNh@q*Kp`n$bfwqBxm4SiVw4yK+ z4Y~O#nQ4`{HJG11^$HY2a2rZ8b5n~;5_1c1>q)rx+LVETK@y}VI6tkVJh3R1p}f3Y zFEcN@I61K(RWH9NefB#WDFz0HW=|K#5Q)pl2?zKkW?o>K5qW_n;^qaG8ZgTe~DWM4fjhr^| literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/light-icon-32.png b/frontend/assets/icons/light-icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad7a6f3774a826763fc0d8fa3e3f7c7fd9e0c15 GIT binary patch literal 1093 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW$3UE9k04(LhAK4%hK3dfhF=T} z3=J@cUr_WvGw1*N5C3cG|7Yd?fA{`>Rn7m5?EfD={O{=gUs3fxzwrO(FaIxJ`TykU z|2=#EzkU0EU%2?cvgZHH+5f+P|Nrmb|4*O(KYRZF$&>$I zzyAOD@&D6j|0}Bh|NZ-a)|~%6egCIU|KC69e{1{ymbU+$-Tx;{`rp~}fBnY)z4m`v z*8lYE|LIx(mn{3gdCUJzoByv}|9|1)|5va6-?Huh)@}dWJO4j={D1#}|L4yCFR%Px zQ}@5F;eSKZ|C_h|fB5)+#j5`uUH_lI_<#7w|2cF2-@5&O#mfIZz5ly=|F2&2|KX$m z@85s@`c=-h<`V-017ni6yGvWoJ?mHo1_sUokH}&M2EM}}%y>M1MG6B01AB?5uPgg2 z4l#aiZpY$jI~W)kR7+eVN>UO_QmvAUQWHy38H@~!%ybPbbd8Kc3{9;J4Xq3fwG9lc z3=AGTtbBx`AvZrIGp!Q0hKYL)#xXE3XuxeK$;?eHE=kNSz^&)l$_I@M3=EPWJ;C{D zCFO}lsSM@i<$9TU*~Q6;1*v-ZMd`EO*+?-kF#3DCIEGl9PEJ^0n2>Tv|NNOlUGetP z*6eYtb7ExfREU1~@abXr0euCHmhi?69V;Z_SRV;Z3aSwGIDO&~KbxFoC9BH1MN1|% zZP>)Mf7PTVN*V%Jeq1%3AewP1EA#b>S1+X{q)wjBGFl;dxgRkE>IC;6*wH|bI^fYNt=qY>D%Dj9=gf(B@JB#ZTExepDot^~~X2?vPG;7+t zDHCV1_TS7ouc9I_Y4Ysp)8{{*#Tu><_26JPi_I7Ai%e@zFo!ng8m_V__UlyGB-9_2 z9>O|{^XI!%4=|iI(3)EF2VS{N99 zF)%PRykKA`HDF+PmB7GYHG_dcykO3*KpO@ICgA{|5ZC|z|NsB`_5Y>I|6jiP|KrF1 zEnELrRR8bp`~U6R|E=5p7nc0b%=v%(#Q&_^|Jix}(=z|3XZ_!@?fsnh>&-~PX5?f=zl{vSR5|KzFvPoDlibol?) z?fN~|0`?$ zU%&DH(WC!s*8Q(*{D1K9|3ypwSJ(cptp1;q_rI#?)~4>|9}31|LtA>d;9-?`SQQM@qb;z|A&wMH#YxoYW@G^>;L+u|L4yCpTF?` zwQK+H-2Gow{lB*U|E*j9`zQUMJOBURzyFUM{hyutzozd0uV4Q+Zu&oa?*I4i{~tW` zKd<2bf<^zYT>byx!T*k~|5|DQhdfAP})lc)aw`04+TpZ|C7{Xcuo|MeUH zclZ2%`SSnH-Tx;~`QOm=fBMY-J9qtGw*3F9)&IBe`2YRK|G)pfe*Mb3C{d7sfq^l} z+udb@bIQ6S3=9mM1s;*b3=DjSL74G){)!X^1_t&LPhVH|R~%ye+}w`E({?a0FsPQe zMwFx^mZVxG7o{eaq%s&87@6rBSm+uVg&3Mz85&v{8fqIDSQ!{Rcv$%eMMG|WN@iLm zZVeOn9E@XNV9l4=!4fd+2 zz)lz5Kc{+BG9QEl9?fi>W!24AD0RhT>z2}zsV!f=MDoty*}B!_>VZXW$JCZtr7_L$ z3o_omVaujX+cs|9x^;7s@Uu-D4cK38cwSf_QczS@SbBKFfxCw1z9;_CV*wGuCrU1^DD;Dh(d(_MZyY&~-1#}n?21_Df3-@K>m zp5M;eD5|~n3g;^J*AsQ)mTs>)d+Sk}>7pymUso8fi}v60DzJfvLG15QvHbOZ`xqD) O7(8A5T-G@yGywnujS##5 literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/light-icon.png b/frontend/assets/icons/light-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4b44c0363e5464a19c0902f30a6d572cdd977d GIT binary patch literal 1248 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 literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/transparent-icon-16.png b/frontend/assets/icons/transparent-icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..9dde357a2597c75b13d69b3eb64b7a8694d5a641 GIT binary patch literal 688 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QU)|F2vBzpLl}vgQB3fB*mb_5Uqf{~tg8|I;T128Q1+ z?lBSEMj7FtC?+`ns~e;t=EK<}xc; zrpLg*pjzS@QIe8al4_M)l$uzQ%3x$*WTtCip=)FmVrXh*XlP|Uoc?@cUr_WvGw1*N5C3cG|7Yd?fA{`>Rn7m5?EfD={O{=gUs3fxzwrO(FaIxJ`TykU z|2=#EzkU0EU%2?cvgZHH+5f+P|Nrmb|4*O(KYRZF$&>$I zzyAOD@&D6j|0}Bh|NZ-a)|~%6egCIU|KC69e{1{ymbU+$-Tx;{`rp~}fBnY)z4m`v z*8lYE|LIx(mn{3gdCUJzoByv}|9|1)|5va6-?Huh)@}dWJO4j={D1#}|L4yCFR%Px zQ}@5F;eSKZ|C_h|fB5)+#j5`uUH_lI_<#7w|2cF2-@5&O#mfIZz5ly=|F2&2|KX$m z@82^pFmUSKTg1S?z?kIi?$XwC&pH+)R^Sm?%)r2R7=#&*=dVa%U|?V`@$_|Nf5joj z&&_34vP_SGfkCyzHKHUXu_Vz@P!Qp(HamwYVfPw*a@EgnO?|85kHOL3)Do(@M${ zi&7cN%ggmL^RkPR6AM!H@{7`Ezq65IU|{t3ba4!^IGvoZz%U`@kpB5Ihq~hJrLEcH zSm(sZ+^G=#@ZrT&wSBYrkH%Su+2b&HlvYTB@g zYyYZAOO!MOuKc)aIzcq!R95Ee7q4DQOGuqOon^E_@^pdZj2TLj32AwWnW?$S$+@Z7 z>F*b0yg703;PWSMngSlYdiL(&!&$xCd`nTI%(Flc~d6N zWbMD1b6!P7VAAB-)2GjWK8rP6A?m@wZWfy_+!vYFo?s4b%r#tPQ|#BNut}&tC_RLA z7U$1*sUBW7r?f99yKTO-Wz{V4RmYqI;v~GEuwJ`zZ7Mg@RSvGK>&gA`t|#xb9qZ^= q_DsxmPNu?!ts7itO-u}6WGGa1JH*Z0c9wyGfx*+&&t;ucLK6Va20Cs4 literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/transparent-icon-48.png b/frontend/assets/icons/transparent-icon-48.png new file mode 100644 index 0000000000000000000000000000000000000000..c87a823183c92371b51a08674c659663070de450 GIT binary patch literal 1451 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_6p}rHd>I(3)EF2VS{N99 zF)%PRykKA`HDF+PmB7GYHG_dcykO3*KpO@ICgA{|5ZC|z|NsB`_5Y>I|6jiP|KrF1 zEnELrRR8bp`~U6R|E=5p7nc0b%=v%(#Q&_^|Jix}(=z|3XZ_!@?fsnh>&-~PX5?f=zl{vSR5|KzFvPoDlibol?) z?fN~|0`?$ zU%&DH(WC!s*8Q(*{D1K9|3ypwSJ(cptp1;q_rI#?)~4>|9}31|LtA>d;9-?`SQQM@qb;z|A&wMH#YxoYW@G^>;L+u|L4yCpTF?` zwQK+H-2Gow{lB*U|E*j9`zQUMJOBURzyFUM{hyutzozd0uV4Q+Zu&oa?*I4i{~tW` zKd<2bf<^zYT>byx!T*k~|5|DQhdfAP})lc)aw`04+TpZ|C7{Xcuo|MeUH zclZ2%`SSnH-Tx;~`QOm=fBMY-J9qtGw*3F9)&IBe`2YRK|G)nj7#O5B9(H74U|>x0 zc6Zs}oU-l+0|NtRfk$L90|Vb-5N14{zaoW!fq}im)7O>#6^9rcu8glbfGSez? zYcM~3>JN&PIxXfyvy{#WBR=cyfY7X2ZnJnJQm(*5tfdvf}xJCy$<~xia&M>&xqF zN=5wq`QwMj#RHlSi`X3N;^HRFQBl!R(sJu!E9B(!)C@B5IBjs^)I<@lPh3kh*sG!f zJ6(AHoa#}@d=L_NG_!S#$vcB*>sFJi2Nt;*3C)6&o*r|V1KpYd0~M_K~Y&@>ER6r?jF7za`W`W^S6&5pMC!N zd3yze6LT9CEn1{!XjJTsb-H@!f(}Q2M^9JZ$B%tq*_7t*nDOI7iwoN{mAJTsO^+Y@ z`g!^~Usag?Zl<8PusFMkT!v>%OppJv(y+2Jz6e`+{qS{3e5=~_EclbLplShMc(}ZP ztnlL?fv^nTs{tV~L0zjB>KvC`Bez7zX?k&J$fl6SxkpY%uRRddyz1cDc5Oahi{h)- zT0Vcg`tjqZtL=;01DzMoSkEmL;FFh?C+N-g?Af(#-!24%*viiB6S%I#7wcKnQdNW_Ye9PU+n8N#ao?&5x!W!Bc+p3CrUTCe2ZY-&ht0 zJ6+gv@?(U17W~+JDQdzy=-$vA;*f^4I(AV_;xl N@O1TaS?83{1OTyU3}XNQ literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/transparent-icon.png b/frontend/assets/icons/transparent-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0dfefd97eddb5248ffd6a4949fa9b757fe448a GIT binary patch literal 1248 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4rT@hhPm4t-!L#RC?tCX`7$t6sWC7#v@kII zVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^bjGF^|LR|m<|NsBRi~pA{|G$0b|F2*F z_aFFQ-}FB_?|*5<|6|Ah-?;I=zVZLw{r^9F_<#TY|E0_R=j8uCd+z_ccmLO~|6g1C zKRf$>cFzBT;{Q*c{Qvv+|Mui@8AEUNB{5H z^Z(lQ|BWsG*R1=0^XC7Hm;UeB_kY%$|HWnhPo4h%`SbshlK+z?{l9nr|LfQPPn`VU z)bfA-{{L66{hu)D|EpL3%PRk$JO6**zW;?K{}(O!|NQy?4hh`oCrC|BCAW zU%&n@EBl|5_y6I;|LZsYKYZl><;(w9uKvGe+yB<~|9}4cZ*KlyS^59OssFhJ|GT^Y z|Ni~IxcL9GXa7&0`rp<2|Ki2}IXVBQPyhep$N%j+{(t}R|HzU5H8ubD?)`u5+W*B% z|KGd!zqj{)N7w)M&j0V<|9|!Re@*@WnX~_I*z|wSy#G_D|9|oFe_rALp1%KY-v0mo z{r`i9|99>FKWXa!XV3qC`t-kV!vFlD|A!9$pE2uyRqg-Qj{j@dF)%Q&XReygz`($m z&pI$LyXT-hvT&DR0akH)e_f; zl9a@fRIB8o)Wnih1|tI_GhG7 z&+;+aJbCf#>ErZvMn=b=Qf|)fj;02QD=dYaleTz!d7eIT>g4I}&d#n=&OQMFDvtXn zZCcT_O3G!i#2LX?DlB{3deoZ5&iwwg=+=aU3yQZGVr^^Lm@9rT`Znx1c%j4N@3BQ{ zJj>Fa&50447Iw`nL0I8og7=&{>uPpScXbK>i;;{Xd+BeO$6?!6gQDaBI zloi^`8E!OW9Qu`f<7TpIV@XU6lfI6>%8}!SDr${94<@KAxi6M6S$c`Y27fbMf2cJ)ujji#6&SYUN%M0sDbmziK3K=U6Z&};+FE(F^jBrU<-L1II-`3gH%Dt z^+&-$?f#qgG3GcZ+nM_QP{~$Yxpm>pGeUb@8KUa8JPMuG7rcx&jN#{&$HomaIkuOS zIQhJKrMBJ9=+mp0PhY&wm&z6IX$o{OVPJ3)aNP7|Z)p_+0|SGntDnm{r-UW|U*Cbl literal 0 HcmV?d00001 diff --git a/src/app.rs b/src/app.rs index 43a5a0b..a9238a1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -18,6 +18,8 @@ pub struct App { pub settings: Settings, /// Derived vault key, present only while the encrypted vault is unlocked. unlock_key: Option, + /// Stack of deleted profiles for undo functionality. + pub undo_history: Vec, } /// Snapshot of everything the UI needs, containing no secret keys. @@ -42,6 +44,7 @@ impl App { vault: vault::load_vault()?, settings: vault::load_settings()?, unlock_key: None, + undo_history: Vec::new(), }) } @@ -88,6 +91,35 @@ impl App { } } + /// Undo the last profile deletion, restoring the profile to the vault. + /// Returns the restored profile summary, or an error if there is no undo history. + pub fn undo_delete(&mut self) -> Result { + if self.undo_history.is_empty() { + return Err(AppError::config("No profile deletions to undo.")); + } + let restored = self.undo_history.pop().unwrap(); + // Re-add the profile to the vault + if !self + .vault + .profiles + .iter() + .any(|p| p.public_key == restored.npub) + { + let stored = StoredProfile { + label: restored.label.clone(), + public_key: restored.npub.clone(), + secret_key: "".to_string(), + created_at: restored.created_at, + }; + self.vault.profiles.push(stored); + // If no active profile, this restored one becomes active + if self.vault.active_profile.is_none() { + self.vault.active_profile = Some(restored.npub.clone()); + } + } + Ok(restored) + } + /// Protect the vault with `new_password`, re-encrypting every stored key. /// /// `current_password` must be supplied when the vault is already encrypted. @@ -254,6 +286,7 @@ mod tests { vault: plaintext_vault(), settings: Settings::default(), unlock_key: None, + undo_history: Vec::new(), } } diff --git a/src/main.rs b/src/main.rs index f4a6768..bd60698 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,12 +4,12 @@ use std::sync::{Arc, Mutex}; use nostr_manager_backend::app::App; use nostr_manager_backend::errors::{AppError, ErrorKind}; use nostr_manager_backend::ipc; -use nostr_manager_backend::profiles; +use nostr_manager_backend::profiles::{self, ProfileSummary}; use nostr_manager_backend::publish; use nostr_manager_backend::relays; use nostr_manager_backend::settings::Theme; use nostr_manager_backend::signer::Signer; -use nostr_manager_backend::vault; +use nostr_manager_backend::vault::{self, StoredProfile, Vault}; const USAGE: &str = "\ nostr-manager-backend [args...] @@ -476,3 +476,65 @@ fn cli_info() -> Result { } Ok(lines.join("\n")) } + +/// Delete a profile by npub, moving it to the undo stack. +/// Returns the deleted profile summary, or an error if not found. +fn delete_profile_direct(vault: &mut Vault, npub: &str) -> Result { + let pos = vault + .profiles + .iter() + .position(|p| p.public_key == npub) + .ok_or_else(|| AppError::profile_not_found(npub))?; + let stored = vault.profiles.remove(pos); + // Clear the active_profile if it was the one deleted + if vault.active_profile.as_deref() == Some(npub) { + vault.active_profile = None; + } + Ok(ProfileSummary { + label: stored.label, + npub: stored.public_key, + created_at: stored.created_at, + is_active: false, + }) +} + +fn cli_delete_profile(args: &[String]) -> Result { + if args.len() < 3 { + return Err(AppError::config("Usage: delete-profile ")); + } + let npub = args[2].clone(); + let mut app = App::load()?; + let deleted = delete_profile_direct(&mut app.vault, &npub)?; + app.save_vault()?; + // Add to undo history + app.undo_history.push(deleted.clone()); + Ok(format!( + "Profile '{}' deleted (npub: {}). Use 'undo-delete' to restore.", + profiles::profile_label(&app.vault, &npub).unwrap_or(&npub), + npub + )) +} + +fn cli_undo_delete() -> Result { + let mut app = App::load()?; + if app.undo_history.is_empty() { + return Err(AppError::config("No profile deletions to undo.")); + } + let restored = app.undo_history.pop().unwrap(); + // Re-add the profile to the vault + let stored = StoredProfile { + label: restored.label.clone(), + public_key: restored.npub.clone(), + secret_key: "".to_string(), + created_at: restored.created_at, + }; + app.vault.profiles.push(stored); + if app.vault.active_profile.is_none() { + app.vault.active_profile = Some(restored.npub.clone()); + } + app.save_vault()?; + Ok(format!( + "Profile '{}' restored from undo stack.", + restored.label + )) +} diff --git a/src/profiles.rs b/src/profiles.rs index 627aa03..fd5f9c4 100644 --- a/src/profiles.rs +++ b/src/profiles.rs @@ -403,4 +403,25 @@ mod tests { assert_eq!(profile_label(&vault, "npub1alice"), Some("Alice")); assert_eq!(profile_label(&vault, "npub1ghost"), None); } + + /// Delete a profile by npub, returning the deleted profile for undo. + /// The vault must not be encrypted, or the key must be provided. + pub fn delete_profile(vault: &mut Vault, npub: &str) -> Result { + let pos = vault + .profiles + .iter() + .position(|p| p.public_key == npub) + .ok_or_else(|| AppError::profile_not_found(npub))?; + let stored = vault.profiles.remove(pos); + // Clear the active_profile if it was the one deleted + if vault.active_profile.as_deref() == Some(npub) { + vault.active_profile = None; + } + Ok(ProfileSummary { + label: stored.label, + npub: stored.public_key, + created_at: stored.created_at, + is_active: false, + }) + } }