From 1052f8111ddeedff07fc8b32c3b7abbf5258eeac 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 15:00:02 -0400 Subject: [PATCH] wip-devtools --- json_editor.go | 146 ++++++++++++++++++ json_editor_test.go | 75 +++++++++ messages_devtools.go | 5 + .../snapshots/darwin/json_editor-dark.png | Bin 0 -> 17115 bytes .../snapshots/darwin/json_editor-light.png | Bin 0 -> 16593 bytes 5 files changed, 226 insertions(+) create mode 100644 json_editor.go create mode 100644 json_editor_test.go create mode 100644 testdata/snapshots/darwin/json_editor-dark.png create mode 100644 testdata/snapshots/darwin/json_editor-light.png diff --git a/json_editor.go b/json_editor.go new file mode 100644 index 0000000..eb66d78 --- /dev/null +++ b/json_editor.go @@ -0,0 +1,146 @@ +package mujica + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "log/slog" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// JsonEditorOptions configure a JsonEditor. +type JsonEditorOptions struct { + // Label names the editor; empty uses "JSON". + Label string + // MinHeight and MaxHeight bound the text; zero means 160 and 400. + MinHeight, MaxHeight float32 + Disabled bool +} + +// JsonEditorResult reports this frame's events and the parse state. +type JsonEditorResult struct { + Element *ui.Element + Changed bool + // Valid reports *text parses as one JSON value. + Valid bool + // Line and Column locate a parse error, 1-based; 0 when valid. + Line, Column int +} + +type jsonEditorCheck struct { + src string + err string + line, column int +} + +// JsonEditor edits JSON text, formats it and points at the line and column of a parse error. +func JsonEditor(c *ui.Context, text *string, opts JsonEditorOptions) JsonEditorResult { + if text == nil || opts.MinHeight < 0 || opts.MaxHeight < 0 { + panic("mujica: JsonEditor needs text and non-negative heights") + } + label := opts.Label + if label == "" { + label = msg(c, "json_editor.label") + } + minH, maxH := opts.MinHeight, opts.MaxHeight + if minH == 0 { + minH = 160 + } + if maxH == 0 { + maxH = 400 + } + k := Tokens(c) + u := Density(c).Unit() + var r JsonEditorResult + r.Element = ui.Column(c).Shrink(0).Gap(u * 2).Disabled(opts.Disabled) + chk := ui.Local(r.Element, "mujica.jsoneditor", func() jsonEditorCheck { return jsonEditorCheck{src: "\x00"} }) + locked := r.Element.IsDisabled() + r.Element.Children(func() { + jsonEditorValidate(chk, *text) + ui.Row(c).Gap(u * 2).AlignItems(ui.Center).Children(func() { + ui.Text(c, label).FontWeight(600).Grow(1) + if Button(c, msg(c, "json_editor.format"), ButtonOptions{Variant: Secondary, Disabled: locked || chk.err != ""}).Clicked() && !locked { + var b bytes.Buffer + if json.Indent(&b, []byte(*text), "", " ") == nil && b.String() != *text { + *text, r.Changed = b.String(), true + slog.Debug("mujica: json_editor format") + } + } + }) + in := TextArea(c, text, TextAreaOptions{Label: label, MinHeight: minH, MaxHeight: maxH, Disabled: locked, Error: chk.err}) + in.Input.Font(theme.MonoFont).FontSize(FontSize(c, codeSize)) + r.Changed = r.Changed || in.Changed() + jsonEditorValidate(chk, *text) + if chk.err != "" { + lines := strings.Split(*text, "\n") + if chk.line >= 1 && chk.line <= len(lines) { + jsonEditorExcerpt(c, lines[chk.line-1], chk.line, chk.column) + } + return + } + ui.Row(c).Gap(u).AlignItems(ui.Center).Role(ui.RoleStatus).Children(func() { + ui.Icon(c, icons.Must("circle-check")).Size(14, 14).TextColor(k.Success) + ui.Text(c, msg(c, "json_editor.valid")).TextColor(k.Success).FontSize(FontSize(c, theme.CaptionSize+1)) + }) + }) + r.Valid, r.Line, r.Column = chk.err == "", chk.line, chk.column + return r +} + +// jsonEditorValidate parses text once per change and locates any error. +func jsonEditorValidate(chk *jsonEditorCheck, text string) { + if chk.src == text { + return + } + chk.src, chk.err, chk.line, chk.column = text, "", 0, 0 + dec := json.NewDecoder(strings.NewReader(text)) + var v any + err := dec.Decode(&v) + offset := dec.InputOffset() + if err == nil && dec.More() { + err = errors.New("unexpected data after the value") + } + if err == nil { + return + } + var syn *json.SyntaxError + var typ *json.UnmarshalTypeError + switch { + case errors.As(err, &syn): + offset = syn.Offset + case errors.As(err, &typ): + offset = typ.Offset + case strings.TrimSpace(text) == "": + err, offset = errors.New("empty document"), 0 + default: + offset = int64(len(text)) + } + before := text[:min(int(offset), len(text))] + chk.line = strings.Count(before, "\n") + 1 + chk.column = len([]rune(before[strings.LastIndex(before, "\n")+1:])) + chk.column = max(chk.column, 1) + chk.err = fmt.Sprintf("%d:%d %v", chk.line, chk.column, err) +} + +// jsonEditorExcerpt shows the error line with a caret under the column. +func jsonEditorExcerpt(c *ui.Context, line string, n, col int) { + k := Tokens(c) + u := Density(c).Unit() + ui.Column(c).Padding(u*2, u*3).Radius(theme.ControlRadius).Background(k.Background).Border(theme.BorderWidth, k.Border). + Label(fmt.Sprintf(msg(c, "json_editor.at"), n, col)).Children(func() { + spans := []ui.Span{{Text: fmt.Sprintf("%4d | ", n), Color: k.TextMuted}} + toks, _ := internal.Highlight("json", line, false) + for _, t := range toks { + spans = append(spans, ui.Span{Text: t.Text, Color: codeTokenColor(k, t.Kind)}) + } + ui.RichText(c, spans...).Font(theme.MonoFont).FontSize(FontSize(c, codeSize)).SingleLine() + codeMono(c, strings.Repeat(" ", 7+col-1)+"^").TextColor(k.Danger) + }) +} diff --git a/json_editor_test.go b/json_editor_test.go new file mode 100644 index 0000000..ebed49e --- /dev/null +++ b/json_editor_test.go @@ -0,0 +1,75 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func jsonEditorTester(text *string, opts JsonEditorOptions, last *JsonEditorResult) *ui.Tester { + return ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + *last = JsonEditor(c, text, opts) + }, 600, 460) +} + +func TestJsonEditorFormatAndValid(t *testing.T) { + text := `{"hall":"east","guests":[1,2]}` + var r JsonEditorResult + tt := jsonEditorTester(&text, JsonEditorOptions{}, &r) + if !r.Valid || !tt.HasText("Valid JSON") { + t.Fatalf("valid: %+v", r) + } + _ = tt.Click("Format") + if text != "{\n \"hall\": \"east\",\n \"guests\": [\n 1,\n 2\n ]\n}" { + t.Fatalf("formatted %q", text) + } +} + +func TestJsonEditorErrorLocation(t *testing.T) { + text := "{\n \"hall\": \"east\",\n \"guests\": [1, 2,]\n}" + var r JsonEditorResult + tt := jsonEditorTester(&text, JsonEditorOptions{}, &r) + if r.Valid || r.Line != 3 || r.Column != 19 { + t.Fatalf("location %+v", r) + } + if !tt.HasText(" 3 | ") || !tt.HasText(" ^") { + t.Fatalf("excerpt: %q", tt.Texts()) + } + _ = tt.Click("Format") // disabled while invalid + if r.Changed { + t.Fatal("formatted invalid JSON") + } + text = "{} {}" + tt.Frame() + if r.Valid || r.Line != 1 { + t.Fatalf("trailing value: %+v", r) + } +} + +func TestJsonEditorTypingAndDisabled(t *testing.T) { + text := "" + var r JsonEditorResult + tt := jsonEditorTester(&text, JsonEditorOptions{}, &r) + if r.Valid { + t.Fatal("empty text is valid") + } + tt.Key(0, ui.KeyTab) // Format is disabled while invalid + tt.Type(`[true]`) + if !r.Valid || text != "[true]" { + t.Fatalf("typed %q valid %v", text, r.Valid) + } + tt = jsonEditorTester(&text, JsonEditorOptions{Disabled: true}, &r) + tt.Key(0, ui.KeyTab) + tt.Type("x") + if text != "[true]" { + t.Fatalf("disabled edited: %q", text) + } +} + +func TestJsonEditorSnapshot(t *testing.T) { + snapshot(t, "json_editor", 560, 360, func(c *ui.Context) { + text := "{\n \"hall\": \"east wing\",\n \"guests\": [1, 2,]\n}" + ui.Column(c).Padding(16).Children(func() { JsonEditor(c, &text, JsonEditorOptions{MinHeight: 120}) }) + }) +} diff --git a/messages_devtools.go b/messages_devtools.go index abe4d0e..5a02788 100644 --- a/messages_devtools.go +++ b/messages_devtools.go @@ -82,6 +82,11 @@ func init() { "json_viewer.copy": {"Copy path", "复制路径"}, "json_viewer.items": {"%s %d items %s", "%s %d 项 %s"}, + "json_editor.label": {"JSON", "JSON"}, + "json_editor.format": {"Format", "格式化"}, + "json_editor.valid": {"Valid JSON", "JSON 有效"}, + "json_editor.at": {"Error at line %d, column %d", "错误位于第 %d 行第 %d 列"}, + "alert_list.empty": {"No alerts", "没有告警"}, }) } diff --git a/testdata/snapshots/darwin/json_editor-dark.png b/testdata/snapshots/darwin/json_editor-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..8c843a6ffecd8f88e77321e049871e29d54c7db2 GIT binary patch literal 17115 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/json_editor-light.png b/testdata/snapshots/darwin/json_editor-light.png new file mode 100644 index 0000000000000000000000000000000000000000..59b92e9f740e4d9d1b6be559b383bf97694df598 GIT binary patch literal 16593 zcmeIZbyQqS(=SW{L4p$`xVr`k4uRnAlOTcM?rsy@-QC>+1RVwq?yduY;O;iaH|IR( zoaerOd~02KudH?Nu39~NPtUIIuBz_(?XKBjDoWDmuZdp6!NH-+%1Efe!M)^wgL`3) z{1Wy}U$K@b9NdRgSqU)>_sm0xkB0gUY5&=Y;!D{Nu_3342uXq8!~#6M;b;Xato-;P zi)qfnIyWj!t|iVTdRF6y*uwp;S&%u!hzv>4^jzb#8iYT)al_kO*`ne*mZW^#2MWr3 z2Fk8E0~C*DUcr>pg5K$Rr>ibC2g+>YJ3|tzYDAV~>!7sF589EOBJYOGWyx>~J_A zUdq6sP`se|h7kLI+Qn2Aa(pEC12Uzv(M2;9;*;XY{llWB*u6K-SiYwKU)xP%@Ze~!xD-?OxPjx-aVstfgnnv2_)zx9M@y0Kp zYr>4T^9@!xS&yYI<^$t&Qm8@V{uJL?9ZrRSkhpgo92|7(SO|VQ{XVsBr$0{4l%Z_z zYT}0qqv)R{CA1FG=--PRU15YOb8hg}x0oMHYlelPOwtoRE<1YmM-x~dW%*Oc6it18 zb$2ii7|Y_y2(^9>6&CgUP?DhvvTM?1ixC;zU6+2J&1c~O9R(_66q|^mQwGxML2t-O z-ZpnP{#4bvwgHQT*g!h(R~~b8?|zz(n7w&JTv&^Q?;*6RFMQjqR_6ZeDW|xkB0W7l zhP0+;sqLy}b{tF9sNgDYP2Bw-5mA)gRr_acRgSh)8<7qeDw8>BhqT^$jv5 z_u!j|m>57s{lc8NKLs~8_myt*TQr23{mPAvph>(kFcjmh*)~5&4;~&c2XU+Ebrg7u zCP#x4Qm<2{Qu(R2vSV#}Wra!t0nBE$SVv3JA0)osClxU~`{s3!rbU?>i+*})>S-4e zz2Kobp8dV&^%NHTj zNvWwE#n{-7o2%iesj1h|W3}?qPCFw?GO2YY!{owb?8~T>ffFeJec;9lzfQJThe4N6 zjM*<0$mFhzTh6#yZKhh8+9+*Ak=kDD+S=qKKM8WGd^mr!p!2S|{BTQt{wu_Q_J)Sh z^-zr`R5$h1=RA zI7+jlX6c8^O?Ah2QfzVax*dDIf|Q*w2DZaAvhCrP`JKpa`|8dt7}iPhz@ zrcvVo#Cne#CZR`}Usza%qP`(aW`|oC8OeQJwIL&`xXl+Giu?Mkx)xTs!4jud_RAwX zR83vI6*RtVP5SZUQw6{WH|$i1J>hZfTeB>Mr1^?82VVN?*V4_lcg;sBH=;{RmIr$+ zsR>+)c`AIQ)dn26#Kc^6)OX$iDeNH4GDZsvrka}zVwv|g-j^e3G0%+`y$4TXe=ZS6 z6Q%XZ=>+wx4uL&XH2T~hnUc=dPZtQgUFgvcw^DA7K__mm{MNhl15Y0lKhsgM=kZvHZV_B%D}!NwJ>@GTes%{d~{4q{i{0- zCOofaW?YOAK|#_8{4xFd))i~Ev5^sjlTiJ(&ia%$CuoBP6D^SJopOD9fpK1L?E*kd zXlb+gQ#HgZ+xvVmIk|CU~grsGvIs4?MvU*Kdz zYGF~1PE2Ns^wdTxkH31+b28uV5UWTLh(!d@%~UH;1>SxwP@q$1*u)=ZY!TARcLTCFzTT8*;h80lOwQ6EmwMxmOuat&bsK5UK@JL(ZtoWir!AuO;!tWW)dvO#oQeIYFYyH@&CN#UjyJ!{%j6!NVv!t+GkEVysnxoC=yTH4eg^>3OGe+WG0`dEaaL|;{ zyh&HA>wmTOBs)v=wg>jCca3N~2y)3T!4 zwFeA1enzn|tE*2-71W)=m)P^Mj7p+CsIRs=BqeLoG{^p=1uZDMUiU#jIHDk{hV&|} zdOj$ZRDs&3X1z>Ul=!TA>G`=PCB?(ZyRI&BgM+OuGjpxOcL401#I)pE#`#vbo+bZlBj?RbmDqe^4g6%J|)!xzjAL(Eeg@rH2-2C;@^n?gM zH_np?b02KV#pcAJ2cx$%`cG@#l|+Zu&V1XnFc-BiSgPxgLHh&qb-Y25mTL6^+09wJ zlD&wBY)&8(hDa>jltGb)Ns642WJuyGK@T;U-0$+r!eeY zRyMDyuD+p5DElp0K_hDzi4pb>F&>dlNpP6grcNS9P5jCDlMJL3WtK8XQU(Zr@BwB4 z`C%4{iu%rdUk5@I2jYtttW>}Jv$QD@uq`1&h6_ad-=4K4B`P0EdYSz-KO`3AUxz!s znAEbwt7_1wl2oZEMUK<^p^b$sAE)73DkDx(m%W0rE+2=DmWWz(7CU6CeR(JkKnKZppc`C?^ zc_c4kP5gBax4?(={2G1ri_+%XuU=5~!#|q;yKduyA>UF*(*zx#Il%Fp|4^(1ad!qT zizrte-r4e-5<2y2nV(!yUq5*#^(V@%*C<*t-^Km$a4KV^EP@bT{c#k@{TK579^F6P z{nuds8!ofq)x4n7Pp2s4)JYI~TlnuQUMj23~wy>+J%(H{`17wJo5U(5MJkM?;1zO5Bi( z9lDNh(HOSg4{`N7YzD;INMZ*R=A;N!0e!{?xpC|QS;1JARyH6VXJtH~fkTU~f9&3_ zxsg%g?9>#~F3zvXiJ~%D6HdKTWGcbXLyEe90Sqe84mIdW0{}Q_xSqaCj(7?0Z)wF} zsxzJ~91*!-1@(KA(84Kl9C`_~kBf+o#z67{2*@lL5RP15Uk?oUM57f` z2i(o;E1wGAGYLO#mk^6cBb%7*@N=db);8DH4vx*W)F%M%&NT&f%zEc0CPGjA+7*S` z&BmWjPZBWiigHMKPE zpms(aBxBivHrCdgph;y4EULhT!|>2h26FP6pFacp`ikw^pYUr2-PgaWR?K^QbNvpF ztDOWuF_`9zGkAf%43wr*g|fuFp5$_&{E1jxovk)=BZO|pD@k%i9gnU#Js!GU)s>Z% zutw9^va}y-ZYJQd8#b;(3|~V=VMQoTtVWp3JR%40)mz|00c@i@6UvS+}zzu zOZ(=A{=vcby%8w_%|hPvyVvND_Pk|YXQ;1Bs}H8!-u!f56mblequVpG*SRBf_oMQ- z<~b&-B|p8AcZ{;@>00ZoJ`K*9d)C~X*5}Vnq$Jj7!ofSu_xJbnQ|c)G9Nd|~V80(e zUT|VqxUY+v3uW|tUhZqz+P3?3>4*@wVTtR!d|y=jg^k^8JV!Z4@P^d2_XI)g)Ln>x zpg%R$eTFqmf(rMwPPz_b;#piA5Lj$tZB6qW5Jk)j8L&TIar^#gm^PLML#Z&A!;~Mw zZ?{uF{~o!vScc462Ju|Kb5Sqe4G9X0l_}hl3T9-qfSJtH^aLN$Qc+cfhuxyC7-elC z+u`sVf0Z;XW1N|dacV+E)c1jy^KSNb2l21vQRC%QAd0ZvhNY!2ujlIR$o3$R!kA<9 z1I6wg!q!(=3*D8t`1lZQZEan)`ALTs$LDncHgWTN=a1eOTSUCoJ6AIkD*a@ZHa1pP zP^*@n5D0Z)g<#90te|*lh5UEuun&t?V{2*v4-Ltlw!R!(5=z zc#!JBpgdfGZklDJoT;Lsyu626yq?o8qSvsMwrbqg=kmC%g$4(OlG|k;mots!@XKIB zlPC{YPDwG{PE{*5y1MZ=iF;X0!#+5ya}Y8 ztd$G-gpHk-cb=G+ZEdJcz-l{^z3~_;Ou}923j*awaQ|bdQi+MNF(HvseeGuzZEda3 znl`dcoe4~`!J9}^Q*(wX0HzJWGzG0MV|^F!vNSle^SsXJ{dWX^2L&m=LY6q08Coo4 zgzoO{{LDjLYwFrhELh02I4EKR2ru(C;4@x4yeQAhbI6+SyE=G&o-EKlMAv3xi{{~J z7#!Tm5%%gcG+Flj^|S0z{p^7jM-~78I5`cMshgc1mlhVzjLqG31sK8yG?|X2=jG*9 ze`;W`T;dM+F*EGqa>f3r#GIuB^59Cawa6GbdL87b5h_9D z_kr*9jsi9dY;0_hF#GBk@t(u@XQrmY%@EbJv>L%#s5(5J|7bp{W#Bky7J8IA!#czI z%<2nr1DfyvCztIq+Ml1qi#ob?>Z!a9c}G{YpTP4EIVcz0W?Phn%jN?ow0R;B@P zB_%z|&F$QMe5WsUoWclZ{rZHoSudfTvd3X)!Th2g>jtR9trJlS-<-a}(n_hdq z^*fphLbW+%gDzih3HMVw<6qxN_& zM|W^=us*q#ujZ8vBv*-(DSR^LHe^t<_VjFB^Y&q(rHzu}8XspbR4a469!{)(!~?Za z8TY>z@`r`GKVzO3E0tjBBr7_46lw>}&JID{F!NmXV_=uP3T>xCi~DuXX6Ao^Fs@$3 zZzwqP@&aIN-KMKvqSVDJMu8R~^#Hzc$u+&}@yxNy9eqmwn!X!K|L+hRQ@+n1zDlUY$ zI60}QubJ+fvhUu>Dk~F=Q;6i~e{xSzD!F=(#4M7Nlc6)QFk!C5Ro~dy?b^Y_)JovQ zkxfxZGWMHAt2JDBjPlQz4^zaKo¬Hd>KSNy*8}ivqtVLq*2Z>P-vvmE}9K=ZJ+Q zC2v+zpPL5RbDNt_X6G~k($f9=%55M0d#0v*^$uFrKRB~w?*WEWmW%Y21)nd{+TZ-8 zw#(ud7C&fkUT0)vj>>?X zMOcJ3XUe!Hyof#RKAza@f~!1FY~ZF)zrm*bv{x28#BjYl2HXxkE}UOd2HE^D9lh6S z`ZN^r%fr1=_1Z%ai`2^O;30do?r2#Y)Y_W()DE6>Z5)dr;6GftE83v??k%zw*M)|L zmMn+T>(T`e?~l0fT(neY0tx^huy+(18co1plVDt8vmjHX1l7$RJ$LC;p{4CIS?;c= zu@y*xDvq0nN4N*$kXsDxS!kO*Z~e$e(KqY?1|F}&;Lsl=`573+2n)R3naBp zdF~#;(D02&Eo!AUiC#$~jrTBoYvMnc&$0016Aho9A(-VG+ahiy!g4EKxOnf98dHVd z(zynF(X0Hb`n8iDav$Ici1(sKIa?kI<+zC*F|}DjY7SjBLOLTf7Aza<#YE3VK2AlD2HX>FBQ{mzmFl;Z^YREvl^PSnT%}quC7ZzAJ11> z>NYn`s;jH{TRulcbsrIeoE+=Xz*oBHHSeB<9&?B|ID z`Oib_n3>paZ{4AHcT+F_-t>>4xU!>VOK1p_jNgbCe=it_MTPqwrFyL}CL`w8{b=B# z!!$Z8G?_(dWL(t5da*>U?8j1vaaEm)D@V;abd_);6gz?LRViqO1;0XB-@bla$JRp$ zncsnGN41{8e#m%gC-|{1DZQdZ881g9%kDpvG4^-B1=@>F$ z6H5wk9njM1V0yaA?06Y(2&0(ikn$@gikHm7$8NHziJRCJ2qd!Pm*;Z(BxWF%nOVv8 zHAav0)XGC^LB$%v8Xn{DnmM8GEFYJ>!yQ1XFS*sTD{aHy>xLMH`*(qpAig;>pONzm zcu6Lk^|M=|s4LeWJ;N6}<3&^Wo+h;D=$Law4!n-@^mJ^w4eM=4|4O8gkkAOrh={Dh zSh_xDt?ap>t}eX)_p~4l4NY@=zn!HeKC&%r;!bm>anV18gh#lzxFyPM#q})84g-jX zXm-q3hZ4DiHi9~ypPz(nGb^l4zF<@STs{rTh>t(nJU1)$VMHC*d8>OoSG6Y?9xey7 z_1@uOqMQzz_U@)*i^kIV=AgP~!Xcy{c$RPMX~JlD_ItKsPEsORT3R|x!Z1$8`o08x zPx$Z5$0fxcCSxYwy?B{!EBDrIZNkO&Xt7D1^P+h0SDcNVlckoMi=TJJ@D^uhwJ%;g z!Fs8TYN&s=x0LK$ou?2FV42=?#^y3C4gvOYj9FNKRj|eTVs4h2&my}0^J9O7Hdwv? z#MrW!P6lif7; zd@aBZ2Pp|Ya9twTTK3Uy>;=1K|5&=!1N3gmW%xvuDajBPr|tsqu@Jn;|kfz#6%~&s=;`ku9ii*bovcSCo$bb8L7xIyz0(s%P-SNsD*O_Q1OJZ?CbG z?5oSmTsK(D!O584gZLwQJt@3XQvg`R#E-XUIZbxYjx+}p;4*Sk9x4gt=+BZ0JJ=)9 zPdgJVqIT~CW{Bi&4O2hS8u~t0qe`Ng!2|tVnyHD+}(`T+; z(MOo0i@|Jhzn%5mY^kd1@R$W6U{AhNJT3SS{o#3!Y?N=Ngmzl;ie>TZAjUtL!NXeP zoBgEZuWvnH{gVe>Bq%IMl`;06>Ov`hH z2e_v|EEN>PqFf23h;O~Wd6^6)feqA4HeiTLl`HF%>OpPvTmYH*!IGxn02^7=@nNX+ z=Pad$mKoK+dga39p2~B zez6oZ$SC6e|95{BnlaMQWAs~dPGkGSX4+-cqOt3S{qC+AGfj1kvX?3$DJ5+F4a69} zPg_PMZ-(AYggy18Jx#13)OZ%`j?-({7$(H!Il+E|Y#8w-V zr${fE=8(XDipztRkU(*9%Dh+=83r0$oYyEp|0$P+`qGqyiRCXI*FFWP6(zV%PsZeX zuB?6@DJkgsIl_^jX_(N}xr^R?DU{G9LtZO6RHGRousiT|W`j*ul85vW$4I5zg)=Bm zT9Te5`?bKkZ}q%C2qYJod^6KdcJl@0L~9ZYMkimsT}rv6M=a8Iu93L@9<3w+!a5ro~FbCb>i7{y6)v3TX=8rnSX! z8KMZ~A;r0u(9yQrI#<@qhjp`b63;ue6i}MTnm_fiL^Ec2kGc^#z9#fIX)3PA88;d5 zcw{9&bU$E~kzQ>wF$t&Uv<`Y`Mi7^oZW!tIk)6vyp>#78wZ2=ghNz3tBidgFpXM{@ zNT!_}`grfOw@c5~5KJiAZ`bjiydXvAi^lJqXfG5U^m+dNYhtcIIXk#~R z(e3C;_LKMRwVBsY1l1ev$kVg*yG5lNII=7+e8QhEZ~5mUVcVeyS6tV2S9(~IJ6H&J z&L>RrbwdR!{~y@LTHvsUo>6ZgrM zVui^px?~PXPL5;Z(Y`qH;N9=8+^tCwr{Wa)EP^duLH_#3FFAr12imRl`3cO&KYek1 z-c5S^w3n4ce~th;&o`YuiFO|&aY9^JAK29jAEb4GIgElvLM4HpF(w9ZN%qat z&MNtu0+04_eh3K|LmaZCH z^*)WA+ylu~^hqe8uhp3b#_&=WzNc+?$CEX=_i?_)+xhI?YA3omn&{4$yl^YzaOjOb zVGhY&<@ilA(p@q=UBrbPziv|}ZH=P47bE4*$vAD_5({h-o6r*r+l%Pb8IYr|XZ2xw z2)*SG7PtcKj0T6b+a%P3c3I+g+r1N4$&wXE@sG}RZllO{JBz|o)kv6kC2WjD+A!zj zCXQN-Q-{YNEwG!bz$G6>k+g0fmw_L6kUO})*ll5y**&5kV>6xV&M!CQdmsL=Ax`!uwL6; zL`*6!`0fwfk5^;3)-%5&weMb{#YSRzu5?$##MG|+c0~>>)0Jx7qjA*vI>6$FJCm<; zpKHQZ-Z1BprLpQwZFL@BOV{MiztwuiT`J5 zS7ivE&axu!*e;T+Ms~UFgvT4Ix}IHgag|bs?ej2uVwl z#@u9*q<8Rk-7gmW*pKt-YzE+53-Aj9vK1%;7ao5*tF@fE+TL#Sd_Ae2avdCc_GH_{ z9L|pydCxHGzd0Nj8*W5EgU5pnTsk{1o}D%8z=JRoaybpjSFGA!T%~2*`Vo&;)c+`_ z+VdW0HQ^G0u#K^nJ??`7{f4(Y;=cnJt()qWk6Y@XWS#`y!oG5o;xKl&&h2;!iN9oy)}shq$U5{1 zgHZEamLB)hdnh2MdO3>r+cb03cb3jgxmhj>gfk5%jJ>Myb()KVFsYjDtxuC2hYnDC zAyZCoG$F8MDd)%_wx*&`F0m07OfQR|q%fi}MO~#Jdbs#(uIu^9@1&3kRGFI>DND!Z z4R$O*&u@#%tB!m)F%{Ff6$J{NgOee z^QgOC^%=`47VnOz?)9*8d&^V-RV3@jIn`rt9SWh{vAgBX>dAjcIN!diXv9g{kfDWP?O&yKh)sv)OdCfbv35HLTO9xjp2gf*8zR0pa&D&dV|tir?ujDgd3$QF;DB!9p2e7EROpU zfhwngC9m5d5m=T$fxX;Mfhr5bMq51zM_G9e^wu|m6@!O@&i1{8%Z9T@`eXgTbSA}6 zG32whA~doP#$Qe*WX|v9-xB>oJ|(_O_5Oq-&hXH1wk2IhwVJw6yo?`O(DQTh>+LRv ze_xFGLHC@>tza1!TBgzX2aVEw4cnt6Vr$HsYo*0se1hmC?4p@z=U0Z+Gw?T)XgA+G zzo+C#BEJ&hRftQ*O9Stgg!Le0OM0P}WY-SHSxD?ejD;ks=A+{_R#{@V;^Mb19{tQ3z!q;)o~L z!BLRL8(Oq8X=Id_TO?t~NL2n(&Rd4Ax_WH6BYec7?3yHaOwq43vqs8<+I_x~3E9ZHiKbXDCF9zE?EYh{t5ZGFyMr-&V z{_>Qqlv)O;c9f8b3cAgHqmy$v7w=5XYh=vID3@n`Kd;AJoNm1~v_(zUd(sYHCElCj zHF?NO;jiRobz_X7cxThTlF|3WRm^P1D=Rz9qzxmi?i(Bpr&;5)wuetcY*aJFQNv6=jq}EKFDd+hP-k zPACXn7s(q7rshL-UW;$ci9K@L!hXOECY00o72{yBcZJBbVNRpQc5&8_xV)y~0iN;& zrSL$Yaixt)p;?HsF9tTk`0%vd&jQVz8VRK=cbmuRR;ymdHI_a6kH7z>{a$la$4IxbmSozF?a7aVglUnN!wvpNhntp!o}qks_&x z_)*jKnU6_uIgz(9s)1LiHqwtzB_-;nnVP1VKGw4&s>U*VPex7AIbTsAZ@sJA>-rpw zIC(9R1xVGEF1}z^D@>gvnHjA?nUA;00(Mxz%*e`d*gZDJl6UwSpU6=s4*w!6_ zx8Vg@&q27>t5(aTww%P}mNivtc-*#c#i(PaCoq2Tp4V1bwWpv*JFQGtAyVrcj>UzR zJD%^OW{Qr>l3NO~=PxThHQwO&`*aweFV@(Av_}c+FcrBfqn4+Iu=8h^a8bM(yFp~;A@kkSq7sw3T zvZ$(P9b74TIi~nVK|#D&(m|il=%f*GrIjs*lH#ARm9kh-WFMZNYn<0qHC-`|3qB&i z))<$uSycHsy|o|o&j)%7M`#G^{2cpzZqQN7s_WwN%g%TGD_N)vQ*}h)Lo!vzeRDo! zwd)93$J_wO+mGxDB&1Ae@j2Od9_u$qO2F^9sxx|{$R2(D3{&;Q;X^C}AG@}}`2!Pk zv;cN?)AkklG>`0={e$xe80&0Zz?IDsS`m+V|2W*Po#rE9qA86ft9ug!1)p2#gicMTb|q0S`y`n%F}^&Gfg zn@<{IsHR^_o_9K*GyeHj(?dI%xoQdry)@d=63-y}nI}tmGQp|$y5rM%c@un_$ z(Y7|rVVL6Hdari!+cj9@^uSZJx(nQWHSnIz{k5h zONIP~oabk9bs?Wvu8_}zU{AXa_NhYnJ#X*e4ZhB$R1+L|e{!rBax%!^bSe$dzT#=~ zs1#iy-Cc5W{@8{sew0<%=pn0&LqI6}yJ%&c7cB1r~oWT2Aj#b{n8u*We4lF7(#lgg#?cG;fKToGLi;mBld&UCg8 z(YAUc*fq+0u$4E?toZEFJCO24^m^a8val%7Woe;_v10aS(0DCTjE@BF)~R4eIg*Fh za#t54Yjzgs|4w6eivMs91onCr&S~YGbz{1Go9lMScY<7~wIh%lQ9|7yfSB@eV$bIK z9NW3osHpsvB7TLD!|-0n2Sr(vPMhly!8Q7`LYb8GH|2U4 zCiydQV${v%6>-U&eWtUi#}!JP7PJ!heOCpuIQOF*$32Q@MKCg6=uTS%18 z*r#`#XsA9a`us}Go4Pe$aYsUk+NC6RS>JbDTB`CE4?@73g8xG4CwwTSdOxHc(Y3pM zGM#QnKJYGo>(7LrX=Ih$9bUSw$m=q&#mXCV`>SXGXo97U z+;TpCE|RmFkiJLCVjejm$7R|)bCAC=;7rmhjF^Vfmz^Cgs zBKlI$(_g2TfEoAIG0s}&01bXf2xLqwK6%k8Sp~PKyj0F-CyKYYEmFq-miRgRvv?X{ z1Z@!a=8QhGT)1zC*xHRsc`82UMb%$F9)MROAq(?wjE|u^t2Jt< zPBBGxI?ZNRXbEBgI++2zhGatDk0!c~92|UZmf^cH|D`0|pHNjW*}uFk}>LmNI;YXqL|cUKYp?b#nn zs==4T*|pQjL6{`fWi=~x=Hhz|)2d+wS%EL%z9A6-Uy2i9iBhl#jhs!;+mWA?4>7Gw zH5t@ucicOS4_c*okQ@(xls+C4YTOvTd1%~Cyn-um#(X7wfd6B3X8Sn1zIP^sWC+fh<=fgm|2@3%E z!+7wI0OY)eGV(A;AZahCQQ4GzY(S{N_$+g$Uo^l65>Q-C^)x6OR#5!lb|aczsdae# zKC-p1net5!8DTgUPAFJt>uVH`+zK-2&>)5Mur5H))yE=5&)ZNarC&3Vv5u?mf^COZ zG&@q~9=~1Gm*%(J@rK^((-Ptgc|NQMqvtVF_pxLP>2+=?$`RJO zpI`PSl%RUpvTf*PXq8AK?_J%$f2yV~e10QauvR#^+Xz2%5fU%sEiTnA)IX^_Th&cu zB5SZ|u!8)@%KL}OXHuAG0#%GY{>B6WODn1ppub`VN5l^} z>$+fHx8G#0^|2R-+Wx)1*owH%Kt_2vw|j;w`HnEMKz{x^p~7L~VmgqGm04Xv`o@yJ z8_bi{dR?Me|2tUW6y-R^=Y!0@$R{#Zs3@C!9J*l4O?tOCFc+n>!yKDf6^jEx*;exOSq8>f-`$lg zGmNDEno3o|l5`-z*>*vPd_+ew5)@Ol-#mWO2j~NIQnKPkw;YPTOto{iYhBCOFN6*AKZBj0v={Bx zo*v$~7TivR9`Fv2tu||El`SRumKHU7)dAb`%QyP#7d;cs4x_BtS)rGvQ8pb?U88j= z$-s2|FHBAZPCA zz%=86S2*|~9W8oZtHOp5jWW^aeEyx0ev3l^LWo;vG2OvBD-bZRi2>VlDz46NuhU)r z6hI7`#DoQ&9LQU}@Kzu!y)dMs{scZ+V}5#m9T*$((`Y}kjGL&chj&FZQr zJKfn$B4uFyLqKpZt#_+Wf_4r1`S~O8H{H8EH(y0N(Tj%^q^HMuc6@bQ7yGW)*lVLM z-qN8_VJvJZ00UcVKkEO~?l3ze0+qY19JwU}^l4|`&z z6~LYvP+%P~Ab{%ksp?wNO9AiQQ}U9TU$_L}sK^pRI8!l71+YMsbMwe6%62c_X@gJ7 zk*cU5P++23(wojuE>?jJLRqQ|SH&&jB7!vQ9yX&!dx#Q=Ilo&iQ^lR#E+kDGG)_NF z;kB;8@^sp-V1L-+Uzws@g)Wo$b=ut3y;_+3$amCl&aLE~mj*5d^WU+*>EFPIgPQuIr1bU19y{6nm#l9Uux-Rg89m26vP$#rEuz&5K9g6Y(cOtn$B>z~3 z3Qd=D|1X-eCDJH?2!GLK=bQNb&7gWGfZn5}per0~@8EBOV2U#d->!9<mGXG%(JXYE`4e#$`a?6;ebpMeISc3Dnqqx1^dE*`7b5b+9 zR+1X&=VWHU`>*JL@UJlOxTYlo2lvYl;UAg*mi~Xr*%Y~w41(CNlt0ig9G^7iK>l*z zPlLKh0alSe-e-o(WknNU4e0HY1}z*f&c#pdIOq) literal 0 HcmV?d00001