From 412ed0605e4516430e78e5fdbb53fff3ca52f4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 5 Oct 2026 14:41:44 -0400 Subject: [PATCH] wip-devtools --- key_value_input.go | 88 ++++++++++++++++++ key_value_input_test.go | 65 +++++++++++++ messages_devtools.go | 11 +++ .../snapshots/darwin/key_value_input-dark.png | Bin 0 -> 9219 bytes .../darwin/key_value_input-light.png | Bin 0 -> 8880 bytes 5 files changed, 164 insertions(+) create mode 100644 key_value_input.go create mode 100644 key_value_input_test.go create mode 100644 messages_devtools.go create mode 100644 testdata/snapshots/darwin/key_value_input-dark.png create mode 100644 testdata/snapshots/darwin/key_value_input-light.png diff --git a/key_value_input.go b/key_value_input.go new file mode 100644 index 0000000..7a0beca --- /dev/null +++ b/key_value_input.go @@ -0,0 +1,88 @@ +package mujica + +import ( + "fmt" + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +// KeyValue is one editable pair; disabled pairs are kept but not used. +type KeyValue struct { + Key, Value string + Enabled bool +} + +// KeyValueInputOptions configure a KeyValueInput. +type KeyValueInputOptions struct { + // KeyPlaceholder and ValuePlaceholder default to "Key" and "Value". + KeyPlaceholder, ValuePlaceholder string + // Label names the editor for assistive technology. + Label string + Disabled bool +} + +// KeyValueInput edits rows of pairs with an enable switch, add and remove. +func KeyValueInput(c *ui.Context, rows *[]KeyValue, opts KeyValueInputOptions) ChoiceResult { + if rows == nil { + panic("mujica: KeyValueInput needs rows") + } + kp, vp := opts.KeyPlaceholder, opts.ValuePlaceholder + if kp == "" { + kp = msg(c, "key_value_input.key") + } + if vp == "" { + vp = msg(c, "key_value_input.value") + } + u := Density(c).Unit() + var r ChoiceResult + r.Element = ui.Column(c).Shrink(0).Gap(u * 2).Disabled(opts.Disabled).Label(opts.Label) + locked := r.Element.IsDisabled() + remove := -1 + r.Element.Children(func() { + for i := range *rows { + kv := &(*rows)[i] + ui.Row(c).Key(i).Gap(u * 2).AlignItems(ui.Center).Children(func() { + name := fmt.Sprintf(msg(c, "key_value_input.row"), i+1) + on := kv.Enabled + box := Checkbox(c, &on, "", CheckboxOptions{Disabled: locked}).Label(name) + if box.Changed() && !locked { + kv.Enabled, r.changed = on, true + } + k := TextInput(c, &kv.Key, TextInputOptions{Placeholder: kp, Label: name + " " + kp, Disabled: locked}) + k.Element.Grow(1).Basis(0) + v := TextInput(c, &kv.Value, TextInputOptions{Placeholder: vp, Label: name + " " + vp, Disabled: locked}) + v.Element.Grow(2).Basis(0) + r.changed = r.changed || k.Changed() || v.Changed() + if iconAction(c, icons.Must("x"), fmt.Sprintf(msg(c, "key_value_input.remove"), i+1)).Disabled(locked).Clicked() && !locked { + remove = i + } + }) + } + if Button(c, msg(c, "key_value_input.add"), ButtonOptions{Variant: Ghost, Icon: icons.Must("plus"), Disabled: locked}).AlignSelf(ui.Start).Clicked() && !locked { + *rows = append(*rows, KeyValue{Enabled: true}) + r.changed = true + } + }) + if remove >= 0 { + *rows = append((*rows)[:remove:remove], (*rows)[remove+1:]...) + r.changed = true + } + if r.changed { + slog.Debug("mujica: key_value_input change", "rows", len(*rows)) + } + return r +} + +// keyValueEnabled returns the enabled pairs with a key. +func keyValueEnabled(rows []KeyValue) []KeyValue { + var out []KeyValue + for _, kv := range rows { + if kv.Enabled && kv.Key != "" { + out = append(out, kv) + } + } + return out +} diff --git a/key_value_input_test.go b/key_value_input_test.go new file mode 100644 index 0000000..c100bb8 --- /dev/null +++ b/key_value_input_test.go @@ -0,0 +1,65 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func kvTester(rows *[]KeyValue, opts KeyValueInputOptions, changes *int) *ui.Tester { + return ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if KeyValueInput(c, rows, opts).Changed() { + *changes++ + } + }, 600, 300) +} + +func TestKeyValueInputEditToggleAddRemove(t *testing.T) { + rows := []KeyValue{{Key: "page", Value: "1", Enabled: true}, {Key: "debug", Value: "true"}} + n := 0 + tt := kvTester(&rows, KeyValueInputOptions{}, &n) + _ = tt.Click("Row 2") // its enable box + if !rows[1].Enabled || n != 1 { + t.Fatalf("toggle: %+v", rows) + } + _ = tt.Click("Add row") + if len(rows) != 3 || !rows[2].Enabled { + t.Fatalf("add: %+v", rows) + } + _ = tt.Click("Row 3 Key") + tt.Type("sort") + tt.Key(0, ui.KeyTab) + tt.Type("name") + if rows[2].Key != "sort" || rows[2].Value != "name" { + t.Fatalf("typing: %+v", rows[2]) + } + _ = tt.Click("Remove row 1") + if len(rows) != 2 || rows[0].Key != "debug" { + t.Fatalf("remove: %+v", rows) + } + if got := keyValueEnabled(rows); len(got) != 2 { + t.Fatalf("enabled pairs: %+v", got) + } +} + +func TestKeyValueInputDisabled(t *testing.T) { + rows := []KeyValue{{Key: "page", Value: "1"}} + n := 0 + tt := kvTester(&rows, KeyValueInputOptions{Disabled: true}, &n) + _ = tt.Click("Row 1") + _ = tt.Click("Add row") + _ = tt.Click("Remove row 1") + _ = tt.Click("Row 1 Key") + tt.Type("x") + if n != 0 || len(rows) != 1 || rows[0].Enabled || rows[0].Key != "page" { + t.Fatalf("disabled editor changed: %+v", rows) + } +} + +func TestKeyValueInputSnapshot(t *testing.T) { + snapshot(t, "key_value_input", 560, 200, func(c *ui.Context) { + rows := []KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}, {Key: "X-Trace", Value: "on"}} + ui.Column(c).Padding(16).Children(func() { KeyValueInput(c, &rows, KeyValueInputOptions{}) }) + }) +} diff --git a/messages_devtools.go b/messages_devtools.go new file mode 100644 index 0000000..9276a76 --- /dev/null +++ b/messages_devtools.go @@ -0,0 +1,11 @@ +package mujica + +func init() { + addMessages(map[string][2]string{ + "key_value_input.key": {"Key", "键"}, + "key_value_input.value": {"Value", "值"}, + "key_value_input.row": {"Row %d", "第 %d 行"}, + "key_value_input.remove": {"Remove row %d", "删除第 %d 行"}, + "key_value_input.add": {"Add row", "添加一行"}, + }) +} diff --git a/testdata/snapshots/darwin/key_value_input-dark.png b/testdata/snapshots/darwin/key_value_input-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..9a3c8a34e519aa7728fdb048d6f09c2385815b0f GIT binary patch literal 9219 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/key_value_input-light.png b/testdata/snapshots/darwin/key_value_input-light.png new file mode 100644 index 0000000000000000000000000000000000000000..e89ea5cb0a26ffbfe093e2cc7860c0ba49ea2aa3 GIT binary patch literal 8880 zcmeHtRa9KvmTnP4f(Q2s7A$yhNnwQtcL?qf+$FdKf;$BS3+^FQ1qu?}3JYEk+}*wF zKmX|-{dl_jKHMJn?y<(&Ywd@*=A7R*=dvCBPDKs}ivkM(0N}`jrPTp|M_j0OFUBKO z?K7lg2>`tGl9zs~>7BEi>!bN%>Z08lTd~57EkXY*XEr7#-$R~GD+=pt{0p&}B1f2bLt^#)!IkmK6n-toK(XWRlS?F2HEb zo08!1V)CK)VxnEho{ClRvE`=22%!V}jfZ=G1c87B3GcwK7>Ee5FvG+CcXYR-Kx07T z+qu;ZE9;}`v{)I}mQPVYYD^xz4GpPZrkc>J+9hdhjrlV5PgT54B+kqNWp$v{=3y{qkjJ$fHh__5=bN=KVCKn^Sj`bj_- zpYS0pjGkdHE@5F*7C={#xS#-&b@)%X7HI)n3*+=%xc(Ey2f!i{%4q3rr-|h0F(!U~ ze%F?qX;8woXuvi5%a`T#_38!hc9Ad%PR_jPaRqht*PWd?Oy3qLGnHt_2|O{v@l4FBvP@ z$t-U(1}wHA)yq<*uNEvUFkTU1r3#1maYuu=nSNr7b>xrQCMjStLmmULDI;cObG&iK zGvy1%qYA}q36kQ{cY4Wjof)*ybrl&oO$bam=+XxeIZL)q~8PBaY&(2!J2dSFPNEpq7#nsnw zby@e~IjQgHSM3IC7&8;oV?erJ-?$qlfc?Ud?Q)zk9*&4(#wp6n2BBUy@fSz9wG@s! ztDRWSZ?t&*L{WqGCayR+K}h)Q<$5xwsx9$L0py}>#oL}|r-n&I!sOA?^};rHWZh5f z*+eAZl8N9-Vf^J41Qa^h+(@>v-^O+2U(O?a`(!w0Nsz}{SD!1;aJNv1hVolb3%3KA zk~I@EBzahQY8uQ@iU$IFitkUJSQ8jR`9^*2yO)M)Lq1bxs=6Vcx zKd`>X{KZ_ip#+9-Q&v(kZaQWj5=ETvnTla!PgWYuRPFSj4QQ{euV!Z9y5?(Q1vfOTL~kZ1CPt#=*Q>ie+=u>tyOWbs;_b&d+}CHY z-BbyMLd#MJ@^l75Z}8YyS+k%t6nl-PV|fxmN4uG>m7IjpVx;UWEpAf?=dDgm&qM+> zAP~kkg@_-?lZ&U?Ux5`Vs5iOwAhIMRer;HfZjtoAXuJ6Hn{BOccm7IIpTcXcJec_7 zJ-KM$VH!T*`-i3F{`RK4pauK0ZDJ(M>*D13>IpBu#78lB1Z+5$H<|wqAyI>h0N{PV zCWE^=OsFi*Vd9zv_Sx6HS#SBv?VCh}2Vp{cGZ~MCW!0C7f(GX#ADZA3BCpdNZRKxv zd1L6^lA66F^=916%?npo8<|xh5LK&-y*YG%@5Po8oo3A8;nE7t zp9K#r&syS-UL8_g?l1KoF82f=aKllppb>ci+n=|x3`hZzU5{$wHUZ}eG; z%8z3z`c`pCu?jB-?d44SLoCrdM$Xe zbN?)H3p{iyzev8fqeKh7wto(An(w_h{6L=wE{D%5E_@4k0XV$M#0r1OzXp4Z)jKwr=k3?$5Bfgz{2e5|Koiw6U{7kp}~uQMp`B zhI`JYn%hxMNGN9bQ#)X*Uq3}F4In>Lh4S02e?hc-P*WVBJ&W7-VqZ)qysIP>9j$#z zD)Q@3E{Dr^;h>|-BMq$Zq(tE6%*i;~;{|=>${#Kl`ww5~-6E>?zRM@?MFv*+n+M)2 z<9%*v8VKn%Y9p6{CwL}$qhnW{ECuDn(Er3Zb}q~;dmIc!B+r_F0SWlGK?tk^CFH7H z?iK*hLgnPD%TTA0&A`N@h)Sn;G{WA8_+O*M0=j>h?`I3}`}#sV)q*rM=pD&wDmBHq zd3fINaG};!Qc}O3LQXzmE}xWj+@v{Jvs*i?&kb73GWDY988JFhQ@^x=Th_Ig4$z`K*SE%TJ!T|=|a z^+t$(h>ZjdU>^xXr6%-DTnBE;GyTFTDLq>~-rP4z(9F!O7Pk$QP?+aldDij(eKIT~ z3%A@$*NBJbNi2yUp+QrWv>B^M=T|lNOeGSc#(mT@g@z^`I`r(f*vFh*QMZ$hPLhyD ziARupNZbUc;8D(JSYTyWxaWg=D)Nf@f!6G9&({szcFx~L!v8X=XGKKWl7f=5>>*ol zbaR%Nt~0Ye2WQ?pZx4yZJ?r71n&di)qr}8(O3e zDj*x*BB7i_H-Wmh)Yb~x{8oL@&D*t{-c%^2GFH(03uM~y%~9m!_?VhW`AM*Nz|H3N z_WhBZVJuwS{Dyf*w~6N35HyyX1Vah-78S3PoB8H|+2YPErjgJjN*4tx7_p(xlGiJ& z8r>-Ff;QH- zdEm9Rmq#nipJ~Oejw=EIe>MdfKPG#>I<7sAx#Pb*UV~>0_MI(6(d}VmDp?Jg{-(r6 zO_P;EmC%Mq2g483B9s|#hI!-R=hX7KozadwEO`!@gg?QGHlmxIN{{R*_&tPEYQn>- zN8V<0Nb1#L(%Q{ZoJ9CV4;0t{7rGu~RW& zNwNqb@5A784wY#j7s|s^$KFY=bzI|(8Z&x0RNT(L_Rc9b%AyQi-j!y|V zF3MS%2s1p`)rV^5=jF&&vnc`Wy8=n3?KMqO~z;u7@JT@+H!vqG*%;~ZmIc5VBfZk?nS#*+Z8&L zUxt6JYdhq*`%ZPy6y<)4_1bFJGZ_5ROp-NK30841-@+>_%$f*h+TS)RIR5$NmR@f8 z`uf_Er|g{|qkW?;J5^8Ju4qDbdbMDw!jQ0H^E%CPT^)DfsG!j6p`ohMpsXV&FKbDF zJr}lOF(hIzHKzuBBYBv@J=E0uH|uES1Dj@r>zVH_=fImUdpKE3QO<1TwlaWciZV*@=n8vijV&yQq|YY8;1eRC$MSE=Q|GYyZgGfh!9Y(;N6EE7<)R6;8jI0nIsmi|)2O!GN9+PKx1ABp#NWYBEmQiujKHKI+= zmW-&8pOG=z=}MzlH!b%v+j;)G=@$@ggy{u|ua_Y9FD`7(PiPDPOvy(HfPd-LXC2(h zwfpF-EtGVKBsSbsH!PzE7;$BBA+THjEJ>Par}c~*8FH&i5?kE7K_AWjq5l2C8u|v4f!|bdc6vA3M#Jb78VgY34&j>gnI9%AsE8J_$M5l_<4LgI6KeJq3P6< z3AGVn{0x;qQyfft@DF{de^tkS(Hc>-u_WT|zz-|PW|76QLc1~N3MNMn&-z-i!~#>; z78z);;%~iCmW|EjI#XZXALTpFZ2?xT>pfq#oVhe;uWVn-M%{`1tu?H&boVs$vA5>c zS)l?QzW-geNW?SrO#o=H8DGkOtnx1p5tg`Ia>uPO#baZQx&1sjWZ7Gg7KDmA0;JwFnV098ieTRf^q%% z!Od-BjlbTLMVQvys!WMc_4LuNxwjWo55JBu{kgW}{(h9VNaj3}Hn34-_hZOL>=uBW zifW!RfNsT>pZde*DqgZ@cz@{~*5VyCls1G8BP&)getg1KxY}a`twN;Q!)BY0%Bxwg z$H)>KyPJ@Zi?Aq3PuJRujgF5)%3<^k_(Aa1{o8_Rmfv+C^7zRjFoAH`se&p39utGm zTdcyr7Ipz*g!mgCgIioCzS>U^fQY!UMB?1zge`N%gdCT76EJ{%ebIYCH-1hkfyw)b zDh|gWN2j~H0eG4MqdQy*2o!M)BHY{25x?0DA|QwlDly67q@@^p0n*o>9TxXvdcFTn zD)hWK-S{2>cXmcX*ZN8!g6^C7E1|P&={x)a&iCeX-<)%<<@@jN>ca4o`!v;#K=z7r zmzUo`1gY7jB+=5RIU~2I#cyi5R#P*)Y-)%8BESK(2th;^fSzRvv|xFiE-yO(jf~SKjurZZWcB zz=j<8jyYZPC`sHZt8MI33}FT8DN;oM$_?L=O;?# zi_eH&snVt<4+LcS^mJ8+l#>Xg1fk;d(_W2!Au*go{yOaN^t2bT_-A>U$6os~>}Xs; zF<$pIeVCvL)nkTnj)4^k3}ChKU2EM@cQD#rIKE3o5-V$YWyPSP0vzVYxPT-l{^M+9 zKLv+Zx3!t>q^a85RW~*^VgMR!2N)x9(^FHk zAKbIrzU`b@!}z2N-3?E#-Rjv@aUf>p7~KAswH=}8S~@!%bXPYwde_4!dwZ;P9rv`> z770KQo5~Z}Nk1=7S_YwK;7B}LB>X|r)%EB4(A@YqPqV|&3(#N&!5h{;c|P>1Lj7ys zwu+%{*2g0qW7E@@1oQ!qd&D+&b~N;TCtR)TTklkxW<5W88_aP4!zLAX_V-_hjo9#U z52V}iQ>^)zMUr7RhvT>URir*E(5~Q!dsA`F*k{@%Q9wW;R3WP!MiVKIdx#15Y2X** z;I;7ma9YnuQN_d!v|Ma+-WZCF$`CPVDSFQGdf{M0;CLqh7LJdD6ZiD#+~Cklx_0=j z`&xa*lrk|eCg$AN4-bbcl$3^sgo48CzHHpY!^6YaSbwc6irVZ-wYprG*Jea=p#DB9 zYoJi>vpR%1%-yX&h_!jN7uwx%My@0By3s9I24`kw$H3sBcTkBGBrQ9PgP93^%?m3i zQ1fZCAKhw9=QI)WsaL9z$UapmXCL^P)7#sE4(FUPti79`F*!NLQrF;dYC?`}{Rj!^ zwyx|G_T25fIw4KcDGf(N+1S}>8-JPx8XM2bCx_HeTGEQS{wUubEKilEp$RBe%KR#- zim|m0P9kc0)o;Z`L`6Z>^hzO#p}0Uq7Mye{p_eA|UBZf^o?YTvEUmu&_zTjHcd|sk zO*}iuIfaDV`t2liH)q~qt=!Md*V(I8EV~(c8@JitpJ!q~MK9lKW|o$gu2UYi6_Re^ zQ&!y;I92^KCr5Q2iCkV;%%Txi#;9YF5D*u?8X{)HiI|g$z#c{{wqx|Qp@L0%p*yED zEMyu03dAGEO*m|GsVKvYQ>a%Do0ZH>*APoS;N|l?Hv@zoN1>Ks^V^YiOUd zE*d*L;C8I`J@ViBxE!vS&gOVwF)pWvX+Oo*#GJdnPST{mw|yN{yre=w>3q7gVS5sZ zu;KgG)a2t^|9#lg1=(Z2tiw>k+9bEVe17g(I67UbN?ZI9-)zJE@Z%n4@c?}W_euzE zz=@VyX?)&DJh{MI+fBWr!_5lO#yHt=RUdC=JGT@HA9fdRxiS>_q3+~Gqvnt`X-t)r zKmSo}4n`9WfBTA-0Koijl`Y%Hef2A$>h!po@d7LPvAOxNEW?_+^?vbgA**Gh6NcBy zWduF3mb|#CacQqTCtsHcYWVy?|Hho+G+v)9BL2?T!RL<^m3v*G7q?5v=^@gM#+6fK zJt0HN3^!Vc;je~kxe$ZoqyL@7l_nv}u7>}&v4&k3K8Q*%sKL30TiMu@s$JDUzeTr* zjzlK^Rjg!rIn)Wv0P#e=V!eOEMU=x2esHfstXsaJ@kK?W=j=1QQT^h1i9O& z+0w5_4NJ(Owmpxjopz8{_!7yPM-$|bwT30-SNh?1J^D6`|4N6Nj$9b3>O6PQzoH;e zv-88R<}fyI%f;2j84BHmdau}13F!D(RTs3z2J#R}$$6Q-o%s~?9wkln(!oP*tpyqR z%Z%~FIJMkj%GHZTN%)7otaNQ%Md_NX7y^Mh=d37f(PF9i5L^WH4Y<$d9Jc~s{_WCk z0WgBK>8d_c%`~~yr=U*J^;;=Q6{KkCE6xGs>xDD93+Rm!V;HtRdQEOGeOSh6E6&s6 zfo8`~Dlzn7N_mtiP28!Tz6{lnefdbQx@jp<(tU|-QfkQI)=gMK3tPIC)3ndYsxxmP z=#C`JzJemWB8`85_;#dSJA7>3di#KXbI$#d+LJvLy8obst$*n1(tzr8L$vm{fT-%NeUraurq>4?S$8F)$6OwgB>_6M_miZ!*O> zduNbwZ~Pb*I?!-YTSB<8j} z8#*?ZJJupGY?%Kd+Fz=yP!?Gyb^mmYErZ`q&m5auN1~UA>M6N~G%fD(MJuZkl*&`x zc=5b{gO{4Kl!X7&ck^ng>;2dO^_w|Kcvm#M^tQw|wM`1y*Q%5K)^~}_T z^Sh6Vi~mzaQt0u#J)+M_{)*-5i z0Pzb#CI%&NMgkX5xe-%7DG5y5lLd`sRBzod6e>n(r8ENocjBF`0$(yhLjBju{>z&+|ej8q-MyY0Rb~6iOt9PxT8eBbY zEu#HFOabe>$3(#~vD)`!xquCOc;88UsUgk?bsMp8s)b4?b5%)_&1CH8D0M` zcUG|ek+T00)c@Zd;Q!0`fe-@Y{xjqL7r_~mqPyb;MBNm&4ZOLnME!{Zke5-Bt_Hmi F`8URs!+ih% literal 0 HcmV?d00001