From 0b1ddfe6e9e87b999c6dc77c53bb6be9e0f7f122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E8=83=A7?= Date: Tue, 14 Jul 2026 17:38:25 +0800 Subject: [PATCH 1/6] fix(web-shell): persist collapsed session group sections across reload Store collapsed section ids in localStorage using the existing qwen-code-web-shell-* key namespace, and skip the first catalog sync auto-collapse so restored expand/collapse state survives remount. Fixes QwenLM/qwen-code#6870 Co-authored-by: Cursor --- .../WebShellSidebar.collapse-persist.test.tsx | 287 ++++++++++++++++++ .../components/sidebar/WebShellSidebar.tsx | 47 ++- .../web-shell/client/e2e/utils/mockDaemon.ts | 25 +- ...web-shell.collapsed-groups-persist.spec.ts | 195 ++++++++++++ .../web-shell-collapsed-session-groups.gif | Bin 0 -> 58196 bytes .../scripts/capture-collapsed-groups-demo.sh | 20 ++ packages/web-shell/scripts/frames-to-gif.py | 46 +++ 7 files changed, 618 insertions(+), 2 deletions(-) create mode 100644 packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx create mode 100644 packages/web-shell/client/e2e/web-shell.collapsed-groups-persist.spec.ts create mode 100644 packages/web-shell/docs/assets/web-shell-collapsed-session-groups.gif create mode 100755 packages/web-shell/scripts/capture-collapsed-groups-demo.sh create mode 100755 packages/web-shell/scripts/frames-to-gif.py diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx new file mode 100644 index 00000000000..79ba3ed6fed --- /dev/null +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx @@ -0,0 +1,287 @@ +// @vitest-environment jsdom +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { act } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; +import type { DaemonSessionSummary } from '@qwen-code/sdk/daemon'; + +const COLLAPSED_SESSION_SECTIONS_STORAGE_KEY = + 'qwen-code-web-shell-collapsed-session-groups'; + +const { connection, workspace, workspaceActions, active, pinned, archived } = + vi.hoisted(() => { + const makeSessions = () => ({ + sessions: [] as DaemonSessionSummary[], + loading: false, + error: null, + reload: vi.fn().mockResolvedValue(undefined), + deleteSession: vi.fn().mockResolvedValue(true), + archiveSession: vi.fn().mockResolvedValue(true), + unarchiveSession: vi.fn().mockResolvedValue(true), + exportSession: vi.fn(), + }); + return { + connection: { + status: 'connected', + sessionId: null as string | null, + workspaceCwd: '/tmp/project', + capabilities: undefined as + | { + qwenCodeVersion: string; + features: string[]; + } + | undefined, + }, + workspace: { + capabilities: undefined as + | { + qwenCodeVersion: string; + features: string[]; + } + | undefined, + client: { + workspaceByCwd: vi.fn(() => ({ + listWorkspaceSessions: vi.fn().mockResolvedValue([]), + listSessionGroups: vi.fn().mockResolvedValue({ + groups: [], + colorOptions: [ + 'red', + 'orange', + 'yellow', + 'green', + 'blue', + 'purple', + ], + }), + })), + }, + refreshCapabilities: vi.fn(), + }, + workspaceActions: { + addWorkspace: vi.fn(), + removeWorkspace: vi.fn(), + listSessionGroups: vi.fn().mockResolvedValue({ + groups: [], + colorOptions: ['red', 'orange', 'yellow', 'green', 'blue', 'purple'], + }), + createSessionGroup: vi.fn(), + updateSessionGroup: vi.fn(), + deleteSessionGroup: vi.fn(), + updateSessionOrganization: vi.fn(), + }, + active: makeSessions(), + pinned: makeSessions(), + archived: makeSessions(), + }; + }); + +vi.mock('@qwen-code/webui/daemon-react-sdk', () => ({ + useConnection: () => connection, + useActions: () => ({ renameSession: vi.fn() }), + useWorkspace: () => workspace, + useWorkspaceActions: () => workspaceActions, + useSessions: (options?: { archiveState?: string; group?: string }) => { + if (options?.archiveState === 'archived') return archived; + if (options?.group === 'pinned') return pinned; + return active; + }, +})); + +const { I18nProvider } = await import('../../i18n'); +const { WebShellSidebar } = await import('./WebShellSidebar'); + +globalThis.IS_REACT_ACT_ENVIRONMENT = true; +if (!globalThis.PointerEvent) { + globalThis.PointerEvent = MouseEvent as typeof PointerEvent; +} +if (!Element.prototype.hasPointerCapture) { + Element.prototype.hasPointerCapture = () => false; +} +if (!Element.prototype.setPointerCapture) { + Element.prototype.setPointerCapture = () => {}; +} +if (!Element.prototype.releasePointerCapture) { + Element.prototype.releasePointerCapture = () => {}; +} +if (!Element.prototype.scrollIntoView) { + Element.prototype.scrollIntoView = () => {}; +} + +function makeSession( + sessionId: string, + over: Partial = {}, +): DaemonSessionSummary { + return { + sessionId, + workspaceCwd: '/tmp/project', + displayName: `Session ${sessionId}`, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + clientCount: 0, + hasActivePrompt: false, + isArchived: false, + isPinned: false, + groupId: null, + color: null, + ...over, + } as DaemonSessionSummary; +} + +const organizationCapabilities = { + qwenCodeVersion: '1.2.3', + features: ['session_organization'], +}; + +const namedGroup = { + id: 'group-1', + name: 'Backend', + color: 'green' as const, + order: 0, + createdAt: '2026-07-04T00:00:00.000Z', + updatedAt: '2026-07-04T00:00:00.000Z', +}; + +let root: Root; +let container: HTMLDivElement; + +function renderSidebar() { + act(() => { + root.render( + + {}} + onOpenSettings={() => {}} + onOpenDaemonStatus={() => {}} + onOpenScheduledTasks={() => {}} + onOpenSessions={() => {}} + onOpenSplitView={() => {}} + onNewSession={() => false} + onLoadSession={() => {}} + onError={() => {}} + /> + , + ); + }); +} + +async function flushSidebar() { + await act(async () => { + await Promise.resolve(); + await Promise.resolve(); + }); +} + +function groupHeader(label: string): HTMLButtonElement { + const section = container.querySelector( + `section[aria-label="${label}"]`, + ); + expect(section).not.toBeNull(); + const header = section!.querySelector( + 'button[aria-expanded]', + ); + expect(header).not.toBeNull(); + return header!; +} + +function click(element: HTMLElement): void { + element.dispatchEvent(new MouseEvent('click', { bubbles: true })); +} + +beforeEach(() => { + window.localStorage.clear(); + container = document.createElement('div'); + document.body.appendChild(container); + root = createRoot(container); + connection.sessionId = null; + connection.workspaceCwd = '/tmp/project'; + connection.capabilities = organizationCapabilities; + workspace.capabilities = organizationCapabilities; + workspaceActions.listSessionGroups.mockReset(); + workspaceActions.listSessionGroups.mockResolvedValue({ + groups: [namedGroup], + colorOptions: ['red', 'orange', 'yellow', 'green', 'blue', 'purple'], + }); + active.sessions = [ + makeSession('session-a', { + displayName: 'API review', + groupId: 'group-1', + }), + makeSession('session-b', { + displayName: 'Release notes', + groupId: null, + }), + ]; + pinned.sessions = []; + archived.sessions = []; +}); + +afterEach(() => { + act(() => root.unmount()); + container.remove(); + window.localStorage.clear(); + vi.restoreAllMocks(); +}); + +describe('WebShellSidebar collapsed session group persistence', () => { + it('writes collapsed section ids with the qwen-code-web-shell-* key', async () => { + renderSidebar(); + await flushSidebar(); + + const backend = container.querySelector( + 'section[aria-label="Backend"]', + ); + expect(backend?.textContent).toContain('API review'); + act(() => click(groupHeader('Backend'))); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('false'); + expect(backend?.textContent).not.toContain('API review'); + expect( + window.localStorage.getItem(COLLAPSED_SESSION_SECTIONS_STORAGE_KEY), + ).toBe(JSON.stringify(['group:group-1'])); + }); + + it('keeps a collapsed named group collapsed across remount', async () => { + window.localStorage.setItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + JSON.stringify(['group:group-1']), + ); + + renderSidebar(); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('false'); + const backend = container.querySelector( + 'section[aria-label="Backend"]', + ); + expect(backend?.textContent).not.toContain('API review'); + expect(container.textContent).toContain('Release notes'); + }); + + it('keeps an expanded group expanded across remount after clearing collapse', async () => { + window.localStorage.setItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + JSON.stringify(['group:group-1']), + ); + + renderSidebar(); + await flushSidebar(); + act(() => click(groupHeader('Backend'))); + await flushSidebar(); + expect(container.textContent).toContain('API review'); + expect( + window.localStorage.getItem(COLLAPSED_SESSION_SECTIONS_STORAGE_KEY), + ).toBe(JSON.stringify([])); + + act(() => root.unmount()); + container.remove(); + container = document.createElement('div'); + document.body.appendChild(container); + root = createRoot(container); + renderSidebar(); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); + expect(container.textContent).toContain('API review'); + }); +}); diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx index 6c594a2730b..d7974dbf208 100644 --- a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx @@ -87,6 +87,10 @@ import { import styles from './WebShellSidebar.module.css'; const SIDEBAR_WIDTH_STORAGE_KEY = 'qwen-code-web-shell-sidebar-width'; +// Same `qwen-code-web-shell-*` prefix as sidebar width / chat width / theme. +// App-wide JSON array of collapsed section ids (`group:`, `recent`, `color:`). +const COLLAPSED_SESSION_SECTIONS_STORAGE_KEY = + 'qwen-code-web-shell-collapsed-session-groups'; const SIDEBAR_DEFAULT_WIDTH = 260; const SIDEBAR_MIN_WIDTH = 220; const SIDEBAR_MAX_WIDTH = 420; @@ -351,6 +355,37 @@ function writeSidebarWidth(width: number): void { } } +function readCollapsedSessionSectionIds(): Set { + if (typeof window === 'undefined') return new Set(); + try { + const raw = window.localStorage.getItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + ); + if (!raw) return new Set(); + const parsed: unknown = JSON.parse(raw); + if (!Array.isArray(parsed)) return new Set(); + return new Set( + parsed.filter( + (item): item is string => + typeof item === 'string' && item.trim().length > 0, + ), + ); + } catch { + return new Set(); + } +} + +function writeCollapsedSessionSectionIds(ids: ReadonlySet): void { + try { + window.localStorage.setItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + JSON.stringify(Array.from(ids).sort()), + ); + } catch { + // localStorage can be unavailable in private or embedded contexts. + } +} + function IconNewChat() { return (

T+}< zE+p*TL)~fhFNiiQGzl^}HP}SlM!?%fp5`P65Q_u6jd7|cKpHv+3>v$8h@;GC^jY~u zD#GUCU0BS=nBp`RtLd>c9wtZ`eh@JS2&{U>0mO?A$Mu8t(^@!*d2c++(3J&|Jm~r@ z%_STvdFiuQPn6?J6VTJ*rq~{FbjmnT?=#-XaqnDkg3$hw#3GegcOWG+?|o7j!<1E9 z*}?t4HJDm{^GqYd(|S^+|NM4og*_1~6)00?rEPwiI4nh~A7N}dCC7rL8l#ke=aa&s z^^6!kopk5yQ~SyIRfl<=nBTx*j8W6MQu- zX7TLDa;Mwty9#gA>n{h{77{8dF=+O&LI;fsGF zXh|`%!As4=;v#$j31SK3BY$O+sCx?8WU1(hp>!}1uVGdtf{lGR{l=99pl5&dT}Z+0y2(;8F^2SHf5K3}95#-y^x5Ve`wahb}?mdKaykQ3Y*=%7?p zRiT>iVg$CW#xScGO2=bO)~sge&)q#=l_OI#|)^#!IPg5;%jWmgzpSX4X`Bp6TQ=uTp5Q{woKa?3?md=Ug)DvQ?7A z#w7JyvR3Qu6`YM%d-OXxRvX+SolTEam$OLi8~w(d&2M`Q2JS@O2@fS&U_3J%CSPlc zu5h;82s0cPU28@qEAV)1gN>&3*IKg8oYn2ZjplvVT9a2VY@R*DHSYw~e@+^wT?tijfKD{cF3tuysD!2y?>t?7?%J;jV`!Gf2lyNpx!SmqP& zehG$VMfrwa+bMIbCbLU6765;*5p8AM>@w~aKs%{Spl(9W6C?XdxWA4DelLm!3A{KJ z(Vnb~%rdI*lVeU@RQ%+ve^Dymi{F$p*7Jx23A2FEPT=7rv%pR1dj9#*MK~E-DkwOm z%}!|R#h`CWCxw07H(j1{|c0ERjqdC?dvRMWg08_%y60G)H ziYVTL2T|84oxh4P#eqI_BVFiN0+hkFIL-mRxW!FkRCs3Ldo$AzMO?Ic zDI}Kenvz36`irIbGT~?f(CKTT!h6RKbW$;8*a69gOYq@drT&DJC^s5Z>NB%}zYKJ| z2^3}uTz{XMhKmB>6tGhQT-aiMV_P^hco!7&lWB|WJe*>I$ekQ9(b#dFNiv8aeWq{g z@OTr#_2P+mkbieyh|mD06f-%9T0W7UJISod`n%{xKB(!t|J01Rds@%tg&t6c)^y@f z7CD5r+YHe11G3RU+lZNtt|<*6nOMDPS+{#|M12aV++`6A8*HMf6G{2gW7YRBw5>)oa~XYTW8Vi(LWz6~4@VW*yS0}!d?UXam}5>RN96pJ8_HVj!4%XT z(R}A!uU&0gwf}tP|0EEuB;yXsq2;Aa>P9R04aH3OHSH++tbK;-6w@VW!pa?%L~C_3 z;nXnt`}S$PC<}Ay-aSJ7xquJ$z)PE;<^7+F&Rr8{yT(F2XYzj~gC_b-3)8o^Dw6uT zR^9mWD(<&9d-?|CF6W?VREu5&gGe&1pW}bGl_doQMs6=ANlUhvx#%~dYPL{*(7zk) znw`9(N~-1D={}PbgdO9I$u@UO)F7r$66^i*!8y+mn){gV$_9V!5YQlgHSB(4na%neaSZxIWU z`u?Df18T|Emd3U`SqlA% z55tDX)A;g8hDW@HIYx(hBI>wThWUQHXgwHyN-8ISHzFWB!oxEnq&tH80!BK4c+a|yQe54jIup6$mG+46eXN3OGPwQ}VN^1AYqh*8BKbjtPg zDh2YYqx5Qh^6K048r$-kKj>fnme<02J4uxHZ`c5DnpBE zqDyEaYHqsSxH*fb%;GVvIPoMy3=ZgWgj zfH?QGo5`p&QzE9oFwJ-qm%AAP=1iA&Bm&gqtq;n4o}_w9#R`Su=%wbll~Wo~r^q{! z&pTv_!Eq!?NE#XAMFVo*?7x#yiDShf?ZD16A1YAKC26Q0(@vD2*CuB}RbRD7E-zHiSWO|$>J=o+7(?n6U6TwW(rJQ*t z=h^)57B1FUVw^&?W{MIxRbW_LT@KXF^a`a#fv-$qD?v3ax{98tQ|@@ z;+ft`3%T=1iIgDmGr}Gs&5qihlsknoYUd`w28d4cg)9|wK`=y(x=d{a_z~)`GD9$x zF^!%PrOh$zKvm#L)smCDtCBCRngQF-#U3J`?8BeRs6eIRWtAe?B1=^NP)_C9C>rqO zR|(Af<54s;rAaQaafc4+eq>Urp9-C%CN;@#OwDK=DsQ$VC6dc8HKFn3nTdK#8xTOg zUsMF|6BnrVdr8D4JZQ>Uy}TF3?iJ1oi(yi2o-Dskd-a_-a%)6;2EGNW?N!YD&WIsacNoEwq%!NoV33OEM4)Bam>YY zRxI^4EpaOl^7be39BFr5FFoar=O3ETMD}RBDw~wx`t%BMi{!rUv*4&bWF!|%-VI# z3zw&3b8}-X6ZvG z78;5HOGBRaK=7uZ*(dq}UkUm$DqxFjsN>HNumGt|=tekITKaw~eZX{iXX#D_oSB$1(pnMP?uw;2wF22l+Lyk7}~RhhAB$m8g$>=FfH=3)pRpE%HJRAd48@<_eBiN+&!aOcY=ZRq(-b!32ap!jV0`pg zA_U~gYGiYDR^#al>$90w<7&6ouUEzAw#0U}M8BFi^>0<+m_Da79p*J%elQW%*e;hg z9d+HV3EW;VG>uL)ooX;$$k~26xBaevyYXxr{%3m;Vzx+UHnU-x&ug}z&r<4n>8zOe4g{pFSTwDK%&gel4uoG@6`ok0##;TTKgiEHAnLIa z+p)@@JW#!`vcWriMr$p|cZdQ>9l|sYX-us}-K-g2TZ<%Fzt6GGdN?@hK2)!_mfW=- zJF|ZN*IE$UrkC#XUyjdGGW7D7qq6!oK3q0(cAu5pK5Hd>mdpCA*6~@r@v{cOXB8R3 zm-Gr6mp1ChpPysevXj~BupPlQk7Q(QIb4sVLu@(CZ1s|kB#Vx2YHSU5ZH+E%P5&NA z%pZNFw7bPV7Uw(WlRg&HJiaqJ7Iiy5jXAcmI?_HqG9f>%66J2daCu> zKEd27sS-bnPcKu`DqE2&N9N!()8x^XrumuTmcxg4{MmOb@hi?B3X=-&Ts}kyI=L%g z#|NaA`9jOwURJPuE`$j-)#OdGz_xl~X)9ujE^){N&U|M&%h;UIXwL6~*{fyP3mwSb z4T`nNoHu1TDUZ{IUzdxWu6Jiq5joDO$uc@CtmP0fYd@*UGBk8s%_1aD?>TiI^(Bn_ z?({UI>ihNCq@4@T?pg3+R_JE=-i2Hkt`a||WhIaSCmjztY6^82kZ+AE5c}T@rdo0E)tMXmFYAtljKw!vhKOUfS5IX z&hECaqr1r)ZeO#SE-rU3vmIi_?&7xB;1v&U;vLxK`;@m)u_>}NPjRxnoTlVC6ZTlN z(^DXkH#zAW*`bisk5br`$1zq4^yvQS)~|BW2-nIQzRq;ShI*3p`+B^Ka=RJ7$gj@j zh)av$Ie#=~Vsdp32~hfw1z8-AC8c6##mljEBFdUmjD%&*4?!s3cu*OM*4$lwD#E6G zKNivK>XvTk zmc{Rp;Q{Fqn9U;c08JeDS@~pJ{=j(aN*7JWpGUWrX{Z$C{BilbF z$m`OhZ_GVdAh-F)LTodW?WPonI0ksh=oK^G=mr+^x*&JQo$yila@z>G+OxY>c}r|{ ze=vJ#AOo(jed0p=#Mk}0qpNG z*qYggbYd|%KT`X~&n~Fx850Y~USR75#EphD0~0< z{x0M!ZttmbnZEdT?0H+3a}(tn7K3w~!Fe|yA;yAZWYrHl;j?1VGXgRGYQ?i^0$hbp zPE}D(neRQ~=I!El?eh4na>q^b8LSE@OcenwW{|`d`Km7QgM*hc-&CD$S31ov(H(_n*|3b+Bu<<{K{7)eNv&a7^@;`_CPayw? zcK&z#cLx41$iV*}xcFiL9gCJjYhe!=u2iV42OoV>Viw(rgsp{7SZlw0LNuZfjYkbwCdK7McrE%@fV zzti(?q4eI+_`fcCQPF8}C^*bN`I$>fFvt?Ek?{HFK8bscfmAFu$_6Rs>x>a^E)?4= z5om7#jX}yo)1%iD14=|xt|<+}0M8!Dz}^5)!!VpUVK5AIfoqtl3mI$|1~e*Uzt|wl zfO(9MP<8p z-IA5sCC-f_VSi+gbHOFK_eN;AiuaVsSPIpJiD(#)n($0u~Z~^cgV6=ecKZYPLjZ!s2XH&$x=*uNu^*p zWigJzmugs?nc~}57*m*=#XA@&pHiku475Vf!M%1BeRH`f&71qv=Wp}LQ$7{N9FXxQ zEVslNxPqzTFZ0U<9O^VQ$4?g;dk&6VeBAKQ%X}gJK@NaI7!3@4NkabsMy8ch)CJea zt&8|`%zot$NhCLn#KDc=4z&$?R${tD(6l$l2&kYwI*cwCo1vy?^@iVIAbt*nO(N*30uE?#ae)SZNQP576%A6kK07|1tA-&Z~o$x{*~+PzeeL~Il33@magp-q7@t?gd4Y!V&6#tbdC z=aEY}lb$k@DNV{-6YRyK=M;{<*&3D-lE%!4jBsN3o+05aAHy(Y;fkZ=}qGSczb7Rzs! z0ulh`!`DRFOky$u1`6Pzn2(eNY<74_UQ|TAvn{E40^d^wJXu)Sr-)Uwzo)S`v#^Ox zO=$*xPcO!+;80ST*3SE$QLSFVWiU0Z-}61QDWQVLUS-DkKwo z;Ka02{D0$O;bY0e|H8$~1?>NYi+6S2{>A%;i*YoO#Q(v?4np{~|8Q|z3Et~}xcK9T z80~+!_;rA^lyl&-DQ7!|;-+MjQz+M{OY|(+gbbFG`q!Y_ zP3y$1k>;{Mj+IroLQARjz%(Z{+894|th1msz{8+gmE zCm|9ffX-4}1V$jetBwAnee1rgZ~=S>5RO)Hfst%hWG0#os$^hRfXyto8LYUfxLJVgks5RWq4g_z=+U8bok_^YS{T0*L_q3A$V(~0h2Jtf+04f86QriF_2Nv zNlYR;60C!UDDW3GG?rP4nH9(0HYvGq!Mn{X)l_df<#s#*M8F9=(08|}sHbPK0bpaOU}lQ=CXS ze=Wp&y)+THS4R)6#ISN!2MML*sDcL*G^Lo2t2adcF(&UnG!%d6yTnF5_AewnjlTJ*S8Wb$^L||~pdzJC^3GqN(eqn47D9iwTAU9HQdy$Q zGn=gNLv5(71@xVJwu^;)*uUFb_y`9el!JC%O20A)GC6xsoxThZA?rbdpA-mP0We~I z0v%D`zMuvBlPJv3-hfaf@m|9vsO(3)K7PqQvF<^BE2M&tWKUoDp!3HWTlwckF}-&e zuos^g>PRW)+W9<6QUEYCQCC)@7FwS4~ilDCRb)IhX)ZzK+q`mdA55F>+;n_;f zZgx&}#jUlrTFYqK?BJh@R$ztL(T|X3cO=-=<&oIYG?t_{xP%w*$TVctBL_-Q+J5hI zRh)FPb*t-rAJv|x%udN1e~u>K%*FEX+HO9rCufitj}F-UY;)(6)XW#=%Dr!Y<#2>M z=RKt26t?kEuMHvHCBU0`QAxPBV?kJno9bg{iCefd%ERNLH(hDeOvnFV8CcKmVp!cu zn@1yai+T5VeJ^dVf}X#&F4Ga&;>JGo;j>6)S?*dpwGb%&gYf6%DeB&6p*;1d!96wn zRs23-wAAEd5DwCN#yaOC6_HPU;csoC{J*#VXb$F?@u%LrwH4#&{>xMqSL?X;J>gIN z*Xf(Ttd>M>h_3Ihvn^&1D3({KHl%K}Pk+9p zTl_kHj=Eib7jo8#`r{Y<{&(`!-^cTTzjtq>|K7L}yrnWjgi>04th!d0I7=AUbV>Gd&GrOaNR<-V}G+j3x_27y*eXa8^dQI!Zc3JTjJh8B>y zpC!U>!}irikmE3*@{l2xrY^_svnFD>IY~Px%xI_YP%%r^;Wi!5{iEv%|vnWWHN>9O0jI;*q8pf*>`Q(!74999<#4WbHYwFBaCt`d~#x9 zHRHN-;*WChA-PrTxkSpjU8^}+ry4oAx!Y~I1vMH)kGUJ9d8Mb}n{ED{MtRgCd7Cj- zffMK<1?2lK+ zPfRc$3rifRwlf)yL>)TYW84f9iM=&aCcR%^Q4g;N8Hf{cbSnJO25L4fG^*V@Y~Apk zOx#N?TK1V{zv-JCKeZ5x8}vyie26V^DD4-Z-se>Jnuzmc%qq8}+qyBQm?C^7GR6AUMXhpB*&pZT;H)ZlTqyZS}F$G4vI6nG5QHDmE&Lo(;IdM z`f0D7Xrmat+>8?~rz^CoqfqejbyOSO%;#t9b{wp0g-*?G&Vor6SPAarRR_XVC0Q$QqZj+C1%zA)KN*k33^_xwGgfwGGs`?&PXRb~T z95Fk#O`qVORSrs&zQ*=0Q3>3N_Q(6Cpr}k9P70;1DHdbitH&oli+cw(3y%XwgWM@9NFC53^Z-ONV|xhaSKN#K$y@t`{`p|Tj^|7N*d ze^|KKUO{@A^X&$9V_a$>4oa=j=COFBhuMlzl>0p@b5W3BC zqj}>Qr0wynbJV`mC%#j$x|2$;GdMo#O*}k=rpr;SOVFWPpxB5MQl&$W>BQx+vj7fb1K<=`uZzI zFZ=ED+c^_vlNIv^e?fPTdKYJ z%ju;{>4??zPpvV7QB45cq)+!jf1joum&n9H7Qo;)Y?&I+oI#qFTH~dp%-i%=a%!MW zo1r(w{rQzV8xOQ_mL1&c&vLgI87E%U>C?ARp#zDAY_XB|As8m=y2?%ubvBE-`I-_D znPlz1x1#B_2RWiBXVuze*X-bJhx^shG@56oE+W#_pCx0bOnv?_k%c{*!8x0zGMnN!n-n`6 zUo#ssHQN;16^lDZd^Fq4GbiRSN1!nGre-d9YA)b(&KG;$hjac$dEP5_PBLa5+jd?} zLc>u{zq5XR_sd%q%J-zV1&a|MrhgPTmg+gS&oE7z>;03yDG>v~TEC5=R*lu|#NXOH zv&1vbEnCYo&Wb4Jfn$GFr+Tf9&l@#k!vUCP#VNQt4hM}pM$>aaNBj{PqA?sMe!Dre z&Jr=6D5sdeWF<`Yw8l4Sa-zJ8kOnIybJWqlF;DV~L@P1i6G^E^Yh zKV@nPOGF0xSl>+Dv6mfup+y_%VA0~TpKJ`AE{2X8%Z3U0#?@f`imAAbZDspVoG7?8 zBBfzXp6)|_VUiMN)Z4;!jUBU(RDs8#-VVMc_#RL~9>L?N8r^5LTirCVdP%`C&iKVv z#d!=YYB5aKTbw_E`dTlCwXKr1#n~45EUu&hX*>4l?VZgIl7@dD|D{nMiiSu#u>QM4 zRC}S>PF<&zaY9y+Mn>zm0GqoEh?_#6rmEJhem0~d3UFKCkUi?TnPFrr+Xg{`3aqoZs z^VNNWMJ87W!|tZ`$^M9HJiB?K&hLl<^+i_Sz7&0KyJPCJC`YV1htC>WdTZ(UwQhJO z@11xJz3qm)7+XB_4;VI^3e1|vbUzbpF%SnvhASNDJa#OPvS2S#Kb5F#5@ja3XFCw4 znqw*=pI<~&XTFFrTI3LT8rzmx(ROZUN4^YLb(?wqIhKq^Nc6G+baNNC{qXt`Arwxw z-@MmD)rpl_@cQ}5GWltENY@Y2ci*>z?^XtKlllu~v@U|9N{S-O%;Vn@oK?;1*3fHJ zxMkIuXTK}byXSUpGe7SLIqxbu?-@An+x=HFo)6Mr49i@MnqLej_1QRIOb=Yl?q1CQ zy;z|C+LrWsiskEC$k&acuOA1#mR+2#&VSvbzdVq++-}sI(!4w=x;z`W9H2*jnZLZI zzxqyadHMJ2O~}L812%y(C47PeULukqepV!IFFGkhaX zPLmV70PSUvJ-zmGzFIFLALV8krsqPhzeog`4D!v8e7G3;#WXa}I{GZoBlesS^~Pmv z7evbsW*!95rJOsFT%3$wnE8Hx`sutr^?Ud3(-bk$_q#JKQ`A|soSiZ^Bf2*vTDSV| zZ#w_79&MSh!7o{<32KS5k|el1*INr{UXv%>}DZ=V630_Mh%?tfp5sZ(W`` zJ22k(yu2Re1N&RtGBw=?pF94@1uJOTRk!ZijU*N}vC%czauvIuNfdRiP}>f(b?ErGTQs-6gth z^)a)>RYW{asRKjK^HU!?H2dBYFzY<~Hk_PRE_NY2%Sy5R==~w+ivGgyA3A>e2VjH( z;UF|(N-eD>C@2gcT`JfgRw4(&C*>m5y0-jhyr^Y2=z;z8|7jciO0YO=u(nRINXV;@5+#-p*H7_O)w zfKUL96uH6)FHyxpBSJ^gKo60LbR;gMF2J*{dv-2c`g89&| z`O(Q?#Ulg~1&y$CzOp8Hl9%!V??bQUlAaPiyWo~TQ!M9Epm84+P@-ce`8G)R23_!f zuy>bFZN~4T=aUfJ-Cc^er9h!bL!lH2?yjK(D^Oh8K!M`L-Q6kf6nA%bclS2S_xyIx zoZSaIJ3F)T;QRsiOy+)&eD2)W`+CVR7QTy+E)0<3lVx@0p6F#w_v0JbN^(+_<0|jE z$UyQ$)m1*RYH%vce-zNr zU=?%L&@%F`8Pzn)tjzC6> z=+0a)cSBEH;zpT~6f(CA;Ik-DzveDrM{WdqzQD(>A~xQ~MV|9u7d4m-vFQ+o_SQHH z84xUF{LP;iv6ph$MZQu%CD~>kYc3e0lKf>QiNhUVac=K7;myoWD2g$aC19>bWX&hZ z@L=B$sJ#Hlq!i*bNzTj?v5huz!SX@rXgxX4=w(Y*PI`%bXW^Ty>seGwc_4C<^4D&D zFV0`E%`(45z?)<8jjNVZ-jQ=r#c+3RQ(I5laJ}OoCG6Is{4@TS?R0lLcm3p8SiJKX zDI4G-m)MflF6|gk-;(T2T=L*ZDiJ4VFXO5z0PJz=JvR8VNpNvp;iOhF`QEn0>RWcS!n_+s&I2;O`e7u*qjAZdyRXCjcA-L+^xE;Wet2&+>-$-y>%pq-|u` zyU%Y1RQX(A@4~~(goSl`TyEbi&=%-#4=jUUXh=X|!7Q)aSC5k}A{H|7-PP`s@CjKu z^~dd1MT3?fFVx>NX^sR&`)Wc#-SIC3BXu>qOv$cuKJw<32OEIR8asrfoxCGMKj$z| zP4^WG)Q)RfIGNFWfAQ&E9#*(>*Jhf9^9%8t3O0AESb9`p8M?4SFFz?3MqHg-DZvZH zkmhh^*84uXeY>c5FXC9HAX&u#*=UvnbM~q$*>^z`F)B>3M8$>ymR!ET62IIC{dq|e z)HNPozl>y47J@$vXz%Uk& z2qa?7#2k9z^h18f2}Jnotjhw=vk%mJpN9Pxhl%aUjP-3|$`XXY_jUWQ9$5xe5JOwq z5+SC*M^gos<7mJwVTf!733+Qou835-mQXZkWF2SQ>*zrKqu52-4{?6BIift(Eljy1 zoHihr1g?}XvvT6^D@L$w=!Xt9LUe5wV=Q|Gl&4CBAG>i@n+m`c1W-qO0KaX~2S6~_ zs1bY!QG5W=1o$@~n&pu3?nB7^*$@5_5CQM_JccV18$sm+I59zGNA~jvf-4GhwkdOV z3D_h8q`l=OW=my9qC5*s@MG|-;p*tgB5U^z;X36XQ6%0FT=jsXv^((sOK(ya7Z;WWIBkj9@|7=3!>^md+h`NAib1&3|C&*D# zoYK#Rm9%(5=~<%4Y^Vt-qOW`Rl2b=bg}6%Nq@LtFtkCNQLknF#h>cZ+?uz8iyet~k zj|u8S72kHpcYg?;2pJ+7M)fH&{=-bZM8GRVGtcdh%-|+6=51obt;Fo$@=k=d1SR;n z*LQ>nNBa=ivEskTGxDIEz!bJ)Ls6Qng;UDQlBKY+^^WV{OFPEBk=^RIaUW94xe)AM z+9AohA9fbcOgUZJoy&9|{x0s+0lAov`t;C2S(cacR;)|Hs9`d9L{i|`;Xo1HoUZ1i zP{eNeFlPT@w%ahZB7RxL6X*Kq29NT z6u|GPdtgI_2`W>yj(E4~4|0JZ6{FlS!50HrMg4mRJ58eGgef}tFVr_v5A#|oU80n% z<%Ztn%IGb+RGelM1k*K2`Thyr2HVRz%W+%`>ugeDDT+ZEs-OGG@f=tlku;Bw8FldZ zP*s{OH8G!{*4&1ODqzNszI2=|A{*gkB+!F_@F>oOGi|<~(zUWZ9P9wWMK&&aCcN>HBS!d2lMymRX8q$zfw#n4kO;Ls%5Sx(DpfWz4wtiN1@H! zr#;VTXAw=nlxm-e#(rC!E5y=+mLi#PPc6!yu-Pu@J1$ha|3STwH_x*3pi}Hi!^fxn z5LiyKLUw#^5H`lp?~w8L)65jGr4LV-9Rq_97OR4cwXXYiacpZU52s)ia8j6(6OSH^U^q^={BlsRE>?e!o!(_|T+=f}PD_cqN`nUU$xJcbQo=FE5c z@Lrf#Pp zu3%`zkD>l6^1-LZfi9@x_|Py8*{DqulS#oi%kTvM@TAo6l=|?r`S6U>@T}kPT+Hyi zs6qkC@T-HN#hjR>DFw~_VPwOjg7C)h8b8^FQ}#Olh?K<07B}^dRQ9%$A~a~^PqHGP z@5sSb^kLUXPD<9TeBvcD|9h3hV`$5J#y-*WgoXVUaakpL;rQdH_?#ed0l3nK!ch=m zL<)4|dprHKFh#_9Tx3wfn2O{##_BGDKeno!x1x!UT}lIqBi|i*Z>BnP?^Xhfi# z4H5~L{YkBu@wq-3SNuun8@p7Ia)KR7x> zfr5W=6rJ0a7tI4zMI2>xoqxDy?)n1CG^~I5{>cq6_iCplB2?@LOAI+UFlTE z)HXmWGH=<@amIm+2_$E1>^l;w1~)nv%;5HY3Rcv~3>`Yc1I%;2^mM`qcqk2nz=#!m z@*t3oS)qMrlkOesWLGabyTi&*u~tV)*tN_sGPD;{QRZ+ROYGI+P5oraoisk}Wc*_SY72VAYM-SSQf zTF%SzF1A{(*Ya**TJE?|k5Vm9R;bsombV!6+p*Sn4JZOv+s78_8w>p*rtL?{>`zPT z7^e8sSuqf%hzwd*3b5458HnCYgs#D8ydMx63(g^jjI~<7aChb67yFf z{#YSlS|PDTAqiC>`5KzC3{CBZrqw{xQ=u7Q&`f7&77UuL0L>AA=F&p*P@(z9@&(iK zg)Q<$x$?zf@+Hplr3Uh4ALYw=%8>_#6$-F zk__=E4r`F~=R!x0^~W?6$5SaMY!xSC6{i#oroE+Ryg$tjE6!bioDVZt*j8M;{;(8F zyqv0zT=5oOHPBf5tG;}_G(@tp-o3n`(X{#3U`J!6)N^Ibd?h#8P_1;OwAoOm-|%?Z zu>8od^3gEwX=QJi`NF^HjCA!xVD+7@(VEoi4XNVQa@KjQ(Opf`L-*?K@akF3>gJNs z!}Ka(XBDYo1X@}7H!OlOVhA>5M%xiVZ8b#yrif8yh?%DdL|X&)tbtb6Q1aIHYm5({ zjCX0*H*rncxlGo@OqQS~YX&B-Jl1)GO?Z<`c#77!8%?+e)>UjxR+mi_k4;wonyBNN zZt!euiJ30ano{a+XdbVjDw;0XZfJRLV1%2_r*3GMZeX>V&JAzqESnOXn9ly)(8V=F z5{a7;)0xfiZ0Z@9P1|l-DVULdGn)$Av`#f6FEg8L*|ZrpqhB$bINr47Fl9b+( z(%kxJu_ZkcBY`*lo@@g`W*|eSC5L7pFRq14CIT&8;Anh3RZN2t!SG)A`M8JE$^cVg zBUEME#&Co|cnC14Vs2$pEgfL0aj?Vd=wSQFzHDOU4dQI6an&8-1BdPD4*)F-fabp> zn4*Ox+xEjDF)=u1kBA1YxMQ%hB9_P*6_J+XF)AjJMc{-b^Op*Jh`8kS=L4b1gH2-9 z6LB8~dUxKPyx+UAuXl1>2 zm{I}rQQl;GDE*(Kf9E1@##PZQ5xf?W;k(2gd!e=bg8KwsyxXy@p}3KKI!CE4h7#k8 zjPy=26N5>inQ0e;A8|5qqV9Uc=Pf;M_B_R{yicO*_ADXypD}$!qofT*&AaGxtPa*N zF>uJS&!qzeKfAyOg#6^XvCHvov25BW7qA%fGhkuQam4~bk#9=FA71{8Ou!-$>mTsqo7oOWn-DY=U zrwI>4vy3lGyynJ=Hr^ZjYPs1;eZ(02GHf6`>QC0I;61$VOajZyZ->WdG}&Zl<9R1U zx5Rk?mN_H4w)b|1g0=x|ML*nW{KN?;V#WVDQ0+I!Y^X6h=ixZ3k`;`L9#u(ug$IL_ z95FZcJTx6i((UqHU|BcT`ECwJ*A@;V)DAn=D7&)17h!fiQ%gDnaMv_dwiga3WDWwi zU!HCh_scGbe;&Wzf6v|MSkeP)mwjO&eMCE9K{0P3K`9~cZvh#FS)DAK;lV_{U$nby ztGjHAm#69nzzpbO9_eLK+>#ucf84va%*F z4tbubO;67>G&H88WBC5v=l=&&l>hT1{Lee^f9DQ7Pu~LnUtjGzUu8L2WH4A;x>#qw zIglz}SGL^bc6qQoSXaLK2Z4f32d)3T-WmA3QhTVrVzW1bSw0QgP`N#r@Wy)OKYuTe zW=Mw6DKu8^PZp>Z8xA+t9M1kW8%$GZsy$w;|9ZGG+*EhE+V%sRUa`6Ue6uf_%V?yz z;c|B@TRvT}rSbZ3uF86Kq^0ThbgeT4Nw3t}e1Ew&S!^`g+VXgNwmF!t^r!Xjgb<8NF)G_eGLI7vt0ARU@KVj#Sz?F`#~VhzV1h&X}RuCVNtLiK;yEv{*xhqeIt-1 z&T=D&J+ELRn7ejwBji;N`(`NrjOAvS&`!Z-xaiH^<}U~u$5w5`56J6 z2L(BCRtJRzc|`|BCA9|!#lL%yoQEaVGggPC_0PZ5Wz9FbnqV9V0RW6c!4L@mvJ~4? zf?3)S07Ay(BLEv^h7AxK_c;&;$x^qOpmP@=0mp-_?En~%EuI=yon-D>Oj-y8umoBw zH9<{zENgrS#x9jS)bzCdaL=Z9+Tza`VFNvTy+<(7@CH;3!eoIfR6rpsMO6{uKgrDR>BIMwx~J zfY>x^b^sajE<^*-Di>f}Xg0ZeSP0!#xgN-_j6iuWqU<9fNuJs^j>@5c0F8a1aUSk) z2~$JGWiJ+xT=78Cew`CdFLtOKsKkZE)D{d?=<_n$$KtQ0lJjg2q>vS-GIjNTSUe}=b2oL z6pM5KB#TUo&#N+j6ga~zSSaFX+CHG>$J|O%0XG$>E#SSB3;`;W1S3`#5T!GVv_j}( zJjt5dcP`V%>RZ0cT)dx0z6nQQCSd_X6#(vUok~CUPpLd(406r%Q*jBB%MC99n%pus zb6TH#aR9$OZ~*jLJH6fwD*8bwL3I&<5v{aJ-r%4%#{TXK2U2ioR0$&}p^lp|snVku zC1(W#UXKWx@ZMv_n8UCiFeh~Y6pO}lB_?$FEKhj69FGHID8Gzw=VA0lNvnK!Tm2b} zFFb}th8eej+mzNg1hB`|Q`V z@(*>{YXB*A8`4q;0K*++pjkA*XN=?J##8e7z``VsRv#Q zlO#Gwg?HjB14;Lcbd@EsIuXG4pV>?oAXpK!7omgxo3_UW>ywZ9ukXL~8EYYAK0eU3 zoO+RLkeLoa#5Rc@^C1U-|LR%epq?pTq>Ql+ zRmi~zw3vH}@5Xk6idJ?QRnO&Gtr&Z&RD_sIW6Z+b=5-bIS9~U2rwe5Uv;+t!Zl*ev zbHcoHKu3b^H{e7NlF?t8wMb@f*ub&nyTqDX*__OO+~dmF1YH{?KxkYfk+%dwsb=nO zs~hIy;d_%ur#T+U2EJVl(++1J*#}lJ^6W*3Y6BhPVZamfIi_~vSNJY5o&?epET`vT$GU6w6i;4QaPe!paDt>5-P2|2` zIlM9^AG*z{cR!##QW;k_w9V^zKWL0smCy^_;Y+?BvQ(@}njP8^Y`V`FwhymL*@f=D zTf84}8>vdW9oiLtydOp2RcD|p?0ukq7zT9uEU3hj}NnL zc(uQODjbg(3A6bYz&X0xHR_7}mTj@P6%#GC6)(s!qc|I<#?WjTiYac%a z@c&{m_J2StdU|^6>i&1hn3$NvGhLytZ~R|q#eazv&*$0yo{T+rWY2WPb1e2;ianop z|4XmY22im)9G$KGa8^?7O?IsI{$Am<95h;g;5b2hc5Gt zUk~o{*LlmbDR6k6TH1aPaPhoI?yaQ@7yVyjbZjyq8OYfa0Q{9FB3|MjFkuz(b3LE_ z`wxzM2oQIx4hTY3fk05~rv0fUFxeBdT%Wg*w*{cre9}Te)h9!E4*P#va$psYFhT)G zu#f#9OoO^OmXJhZ<;)8so+3bm6wEz@J zalb9Z_d+4%fLT_#1QfKs?HP2?{ae8I&qUgk`7c$i_5lei90aJ}&p%0c%Q?EU#*Sek z{kA|POayGnb`3#?pn8ssUBqe&1-ttIp`WF**MjbHVIkUYtCBqFBTkcW6fi32j0tOigf7k82!BSo80wrEj?DpWa+`%`VncR-;7}}h^A)5A|9dUJV-!V&Y`{AuH>tP1u@dkWumvR z<*j5d-W|V`^&59b00(;V^%Thi`Q@yP8TUc8<+NDl~3_bwB!rgtmuJ#lg4 zn8yni55V`ud10w#s}>2#UrM+adrjz%OWhc9Ybsf&kr z!`w`Lh3H~m#+kmp-?->PvC|GXd#C<5RrjW=xykPJ+|x;K+>O?~6O5|$LkIry-xiuT zacsVX)RE_CzAXLqz22%-eNP~Q_Ym7t6E3S&2>Xv}KgPH7?TWjd0GblRfyeLYJpOIN0$|D(Nlu1Z+t&YN8R z;nHt6lS3dWKdzTMver{r(avXR*;NwZh;Iv_D7hcS0`<>Ls?K$3^=XrB)Sfo%%wjtVPZpvIifqbuM~0N4 z@fYsZjl&=Nxc|?Rv0*FS%l|PMTf@o!@5vZ6|D6B`@qg-3Sc;-B{Er@mH&J!w|K?F_ zTxOE}uO7t+aW>t5c@zlW>i_0Z$a+k~K6@0eY%S%@{^L=or@v__RCFzxOu2aWC^QO{ z{^LPkEU#pb!LFp~lHg7d2KD-ZcZzo`W(R?dxLM?jkXXSJPPb8X#i@ zYvpcTC8B!bB9&N#{w^mFx8S<$CG1f2qp=nMfhb0x+Mu1u&BYvOq>itwjMu86I4STLh^{vbK{(OLdk4bFB1zu@*>e>Jkag8mw zit^DJ$N~RkA&%(wi`#s$=^_4Y#*2j-UjJs&;0wF}ZkNR;^Bcfdr_X+-#fW)~zSKKm znDaYorN*{5Z*VR10p{wo=r&q_aUBRKW^n{1u&;fGXU}93+vfnQ4IHNN5-$6=;$gZ6 zJePU}m~ce61YXL3}fkbd$s_(4fGQ78)t1A)}vLTx+k z;sQ94&gcAX255O60((3d(#Y>lM9rSk+?X%{fmwW6ubj5oGljGsu4jVZZjMxE+@<=x zex1AMUc#xhTXYR7cUrn{5jlaC~kZ>lcCzPOJ4qlwi-s#M-~}0_Rjj zH3mSEjJWOeBAQ_5Aw=r5#Pv8?BW*hm`W(6D7&y^a(UbdiH`|WI%NA*dQtYp-Qpd*qQ__0!Oi8i$q!!PNoQqjO(-@ZLsb@c8SVJ< zA$md8ySTPuM1x@NO?^^iW2r;gDPc`jh-TK&7?9x9jm*`qZ`;V3Q*wH6T;iyjUDCXR$mgnaVDn{7^TciWyy2Bw@OYC}4N)ndQ6hFXsR-Xi1eY^yq5PXSTRN&So68UiZ9kl0-qK)8#Au#Hdfe8p4Hx1l7gfyBD z(VP;tkPoW14;edxBEjdb&W8r$JKden9=R{moHvWM@8qodVGQZ5lP|lF2k)J)2Kf)Z zyD$9OKk{6D2+4Sh^!dD-`!RU^1G1;{EK&Gr_({8I$t3u}Yy6;h8j5#*8l?WJ_3G-{ z{wDtZ7;gT$GV1zs{*Y^b<32T0p#U*wLkk&=hkUar0i6?dfIw7$rll%)ETF2+tuL0` zF~J|nqI1Dw15!2=j0J4e{6s-j{^n*3K!c$$SdJnkjDe)yPb7>1Zh_tfZXtDnp?6;$ zH7!Bc?hjHyv3H6G@qvkM!J+jWpLc^$5xkuObvme7hIZjuY;hd3+y$FtA z^oDB(i=de#6#zQWm8>EuW3B@M5UNrP3Qq~;LCYRsYWIQf(_-CPg8nl=I%8h5t zK{YUdJ+y+|xg!sjz^-IPXr|s1dM{wKLS(j~L90&2`c~5r4PqSF0kBmu{MTm&$_mc5 z41e!Jo|PYd)8!CI67O%9IGAr}ljHV^*~G64xc zHV>|3vxyj|#DQ4i6GY=O81hX7liyNBBjY2b^63)^^;kKWDqyT_>_5@=y|8qYK7O+F z*L081giZLE63wfozqKp(0e~WOLZglUsMAUd2cQWnarTGryK$$YQa%$Co0g9Ln$~Y? z-QkQnxmPrJwdBw(QO0`ZhrE_wI7A>r8T6P(UZ8UpL&a!#xEnHf114d1yWb?K)+w&=Jkw zGhH~fs?Y>;K7LL|_2IcYI|J||KLEDo0gD(x%lj$^p8;~F0JTb{dcvPP81y%OBUZ&Ofj|9Ojb2@4_DU z(fQJe4pUAE2MhMI&<^d4246YLlr@;-K`HrTn!g(h>Tk~*5;Ko{fPVpiJnrX~#+%h# zZlWE;+~v zs(os7-p^jmmqahDwqhySZ)iZijH$7f{F`p$bBcF;p(6=oU}w{R4hg4M?hAckjcDF9 zFT#u16Ym5v*|amYoZ&La&zUeXHoG1BG)t2#HfQyC%2z!O2}NI`9+aJREzjr3YR=sQ zJM!Oge158rd0+PnbU-;?bn^6$Je=Z*1SLrB`G3Esf>%mN2^9|9y+xZ?mRW3jL zHjscL690_KbT-GkC=#zNvO>cFA7ac%84~Fe732~0<+&jLly{dC8qr=1xG7F%jII_a zp?jC#695D_xA; zz=0M4XOS;X-^s*99>%16q>38QfivYc6ud~)j@$B&D@q{qvxHY_8h#04(G)gr>VpLf zT2b)nP%!0<<$a6yPiS&wS;EXh8-RcJF~B)2KF;CzwGaQ2nGp`J@R@h_zbCg!s>V;M ztzdl{NgfH4Hv~#W?B;*-!T4#%26#i9_7tgvGo_jauTPczLh;TB1y&>INb-M!xkj8_?`gy;jUZS(NzU7R?lZ>M3f+TQ4 zvxD$XX{#{aUPvFm#2Bm`c;xO09@F6rrLsd~SS5{kdQ^#Wb2)lMV2q_h7V3Wpw&hE= zQa-hTUH&i&ciY;;s^PG1%cr&SC8o^Ops)r3MSV;}mCzwQCoJS&MSBq`X6)RsKWHXz zk-E%$I3|aI-Tt!5dMW9zAG!(HyRIzyli)q$E`8O4?$|>BH^NRk9Pe0=w^m_C^DZ!r zO0uw#U5W7s`7jon|L?DV#@_0*EV-sYeJEMUO*oTX8C&sH`_5Q;0P!kX@Ij_Rzz~hf zZ_(6CoYd>3l-rV&d#{v7{gl6=DM*|Y;BhiaOEPL$GP*%Brou>laucpX0Dg-a_TMDp z;Uv=3By#U0N`oY7o+Mh_Bmhh&DtCmi6p;k`Ne5L&?kjP5Du#wf{AGM z02hi;{@kqSzyczw>IW78WC!r3w2>qsBGBUJP4>8L)yD1FzdwnSsC$B=%*%Ep zdDpL}x@mt-${fPUKHI!Wwq{JL`_V&}W#LdBS@k0+WVb!&bSqo{`2;r&uVS)UhS*wK zjqhpAP!dT8R*>#Roh;YX@Oxbj`#h1a1h9+6Q{vZz0J9U+@TMnh6Tb8b_K`AvU5<4T z(RULmcOT4-xZi6IKJ;-qoKTq}@V3Gh?b{X^*`EztPc?gyP#?MI%QkcbxIJn+8DrWm zVNLzcoZCK2_kB@OjXo@XnNDrhFlf^-e4Y1X7f)@w_2;2t;{J)M2ddqL`0175>CJyj z#vWQvpH}``GDd#}dVdBq`twHa3?uyv>p2-)J;OtuUC=cSU_U+0^E~5Nz2HS&(EYh!LE>NX8(j*PpYbUvz5R13x_Wt+ zcKKuH^27TpLHsL8=#@8$SW;Odf+I;#a zGv6@yXE>rfj#Okl$M)7VI@SKZhq4N!ApkKx_h|N~`Io08`DY=TH@1IHd)#hr z#ox5t3jf6$U4w`A>vPzT9>1YKqkRR8F}im*Iz4`4^5^8#JN@kY=xHm_-NQ<7%cr}` zaB@tqk-sNxW3xB6)Y-q%zY)`)(J0@?jskDQ9S?e931-~a$N>MYqrB-CaE~I_;^&;VHlK-5(rG0qBdzynitObzAn+$jvJzSh{-5}|1d)dxdjUQu-Kn;I5_=)pk!e7Z- zt+q)x#Ed}3+HL&u=;JDuuU4NPcnMhDTgQLVFW_GTOfnE4AQlz_L4@-9hX71`b_`<( z!Uuqb$4>%)Y)Xeyp*Vpt0h=-btZXS#I-pHJ91k%@0mXPb00h)#gRZr;`2ev!&E?bpB1HfZ9!riw zM|(-&%=s}?yWIm#LHF?{Aa69v@VGY1+W)s$61i-$WFP^J{VgjbqhhiLKWma?Z4x-u zCQ46X90UY=yo0b8CWVyoq*D>dy_@dqEI=I9XWKWSusq<21{%s4H`fnEmzH7GIF`IX z^Rq5|T~5MagNS##kb7OLJAl`o$;M>E*40ya!iD;Q`gLE$ToQ%=wpubqM<_%}sjrE_ zsvzrk1~^3SMR}KM$vW06M-ea)gkk7a`x8Me!S(>3g21e~S`&Z*;Mf${g%jNvFedUQ zNASNTV~<@`{qLWXF=a_)H`Y|M zF9%FEANFbjcL7&8~<=8;0fTsWzRYu~;j+=KZC zlp?Zh(J;S6##u0kr(wzf0AR?<+)nGKZ~Z4=qE{(^GT^Iw`w~b%kUThW$KwoyrL>@x z21k?S0S~px*qQH_4W49)?8&pN zyKwT$?sF<_m1h_WngRVUas{^i?Fa^eZqtRPC4B6Fx3 z;L&2Y4Y@@4#ooa;MN2%lE*t`8naGX>5b(84O30hh$Y;z$$k>`3!o2s)biL~N+qLF- zpfNw@B?9Bb+u&sk3|uP{{JZ=_NxP2d>{#8}1*IUokRTcTRg~zwitzqZTbpr4gGN`t zy~BwE=(Vo&)W+MtxI8e2^f%x_W8}@x7OiuYADNqG9vgk-!kU8&w;`FO zc0zqeq?NkF%owyB9eu!eQBm>oMaDuqwP%yDjWXi%1_mrcYD zS#5K{8cKC$_zWwmVZ2IguMMsz;&sM5DT?FM)UErTZ@vle3%Qyi2lol$+ zuov5}{7f+ScQp|m1YAIVMV8FnQg(HwC$Oz24L!qRJ_dB2<*1gg6o*=+i`B2IH*Dro3t_NFBkk z;LVzHWKtzwulE1UJRB@j+DYcu7Rsc?Iv`~+qwZ^Clfj(0AIPvk@W?LpPJ$Ivmy}Pv zhJ=j~*B)XPI1}5A!w(eKfv8hgBZyIr%U|dQDz`r4rbNaP2pGVG4pK0*w#AA>k^n|I zC&|Emc@6ppRP)Pg*x!AI!MclPdQT?R`LcItRNS%>dWBw=hU`Onp$}U zl>rTc-~J0L^4T(8=H)l`!uSl-YbbHk?7(`B@xkshMTiqg<~nJl_GJ=ZCLZ9oXeO2B zKrsQy9|zzetPO=qIsNHKhTVF2&(y*zI`xB7m6%JLMYa>--btpHKMi^8JHNH$hvCPl z{i9!#wu9uSWluXGH|c|P96^G24b*v^OhG;*@_BHobQR`cwVI^DfDNla)$9wK#i2x> zY5*D%jzE1o0b=E@EM>NGshb{zwIuUOV!<|=oQqpYLhiXdVK$$daEMF1raPejekUe1 zjJzsOHSnGD z@#>efsnue8gr+{CS>Arg`e7%IzsB_8^^jv&R&*+_;5!l>2ps9!xIt(lIFKovu1h##uT{|BvWHpeAsYjEu9UEbmc=a(iCB~~4DG zY2Cw%jT={P6G7pL6nPeB4BVj zO1Gn?y)8S8Q0yM`oOqb&PP@+f`Ss|tk=opN>kU_7>hYKIh{bhr*9jK&Os8H?-7Jok z=39e2mp@Ibs7g+4=-%Qrh{S^DePVcEaBJNL{hLj0Uh`2pV(PS{m60<2S z(;6|*g6Z6hCkPxl<6&llI4UK9c^WwCe z$$`uO3LJ=nj2C#U%$s2g5+jm*HI+C^`fq8OFW2dof8UdaiA&5%-)Em{9Dyds~eeE z$Pl5DTA7Ijb;^i@C(EnTioc_8)9GZ`l9btsiU9#=J6b#m3v1wMQj65{a|x{T9kb^Y zAD06EC4V$|rq77q127m$`uKJal1Z4E1IIE?Dj-pOJ&_X9Kv0^I7v?2)rp8r!jI6}x zkYBxt{4>NAok zQdl_x!-cxr_Wx)#w=c=cq^l+Ez{$}UG1#%%ktGbwE+q`XfWF1VKL~w1rku(U8cA!} zzVA4_3M6e>Td%hJvnvwYs6g55Uuj~hJ4fIQJ%w09>m94@vSI7LC6B0aY{(s1q<$&W zK4te21XHF|h(9g93L?gg%1SozqsCt7y6fx3;FEe0I`Ej-mYp z!P{YYwkv)vM!_RR3GWLR@5^c z+09`RRVg~C9(nG?j;2PAw?|?>ynYRcqKQt4q89mZTC zH~Z|=xZ_o_Dx@{ik0ePUOUm>t+&dLb}6AW(L^!l~bZR zOcl$<_92ZVZcaa(_N$cs_ZbQ9idpbs2j?sFd86CMDmJ zI>8-d(}zYZOr17Ya%%4tDJUgXM&_V@;hS`5#^mbFZewXpxrqTys_nN_3K2=2wU2o%gjLj^@dhb<%Kko$hsXXmvM4bvvO8_8hu5 zHo80p3tOSOyD7RwrMkWix?kLN_m>uSj}~^GbhmNzwrCbNxEI$&_12(ztNM$}HhN25 ziwmK8^C^q7C3-W>i&H~-lS_-^M|xvViz7Jt!!-Is+)D$Z`u)(QUVZ%@o25>#rKJ%4 zh2*8VV*R)`kq0OAY@v z8)^@%{97^v985t$6^ywLGLRa0rjL^f3u2YR3N>{s@R$B&F zt(J`%ERA{}jSR5XwvUXiN>+gZBcN7g)K*cnZ(`_*nMrKx>~S_TGF~o(K^y^on~i^x=f7r#+csF zgrU}$j!l^{&zNb4g~d>rInJ21hvkLh7_EgVMu4eYk!fFwDSGRM#Db}1?8eJ~Ca>^J zU(;=HTdW~@<22uB8uPno@`;-Y@EQx2u?U?Q36IRY>e+lfvdQPN$(FbIx5jJ|bIWdd z^X-Wl=fBM-+AV7_a~{R5M}w^|-diYXTlZmGVv*!msBeWf3vKlHrPu%=I**^<8XVOnEW5mJU2`eJ4&l z@l|GWbO1a)&M4@0D54ho)qZj)9_{FbS~vyX2?%c1jB(+aSnouow7ydVjEnsi$qCDeoXj(6}AAABRUgcSn>h8pcYwUuGgXv))ZWBB#5Q657XrO~P2S zMpTkVUxJ`B@;Qh}JMjLt--Z^q6>(s)Zq;;$QEq5FmjK~SNipKb|H064zE2}cL>$>@J{4{ApQBAOvzCua?* zn`emqZW4TM0uN_e7A%tL7{4IB4#q8lxg&tm3NV|BXNA4Db5u$Ntuw|#8|ABQvl@~a$S3P9(1bJeI-rj_bXRJI zXSp1otYG}&xHSD=0=fW2|IKH^kgQj!o}NYzFx8!hiyw$bPb1a!de`+#%S}sCNT5q*{8?L;r^2hZwB zH}!gvX+%%;R{!dn!LCbca|3$k;#nzy{%v!5=sJPuV!8B*E@)my=Y+=JZpLO=7jxL~ yW)2MJhskGjKId|tZ96*c6f);pmv+-*W<{3f?S*EPRc1uqc1Z5_>?ULhkR!^+x5Sin(0 zKrk#UQc4`AqoYI5q~_)2eROoRva$jZ^=WNw9T^#M_l~faKK>e*tSFa2K_#a9*^-i4 zQca~mlfO=o(fP|*n24yHk%^rQTQoEmE&-7?msn&VQ(%_kF%%h~CK1!hxBrx5Yu^3M zya#3t*#i83{XYW#w+K+fF;rzWKS@if%kpq=Ap-yFPW_04-UQ$Tu>J2O|E&oC4GsXJ zkt@{Y_Xnflu^5cj6%2%dX`$H)^@Xy*Kn~l@(fXp1I4bE#a>a(?u_UHX6$WDsKPJ++ z%*V488%w6Lgv_lb#~Mp#@*u$=3TRW=T#-T&i{W@v`D~7A5i|$dT(MNC+i1Hr-dwp- zYCI50q0~~f)@ZX(VK}i&zu4luKc1u1TC>&ZeOK|dvbFYCFANcbQn~F$zb6!r)o8M< zet#sMnl@Isz2R^oo!4$#%#G)GCSN*=Ql;aM@l465N~5Wc=JOT)j{~_Xoh_FeZJ#El zr#f4&fAs~wCsgff`@KJkj>TkJ&>nVBU!aty+TCG;gP1A!-ek++;d*Bv3Vxln*N%Eg zFIz$b^3G@bVTTZaWGhpX*UEE z!KIE0va$*Mj?D}MAbFiv%R(n}W7e!aBUF)50${oj%Ox7rg*fF&)qKzuvi_ULZ|MY01#f@deGH zih&R*n#jI*02jJs1~$^8Wg3f76_aF+qrQ3xizl>V6bTIst5+p;wyA;a`KI+E99P{< zn?~esr7&592T9U^Bh}lHN1^ef0DHajRAp6ihE~cneMlR`wbK!(kG<#uTz3_pO*IMA ztAw1e*p^N41ji6fi;b{9Y^tbyzKJ0r6@q00X5r=P11OTp_1&gM2CPX`%VYTRS*nSoF>Gl1|j*>VHjBqPz+G^ zWPxnt1&9{MfYP29`=^k-3rQ*(CeG{+e=3U&TUYFtf{zN&(TzDf<98VYEmj5opfk(#yHOvgGBAjz zFar$D(yCU!)A99>dzmHWVvQ&a^Ki%jFzsV8&}55*znlc3J?eMt(1UdYXB`k8andY^ zWgkh9zDtoVLX3v#MR0i(2ga9>c$wsQMQ#(!>2yz(A!C$=k-&9HV1WJz7|~w=_@uxotj}Vra@K^DY~Z_>r9*AmLhg zmvfVidkS?{vL3$6{Wr;IH(m~(33$58i$N-bph9N>gn#nURLTtgmCuHX{3$RAER$h` zek|c?DkPpNll$O07vuY zh0m#CA@`|?`WRH99tK@3B>h{api-fcHojOQ^0!<)rb4q+L^I*-PlfJZMzzNArJ5ke zN+ZllU1XHy`pmyoF)Xb5E92V4<;~U3G3T&tmX(fmt#q%cN~8C(E8S0jYX?tRO;D9q zH_%$@!c{J8u_{-GMecc{zgAf=l4Xo)+&82fYFY_StgY#`H0EQn8%VIO&u6Bx6^mEf z>SU!XHrzMYIYio-D{ZX9u`F9!rmDr$zJK0&B9yv)nq@?F1t1_Lee8a%c7@{s5YW8Z zs#q3)iRWZz91k6r89J>jtt|Os`QPj6IXp)&C@*m>B0pst0HYarf8?CBz2hcu7*yJR z(irLTAXo5RnrJEhg7gh5q1O6RX{T+Ms2i(19kR|EkGOcD)Q77u(xb`oj~~bWnGCm! zboR$P&_9J6cFeCB%4T~u4?0iP`K;)!Rhr+!`vVJM!Iw+*(Zx{3&mv|3Gja}T6_a60 z(s{(#5T?ga^sXrO`b7ATE!0ux$baIa_2XKsC~*}*I;=#6q4?ZlEZ8b2@k^<%4bl#M5<4jzDdvMXmrUWWma4VP%HG#6Cp9ztX-7(t(OA6w(B= z6NHG&78Ek)+Z?ANup3T;Lb!CKH0ElBQxc+NVV?s$i8B8QO3PyO$(+!nl+QMEuWX8} ztO}^qqBb}jswCL6h|B{?oh?kBb({x93F0OGaAf?gFoz~`E>`7S`nHy}V|v&0px0%Y zcB*UtGowqY+vE?rsQePB$p_&1-ox{^y&td~)6K|HuygjgZPS2TIyS7u2qixav7~KO z{w>}v>E$2^&M!Q!Q=^;G_;MKRAv|eaqm!}la^%`9Jnhw^opbkcZ1z`pHY``G0Ppog zoL^)r-Wk0fk>SKXstz@&deSv@qVSS7&m#Y<2Fh9(+!GH15|7)6jS8iAT7 zk?JcL`26EfDYk@+ek~xT76T#w$Qct+-Y*P^g}TLBx8KVekRfYy3p5I72$^|p%D1^; zPhNooRej_(S@XYxyqny!@acFCxpfE4_*OaO%06ub4CBO8Um1n_85c1Rlp+Aa@PQ=1 zO_dI$q@{dpfc{%6Fx20MlKj#c3<0QIUWf(4PP-rErC6SWXv}DNzYNo{?D3(RumRM~ z5pWnD_l>y)%#la=pEaD>tRy~oiZg*_2p$G#Md; zhd_ivoJR?cz602kz48~qZY7KygZ@uite3~uCT^C@XaY=a&L#^+{zuk$&fM~Jp_On3 zQ~Y=v*#-$bD%Q9o{-{PP2b>U>)vrhVhRjUT`6plwPda~H`G8UWA`~$JTEFjY-~AYv zitY^+L&SLgg`e-+g1mz2aGi{Jz$h@O4?^TQy3tyuM%Vz(-aVdH1@IgZ69^E~UP{*x z7Fnes4FuDaoAFPfaBhs643q%Y{zgz^j*+lS93ff(!J7@`p+#2e+S=%%?(gXuE zmjUBpR%nb4k*NXp#6;P;aZ&O#C|=OpL@$sjpw^m(yo8aOC%ziNsQlK_VCe6^ zk)($RQwHm$hp$nDWu!+lQ$($$$A2M@BgsfcCr{GLNY5Zk%gD$UHqLp>ux!f67tX|$ z&n!`d6#8UVjFFVLXV&bVY)9HT9xy_|BnJ!ktAf9o-)1b(Qvgi3&sZ%dXC zJxQPIqv##n3b1SdWP0PE!g8;0axX~q|32Dd?-a~BTSB;dHs_<#ya*(b`w zzEQ%Uk`|RqaWBr-{gUTRpYN!c-!26ZC**JwaD08FANmXUc1-t4*b!|f?*PSj$g+Uf z-5{5b;$K8T7QgqSwgJsq!Eg#I5;VnU&|3( zk>XgN5{8VD!R8Xp^-{ej7fn5XKxUz83&0IpYNcP|tsmzLA20cKR;Ih|ji8%Sw3y`oMZr|3w=DN@z0Up1^>Iht8j-%&NWUNQYtRZdzx|D;jxR=t{8z1~s1 zxn8~fRJ}u5vnNt>pkH(3TXSMt)1qH(kXCc`RC7aGdn;1=N56Je-}S=Rd><4Kz?-)tvdDAIxty1u4w(ftrpgVJAPI@X=gpiuAV5$ot&(J zMzrDgS^aw@ce<B9Z8EVl_%Ulq0xl<*8|nC3Y<{W>=(12E*-6pOf89VdU0@HiT{s z6til}LOS8BBGtfrmRou3BTD!~Lo~%gE`>a@Nmmh|Od^^gSh(^87(!P5HQsDE;c83< zBp*vCcc()G18zLOM7Ff$hgRG8Rog1n-)Q^G3p7YAdDChHV)}9%pEkW{cgR#ENV52p zi8^*J*c4-3T#c20>rdt@>&|JN)xA-oWVG#yxS}}V2pvSW3@KQ>xu`Tk z-eehLJ|W{dDKMNhZ|WJ)N!yG`D*DYUMT52H_}TsirCs5? z1O1HW&v|u=UngB!=XM7@J6@M{C9~9z79Knx{fT`vVYIYH-+@w4_#G>9UxDOaAGphC zl{7vssydX${8HLdI@J|QJNR~yA1;=(ij4p-VXf%!G~tBbM7(o01dHd zphn*Dmm!JGVDA^^6Eg1&*C7knL3}t{SA-CUu{03-#wCt?Y@@?`7c8Q(Dw#xTRXZmPZ3msFpX$P^FOw)%l_4!2y*tl^x^ z2Df(;My$1;1coRlM>6ND`zu>CPk87KCY})|$rlGeb-qKTqaRtD6{6cCvIi{^rw>`J zw>F*8M5jF`MvRFI7NUEGwfx@`6{n#Nzv}txR!(8Dk09DjACuS4M2%DuF?spt9&C(w z8%$hePdqmQ(DKKCU+L~Qx$)sJ0C&qb)nU4jHE!OuCLuu+3n9k67P=_2^vK&3g;ucLRKOJeK1R{-Xf=%qMN4Zm-Wt9 z*DIQLp2jU>>>yFi$B}DVeiMm?W8`b&Gt1+dhP|2?>wj$*#$LwIl-5$jzbHPhgY249 z?APJr>(yIUbvYXyUu#;vZa`Z$`Y1NZw|shXdVhZ19NpR&GBg?8+U&31oNzFn7T;>e z+?q2qUd-A0uu-=n?y?Sl-Qu0t+M<})G2G^3-QG8JI_lmgi>f`n+`6Fn1@5f5HvGjd zS$Ch4^)KgFbNjCs_N=$pUo|8<2sN2VMmr@wJ7^M_pq`z)wH>VK3^3(xy6`T(Y6fAz zZhXcrY3`?28=^`b;R?D~8Y3{mh6}^A3i~X@ne}6U;sjUPIb}g#8C9v8qV3c~J8*M4Vz-jYHnS0)1d;7^bA5k1aXg@@l8mIe-v zvaVDlKqq-UCPT7^Mbn7_Jq5c##l1mHRjQ5*QpX2`!!AZlnK}d!Z>*x+DLxw}P~H@* z{rSNk=IHBZRnM39J{PnPtQhe#TW=u-AWrY;Gv?7&@6I!!RIjS-JPi@=jVvdZw-1g& z1i2#@4BPo+Ac9TuQ+ZpO6A7@}`b`7NHBH)n$ir!r9pCe`!nIE>()w!d^jT=+$TRcV zU-X}BJ_KNRo;()fsm=88=5Voqp`^0zk=()+0`VQ5=>@ryG79z_wb=>d-NoQfg=3z5 zh5sgiGR-iU2BVq|E$KUxvNRwJ%XQr8vG^DX`&c^ul^pUla42>#`g6Z*;4X9hF91x7 z9Zi0w=w@3^Urgs{@HP0jM0vGPkNXaX^F-n;q#me z7^((#n{nV)Wt#G^A>p$YiT!PpSXhuo@NczTzJrWo0bKd(ePXYN^(mOqk@Z>pLVC{9{6((@yk!_utDP&DesrjU$F7sm(7)z>G%G#?_WyHy|&@6_C#-cah?apZx&u}C)l26y>A99Z&z_1H}Bz^g781s9{1ni%4zVY zIQN%d|2<71JCGn`OiGz_^#INbA9Ot$fANye7MilDzvJN zhBCFzwQCH!1CYtI+2jx`Cn(eP)qy}{0h}g^s6P9(76de~p|ijl5D|z)PF0t0gV<+- zO!`j=(BFspeOFsqRQuLqJcZqOI9u<|YC1;{S12D6Xn=seB9U9J%k!}imJE*WI}T1OQP<0lH+10#3jB&|!4%aQlI!c(i_@l!j04XG_(_Be`2vJAtknPwbr= z-dkPyQ8nzWx+Xp?2>p0qwVOVKz8jLf*O;Cv$b13vS=6D{R(=)fi;kho(onkpPfo=zoVK2ARt~$jRUP^ruYl6i08SY za(U~Od1qvJRrpsd?n(#-kO1;+x1=*QLwjPXFhp(xF3tjkI*sYS(DOO*CAh@_rtDI( z0T_Tz7JoA>Ub#NcQ4IJ@1!as-QXdU0cE_KTK|hI-C|{T1*V59lY}V3|&7KfjgD73p zEEp7dD9V#nVyn@9da3L=dLu=X5dA=}$xjh{c*T}mlUP@}ine|#phRNsDE|8*sInC) zCHkg~+nB!9z->lW(9q-4p6ZCCLy=nDf^+sv%PbvIbr*d6oR`aQ>AC46{d*y8sy-_N zWqEI2@aJMUt8ko@{%pe@ttOs-HU( za%6~IZ;8e`NKGLUBxc6UOFChWWC){!xy%`Fl@a~@=RU<)yU$$at8fL(9V_(jN;iS~ z6)3puWooj*_VptSo%W5*p!K7McYPjW^yoGaU>^We8Tb`=j=@#7?D$zC@N(e^h%fYr137O)!~l!{+=7T(bGLb`z62jb-51p4#s3Be4ZCm~ zoVs=jHU?BqRRxQNJ=eASGI*Xy>RdSX5>4T%oi_?pK^=d1q6ilhk@!;DFO`<|_@37? z`jiJ3zGvaN%#+HczRrreII1p|Pn-iL3m568XV;GzS+ z;7CFc1j1PuDw`qzrwxK4FdK+{k%vGnBni@<4I(1XK;mwbgkyQm2ESj7MUfPe!cCnG zVQh*-``9K$*fRT_Gnw+8rI0k~@@!~qhbfjvn>3}i`gd_8Gw^qz!N%A4aCsRs+>|yJ zT8_C0wGcD>+65VA?YT&u!9ty~bXgA1xhP|cF~WW!IiA$HXfVz`$x@q~Kmd1)Lx?%~ zVfdin^jxghV9~qtba{#Uxj6F8{rAYi3Q~kz@!w@EXb9GNq&emj;{H?Cq$G+^?fJyR z%maGv(2~O=BH{DZ zd|EHkk)$t)vKb*>+5n#wPf7&Tf@2|LX3>JTKv>0=1+I~~H29x!;H~1|xsYXla3nY+ ztQt%V0NN+z8w-3F7pLCI-uLqL%ZSivp`Kat; z{@x|cqCQCSFHO@D=rnT?tH`-nflN~T9g~Rmb`qSKD!JbDv)i+=I!GI7aeJESBbKut zkWF!A*+gn$mKRI#PnjGYgUeAaWVl02V+{j}gnn+yN%hR>8{@C=zJdR6VH&0Y^`Y7C zr_dxs9aNB>I2aCF7)FVTMa?3b@z6 zldap71);=tC3RP;WoC)V;ltnR^}rI2_R5-MFkWs9@|1<^#-b{OAYlbM*fuNc#M0cF zdP7qiKt!Mi*ptm`?Mm*N`tIQSw{xZ0;`}QNH2&=ZBVk0}vln%lWoGAG0$GTk{#e#I zgx){^O>Olj(KJ;kf7dRucKX136z(iBax>htbQxL!mq@K>3jEsDAx;#kXiKsE#OCy2 z!C`J4`)qT{2i-3cD6aA1%oa36kJ6$7aM#3?NgO)PwJ|jT&g36rhxL3J<2pSJDfL~4 zjCL6l#sUp#{bE*BQ5log!}S?+T~@@E8B~^mH$P6shJ?n+%BQ@rFzkUP&i7D|*nDaUSnTbmZ2@;5qoQZ%j zJXu8BZOf;KRuYWms3RnvpvOD7kRLFL1nEvfpu-fYu=1!=IQUNcAkZC;=PA39^5}2ds~V!WuAkL{d9nb~E4H z13%=r<*7BT)SU{~@PE1q78Dm9^t4_oHsVLl*028)yU+pT0;+K4FN$A!jA6*zqC}AW zLIztk0!nycaGg{{`gg-t>9@97xsaJiz(aDt+7g2rhDnr6wc^eIu?8BRg;% z6JD2<0EXs~9ykjF){WCdYgjj)tn!2&Xw@_I)xN!``u26L{km-aB=-6)@58*bQde{B z=hqP5&K$Dv=g%Jw?RpSH@=iq2qUF8#B zNGN_ms!{_O;grH$l8M)J&bV89~ z3-@77+hOZuxsQ>rEF*53@}kfY5BCuP z+Yzs1dEUqopC)<1juHRjQs2dqfMlA0KO-;$8W{d)5FJer-)P7Mb%^F@Xcu*;`)K$g zZA7sG+%RY~a!>)UV>J3;6nlL%c2d6FEDJvbt-YP^PG7DQH>U1UA%1ZfPZvmN5=bBf z3<*yGnxn12Sh^apql1W2-N*7LBk}&h3i-w}`OrFu$*;#1=Ypbh@!xwt5+;37Ebx~j z;DQ!u0>|v|J8hD=TyTG9V#m*prL1Fi0x?U0c)g^cryI;N1GLVB+Hxf6c3VD%hVibK z2*T0vLi@la>XaWZm@8o1KAWg6Cgr19#h)ROy(T!d2rrWe78cfaU|R}63MGsGBT1%dzb7>pQzwb<*DKUKaCXl z43diW;CRpGIJH6ia2JM7HU+v*_6iR6y%0uTKc0eQLUVdWe@4aD;$+{+BoAOPP`cKzv3%tmMaBkTJ z{=Z;;%LTz48ljX=#I6g%%?rex3u5dAB1;Pr^g4f#aP3oKTTZGotXN)@@-1VOs_If3v9x#8!xQY*%spo%ucRk6X{RruE!3XiiQG7 z)^M2Kq1SpiFsc8J3eh8bEdkT_=BDS91_v@v4o;>B(GxZJ< zM-EVX2ZfLVHPj?OdIvrABdBG^!*mCuW#`@U4*Jm!R_G2k$}S4wE{^IBuE6e#>@NP% z7J=1ntH&-8!y0kwuHBDa(zivj;a&NqT?(s3%6n58%`}A>+0$h@m8s zxLcW}Ub9Pc&q+DE*BcNfoh-wwmDQ(bm<&xW++$fD9t+BW7vmGBRY!7ahp(wuAPbH0 zE1L0OF_eDaX4f_!)Qzn_z#e&F5@#SR(ke?~$^Kw8(XUdzcd{=X5RMv;f4)W8d0{-A zZZ7in=W!UeGkDe;M0KA{|3F^7wYbX8dHcV(4 z7FtafUj{8U<_>;!$1E!*BWFw%{lxoSH!b;vTvK3epFSleVk!rh$jk`xbwhc(Y31|q zp@FBID2JBVk%a?2VW6QEqv^->!2~G@OV?1cQUa?$Q}b<@8Cw5rDMZbO-E<^{6geKs^9Fqsf z_FP)}pTJ4C&J$)F(LZr?{+4kV`Jk0I#rR0zMK7OPBW!&)Nz9;DKAu}VGWqy$W=mOp z7LrW1aoQisWc^TZR!_+oO|LkbjxT3+-i{L0Tw=%8eBN0y-?ePVba>w5INytMK}T@W z&oTc~;DSo_V(@Bi$m#;i<6@+RcCq+kyyaqI_+oPTV(RE(`u<`D<#Lwra*n|P(Ta5} zw{c$ka>?p)+4FKG)L}WmWYMT`^}ndt@a5+6C45WZGA{pe{pfOs@M@RAaoaI`^zD+D zd4b^hA{7dq4Bun>a4|aFV^uCrSbge>gwKcZCUnm`|6tE`c~k2V%qWdcEg?3 z_5It`AMI;ax9i6er{mD;f4L1$%h#_0S2x2>kK32o+@xSE1pc5 zJCo~+%_a|FVck;x=mn_thJN4%Eq|jeuq%g`74xB^t?)CrRCJN<_c4cXi*A(F-Br0; zObJ!A?H8BzaKcy;{oDdljt9(#Y?7bp8Sr6Z=ZT}=lxjThKJbs{0`MOO!6Wv<)-enZ z9U58j%SFPDye{4YE`AHH9(dAa;6J%iDu!e?-{#j{5nT%*gAwMA8r$eWyX@qpF|6M( zx~S6Es6b5ItVQTk#(7E~lYAUWYb%Iz7X0g?!}Bf-P>%n5 za-(_ugx_1(Yx$u|2oe{;^OX7b4-= z>ETG^XtRQcuXYpT3#G$&a5j6DcRqBYE~|0b%y*xUNp9aR2%AM$Ql7YoNJT6HF= zo!mK6$M?^bcMT^`Ew;Cdw2DZp{p5_8XPD?$LEp^xt}XAI(~dLp!7-V~)uAn345+Wp z8$w!6F-5a3OLnhY0sdcFW8j3=v3(z`|AXy5Q;9t67VU$7(V_Quh~rooO?(=3mrKx_ zIz1&UpuyVJ)5#Ml{xPW5XO2@C1Oscy3-kl~Zv+8g85N5-Rt>y&adt4PUI9>ig~xe} z69&wBzd70IvhP<`D2Ls7;h_yG8Ug}38<+~v4@5vkKvU;Zr$PV%3DLyz`>2MaQGm`D zCkf`TI2!pxwlBAI`1N;=fpJu+Dn3e-^kZ1+g7Fj&9^1NB3u;*uwA~>mR*#K3F3m=RiXp zDa>uw*IV7LuU1sJO^XtP@z?;O|%hSW%^)>th40t5- zJ`kDIn<@xh!YU7jDe{jh1P3Za{hi>xlq!_Owv9TBgxiKXoH|m7CW1cGdoP%xqKzhs zefyp|l6zc;Him!Qn>LoQy^S_b+!f6{RuUvkmmo{(b6_jM(oUCj0gRwa{s zqf6DZUDJd^3-uo9(@i6V88RLmd>As7JoOl|96BBuvR&?z8FD;4%M#OE&)OODzCNLs zj5x9v8pSGOHz`f+rGC1!DSUUob?CZGQl+CimF; zH1b>c7;RP;&p2DLJnqsU-@KvUYx6g3=7S1pP<|+EZI7C&Wi5n6W^R>&!UO_9rPBn< zW70K&cAoAE3IiAV}!C!^;BK%6@5kOTc5Jk%(Il#`ItQ-L#5qQ(*j={FWsf8g1 zxcN`J?ZhwSCOLLUQ=d}cN@*)q2wgUBa8OE8R6BTXA;biTSOEsaN#wE6Lj(cNsNX!K zkY29lfEax67es>kH?Vx%XU!ML%m9OkJa)9I*L|LK004j%j?&{!_0eOt#|=?k1U}a= z6CnEbcs`?lwcCr0BP++)4$#K_3IOEa%^!E6pdp!{{n>uOzZIITasZ%EW5T#l+gh-an$kv#nm+}xSSe5 zoGuUvxTZ;-AOoJzkap5zy8+b;NQHmOok(8UI%%SvOg;=Fl5zpw^I05@K6L-Wk>hiw z9wnDm5V_M+cVt0A$D~jIbSOG82(i&JNi&b9gvWRzH)TOcgGdlHi+xJq7_gnpwBh}T zo8+lyA_~*+G)XydD2z5*O8v1tl8s#;6hGz&Wmp*1SJC;|m)dk5h3JlA)_@lL=2-M-`j3pwG?tLyPrcY$`&d$@BH|1`0PSS7XXlJdM2q%EB_F%7G)+ z&X08WU@ZaZJ}$l^gqlAKvBRffI6mYv3{Ct&;16?hiu*t&^(X6|Gc6Q5v?-8^G0j{g zPjk6~-Khey89-Y~)fM3j3<1|wK8VRA2fg646i1_CMF3seAGT9C;yXx!5|p4CR7R;y zJqAImV91{ww2+kwTELYU^!JU*qJDonvHR7vJiH-K;pd4o_Zv9M=z_+Af^3adBQ+{9 zMHfXZaV^$9l{N>t&sw^2ZO%2dDVLw!=Hq{v(}w$|YL!bnM!U6fiTj486bA>VbKRjt zmwIVlbhFYW4HSgf3WQyo8%iIoT}(Ra0P=9O7&7UUo~BsvoFQU$R$rnvU0cPN?Q$-m zJcY259Uphqr-e$RVo3+)P@e0)s^x7d`g`Lf8fFK45!{;+lBy$2aMTp|Q_a0t9Y zhR9uD@+O-&B)vkz0=d6q?4tK6&f7)+`M=}b$So+b*G3=`w~d~B-w58kg|l%z8k z(zlyR(%4Qu%4aMxT)CyZMxT91v!C#;xyhE-Jl0+4SZ;Y6t8%J7H@sV?Z@=QN3ueEl z#Oz!hmhjvyIj|M4?O5{=?emwK- zxvQ^~_}h<1sTag4wue_+Il$WUJNEs`K5gwyp$5F?Hnmsmh`07}%B|-v7cO=p&G|H! z)bl6pTcY|)&BM~&>z*MI|JGs8{n~`l73GZrf^+S2>_P5B=dr?dAlwgtl=p9dQ{pzR z*8dl;@xLj-x4WeGuZN<&PqVE(f9lm<&)q&eZ~f|daC-mndnEV8?Zb!o-&jt}PTjA6 zORCQfC%wlOdf(DBdtdKrzj<8t!rzFrGbJMdSX4m5d>|zi0z*Cm2Nj}Kct*hrKnR9N z2SC=&M>e8DvC2nr+(CxIkbO;%gQ$=rO%USq(F5|4c_5$~6J#?Oa)${-zu%33X?(@NKJ75%Q97=cr4nqMJ2Q`?-6i0*_S85l_6^5)~imXqK zY-WmJLrtVzKxDK_WMxY1xJ&FwO`KamTu4pAVv5(Yi`QX_|C5^RKA)tdfTCuXVwsxq zs^Gs{oaC*5^lX<5%Zwa%kDQQ(hO&?bN=-X%N=v#&%V0)#l@HQ3q1V`ZO2n)x5wq_p^O%b0eEx&dVzY(o~Rgr)rt)OR-U;wR9kU4@7 zVCM%BR=t@}Ns&kmt!PV;Xb-K}FuX`?npT{&2uYx*e=G!Xk@mxV(T6u$2ud*oi%ycz zLJ&bO$OV({sz_3xSXzQkMy^;!l}=XMf)h$r1teaP$(QvkmJgs)2rX8KrBkG#2AFmY{pXe+ zpL;B|833B=GJj`N6lG>~p&Cj%^iZZLgSW8`v_3!?y#Zl~DJ6p$gOxl(AK>MOvKa2~ zG9=^mL5tiFePM0Tt4ddkWInC$@610Uykw zSmlB#W@|_Geic(n9S>??Rcgav8d~Cv#pps<>N37(gC)!Oo1RM?V#Xwcs2K;M4{-}6 zFqIhuzF-;sOxgCwLfl>4sta+limXP9HbFUdDJk`>Ve|u8yPj>py5Br!DeT`L*Q!QfW~|Fghk$D`i{zF_Z;yFa;YO`XPK5R-8cr zwlD{lLf^Bp8kWkIv&dB2A7|JVo-9XSZ9lWXfPAVoj>BqOdo>*_btra`KW!yy1`FoT z4r2faY^;qmklG_U5PPNlRp_B`tUW1U9bevTlBPzZvJu4E7Ru^}dtPo8Gk|tf@41b3 z!m`eX)jSR_Zth^Mv9dcfwfp$7(q*=A-}}5Vva&e~P5Wcj&^=3w?Zp6;wHeAb24x*|L>aw5 z9|*D^A3qu-)avo9?3}k7;IXT*svJmN7}d|Ux4Ib5v>(&I(2=tnMPOSHxcrQ3SG7Ie zkD&b1b-R@3f@1Exwa0#F9&=VzYwC(^)$zRBmu=3>&di^^WqxYCP~EKQaw^t-=n7?> zrFuf-e0{lc)5>n_jb%hYwQ+uGXxnZD;mUFFa`xU2Wx-wYy2Jc{<@3Fmt$O;W)CYy}zfc zvaE^lKWe9Dm;RH%wj~&%KAJr_s;V(PZqBwCWd)7X1Y<|!A z(xK1(sCqLfQgzY74tiw2WZ-z&t9+@51F2QM-PdgCvrgSQ!bPq%t8Y3xt{#USe>1>C z2OJ%GIk*On&JSvwZwfir7cVdMIZ@0xQFZDGz7_XHI?u>jLM=j$8EnycIe*XBAWS%) zII@8`o!5SHoyYz@JG(ml$-ORCLu^-xEXvt@=J>In<4*;KO3sPo z+hL3LHFDT*f)6}&O2w%14wta&j7X;j5uVZYs^KGtuGFO4VY{&&=Ye6aJ2L{N1;_o# z%T0vKu7HbHk=P;VIdg5ptra`lf&-Ep+Xg8rlTHno(PiD!b+zS&wX%WjSN(x1Hg_kZTeNRAw8xi^Ru}UK4sW?tO>$hzSRA-^ZdX?Jmzi$D-*{2fv{-(@9R&UE z2A_VvC*nQAaYY=sn@{Bw-gg=)tU=`EgyvzA@Bh*#{nhK`H^6Y$|HdWT>^|D}2hiqW zaLsS5!;SKezr&GRFv7#=q1p7kz|k~6{Q{QhIltKl0Sk;uGd14NtsWM}Z~?2@3Nud6 zJ>n?2Xb-Ef78}kpD?G~~dgWuv3iCJtyVjP-HxKi|^PZ;Ye(e_9P65Z``=C?-r40d_ za{*^mFPB>Z?elOVq84YyR#!I$m#}6h+6PzpR`*s*w{O>91eI;!_wG)DUZ3dgqi*b# zlpV;t{?&MT?J?%Ww_BA02hSm(0!9X z^pIEfl=gx#wgq7ycujbMV+5VsAA;0`zCY~4uv@=!bcXiM=DPWUCXINK7Ry%WFrR`fo( z#`)5Bid3zLI8TU}FLYF2``Yby?8%7KqITLn`&z?&%uzb(7)7mWeQ~He(xGh)@}gFF zqI-bOCgW$*PtUu$qAgy226ldPryZ?vqB_CPE35prPQvZA@J>D6&h%E1j60#u8BwjQ z&J}0T?&D|04ZoG0&i`_hymzAW|3v$UUI;M!d-6s9qwsz~F}lu9?>OO-wDtj=7Zlo; zZgAI-lmCKQSGSGWNLW{!uYWhJYcx-+E>WzTsVk6IY`oXMP3omU$-huqbn=(~xQ>XS zxpx<>=rmlctfFf^LUfi$JPRb=mEJYSDV|94+F9p6lr6HLCZ09!KPL37u`0IY^mXA} z%vwiu&aHcuwQQj?YCcYUz0`WGbz-fwdjtD;y-`UiuVt~fd#kooeMBJkQ^iNMuC8C= zJJ_u&wavet{ZGC?kV9Zvs_r{+UpGsYHjHx-Yyb!ba2U`4fM5gpbbWAO+=pxvu zwnKL+F3};=Gph|bM1`H{z))a+x`P6aEZ>euU?}+J$bAwVq!8o-iR-25E5?d^ZqyUVbu)^2a$tLbh8q#L9gTp%soEiElA?Lq-X z1ZgFuL%JIgkrpXYN&#u9_BbFe{Z8kWm2#4x%lvR%%vIG#7Xx8LBC$5=nQ~E#+VzCM?qF0YR0h2C z6Zk6-9{f!#mni~J0s;gPiN-tVS6m&)(Qb%$G_1Kc-5w|Uz{$7{_$Gj;MO#V*3sv68 z)Fv$-n1DeiOP`hBwd~&n4paJHcZiJK430^+5a}qwt+K!U;!%tzCRtz` zhCbRLqkehoPWHk;uI`gmf8USm(;W{ko-l$spR!3Gq1|M?iHe6?i{2Ply}ZoUfUBhT z6iVjM{}kn1n>|O?vzQl{KmYGL$YmLrE5ZCg<8W1eye9|RD zYHX(j24g;MLBNzS)YCAe;8CSek!a=gxpoW8>IsUB7iH+>ptbbrdj;^!c!6@B%bgN@ z@LNz*`+JwV9K!N|G46->)p(aJl{z+IQqheJO9exHqMN;%FU9v)hE$XMhTv+FL;y22 zN&g*|mb~Lz`G6zTS;Y&eEJeeEm$_fKOQOBXa7Hvc*cI_CV{59*CP+MUNkb1gM?_0Y z_IQOiS}&NTs-NnPHuZDJ7FJG>7ss9F;rGPK1sJ?P+fyIJdnl&A_AKI-52EnlpvCVW z<_BC+<#nQC*Ea+h3OZ_QQ}?n)6O3LQb$mT}eL7tCt@rq)@JrX^wmds#LhMcmb3sh6 zw59^lINwaw3t?ffm%-G$N}42^@^8YVZo0kJK>57&?3S}dZxsq{Oy{UJUOB#w`wiv& ziJ&clI~{?9nwY#$3AY#7=0xadK`WjbI%&Anaf1S*Ew6gHhV``4fUhYo-Jv-$nI9+{ zZZY<;%N1`1SjMppCS3J-ify=3*2Ou#g51pH>w3ETZmWrd|CFX)aAtrZT*o8dRNG{u&T3NAS4UP1j+)AX&9gD z>A5l|QTY$lAjCR14X~%I;*HR&mD<$TM0P5?*C#)|3&aU9X=OzZR7oGJK4AeW;IxhJ zFQnf$;b?j}bHLa={8m2Q?1A#?r5K9nLT;g$Op2V=IN7EK4E6is9naXdS9SVqoArIe zBHo40zeax%*+ED$ND5w25Q=BxII|GF|5-tCv3xdLW7A6nTqs zC#@k`H)f8klMU_j6x!#_VPs(o0#OBHHFRMrS z5Tq~;=PG-!8X(Y#(;4F)Yga96JJt{9+AXIV`ctV9F=`e@#1&fce-QOZ%m=io1C{wB zjO(kSN=w^?9D~CEJFgJFWViDj2kdWd)MqyN6qF3PUFM+k(Rt1pUl#74sgtr#4V0z^ zfT$5@A9daoSIYoQiFyo|GWf31w6LLWj3m6J1q?-aR<+Zt8WC>*Ao(LzMU;+a>4^qq zBJnj%yQ|lp$LlDf0V0Djdcc5vz(j@&5QVx0h^ZEjf{OrPOl7*^AP_awQ+55uVID6) zZ)*_5fbQU~tC=5&w?L*%A3QLWD9lOv>mM{N`G;M#T>_w-03JL!+Fie~lz9cL-CcgO z@`4LqKeC$qSf3@+0yG^(fVBh~ETirqy%0b{F~c)2Z!W`?1O2+l*NtOX82&?pZ*eod zI6g;T^I7VdshlD)KY(t8yChpso>(TwmA}**Ik2dthF8A|a&{w`H!jm61>nagg*?L_ zuOOWQpr~;IVu|B1FM9yBu{ZVNVT|>WwH1uT>t4Fa)^(lB&zU5+Oe~WPCmB6EJgT=- zHYotp8q4Ko`D>uhmmGIFpez6K#ekVhIislgBGfCm8y-?_MKncZ{Sv=&W|FV& zi5QJ*pX(&Gbydh&0g*6rHJ z{l;5zzP)qJ!~*vNM$F6Xmuoml?v%Ub`PCe)y1ZixW3@`%HI zJ$Lec#K(!3kB)aQ8Xo$tA5<~bbe3T?AD`!_NW1&g9BU(OHMO_s(6ngBhx4$v1o%8c zU$$_!8RyXY9;fwvwg^rjJ{%;r0hZ)~$ZUX5$kHIn8xVCasJ1gyzZ7b`0X5@-SvkXO zOJR;1Fc&Tq4`&qbQWXCUlt3=jU}x0OQq=Gb)F>{rIA^qkQZ#jENIDm~cqG`nkFM&iFo z639&&td2YWqTt>pDcHH1$C)&tjO-+3lPsN^Jllmlzl^+Sle{b{DAMIzolCLxhjd)F zb8}qanMjJ&K8iYX@QfPeY6Rtw3+4M#bbcP=|o*=W$@`F%jxB| z=u~;=6?hnwT^Wq=8LntBn7J}ql`|SfGhRb}0lG3-@G$vDGr8b1dz90J^3dRHF^BW8 zpy9JT&|pE?Vj;q3P0(N+$D=CPVtuzs(c{AQoEv-B+)S(;Zt;q2D&7hqH$H_5KILsbbzXjLH-7yJe&cO^ zGhP8JHv!uU0mp3t7hXXRH$m?TLH}*RKwhC>0>O(FLb|McL*{%0Zo({C!ew2;wrC>f z=Y{imMGD+Rig-oK+(Z+$MH+_Kc;FXWx7pf<*aYAg`nK5yhs1VUfn(d^Pg=#_6G${k zNqmTrK-MDL1Q&NJF79t%T;PSTYr;Rqz>kLDKzDc(2(m*9B;S#I(ke+uDAgn-#TF|C zZIR+7yu@F5NqFZH6Q6Yd1ubd8SZVQLX-Ri!?Mi9g9ce>88GSxkV=Y-TcUi|uS+iJ4 zJa;*wO1SS1{05(-zq@>3tbA~#eAJG7u)8=Ep^%~_mL4mXsU`X(Ml`!Zv1~`NYFM-; zRk+a)hB|u6_N~8gZlmL+oAX5KBGJr@75NQA+0l@3m zuYcA5zM=n+=pT~%Lt1~rjBZHlkDQ2AOk4^n{{6)L>1H_bX^5YSf23lUxstwee_Yfi zRMd3;pnqSGxqlA-`8V+Y1@dP;g_~X%{1ftrMVcts@iX#wZl(^pO87JKcNdBL2?rm1 zVsSr%{4pSrKU5_0M~!j@`LpH1g`!)NB9T8b=N2HWjUEBQ2-3p?;V}P%{F$W%LWz5s z)lg26Khz0scK{d+5e9%!YG)B&kUub(`Wxgg>lFFZ|BC#9zd`=+Vo#Aj4ja!Z{$SrCe-)^w z$lo%;QwWLt(F1@#A%Azl-ynY?e}??g{fztx{e%4dG4glw*T^3M=o{pZ{Tt+O0`S+6 zKU~PKkw3yeME-n!h5QMBLH6$RFWXXog{80n3e?xXM=3gRz*585rT|**&VZd*ZKiyv=efed{wE<3%zdL7;Kg1X0&-wo-@`rX7`FjAciatgDP|qNL7{5aPP`@C5 z7#o+-{uueI`T_Yv`3~d{_TPj2#s7o+{Z-@-`d@|o)t^EB#sOcEKa{_T{2c&(jr?K# z1>~>cFCu?KfNzjL@P8Nbhjj+|L-{`B5A(Z_zij}<|6t?~_Fs$q#Q;!#ME-D2oPUY@ z`2o;>LjJY@Uy(n|e~`a_kiY*J@`v^h^7nrc`2(FIf9zk8KdkRT{%9W>^aFrk0h~%o z-ye`aWoH4_n;;-AHFacK9tes25r4Wo)zSj}P2>-5+JZQP{6SBVKhsm>4-Fr)DajOx z{2^teGsxc-0QCLHAFkwYAb$p5kv{^rd5&neyp;H&0 zMgDOA4)RCzGxB%&e+2SJ@~G=fPxIHv z-!kyui2Q;7w_mICkU?lPf{3Y^-#mo;Pb$$T6a~p_* zMEr^p}HZybw)%8Jg93a<{TGtXFQpr89gM^WIM9eMna%VOLb)`RbX@m^RF4 zt*$uBu6V7kkUY8AL)iyeU5S-lN%ZoYQC%rwoXI;~k06Ti!wRYNil5QCGo`z;Vms5> zyK{QcvxBe~+Sate+2#0(7cOOJV zD;pQSSP=^i+5t=sE8|Sb-5VlsqeBm6t3pFVcoRGu)2n$~T9;BxLTIn%pn*>h|eK04e7eSRm19=P7CjF0-#+P==jLiO{6(p0kG zzCn$UL-IDe;CyGbcM_RzQqAF^TrtfHLVroH_T{#RWOezGASj_YnIwcm;kkJ~QvQ*J>wdvxP%ES}j~rd57=NA$y4xeD~V{ze0Y#??bX z>Z#LVx3butEQX&;xqPxRJHYR#7PO**3RR^EzH6V^(1Iz^cS*e@sjYWNDT40q#x|Df zFt6k*IYt^%&gTQ{jsvZjnsuWZx7an2vj7_uG=uLb2#D8?AF96{mfIvmxQYTSF|a~K zvA2dR`um5F_>YD?ZvM9BM@%eDtc!#U=x{Tokwpy|Xn&Q%Ft<#nytt-flgj1i!{$YK z<5@~Qv%@My0-!X3>xO~~jbX07fbGg8j{`n6urN#>@X}Wsel&bTQ_uj~X*4M}C-wq@ ziY}+!$-_XJiaXM-rD>6^+kmd1X{0JbByEV+W^M6uf|>sbmpFh()wxPDiB4-;Nz0$_ zMJu0Xo7eD_aJ>_81ubaN>#(`-M6tX66q11xZxDxXkd2?TE$v_}PCe04MF!4$R->zU zqYtvX+Fb^MAcV227;*ev@vrp#f<{w!^}QKJQeKTdVi(r^3`$U z^!3AaRpajY;|;G2UAxAcb`2RnkGB-1Hxo^?I`Ti0F|tsac+OALYBkZZirW!9(dEI{ zU1fw3J<*fGi?r?fvhPlNp^B>`02Xn*uO?V7CqL2q!rSc=KU$=GD5eq%ni!fJ=(myQ!`kl(t` zynGba35sZB#ynbx$;q&=TlY6D}9qSn-pn%+@}49K;rvV zxc3`vC~j|(QFM`O=qKRKM%3+_Vh`7#cv7hbMxw@>qMP);SH{5%K*#E%d=zSI{uGai z%>0--4j}Ll$Y@?WFpW+8@SQ(;KL;u*nUGyqn6LR2iV>BGBKAHzG9-IP3!B+nyddU+ zfAYGPpvd;}%HWlQheU3)(U9G#OsS`x2;98ZTy!!4j<+*0u0n?%OpV;{=~EMp)MwHG znAdskADc%3LvRk~$odiQe_-#(pA9gWNVLB}vUl8TDQ)`3G{@6w17Yv#OvG$46(sI` zR=dAE6L(ukWjt@@*_>s35qSuby>khlg`PhkyF=?)g4LxSn=obA%v)EMLgj$U?fJ+; z)>?#Ml8_=s!7R(Becrxr-Z6*DW`6#fC)zb+Rz85sRp2VR=z=@;ITxJ;&kdAFs6W4VVB zOBXYjlBq`GnwHMfcO}=o8lres*{Gja`|1s!o&PL(NYkqbEcv80TiYD@7mXM;{8c&f z+CxKfCA|Gg2MXIt0@?If6{vS@Tx33&WBowa>8qU5b$WBrt8#e}c0vW6H+p8j&~*iN z7B6eNXNz(g{C4b;*^zV|#toV7ALzRL#N1Hgh|Mr`w1@$|sZ`hS1Q+E^^yPcWuN#~i z8zuPt-4RcAq>K0DMvwt_l~-uC;)(ACwu1!gss+bT0Vf`YFDLt|Q@iDiV|rdyhPm1U zrexbf3lt_J?Qcphk5b?548Umg(j7zKSMZR(TMS3Hy^Hh%(->9$Z+4!KTQXAJ^(N$B9sAk@6l#0 z1V4EyKwif@Db)V_CTTGXC(~?AqC`8GRrNz3!Q*?bEF=O$YjPT4&ada?$*jq za~olH%h<5f^hKcPJz3OIReM=|BMVNy>aI$H`6SYfT25s#C$dHYyUjFw*fPA`v`36g zxz2J+UUnU#>@IVaU-q3`=m%8=rj=qwms;OV5vFbWlpS?CmujQeb4HsX#xBqLR8{?6 zjtJf5&=z-vDu`WRY3wB2H^$_r+$!)T3Y_68$wX#4SU1EXp5SS z4`E29j{K3lE{Qw0cv!-y>!_qj25OAQeWJDb2PPjqd`do=KC;u4cG0v{O?@e+@X-$L z*R;p?shQa8qhC{wklqdB&o?f54JAdA>8>59d~OaQRK>iQ`$S&3~_E^h1w|4%-y$p6ou2b!-L9g#)(j zgJ(>Kt&hr|eLQH>^<%d1OYuDHxL)4*a;YoVkDNOP5s)C2<%~VMg&b1{knwyY&7cWF&R=cXUMF(dbuUu!7EQZcf z31Jeo0P9Na-JDpb07eifAlU<5kaK#r`vLE@wL{^Z#lc+75|g3Q)7zBUywgapHE+rKg9`cRD_R|)t;V?Y(7bg21GV4K@*7ifYw@~>2(qu z#Vpv^TXr86j&$|aveYY%=1g^5y{$+Cfr%cZ{^&%VWWmO}d`TxZB+;LKK>n-}y%`@S zD$Eaip>i<-)L>j5xE7{RHCzj317g#pYd2+0yfG?mvLj?{6>=qY6{-Otenc#<9Wl_j z;^Ik(AtW)lJ|24Bx5YoJXWHBqVa?C}g8Ze1Qu+!TFpz7zJSIuhMkVrAvlbzb{BSSi zl}|z(LO@^<4ijg_^(`URJr6|Nl3_8wJtwQL2?Hz>hcu>JC2MzBDOeh$Jr?1t`-1!} zJwAsYDK^J+MHlV4z9qu>Et8fV7k(_Us`JyX$Ru=c6ie?}@7GJ06oiFmO=QraCWy&s zqPS|M7dh9WEaDin4S`zbd@a-M5}8gi`r9bjnA z_P`4#EL^(T9BOcDG^egj5G*zapoRuXOSSiwG-Qd7mW+iX?&%Bk8=y$K7g%?0kuS?y z7WfH_*%mYjj62fxR;9b`(XU&petxZ8jIs}@jskaKnU%s9I#Z-Ju>zcJ?a{3{E-4(@ zRA)sBa(c_gHuwwAC)hU&FPs;l8t97=+^NgzFq0Nok`~BXHysmc`(zw{T|yLz`~`@v z)g{IHyw4e8XjprSaZzmZ`MDcniY;_c#I|3Eo`~((v7Hy+ePwtGH5F9WAIDr8kKi)AkteJjHS?y+u`-0=vlEiWglPM2C@%rkU4DRL-Tu z>_CHQ!TgMjb0L_okp|l>?^5C$=i3s8G`P2rnhp~AlNFo64!zUHz*81Vua!ZN$^4u} z0Qas|BQ0@-*F`qFvM`fF+E2Bp?cC92_j(92$%gXU`8~129sKAh-rwO7>@JJ&Y@~a< zc&9^rE6S1TfQ}l>(kY2w9vLD=PfL*BDZ^7972!uuPe;~ycd_h#d?P($fo7MIYk5p6 zq?hp`OSgLTAY(c^1FL3!cXYCAT=9t?13SxLx87iRd~tF%hg<$-)fv|Z&4&zBzG%H> z_?qFZ?2J4y`Ohs_D-!xFX?Y*9ys-Vul{nnUC}3vu!ojs7iFP7K@EMB=`c1dw`9do| zPB7jk-x@oGK0>`;x6f^GC{C(J-e!!2A$+!iHfC#bdtU!#VDy$kOr88ixoEY!xL9c~ z2$&`5#uRe$m?TYHE^9gLaR^S^3Yn+_w1Q^hv!)c(?zqGl3Ta1$Xy9bv`{y$>46E+3 zPsyXG!)-$>A`_PjWlUU@Bpy`97v!PXhjyc0PG8V?$nz@V*7AdR3a~aI1HGvaPfHpn zL%fQ&lBn@uw4$z^Mz%mz;Y@PeiOuKuxI4gZ#p<&A=`(y9%`Z68F|3$NJMBiAtle^F zHU|u}!6aaK)+42-kJImH;uhnwIh!`gYh>1FHER&xlV>$D0ENXT)`baOlPOeR&ZM1Q zS>_NUGq7$&uWwYWE-!XuROJeye<5MTNAQ`w>@Fm)lY(9{^j6$w#)Glx@~G?Qrc+#A zG8rL9!qyGCak^(FHP0#5)Q>Qbc$5@OuHI#Bm^k9}dNMn?5na=Gj=aQssNn5F+V!S2 z|7)gW81ME61wD70E(CNiygS^gdBQeYas$jh6$$Wafk|x0px>K<@_NyrJ8=c!98RHX zzIh@}h(M#ypT=^leMWW6b?4Fv`wU)8Z5yKm_g&4x8KR2X=bQoD_bk}olU=|EV+X&- zxGwvia=W%e;+Q+~7W*s>L0zY;1W$B~!|b^dykNxup4d$GITp>jZk=YH_=-X+vT^lW zB5!#fK4YKfK1Kc{coT=%XGQGPUN{AOLH>k?>iRr&$|#t;7R0yfUIrZVrhz$Z$UC9} zuTb%25EL!S@YWAR5c9@#bhgN8)(^%v^X2ds*(%J`(%pW`mv@O{Sv{tH*g9f|p98V6$t2jRod`t|l-V>P>zP3AxE3sOU?)#GX zq%uC&XhB`qd*1${*VuvBoOn|GI?k|H)wAT%s*8tf7FWFbZt<X=b`g<#k@gLS1vZZe6r?Oy3oE`O6>(Bf zV0^NNb8loBxNmgw^j*~96({M`zE{c1;>I8zu zOpThO33h$-IW7&&fc!w1beV;6gTwSqzRBuba(8g*M*hUH!sK(fF=jR8-Yd$({j^VA zrX2~!81oc3pc8yN>nUnw<2OYqQ%88ifm6fJlQHe6-%HZWc7PFk8)tJ*S`hrIVV_GLk1P9VL>r8uw(IP`JJYryB1V8xy{z z!v1_^dF~y`TO&4FV+s}1J{L1s-vr9>0MKcQh1|qxgJv@-jahLzs&R^A@I3L|G$z;S zWd;jIflS^$GBQ_Lrol(6fvM{>X)2A>0^O5XH;u9PGRD&QrQ}GKvc{;S-NPeCQ|{KueVLSy-7K{b6YjlxOgg5jGw*1jsp_(Ws$2Bx_=B2n z>9uvNV&~}TpHhJ}$lmS0zth!4dH11!0fT;gwxknnxs{cs)U*~YnO=Q*?9*)hJ_ch0 zGMy0yllg3ux7j9p*;jNJt{i8ZVq5dV7*CPE$B)by87%}D`#EwfWOFP_e`91WRJpF4R}&eS+$5OXof6z+nLKnBwC!@L^>aO)n2_o6 z>j6w&_~f2<4ZY%Xy^BY^9*=t?k-xr&*Q=TQ+ON_#<@$}}`cvWizs(I;TL{AHU)p3s zz?f<0auLLN-uQWejLg?*^KJ?-d+_All3jF_$_vuTbJoZUwqA5H&kOOiad6GM9l~sP zGw)8kjZJi3XwITlM&8}(Jd5(Yu=Yi>*1UToc~=JW!sl&_XYwNUnDw{v?!)qxLHUuy zCrJ4vKZoO0efcTThAD6JQ(X;H*YY3TJeRaQ{s?>70`|BCk2S-< z=n*4pCTu)IfHiA=EK`;>yLBu}hczd0EZdqjxA;*yt5I&qa)aaJym;2^sK@y^%Ngm9 zA6Gw4D|=kfZubap5dv?4)*5JZ+vPRq0+m{T@^GMh3s4DeXkbvHh=AZ06pz`$EL;2n z9=AzGgWxSCFhG@53nV#jMo@3#5${V#C%c*-}f4sEhtk?qdt&0)X>Q*aD>N z+a;q*oDdK_`)&n5wI|yMDSj0dK4cj49s{6JW3ogb3xsN4miQ0|M^tbQLStv^MXZZc zC+}2!0ARK>NDTUpd}xaJfW(sO6|*7q0LZ6;HmP*&pbueqE7V7`tyCIC#F;89Z1+|b zpTY)#$tyj3D{b@HHP{-RA#Y%O@OEnluoyd?#|rQbTQl~DPIB6(=qsa=fO4s~PXrtk z)>itC2cT+f<>f1{kL}8RdaK^rKQk#xmT&3xFA9egLKc&IV1vkCht;K8VDix=>veR* zd;zUp*&v2*_JzE%W_uT5-%4vd3PV7Q5bHU~Eqxp)V~)d;2vL#f5*U1vw%}O~K@a-i z)Sgm{XL3(YNfoVey$bw^!RktUR7648)LgYK^Dy%LnfzLlo_H_;WbHZ)<6`FdT2d9B2}xW~MN`js8*O<~B9Gr$NA zjX}=F;OLXxs*SLpAL+e8jg%Q(piBm7=;lyWQ?|=PD+#cS_oe zX~Ov=dU7s`;_pw;GrY2<3wPV8E25UE=$LXYH3;6bE?SjxM|pOQ=GiJ}txzXvmHsuy z{LSrREz6ccIr$3G`c(URat(^4-RGE`eJ_e&Nh@f&&ZYPoj>FFS_SdwA_&?RVtLZL) z#Q>b=2s8vW=w1)6uR8S3T(NjxHDkifQtnZvvvPO$WVyS&aKNQPOTu#)%B3!AWvY3# zg4mP%Xn}5bc}k`Ds;}O8x@mIV1gpyoSMOKHw?1}UsE(bkcKYZUy;*$?MKB6j<4p4L zK3c2}$CZzvMar(m*mf2**Dt+Sg0z@k>Dgp?_F&(dk9ySOW;bdA8b*~+TR5%;%ggt9UWfYKS2=iCBbP>b z4%BfCLhF4HL)mTY3zm};>XhTF4H_2=#!;6uZ3(zhn`j15QtG}z8tgTt6(|_gYt$um zII>9ER$~NFtqrY^53)0Bm!+=NT2T^Ps+IX96#4q()6yGidFtBx2jP()l5zuuVjhiM zF4XQ}Y0Ju^Y&30?Ick@5!7?-=yySj4*{BUMC2Oc49nG|VUo(57Q7!m4eI6yy8p3GngU_L4ojvW3?sN5R;jI%CqFEWDklsbQ^%<3 z_mVzD8Sx5=$o0NIUU#W+KhdpCr$;#YvaodjzWRl_)ZEW;*`MSW<#P)=Z>a@U2|=$F z21{v$#?hmy36VC|_DKPtL)7qLLWngrq?#|tFSAoZJ0dMi&L$K^%;kRW{d!p?bjebm z+?RF5D0lLE@bmISYE=`IxUdIILsZ0TFA;X?*PcWKKe(+%*yXZPA5RcEPzBXymocP_ zk2ifK=K;N0Fs$8wy^2j{3DZkj$v4H&dj$` zUWQkJh>pm2a3bGK~;w+R!T4=9-ct}%@hqvD^a;8 zsNjbpU;Y5ACz!ZLp&_Z{C|I;zk4(G@=jdgL3PQAL@*}A-K%|OJwo5K3X*jIZLb!;j z&A_WypBYQCF}S&))_)^Jw=}q8Vx*6vUP|xebNREQs)GHukLn&(9LWXwM97I*IRxAc z54!zMeEA7!^@BD8<=`j%w|!bZY6U~@F?H6|e(FAla_{4jlosmrlScDlv!KH4Q)`$g(u&VWMW?n90FC*?B!!vswqml3p^4D;EbN*GsgLR`ye;^96 z*h6RJW$~DF8Xvk?bU2(y#ET`kTJ=2OzB3yiFWC)2UAjuCT z_8~1AqL_N}6ZwJkK9K6iSMuYl_wkSP@t-Fy{{Op!|1VGvY%a5O@7q729(dqH!Sg?( z9t|_zD68jwMm-{ssKUmv$M7lY zVGFnnb!M@Ie1m!r3!S1KDM-`<1%ehBigJp2U_yi?69DYEv*0tRhrv&%2mTk-1N~_# zh#MdY0m~p!55iN_gAl&)BkFm3P*g?kBPf-tCu;1cY)C1*P)Pon}6!j1Z^i)Ek9$cnBqaNLmZ%_}F zKSMn@envf%{y{zd81*PefFLpIO-DtpCjh;<})}e1v53z4h4+`L4Lp>s)zeYXC z{}A=a_!a7*`~~#@{}S~;`CZflhD1HczoH(vXHXA9RKzb(58^8eg?bEv$o~lSz(S%P7^O*O z4Qe`{%;{rFC-`r!6xar8@d0>(n|XHYo&rX~o0vG$rFJR zIKRl?0~O6|-lHWp50T+q6^(q&qvdl|A|o{_>P2oxAFy{t-gE@0mBk#b0Qg16UZ<*7 zRUEBy(lB%mp?|E;4edMhj2~a^yB@`73M9A#2g0r(Ebb@i`>r?42=iPOR)yygaD7ER z_)G<1KcXIYeu;YIdNXiIG+}b@|&m!-Y-!PLg4SA9vHua zdSLu}P!G&MMm@6s9Q7dh3H2cSf_f1AZPWvH2KB%>gLIBm8Pucn3+j>fe-!n=Jd1jC0IXV1Q4fqWs0YrkP!EhRs0Yr*Wz1ir z9vT2~Voo;!r=xv_=es|k9_ZhJdZ4HQ9*_VeK3|S|mTrxv)FO1ZpF5N9uKq2#xZAqS zBJ~6S^tAnF*JkHahfy!;`QS=_W=L4bl9tR-711ZRbJiX+rK~rFkJ;dC-;f{ke&3N5 z;hn>nkkeL`qcxVR|Jg-L-w%+Oq0;^j>hV`m57^&CJ@f%eWTFXz8LvN|K|Rg^zoH)K ze-rh%4E#0ff%g|skAuI6dJq7=K|LV2JK_o+49+I5w3`} z%=A`y&t~UE+y5Bqf%y;W@qZEZ0H2~Bd|y!yoIgW7+Cf%5oDU5);DKNPqDsnwFQ^AP zu9dUEG6)PLW@m0mvjHPf5Asij@TV;(zlC}b>p4#$CO~MfB~tVCc+@jsr>Mu&De8eq zirLgNg+x6t$bUpVFc9>Z8c)9$^&r7jbq0c4Hpm_wg*O+%2h*X;_g}J3LVUiW9@S8Q z8WQy&`ht3>{RPy6{0!<5b{6#@{|WWLr0zY7dJz8|)Z^UGsE5TbP!Ej7Fp2yzX?|^O zU8^c>U76$_u~GQtmEs|zOb-RQa|Jl6bee|+&GX0Ps1l+V8MOE*Z_TZ-rxr(-=DtB4 zTN1cTX5l2hxpK3h1`>B~qn7&$LyeK!OBq3956u~s4oSyM)zTs7ih3XcO8P9$7geLyYtU2F!{jUKfr~^vLJ;L>e~x-o{e*gO0hM`wfqFo` zK|NG{LOsyFAN7EIK|Mr3XHXB^hb^b52kPHNJtTl%P!IiIpdNHTpdLQ|KGY-gFQOi| zzCk@i(a)eBs5XBA^~gPgdW6DIrBVMuJ^t%a520V79+m*~pHL6J-$y;90|`xm#PUGG zzlM6q6K?#7dieb%)I$k4IsAg~4^WT5??ycwkf_HC;{O8FLk;H})FbUbj(TYRebl4+ zXVfF}XVe4L^w+3|GVCAJ<2O(b+cT(#*S`(*K=}vt_y_g)ACGz@|0UET?C+r-e7}Qw zWd19t2kIZ99;p8!>fs9e4b&s^N7TdWZ=)WW0QCR)s0Xq{_!0H+0{@J9)WZHjJ^pQ| z2PzZ*lh9K$efqDV9#6q2goMo0oA6WAgZKy31CtOJ(9!}z@^182EvKjlCKUj;G!2P* zz`md!4!}ED#Biaq8)qaIYh ziF#Z)gLxw`2D#nZ&ofUwW@qq<&h-l{jHh3&0+J1QE}Z zEpBUTu%h*_5R@xU9$%1o-GMR!j0!KmoUEK0nkRfZBWAy$6V!oV!wiYSp$G9ojLAihp7Bx54;S}tuT$!UK^;?R=+2nEg@*a;hl2~8m zs_;M7YI>|{WuTeZ8e3qnx~%hTStTx`qR^6m>10mr?nqU+N0FT)o6-CFftv2BVoxZO z@%rP}O=~}j4FY4hI9Qx#(+?IhrHRWfwBijLeStk& zp7LttsxSuNWdw0$HW7!droF+d49}`!M-B%Rkp8Gje05YXt3^TLcmb7XX)_1A*;A2` z5c~((enfFs?iWnVS##I*`g6KJSe#hYsHs<|)_c@23Bij`~?_OnkQ;Gj}kHO)Z?vs+a^&3a$41r*;mLqXz1fIhbgxBjS zHYZmQn$o*56`^LmNk^Sq4JNyGpRyN>S=dufm@4lD%{OQa%7?P?u)Gs5X&m$4bbZ8^ z5SHad&!2-jpf@2#@~pPQ$Lg*R{wz_A^mFTXt^}ze@8e)_8D=L1^i)VNsbF2OCQ%pn zuGh+Yj*hC!EY>f<5$=(&SZg5|T1B71EKx_Sm#ux>U7ynUynCi(J_hqVsmdaG0k&P- zezNLPpqyMV=CX$QQfx=%5Cez)ez!*Tc9(?12zJeb!4>T}Uk~5${Sn=#czcyWk{rvh z8;Qf|0=s$V{p_@J>xT%*r8bIjn{XgNjLilqz`Ku6 z2qZRw&o@pJ-2e0(4BrU9c=(>^ym!0kN%K}@P2()L>VA(~z`FNEpNS9HpXk59)ZPu2kE0oc^Q5G`e9D2Oo)>34ty(!dhb5ZN?{3N=(G z4QfISvra>XvQa$KPy(n?L()(qsL|rn&{C<Luqd7M+O%?$lpP43O1dwk{ zQ|-}EAE#3%rk}?*rzW4FriIh6&d~6f({j?%3z*YOWYFu-GMLcPT4&JN&Ct7MFxbtY zq?s|NPBSN(vCy`#Ry)WF})frTMd2g(y|_hazClmtQseSra-o) z1cR1rww4NmwobNoswG+Tth!H-l1a910E1pgwqC?3>Y<;Sts0%K9<6>t&tTBZVAP&% zghV|?vW?$b$v>Vm2v9#lZ6TduFoorqVl$c%=a^B=E7s4N_^4l@1(-`PS|U*o6-Fx^ zYkk@lb5HeyPbb+H202#)7;TWKM+Bp-H30|{ZY}VjGs?`iI>){liF)KXWMmu65AKl4 z>GtKEq8@uW*N*4q&2yf_s_#29I#V&ZG9porg=?wSb|&gB*zl`7NYo?O!-UCmCfjXB z!(9>Xi9|g@a=juJ)D!`BuDPW|EetMVvsrW zoshgc5zKma%>If|Tco#xqM5_0^TL|*R1?r z6r0)I-~<9HX69R3ywAxJBanYTF;Am3JBk$=EfEtf!V+(tAFsn48=Y+Tm^5zwY6NF) z@?sFsFDco1Ho=J{sX9N{b19SuZ-XNLA!9yhf+dx;<+yIe-h6sB!&zD@-qZe zgv!L{cP!>72|UjH%#>U{Cp>@`H)9i>`2Pay6cp=eq3Q&nTBe|Cs+<(6G7;)A0qUV% z`jG4-V3j(PlAfx^l&Z%Z$kX&`ss_LTG=KsyS)dYY zv2Loz>;$3e>98j2vJ$JY_Nt!(YO^v;uOjQ7UaPMPtF-PZGW998+6a>c>#iP2nQAJi z9uu-UE2#4S>a$KOGBKI99@DjcTeB8x0O+Z-Dl4o0E5ITroX+YI)heBSC^O~ix)Lg? z&c?SA>ufx0&zS4e=qaHh>Zsl+uud$xLM)-q#>XDWk%Vli3TmI?Yq5%}w^}U4UhK)* zh{h`GsqO@xIxM(KtWLZux~43#`s>3oD#_-?r>?2SHmkn|EYc=zY!2)g87zL@YBMP; zpfanlzU#UYE6kE=ZXlCRP-`+#?Y<(as50!X=0?v7tD%Bw*LLlq&W65L?Z{s3*0L+2 z3ToDd>c}z;)$*#nZmq7Cs;Hi8GM#P3qV2M(?Yh3Lt|~3#Hg0KZC~8J27&)zh)~S^q z?xq6&?$+`x!(Qy6-l@Ne?45q?)n3fl7VGC`YPB9Nv@WaYo@uaZ?x7;9vc_)QE>o%U zsGq8GqN&OXtI7&$xw`G1YOeYIX~w?q?c(k4w(YMbQ{etB@LH_* z4sYLnD!n3Z!(y-V9xwtIrmG5RJa7DAi^$_)XuNm_Ac2P?)-W!*Oso=X7Jit zYo?0uvmUO)VyoSPu)n_T{RXc73N7lU?7H@D)f(!N@GaYdZ|^ED0po8CCom8P@m)gy zujJC`MA)kJQm_G6EXMZh$HJ{n@GTTKE9sJ|>B6wy=5D6;Y|lRH&`NC87OS5^@vven zwVvtOx^M@B?4g$N(eAI#PBFzgF{5@d=tA+nD(?&nF(3DFSrTy(YbHe?vE}0G5*_MH+O{j2DsrmI2^!a}BENCXa&V|pEHlBe4O8pt&THzT zaLvjs@apl=`mrp}@>Bw{m)7SX(`g|`Z8K%?{pRqf#`3a8Brt1mFn_JG_RQg$aWQ}G zG;ymkXOl3OtiKAgFw-(NXERjV@&gMI>%davR_`uf@3ESz6f@@LPxYj zPc%atu|(|iL}#={D|AIOrbAaRKX0^1k2DZ>v}U%{9O*JdlQc`WbmP)9tPZInMUg)1 za!9*0PUrNanzRC4^=5~*Xd7l{%jsor^=PNIYLjJYOBW{@_G-trY@g*~J5FPJ_G!KlVd9OEnOC)*+(Q~7Ad(St0AJcmSQGD08e(yIk-S-b&HzQ>?e-F5R zd$&7<_fd~GfiF0H`!}tsH-krbc{})ZiFaK~IEHgKg+nleXSjze_lD;XfKQTuf4GSY zw}IcbXnR9iR!_birQj(WB zn&oyEH z(VDY5tv4^FU(%C11V-07uOBd~n-Hn@y0HK2ua{7;4?D6uYOz0&swca%H|nyF&^IIb zqs#ZRU;B^Vx>m0ErMEb?e>d zI9D#D%e%f`sJ0iWoag(#2mFH4I|sqLz#n{n68r`kJi<48T)R3x<9D+=JjL&)!eh|1 zt9i9oJjZ`^kB2pp;`YB=d&if&bzb}hF+4M6Km~*VZ#J{ZA3Vxikjisy1}MM<7{CQ6 zKn4gjXdXZYT)+kNe9Q|w2!KE~&3pybJiAr^&If?gS3m}UYoHQ+Gx7X089)Z0WCc*Y zM;br{bUoO2ebLvt0YCr*c)c_!Jq1nseQrE9-FyjL0Mrxe1xWqXV~sO8{WDcS&733y zsQo(Q{318N&ii~v9zX>k{WOIDB$vHV!@RI+ zfITxEzy&Zs1?>IfhpSa=y-5cCGQ~3DKNHIXvrgdsN$!f^Gm`?Oe(e7~^E~M<6VVXO)K-AdgDT%E{^R$4?;leLIK9xj{`lkk z(0feO)4k$vznokEppL(d`27VSD(ojy2~8vja6tjbk|8@nRB({NN)RYZRKUov z<4Kf1fd&;ilxR_-N0BB~x|C^Cr%$0ql{%IGYE`ROv1Zk}m21}k3T6iF7@{hO2m};F zh@gN#gqukv43H~;E?oe3@#fXLmv3Lce*p&;JeY7{!-o+kR=n770Kj$W${irVX@;2% z0)Qacz`#bJ6?8^asNg~giU~&^V7YUl>I@(l7#Pjqfq@XQztXmi+hYa;sw)_1uv&D0 z3n5f1Fra_|M%+I&vhIlaspSAUAy&AFFzCaH6=EOY&3h(j0~r`#_Q-kU&jSY14>XS+ zGilQiI|gUHFna3>)V&H&zyS#?(7*!`Oi;lE8EjC&f-;CO!UI5%Fv8-bVv8)bz9N7u zxFmy20LDlx(Zmx`Oi{%ZPju`r5hbJlY^lx|Xz8cUfD%tOl197Ww}mQr$VQal!>GNl zDiY}dBzFo@BpZuVsGzSlin1ZtWP1)eom>L2vLV4*h&!MzpbkFSDnO2b_R55>1o<{P za?1B6l1;||+=LU!A4l^HsQ#vNP|!gME!5CM5lvLlMGd5gvpNT=Q#c>7;;^Z>=7NmH zO*!q<(@#MyF@O>ABK58TAd_q|rvA%-OCtSb2tCj)VBn;VR(in5nnFSeJkV;wh{ryu z6f@2bhg+z~B6&Qh)>^ZaEhtw(07)wA6bj+PuX^R_xjRD$F1QO|JD`D;AX-x-WG~1F zB8UdU?c8_O#q|PRixrAmYZ>kT*WZ5u4p`uU2?n^c{dfv4D@&P5AVgH{8XzxIL#^22 zi!sg^FcNjx)gV|gZ)s_gk>c5{r{YNuJwS6>VZ4qNQ8$u8UMv;AEz zs1Urgis7X&g}5$_>8{)EyF2B$FOOGUsw0#AgLxq=#WV;s`C4u{NuOE6nYqi-%MEh% zJ_PPPmVB=0+}~&ppy)rTa<%~qH5)Fh&wQ-*IcP;j#(BVt1`m_#Kmk%>)oVp-n86c5gggjKZS6)`3~ zipfm?s;ZvfGVqX(V6Q;KVu%^Zc0{Ax>4|N0;~U`^M>!J8VRMli03=o_EbftyeN>DI z^@74V4w8_CG~^-w5t+z>P0=oXbmSu;xv{{c>SKzu@F0e7%{P#O%&C#RFwQ8G{G58agLLm#KRQxw3$wIs)(D_^kzBX z8Bcl6lb(Y*=LgrBPkkn)oydG=Jpmd}few_QAmZXR_t~jNCbWwE^k-xSfPjKdl%f^2 z=r|FOsfBJT02@3&LMOr)I?3iPc$&yZ4I=O{aI zDXb=jHYD;YSigEzrvz*_xsfCUs_0S4I1 z0}4<82ApuP3s68|6hPR*24=B*scd1W3V^?U1uvNujAub>*u#9KC5)k}XYYbq!8nwX z4sZZnVH;c7&Q@%$)hJ*SBY*<5P_)Q=fC3s&0Ne)Bv0hyO#Tu}HQ8`vGH4(r83V_nU zK-Vz}SiooZ(p${-cC(L7t#$(w+PtXtvvo49XjMyINwyWDJf-b>;TvE1Drl&8ToG8t zt6lv6`b7Z`DCuzpORCA@g}DGo?!=;dm$LH301H^aUjbmQyFhnD0$_lH!Hbu3rAIND z?QUl?{1@?p7%?{TZims^+o%FSy?>FaTjv{N8PAx;P_Zi&SG3f-D3$>_27r(I5?}x@ z$H3QB@QDF1VCd#00K&Zsk@o`K(LGo%4>0nD52DRmAZR;u64@0nBT zU(F8JFQ*l)gjpQo_3rJMT^xV|-dbZl@0rgtMgRup@>CWHmc^BgO_HVSm&q=`%TNZu zf_IG4CYx%>cR6qoF*0NZ(51oSO|WhcKo>?Mld6|}G^yp>)Q#S?yl6J_U}icKOH0@P zq}w&Ka7f%3!c^GKuI=-$fgS9)+LoGZPE4oaqhP}h_%D6c?T8M1i6vD4Mh$=_ce?O&zA{>MBL0Zh<6eH8i@*Yf? zk%>&nAmFvaHu%92PES0CDBzs(F^?ZUT}D?{$O2&YqrDq#yd?P1YSv4C_X6${>z12b z=JII4lJX~~ytITFvA@&IS9rggs&LMCUjWYQffqdCJ@@&~d%5sTHJnBJmABCq9d3*N z)w&~+b#fIlYKQRkxCY?3UZ5LlB3oR!?@scj!5!ia_Zu-Dj_QeTK2`0$JTWH!F8jVG z40D`z)t>{;Gtlv#cfDJZ;32B8LU;RsO;@uFa1F*|2P-xs#4zJy`={JhmZTBz1tRT#iC0wgd3 z@sA(@{{b)n1#kcfumBD4 z01+?&6>tF=umK&g{KilGBtQamZ}*0eF@#IEKq}P)!>Yh4tLlWSb}g;{!b+`31o#l+ ztd=9KK2ZC%FPT&&24!#tX|M)u@CI=(2X$}97v%@DA}X5A|>l`LGZD@DBko5Cw4vMdS<_>Jank4dE~j2eAQB^uXRq89El(8BA2hJJgVi~0o8kzAK ztuY#@aT>3&8nJO3wXqw)ksHPF8^>2V(I zF(2!3AMueN|)4aq%DxG9iT#As131i;*D*Ko#XeA|sL&CBqcu0wYN= z205`4Kk*}nDHKJK6F0IXP4XmBG9^`VC0Vj1U2+OLlGQ+RCJ|92N3tVdGADI%Cwa0b zeex%PGAM5_Cbf_zjq(s}vYUt!28FUIo$@K6GAgBVDygytmr^E=GAk3&M6Qw~tMV(s zGAzY%EXlGgqp&LrQ7hFlp^~zc(6WnCB`!@#E+d03;c_nj@v<)S@-FqVFZuE>xhOF0 z5(6NEFzdoF=>jnYfCC`I0UGl$>%uW36Ef*SGAolZ1;8>h6EiilGdc4!Co?oJQ#3b| zG(FQaL31=!QveVEGFr1PUXw0jlQmtlHeu5?WfM1P^EP=iH+yq8eKR ImageFont.ImageFont: + for candidate in ( + "/System/Library/Fonts/Supplemental/Arial Bold.ttf", + "/System/Library/Fonts/Supplemental/Arial.ttf", + "/Library/Fonts/Arial.ttf", + "/System/Library/Fonts/Helvetica.ttc", + ): + path = Path(candidate) + if path.exists(): + try: + return ImageFont.truetype(str(path), size=size) + except OSError: + continue + return ImageFont.load_default() + + +def annotate(image: Image.Image, caption: str) -> Image.Image: + """Pad a caption bar above the sidebar crop so the demo story is readable.""" + bar_height = 56 + canvas = Image.new("RGBA", (image.width, image.height + bar_height), (18, 18, 18, 255)) + canvas.paste(image, (0, bar_height)) + draw = ImageDraw.Draw(canvas) + font = load_font(22) + draw.rectangle((0, 0, canvas.width, bar_height), fill=(32, 32, 36, 255)) + draw.text((16, 16), caption, fill=(245, 245, 247, 255), font=font) + return canvas + + +def make_reload_frame(width: int, height: int) -> Image.Image: + """Explicit reload beat so viewers don't mistake remount for a no-op.""" + canvas = Image.new("RGBA", (width, height), (12, 12, 14, 255)) + draw = ImageDraw.Draw(canvas) + font = load_font(24) + caption = "3. Reload the page..." + bbox = draw.textbbox((0, 0), caption, font=font) + text_width = bbox[2] - bbox[0] + text_height = bbox[3] - bbox[1] + draw.text( + ((width - text_width) / 2, (height - text_height) / 2), + caption, + fill=(245, 245, 247, 255), + font=font, + ) + return canvas def main() -> int: @@ -14,31 +67,37 @@ def main() -> int: frames_dir = Path(sys.argv[1]) output_path = Path(sys.argv[2]) - frame_paths = sorted(frames_dir.glob("*.png")) - if not frame_paths: - print(f"No PNG frames found in {frames_dir}", file=sys.stderr) + ordered_names = ["01-expanded.png", "02-collapsed.png", "03-after-reload.png"] + frame_paths = [frames_dir / name for name in ordered_names if (frames_dir / name).exists()] + if len(frame_paths) < 3: + print(f"Expected 01/02/03 PNG frames in {frames_dir}", file=sys.stderr) return 1 - # Hold each step long enough to read in the PR demo. - durations_ms = [1800, 1800, 2600] - images = [Image.open(path).convert("RGBA") for path in frame_paths] - width = min(image.width for image in images) - height = min(image.height for image in images) - cropped = [image.crop((0, 0, width, height)) for image in images] + annotated = [ + annotate(Image.open(path).convert("RGBA"), CAPTIONS[path.name]) + for path in frame_paths + ] + width = min(image.width for image in annotated) + height = min(image.height for image in annotated) + cropped = [image.crop((0, 0, width, height)) for image in annotated] + reload_frame = make_reload_frame(width, height) + + # Story: expanded → collapsed → reload beat → still collapsed after reload. + frames = [cropped[0], cropped[1], reload_frame, cropped[2]] + durations_ms = [2200, 2200, 1400, 2800] output_path.parent.mkdir(parents=True, exist_ok=True) - first, *rest = cropped - durations = durations_ms[: len(cropped)] + first, *rest = frames first.save( output_path, save_all=True, append_images=rest, - duration=durations, + duration=durations_ms, loop=0, disposal=2, optimize=False, ) - print(f"Wrote {output_path} from {len(cropped)} frame(s)") + print(f"Wrote {output_path} from {len(frames)} frame(s)") return 0 From ed458e7cc1625fcbbda8a4be09f14a8711569c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E8=83=A7?= Date: Tue, 14 Jul 2026 19:06:49 +0800 Subject: [PATCH 3/6] fix(web-shell): address collapsed-groups CR feedback Export the storage key for unit tests, use an explicit first-catalog latch instead of size===0, and cover corrupt/disabled storage plus mid-session auto-collapse of newly appeared sections. Co-authored-by: Cursor --- .../WebShellSidebar.collapse-persist.test.tsx | 75 ++++++++++++++++++- .../components/sidebar/WebShellSidebar.tsx | 12 ++- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx index 79ba3ed6fed..34793abc0e7 100644 --- a/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx @@ -4,9 +4,6 @@ import { act } from 'react'; import { createRoot, type Root } from 'react-dom/client'; import type { DaemonSessionSummary } from '@qwen-code/sdk/daemon'; -const COLLAPSED_SESSION_SECTIONS_STORAGE_KEY = - 'qwen-code-web-shell-collapsed-session-groups'; - const { connection, workspace, workspaceActions, active, pinned, archived } = vi.hoisted(() => { const makeSessions = () => ({ @@ -87,7 +84,8 @@ vi.mock('@qwen-code/webui/daemon-react-sdk', () => ({ })); const { I18nProvider } = await import('../../i18n'); -const { WebShellSidebar } = await import('./WebShellSidebar'); +const { COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, WebShellSidebar } = + await import('./WebShellSidebar'); globalThis.IS_REACT_ACT_ENVIRONMENT = true; if (!globalThis.PointerEvent) { @@ -284,4 +282,73 @@ describe('WebShellSidebar collapsed session group persistence', () => { expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); expect(container.textContent).toContain('API review'); }); + + it('tolerates corrupt localStorage data', async () => { + window.localStorage.setItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + 'not valid json', + ); + + renderSidebar(); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); + expect(container.textContent).toContain('API review'); + }); + + it('ignores non-array localStorage payloads', async () => { + window.localStorage.setItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + JSON.stringify({ group: 'group-1' }), + ); + + renderSidebar(); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); + }); + + it('does not crash when localStorage.setItem throws', async () => { + vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { + throw new Error('quota exceeded'); + }); + + renderSidebar(); + await flushSidebar(); + act(() => click(groupHeader('Backend'))); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('false'); + }); + + it('auto-collapses a brand-new section that appears mid-session', async () => { + renderSidebar(); + await flushSidebar(); + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); + expect(container.querySelector('section[aria-label="Red"]')).toBeNull(); + + // Keep the same React root so the first-catalog latch stays flipped. + // Color sections are derived from the session list, so tagging a session + // mid-session invents a new `color:red` section id. + active.sessions = [ + makeSession('session-a', { + displayName: 'API review', + groupId: 'group-1', + }), + makeSession('session-b', { + displayName: 'Release notes', + groupId: null, + color: 'red', + }), + ]; + renderSidebar(); + await flushSidebar(); + + const redHeader = groupHeader('Red'); + expect(redHeader.getAttribute('aria-expanded')).toBe('false'); + expect( + container.querySelector('section[aria-label="Red"]')?.textContent, + ).not.toContain('Release notes'); + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); + }); }); diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx index d7974dbf208..84a8f844463 100644 --- a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx @@ -89,7 +89,7 @@ import styles from './WebShellSidebar.module.css'; const SIDEBAR_WIDTH_STORAGE_KEY = 'qwen-code-web-shell-sidebar-width'; // Same `qwen-code-web-shell-*` prefix as sidebar width / chat width / theme. // App-wide JSON array of collapsed section ids (`group:`, `recent`, `color:`). -const COLLAPSED_SESSION_SECTIONS_STORAGE_KEY = +export const COLLAPSED_SESSION_SECTIONS_STORAGE_KEY = 'qwen-code-web-shell-collapsed-session-groups'; const SIDEBAR_DEFAULT_WIDTH = 260; const SIDEBAR_MIN_WIDTH = 220; @@ -561,6 +561,11 @@ export function WebShellSidebar({ Set >(() => readCollapsedSessionSectionIds()); const knownSessionSectionIdsRef = useRef>(new Set()); + // Dedicated first-sync latch: do NOT infer initial catalog from + // knownSessionSectionIdsRef.size === 0. If a future change seeds that set + // before this effect, a size check would falsely treat the first sync as a + // mid-session update and auto-collapse every restored expanded section. + const awaitingInitialSessionCatalogRef = useRef(true); const [sidebarWidth, setSidebarWidth] = useState(readSidebarWidth); const [projectExpanded, setProjectExpanded] = useState(false); const [projectsExpanded, setProjectsExpanded] = useState(true); @@ -1974,8 +1979,9 @@ export function WebShellSidebar({ if (unseenIds.length === 0) return; // First catalog sync: register ids only. Restored localStorage (or the // empty default) owns expand/collapse; auto-collapse would otherwise wipe - // expanded sections on every remount because the known set resets. - const isInitialCatalog = knownSessionSectionIdsRef.current.size === 0; + // expanded sections on every remount. + const isInitialCatalog = awaitingInitialSessionCatalogRef.current; + awaitingInitialSessionCatalogRef.current = false; for (const id of unseenIds) knownSessionSectionIdsRef.current.add(id); if (isInitialCatalog) return; // Brand-new sections that appear mid-session still start collapsed. From 4a0a7aa85e93b3868d11d16caac51505e891f63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E8=83=A7?= Date: Tue, 14 Jul 2026 23:57:35 +0800 Subject: [PATCH 4/6] fix(web-shell): harden collapsed-groups persistence for CR feedback Wait for groups+sessions catalog settlement before the initial latch, persist secondary-workspace collapse via shared namespaced localStorage, and keep primary/workspace writers from clobbering each other. Co-authored-by: Cursor --- .../WebShellSidebar.collapse-persist.test.tsx | 101 +++++++++++++-- .../components/sidebar/WebShellSidebar.tsx | 108 +++++++++------- .../components/sidebar/WorkspaceSection.tsx | 23 +++- .../sidebar/collapsedSessionSections.test.ts | 66 ++++++++++ .../sidebar/collapsedSessionSections.ts | 117 ++++++++++++++++++ .../web-shell/client/e2e/utils/mockDaemon.ts | 7 +- ...web-shell.collapsed-groups-persist.spec.ts | 12 +- .../scripts/capture-collapsed-groups-demo.sh | 20 ++- packages/web-shell/scripts/frames-to-gif.py | 5 + 9 files changed, 385 insertions(+), 74 deletions(-) create mode 100644 packages/web-shell/client/components/sidebar/collapsedSessionSections.test.ts create mode 100644 packages/web-shell/client/components/sidebar/collapsedSessionSections.ts diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx index 34793abc0e7..20d15deedd7 100644 --- a/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx @@ -6,16 +6,23 @@ import type { DaemonSessionSummary } from '@qwen-code/sdk/daemon'; const { connection, workspace, workspaceActions, active, pinned, archived } = vi.hoisted(() => { - const makeSessions = () => ({ - sessions: [] as DaemonSessionSummary[], - loading: false, - error: null, - reload: vi.fn().mockResolvedValue(undefined), - deleteSession: vi.fn().mockResolvedValue(true), - archiveSession: vi.fn().mockResolvedValue(true), - unarchiveSession: vi.fn().mockResolvedValue(true), - exportSession: vi.fn(), - }); + const makeSessions = () => { + const state = { + sessions: [] as DaemonSessionSummary[], + loading: false, + error: null as Error | null, + // Mirror useDaemonSessions: data is undefined until the first list + // settles. Unit tests treat the mock as already settled. + data: [] as DaemonSessionSummary[] | undefined, + reload: vi.fn().mockResolvedValue(undefined), + deleteSession: vi.fn().mockResolvedValue(true), + archiveSession: vi.fn().mockResolvedValue(true), + unarchiveSession: vi.fn().mockResolvedValue(true), + exportSession: vi.fn(), + }; + state.data = state.sessions; + return state; + }; return { connection: { status: 'connected', @@ -209,8 +216,11 @@ beforeEach(() => { groupId: null, }), ]; + active.data = active.sessions; pinned.sessions = []; + pinned.data = pinned.sessions; archived.sessions = []; + archived.data = archived.sessions; }); afterEach(() => { @@ -341,6 +351,7 @@ describe('WebShellSidebar collapsed session group persistence', () => { color: 'red', }), ]; + active.data = active.sessions; renderSidebar(); await flushSidebar(); @@ -351,4 +362,74 @@ describe('WebShellSidebar collapsed session group persistence', () => { ).not.toContain('Release notes'); expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); }); + + it('restores multiple section kinds and keeps sibling ids when one is removed', async () => { + active.sessions = [ + makeSession('session-a', { + displayName: 'API review', + groupId: 'group-1', + }), + makeSession('session-b', { + displayName: 'Release notes', + groupId: null, + }), + makeSession('session-c', { + displayName: 'Hotfix', + groupId: null, + color: 'red', + }), + ]; + active.data = active.sessions; + window.localStorage.setItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + JSON.stringify(['color:red', 'group:group-1', 'recent']), + ); + + renderSidebar(); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('false'); + expect(groupHeader('Ungrouped').getAttribute('aria-expanded')).toBe( + 'false', + ); + expect(groupHeader('Red').getAttribute('aria-expanded')).toBe('false'); + + act(() => click(groupHeader('Backend'))); + await flushSidebar(); + + expect(groupHeader('Backend').getAttribute('aria-expanded')).toBe('true'); + expect(groupHeader('Ungrouped').getAttribute('aria-expanded')).toBe( + 'false', + ); + expect(groupHeader('Red').getAttribute('aria-expanded')).toBe('false'); + expect( + JSON.parse( + window.localStorage.getItem(COLLAPSED_SESSION_SECTIONS_STORAGE_KEY) ?? + '[]', + ), + ).toEqual(['color:red', 'recent']); + }); + + it('does not clobber workspace-scoped collapse ids when primary toggles', async () => { + window.localStorage.setItem( + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + JSON.stringify([ + 'group:group-1', + 'ws:other|group:g2', + 'ws:other|ungrouped', + ]), + ); + + renderSidebar(); + await flushSidebar(); + act(() => click(groupHeader('Backend'))); + await flushSidebar(); + + expect( + JSON.parse( + window.localStorage.getItem(COLLAPSED_SESSION_SECTIONS_STORAGE_KEY) ?? + '[]', + ), + ).toEqual(['ws:other|group:g2', 'ws:other|ungrouped']); + }); }); diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx index 84a8f844463..365fb55e1cc 100644 --- a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx @@ -80,6 +80,12 @@ import { DialogShell } from '../dialogs/DialogShell'; import { AddWorkspaceDialog } from '../dialogs/AddWorkspaceDialog'; import { WorkspaceSection } from './WorkspaceSection'; import { SessionGroupSection } from './SessionGroupSection'; +import { + COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, + isPrimaryCollapsedSectionId, + readCollapsedSessionSectionIds, + replaceOwnedCollapsedSessionSectionIds, +} from './collapsedSessionSections'; import { SESSION_LIST_PAGE_SIZE, SESSION_ORGANIZATION_FEATURE, @@ -87,10 +93,7 @@ import { import styles from './WebShellSidebar.module.css'; const SIDEBAR_WIDTH_STORAGE_KEY = 'qwen-code-web-shell-sidebar-width'; -// Same `qwen-code-web-shell-*` prefix as sidebar width / chat width / theme. -// App-wide JSON array of collapsed section ids (`group:`, `recent`, `color:`). -export const COLLAPSED_SESSION_SECTIONS_STORAGE_KEY = - 'qwen-code-web-shell-collapsed-session-groups'; +export { COLLAPSED_SESSION_SECTIONS_STORAGE_KEY }; const SIDEBAR_DEFAULT_WIDTH = 260; const SIDEBAR_MIN_WIDTH = 220; const SIDEBAR_MAX_WIDTH = 420; @@ -355,37 +358,6 @@ function writeSidebarWidth(width: number): void { } } -function readCollapsedSessionSectionIds(): Set { - if (typeof window === 'undefined') return new Set(); - try { - const raw = window.localStorage.getItem( - COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, - ); - if (!raw) return new Set(); - const parsed: unknown = JSON.parse(raw); - if (!Array.isArray(parsed)) return new Set(); - return new Set( - parsed.filter( - (item): item is string => - typeof item === 'string' && item.trim().length > 0, - ), - ); - } catch { - return new Set(); - } -} - -function writeCollapsedSessionSectionIds(ids: ReadonlySet): void { - try { - window.localStorage.setItem( - COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, - JSON.stringify(Array.from(ids).sort()), - ); - } catch { - // localStorage can be unavailable in private or embedded contexts. - } -} - function IconNewChat() { return (

T+}< zE+p*TL)~fhFNiiQGzl^}HP}SlM!?%fp5`P65Q_u6jd7|cKpHv+3>v$8h@;GC^jY~u zD#GUCU0BS=nBp`RtLd>c9wtZ`eh@JS2&{U>0mO?A$Mu8t(^@!*d2c++(3J&|Jm~r@ z%_STvdFiuQPn6?J6VTJ*rq~{FbjmnT?=#-XaqnDkg3$hw#3GegcOWG+?|o7j!<1E9 z*}?t4HJDm{^GqYd(|S^+|NM4og*_1~6)00?rEPwiI4nh~A7N}dCC7rL8l#ke=aa&s z^^6!kopk5yQ~SyIRfl<=nBTx*j8W6MQu- zX7TLDa;Mwty9#gA>n{h{77{8dF=+O&LI;fsGF zXh|`%!As4=;v#$j31SK3BY$O+sCx?8WU1(hp>!}1uVGdtf{lGR{l=99pl5&dT}Z+0y2(;8F^2SHf5K3}95#-y^x5Ve`wahb}?mdKaykQ3Y*=%7?p zRiT>iVg$CW#xScGO2=bO)~sge&)q#=l_OI#|)^#!IPg5;%jWmgzpSX4X`Bp6TQ=uTp5Q{woKa?3?md=Ug)DvQ?7A z#w7JyvR3Qu6`YM%d-OXxRvX+SolTEam$OLi8~w(d&2M`Q2JS@O2@fS&U_3J%CSPlc zu5h;82s0cPU28@qEAV)1gN>&3*IKg8oYn2ZjplvVT9a2VY@R*DHSYw~e@+^wT?tijfKD{cF3tuysD!2y?>t?7?%J;jV`!Gf2lyNpx!SmqP& zehG$VMfrwa+bMIbCbLU6765;*5p8AM>@w~aKs%{Spl(9W6C?XdxWA4DelLm!3A{KJ z(Vnb~%rdI*lVeU@RQ%+ve^Dymi{F$p*7Jx23A2FEPT=7rv%pR1dj9#*MK~E-DkwOm z%}!|R#h`CWCxw07H(j1{|c0ERjqdC?dvRMWg08_%y60G)H ziYVTL2T|84oxh4P#eqI_BVFiN0+hkFIL-mRxW!FkRCs3Ldo$AzMO?Ic zDI}Kenvz36`irIbGT~?f(CKTT!h6RKbW$;8*a69gOYq@drT&DJC^s5Z>NB%}zYKJ| z2^3}uTz{XMhKmB>6tGhQT-aiMV_P^hco!7&lWB|WJe*>I$ekQ9(b#dFNiv8aeWq{g z@OTr#_2P+mkbieyh|mD06f-%9T0W7UJISod`n%{xKB(!t|J01Rds@%tg&t6c)^y@f z7CD5r+YHe11G3RU+lZNtt|<*6nOMDPS+{#|M12aV++`6A8*HMf6G{2gW7YRBw5>)oa~XYTW8Vi(LWz6~4@VW*yS0}!d?UXam}5>RN96pJ8_HVj!4%XT z(R}A!uU&0gwf}tP|0EEuB;yXsq2;Aa>P9R04aH3OHSH++tbK;-6w@VW!pa?%L~C_3 z;nXnt`}S$PC<}Ay-aSJ7xquJ$z)PE;<^7+F&Rr8{yT(F2XYzj~gC_b-3)8o^Dw6uT zR^9mWD(<&9d-?|CF6W?VREu5&gGe&1pW}bGl_doQMs6=ANlUhvx#%~dYPL{*(7zk) znw`9(N~-1D={}PbgdO9I$u@UO)F7r$66^i*!8y+mn){gV$_9V!5YQlgHSB(4na%neaSZxIWU z`u?Df18T|Emd3U`SqlA% z55tDX)A;g8hDW@HIYx(hBI>wThWUQHXgwHyN-8ISHzFWB!oxEnq&tH80!BK4c+a|yQe54jIup6$mG+46eXN3OGPwQ}VN^1AYqh*8BKbjtPg zDh2YYqx5Qh^6K048r$-kKj>fnme<02J4uxHZ`c5DnpBE zqDyEaYHqsSxH*fb%;GVvIPoMy3=ZgWgj zfH?QGo5`p&QzE9oFwJ-qm%AAP=1iA&Bm&gqtq;n4o}_w9#R`Su=%wbll~Wo~r^q{! z&pTv_!Eq!?NE#XAMFVo*?7x#yiDShf?ZD16A1YAKC26Q0(@vD2*CuB}RbRD7E-zHiSWO|$>J=o+7(?n6U6TwW(rJQ*t z=h^)57B1FUVw^&?W{MIxRbW_LT@KXF^a`a#fv-$qD?v3ax{98tQ|@@ z;+ft`3%T=1iIgDmGr}Gs&5qihlsknoYUd`w28d4cg)9|wK`=y(x=d{a_z~)`GD9$x zF^!%PrOh$zKvm#L)smCDtCBCRngQF-#U3J`?8BeRs6eIRWtAe?B1=^NP)_C9C>rqO zR|(Af<54s;rAaQaafc4+eq>Urp9-C%CN;@#OwDK=DsQ$VC6dc8HKFn3nTdK#8xTOg zUsMF|6BnrVdr8D4JZQ>Uy}TF3?iJ1oi(yi2o-Dskd-a_-a%)6;2EGNW?N!YD&WIsacNoEwq%!NoV33OEM4)Bam>YY zRxI^4EpaOl^7be39BFr5FFoar=O3ETMD}RBDw~wx`t%BMi{!rUv*4&bWF!|%-VI# z3zw&3b8}-X6ZvG z78;5HOGBRaK=7uZ*(dq}UkUm$DqxFjsN>HNumGt|=tekITKaw~eZX{iXX#D_oSB$1(pnMP?uw;2wF22l+Lyk7}~RhhAB$m8g$>=FfH=3)pRpE%HJRAd48@<_eBiN+&!aOcY=ZRq(-b!32ap!jV0`pg zA_U~gYGiYDR^#al>$90w<7&6ouUEzAw#0U}M8BFi^>0<+m_Da79p*J%elQW%*e;hg z9d+HV3EW;VG>uL)ooX;$$k~26xBaevyYXxr{%3m;Vzx+UHnU-x&ug}z&r<4n>8zOe4g{pFSTwDK%&gel4uoG@6`ok0##;TTKgiEHAnLIa z+p)@@JW#!`vcWriMr$p|cZdQ>9l|sYX-us}-K-g2TZ<%Fzt6GGdN?@hK2)!_mfW=- zJF|ZN*IE$UrkC#XUyjdGGW7D7qq6!oK3q0(cAu5pK5Hd>mdpCA*6~@r@v{cOXB8R3 zm-Gr6mp1ChpPysevXj~BupPlQk7Q(QIb4sVLu@(CZ1s|kB#Vx2YHSU5ZH+E%P5&NA z%pZNFw7bPV7Uw(WlRg&HJiaqJ7Iiy5jXAcmI?_HqG9f>%66J2daCu> zKEd27sS-bnPcKu`DqE2&N9N!()8x^XrumuTmcxg4{MmOb@hi?B3X=-&Ts}kyI=L%g z#|NaA`9jOwURJPuE`$j-)#OdGz_xl~X)9ujE^){N&U|M&%h;UIXwL6~*{fyP3mwSb z4T`nNoHu1TDUZ{IUzdxWu6Jiq5joDO$uc@CtmP0fYd@*UGBk8s%_1aD?>TiI^(Bn_ z?({UI>ihNCq@4@T?pg3+R_JE=-i2Hkt`a||WhIaSCmjztY6^82kZ+AE5c}T@rdo0E)tMXmFYAtljKw!vhKOUfS5IX z&hECaqr1r)ZeO#SE-rU3vmIi_?&7xB;1v&U;vLxK`;@m)u_>}NPjRxnoTlVC6ZTlN z(^DXkH#zAW*`bisk5br`$1zq4^yvQS)~|BW2-nIQzRq;ShI*3p`+B^Ka=RJ7$gj@j zh)av$Ie#=~Vsdp32~hfw1z8-AC8c6##mljEBFdUmjD%&*4?!s3cu*OM*4$lwD#E6G zKNivK>XvTk zmc{Rp;Q{Fqn9U;c08JeDS@~pJ{=j(aN*7JWpGUWrX{Z$C{BilbF z$m`OhZ_GVdAh-F)LTodW?WPonI0ksh=oK^G=mr+^x*&JQo$yila@z>G+OxY>c}r|{ ze=vJ#AOo(jed0p=#Mk}0qpNG z*qYggbYd|%KT`X~&n~Fx850Y~USR75#EphD0~0< z{x0M!ZttmbnZEdT?0H+3a}(tn7K3w~!Fe|yA;yAZWYrHl;j?1VGXgRGYQ?i^0$hbp zPE}D(neRQ~=I!El?eh4na>q^b8LSE@OcenwW{|`d`Km7QgM*hc-&CD$S31ov(H(_n*|3b+Bu<<{K{7)eNv&a7^@;`_CPayw? zcK&z#cLx41$iV*}xcFiL9gCJjYhe!=u2iV42OoV>Viw(rgsp{7SZlw0LNuZfjYkbwCdK7McrE%@fV zzti(?q4eI+_`fcCQPF8}C^*bN`I$>fFvt?Ek?{HFK8bscfmAFu$_6Rs>x>a^E)?4= z5om7#jX}yo)1%iD14=|xt|<+}0M8!Dz}^5)!!VpUVK5AIfoqtl3mI$|1~e*Uzt|wl zfO(9MP<8p z-IA5sCC-f_VSi+gbHOFK_eN;AiuaVsSPIpJiD(#)n($0u~Z~^cgV6=ecKZYPLjZ!s2XH&$x=*uNu^*p zWigJzmugs?nc~}57*m*=#XA@&pHiku475Vf!M%1BeRH`f&71qv=Wp}LQ$7{N9FXxQ zEVslNxPqzTFZ0U<9O^VQ$4?g;dk&6VeBAKQ%X}gJK@NaI7!3@4NkabsMy8ch)CJea zt&8|`%zot$NhCLn#KDc=4z&$?R${tD(6l$l2&kYwI*cwCo1vy?^@iVIAbt*nO(N*30uE?#ae)SZNQP576%A6kK07|1tA-&Z~o$x{*~+PzeeL~Il33@magp-q7@t?gd4Y!V&6#tbdC z=aEY}lb$k@DNV{-6YRyK=M;{<*&3D-lE%!4jBsN3o+05aAHy(Y;fkZ=}qGSczb7Rzs! z0ulh`!`DRFOky$u1`6Pzn2(eNY<74_UQ|TAvn{E40^d^wJXu)Sr-)Uwzo)S`v#^Ox zO=$*xPcO!+;80ST*3SE$QLSFVWiU0Z-}61QDWQVLUS-DkKwo z;Ka02{D0$O;bY0e|H8$~1?>NYi+6S2{>A%;i*YoO#Q(v?4np{~|8Q|z3Et~}xcK9T z80~+!_;rA^lyl&-DQ7!|;-+MjQz+M{OY|(+gbbFG`q!Y_ zP3y$1k>;{Mj+IroLQARjz%(Z{+894|th1msz{8+gmE zCm|9ffX-4}1V$jetBwAnee1rgZ~=S>5RO)Hfst%hWG0#os$^hRfXyto8LYUfxLJVgks5RWq4g_z=+U8bok_^YS{T0*L_q3A$V(~0h2Jtf+04f86QriF_2Nv zNlYR;60C!UDDW3GG?rP4nH9(0HYvGq!Mn{X)l_df<#s#*M8F9=(08|}sHbPK0bpaOU}lQ=CXS ze=Wp&y)+THS4R)6#ISN!2MML*sDcL*G^Lo2t2adcF(&UnG!%d6yTnF5_AewnjlTJ*S8Wb$^L||~pdzJC^3GqN(eqn47D9iwTAU9HQdy$Q zGn=gNLv5(71@xVJwu^;)*uUFb_y`9el!JC%O20A)GC6xsoxThZA?rbdpA-mP0We~I z0v%D`zMuvBlPJv3-hfaf@m|9vsO(3)K7PqQvF<^BE2M&tWKUoDp!3HWTlwckF}-&e zuos^g>PRW)+W9<6QUEYCQCC)@7FwS4~ilDCRb)IhX)ZzK+q`mdA55F>+;n_;f zZgx&}#jUlrTFYqK?BJh@R$ztL(T|X3cO=-=<&oIYG?t_{xP%w*$TVctBL_-Q+J5hI zRh)FPb*t-rAJv|x%udN1e~u>K%*FEX+HO9rCufitj}F-UY;)(6)XW#=%Dr!Y<#2>M z=RKt26t?kEuMHvHCBU0`QAxPBV?kJno9bg{iCefd%ERNLH(hDeOvnFV8CcKmVp!cu zn@1yai+T5VeJ^dVf}X#&F4Ga&;>JGo;j>6)S?*dpwGb%&gYf6%DeB&6p*;1d!96wn zRs23-wAAEd5DwCN#yaOC6_HPU;csoC{J*#VXb$F?@u%LrwH4#&{>xMqSL?X;J>gIN z*Xf(Ttd>M>h_3Ihvn^&1D3({KHl%K}Pk+9p zTl_kHj=Eib7jo8#`r{Y<{&(`!-^cTTzjtq>|K7L}yrnWjgi>04th!d0I7=AUbV>Gd&GrOaNR<-V}G+j3x_27y*eXa8^dQI!Zc3JTjJh8B>y zpC!U>!}irikmE3*@{l2xrY^_svnFD>IY~Px%xI_YP%%r^;Wi!5{iEv%|vnWWHN>9O0jI;*q8pf*>`Q(!74999<#4WbHYwFBaCt`d~#x9 zHRHN-;*WChA-PrTxkSpjU8^}+ry4oAx!Y~I1vMH)kGUJ9d8Mb}n{ED{MtRgCd7Cj- zffMK<1?2lK+ zPfRc$3rifRwlf)yL>)TYW84f9iM=&aCcR%^Q4g;N8Hf{cbSnJO25L4fG^*V@Y~Apk zOx#N?TK1V{zv-JCKeZ5x8}vyie26V^DD4-Z-se>Jnuzmc%qq8}+qyBQm?C^7GR6AUMXhpB*&pZT;H)ZlTqyZS}F$G4vI6nG5QHDmE&Lo(;IdM z`f0D7Xrmat+>8?~rz^CoqfqejbyOSO%;#t9b{wp0g-*?G&Vor6SPAarRR_XVC0Q$QqZj+C1%zA)KN*k33^_xwGgfwGGs`?&PXRb~T z95Fk#O`qVORSrs&zQ*=0Q3>3N_Q(6Cpr}k9P70;1DHdbitH&oli+cw(3y%XwgWM@9NFC53^Z-ONV|xhaSKN#K$y@t`{`p|Tj^|7N*d ze^|KKUO{@A^X&$9V_a$>4oa=j=COFBhuMlzl>0p@b5W3BC zqj}>Qr0wynbJV`mC%#j$x|2$;GdMo#O*}k=rpr;SOVFWPpxB5MQl&$W>BQx+vj7fb1K<=`uZzI zFZ=ED+c^_vlNIv^e?fPTdKYJ z%ju;{>4??zPpvV7QB45cq)+!jf1joum&n9H7Qo;)Y?&I+oI#qFTH~dp%-i%=a%!MW zo1r(w{rQzV8xOQ_mL1&c&vLgI87E%U>C?ARp#zDAY_XB|As8m=y2?%ubvBE-`I-_D znPlz1x1#B_2RWiBXVuze*X-bJhx^shG@56oE+W#_pCx0bOnv?_k%c{*!8x0zGMnN!n-n`6 zUo#ssHQN;16^lDZd^Fq4GbiRSN1!nGre-d9YA)b(&KG;$hjac$dEP5_PBLa5+jd?} zLc>u{zq5XR_sd%q%J-zV1&a|MrhgPTmg+gS&oE7z>;03yDG>v~TEC5=R*lu|#NXOH zv&1vbEnCYo&Wb4Jfn$GFr+Tf9&l@#k!vUCP#VNQt4hM}pM$>aaNBj{PqA?sMe!Dre z&Jr=6D5sdeWF<`Yw8l4Sa-zJ8kOnIybJWqlF;DV~L@P1i6G^E^Yh zKV@nPOGF0xSl>+Dv6mfup+y_%VA0~TpKJ`AE{2X8%Z3U0#?@f`imAAbZDspVoG7?8 zBBfzXp6)|_VUiMN)Z4;!jUBU(RDs8#-VVMc_#RL~9>L?N8r^5LTirCVdP%`C&iKVv z#d!=YYB5aKTbw_E`dTlCwXKr1#n~45EUu&hX*>4l?VZgIl7@dD|D{nMiiSu#u>QM4 zRC}S>PF<&zaY9y+Mn>zm0GqoEh?_#6rmEJhem0~d3UFKCkUi?TnPFrr+Xg{`3aqoZs z^VNNWMJ87W!|tZ`$^M9HJiB?K&hLl<^+i_Sz7&0KyJPCJC`YV1htC>WdTZ(UwQhJO z@11xJz3qm)7+XB_4;VI^3e1|vbUzbpF%SnvhASNDJa#OPvS2S#Kb5F#5@ja3XFCw4 znqw*=pI<~&XTFFrTI3LT8rzmx(ROZUN4^YLb(?wqIhKq^Nc6G+baNNC{qXt`Arwxw z-@MmD)rpl_@cQ}5GWltENY@Y2ci*>z?^XtKlllu~v@U|9N{S-O%;Vn@oK?;1*3fHJ zxMkIuXTK}byXSUpGe7SLIqxbu?-@An+x=HFo)6Mr49i@MnqLej_1QRIOb=Yl?q1CQ zy;z|C+LrWsiskEC$k&acuOA1#mR+2#&VSvbzdVq++-}sI(!4w=x;z`W9H2*jnZLZI zzxqyadHMJ2O~}L812%y(C47PeULukqepV!IFFGkhaX zPLmV70PSUvJ-zmGzFIFLALV8krsqPhzeog`4D!v8e7G3;#WXa}I{GZoBlesS^~Pmv z7evbsW*!95rJOsFT%3$wnE8Hx`sutr^?Ud3(-bk$_q#JKQ`A|soSiZ^Bf2*vTDSV| zZ#w_79&MSh!7o{<32KS5k|el1*INr{UXv%>}DZ=V630_Mh%?tfp5sZ(W`` zJ22k(yu2Re1N&RtGBw=?pF94@1uJOTRk!ZijU*N}vC%czauvIuNfdRiP}>f(b?ErGTQs-6gth z^)a)>RYW{asRKjK^HU!?H2dBYFzY<~Hk_PRE_NY2%Sy5R==~w+ivGgyA3A>e2VjH( z;UF|(N-eD>C@2gcT`JfgRw4(&C*>m5y0-jhyr^Y2=z;z8|7jciO0YO=u(nRINXV;@5+#-p*H7_O)w zfKUL96uH6)FHyxpBSJ^gKo60LbR;gMF2J*{dv-2c`g89&| z`O(Q?#Ulg~1&y$CzOp8Hl9%!V??bQUlAaPiyWo~TQ!M9Epm84+P@-ce`8G)R23_!f zuy>bFZN~4T=aUfJ-Cc^er9h!bL!lH2?yjK(D^Oh8K!M`L-Q6kf6nA%bclS2S_xyIx zoZSaIJ3F)T;QRsiOy+)&eD2)W`+CVR7QTy+E)0<3lVx@0p6F#w_v0JbN^(+_<0|jE z$UyQ$)m1*RYH%vce-zNr zU=?%L&@%F`8Pzn)tjzC6> z=+0a)cSBEH;zpT~6f(CA;Ik-DzveDrM{WdqzQD(>A~xQ~MV|9u7d4m-vFQ+o_SQHH z84xUF{LP;iv6ph$MZQu%CD~>kYc3e0lKf>QiNhUVac=K7;myoWD2g$aC19>bWX&hZ z@L=B$sJ#Hlq!i*bNzTj?v5huz!SX@rXgxX4=w(Y*PI`%bXW^Ty>seGwc_4C<^4D&D zFV0`E%`(45z?)<8jjNVZ-jQ=r#c+3RQ(I5laJ}OoCG6Is{4@TS?R0lLcm3p8SiJKX zDI4G-m)MflF6|gk-;(T2T=L*ZDiJ4VFXO5z0PJz=JvR8VNpNvp;iOhF`QEn0>RWcS!n_+s&I2;O`e7u*qjAZdyRXCjcA-L+^xE;Wet2&+>-$-y>%pq-|u` zyU%Y1RQX(A@4~~(goSl`TyEbi&=%-#4=jUUXh=X|!7Q)aSC5k}A{H|7-PP`s@CjKu z^~dd1MT3?fFVx>NX^sR&`)Wc#-SIC3BXu>qOv$cuKJw<32OEIR8asrfoxCGMKj$z| zP4^WG)Q)RfIGNFWfAQ&E9#*(>*Jhf9^9%8t3O0AESb9`p8M?4SFFz?3MqHg-DZvZH zkmhh^*84uXeY>c5FXC9HAX&u#*=UvnbM~q$*>^z`F)B>3M8$>ymR!ET62IIC{dq|e z)HNPozl>y47J@$vXz%Uk& z2qa?7#2k9z^h18f2}Jnotjhw=vk%mJpN9Pxhl%aUjP-3|$`XXY_jUWQ9$5xe5JOwq z5+SC*M^gos<7mJwVTf!733+Qou835-mQXZkWF2SQ>*zrKqu52-4{?6BIift(Eljy1 zoHihr1g?}XvvT6^D@L$w=!Xt9LUe5wV=Q|Gl&4CBAG>i@n+m`c1W-qO0KaX~2S6~_ zs1bY!QG5W=1o$@~n&pu3?nB7^*$@5_5CQM_JccV18$sm+I59zGNA~jvf-4GhwkdOV z3D_h8q`l=OW=my9qC5*s@MG|-;p*tgB5U^z;X36XQ6%0FT=jsXv^((sOK(ya7Z;WWIBkj9@|7=3!>^md+h`NAib1&3|C&*D# zoYK#Rm9%(5=~<%4Y^Vt-qOW`Rl2b=bg}6%Nq@LtFtkCNQLknF#h>cZ+?uz8iyet~k zj|u8S72kHpcYg?;2pJ+7M)fH&{=-bZM8GRVGtcdh%-|+6=51obt;Fo$@=k=d1SR;n z*LQ>nNBa=ivEskTGxDIEz!bJ)Ls6Qng;UDQlBKY+^^WV{OFPEBk=^RIaUW94xe)AM z+9AohA9fbcOgUZJoy&9|{x0s+0lAov`t;C2S(cacR;)|Hs9`d9L{i|`;Xo1HoUZ1i zP{eNeFlPT@w%ahZB7RxL6X*Kq29NT z6u|GPdtgI_2`W>yj(E4~4|0JZ6{FlS!50HrMg4mRJ58eGgef}tFVr_v5A#|oU80n% z<%Ztn%IGb+RGelM1k*K2`Thyr2HVRz%W+%`>ugeDDT+ZEs-OGG@f=tlku;Bw8FldZ zP*s{OH8G!{*4&1ODqzNszI2=|A{*gkB+!F_@F>oOGi|<~(zUWZ9P9wWMK&&aCcN>HBS!d2lMymRX8q$zfw#n4kO;Ls%5Sx(DpfWz4wtiN1@H! zr#;VTXAw=nlxm-e#(rC!E5y=+mLi#PPc6!yu-Pu@J1$ha|3STwH_x*3pi}Hi!^fxn z5LiyKLUw#^5H`lp?~w8L)65jGr4LV-9Rq_97OR4cwXXYiacpZU52s)ia8j6(6OSH^U^q^={BlsRE>?e!o!(_|T+=f}PD_cqN`nUU$xJcbQo=FE5c z@Lrf#Pp zu3%`zkD>l6^1-LZfi9@x_|Py8*{DqulS#oi%kTvM@TAo6l=|?r`S6U>@T}kPT+Hyi zs6qkC@T-HN#hjR>DFw~_VPwOjg7C)h8b8^FQ}#Olh?K<07B}^dRQ9%$A~a~^PqHGP z@5sSb^kLUXPD<9TeBvcD|9h3hV`$5J#y-*WgoXVUaakpL;rQdH_?#ed0l3nK!ch=m zL<)4|dprHKFh#_9Tx3wfn2O{##_BGDKeno!x1x!UT}lIqBi|i*Z>BnP?^Xhfi# z4H5~L{YkBu@wq-3SNuun8@p7Ia)KR7x> zfr5W=6rJ0a7tI4zMI2>xoqxDy?)n1CG^~I5{>cq6_iCplB2?@LOAI+UFlTE z)HXmWGH=<@amIm+2_$E1>^l;w1~)nv%;5HY3Rcv~3>`Yc1I%;2^mM`qcqk2nz=#!m z@*t3oS)qMrlkOesWLGabyTi&*u~tV)*tN_sGPD;{QRZ+ROYGI+P5oraoisk}Wc*_SY72VAYM-SSQf zTF%SzF1A{(*Ya**TJE?|k5Vm9R;bsombV!6+p*Sn4JZOv+s78_8w>p*rtL?{>`zPT z7^e8sSuqf%hzwd*3b5458HnCYgs#D8ydMx63(g^jjI~<7aChb67yFf z{#YSlS|PDTAqiC>`5KzC3{CBZrqw{xQ=u7Q&`f7&77UuL0L>AA=F&p*P@(z9@&(iK zg)Q<$x$?zf@+Hplr3Uh4ALYw=%8>_#6$-F zk__=E4r`F~=R!x0^~W?6$5SaMY!xSC6{i#oroE+Ryg$tjE6!bioDVZt*j8M;{;(8F zyqv0zT=5oOHPBf5tG;}_G(@tp-o3n`(X{#3U`J!6)N^Ibd?h#8P_1;OwAoOm-|%?Z zu>8od^3gEwX=QJi`NF^HjCA!xVD+7@(VEoi4XNVQa@KjQ(Opf`L-*?K@akF3>gJNs z!}Ka(XBDYo1X@}7H!OlOVhA>5M%xiVZ8b#yrif8yh?%DdL|X&)tbtb6Q1aIHYm5({ zjCX0*H*rncxlGo@OqQS~YX&B-Jl1)GO?Z<`c#77!8%?+e)>UjxR+mi_k4;wonyBNN zZt!euiJ30ano{a+XdbVjDw;0XZfJRLV1%2_r*3GMZeX>V&JAzqESnOXn9ly)(8V=F z5{a7;)0xfiZ0Z@9P1|l-DVULdGn)$Av`#f6FEg8L*|ZrpqhB$bINr47Fl9b+( z(%kxJu_ZkcBY`*lo@@g`W*|eSC5L7pFRq14CIT&8;Anh3RZN2t!SG)A`M8JE$^cVg zBUEME#&Co|cnC14Vs2$pEgfL0aj?Vd=wSQFzHDOU4dQI6an&8-1BdPD4*)F-fabp> zn4*Ox+xEjDF)=u1kBA1YxMQ%hB9_P*6_J+XF)AjJMc{-b^Op*Jh`8kS=L4b1gH2-9 z6LB8~dUxKPyx+UAuXl1>2 zm{I}rQQl;GDE*(Kf9E1@##PZQ5xf?W;k(2gd!e=bg8KwsyxXy@p}3KKI!CE4h7#k8 zjPy=26N5>inQ0e;A8|5qqV9Uc=Pf;M_B_R{yicO*_ADXypD}$!qofT*&AaGxtPa*N zF>uJS&!qzeKfAyOg#6^XvCHvov25BW7qA%fGhkuQam4~bk#9=FA71{8Ou!-$>mTsqo7oOWn-DY=U zrwI>4vy3lGyynJ=Hr^ZjYPs1;eZ(02GHf6`>QC0I;61$VOajZyZ->WdG}&Zl<9R1U zx5Rk?mN_H4w)b|1g0=x|ML*nW{KN?;V#WVDQ0+I!Y^X6h=ixZ3k`;`L9#u(ug$IL_ z95FZcJTx6i((UqHU|BcT`ECwJ*A@;V)DAn=D7&)17h!fiQ%gDnaMv_dwiga3WDWwi zU!HCh_scGbe;&Wzf6v|MSkeP)mwjO&eMCE9K{0P3K`9~cZvh#FS)DAK;lV_{U$nby ztGjHAm#69nzzpbO9_eLK+>#ucf84va%*F z4tbubO;67>G&H88WBC5v=l=&&l>hT1{Lee^f9DQ7Pu~LnUtjGzUu8L2WH4A;x>#qw zIglz}SGL^bc6qQoSXaLK2Z4f32d)3T-WmA3QhTVrVzW1bSw0QgP`N#r@Wy)OKYuTe zW=Mw6DKu8^PZp>Z8xA+t9M1kW8%$GZsy$w;|9ZGG+*EhE+V%sRUa`6Ue6uf_%V?yz z;c|B@TRvT}rSbZ3uF86Kq^0ThbgeT4Nw3t}e1Ew&S!^`g+VXgNwmF!t^r!Xjgb<8NF)G_eGLI7vt0ARU@KVj#Sz?F`#~VhzV1h&X}RuCVNtLiK;yEv{*xhqeIt-1 z&T=D&J+ELRn7ejwBji;N`(`NrjOAvS&`!Z-xaiH^<}U~u$5w5`56J6 z2L(BCRtJRzc|`|BCA9|!#lL%yoQEaVGggPC_0PZ5Wz9FbnqV9V0RW6c!4L@mvJ~4? zf?3)S07Ay(BLEv^h7AxK_c;&;$x^qOpmP@=0mp-_?En~%EuI=yon-D>Oj-y8umoBw zH9<{zENgrS#x9jS)bzCdaL=Z9+Tza`VFNvTy+<(7@CH;3!eoIfR6rpsMO6{uKgrDR>BIMwx~J zfY>x^b^sajE<^*-Di>f}Xg0ZeSP0!#xgN-_j6iuWqU<9fNuJs^j>@5c0F8a1aUSk) z2~$JGWiJ+xT=78Cew`CdFLtOKsKkZE)D{d?=<_n$$KtQ0lJjg2q>vS-GIjNTSUe}=b2oL z6pM5KB#TUo&#N+j6ga~zSSaFX+CHG>$J|O%0XG$>E#SSB3;`;W1S3`#5T!GVv_j}( zJjt5dcP`V%>RZ0cT)dx0z6nQQCSd_X6#(vUok~CUPpLd(406r%Q*jBB%MC99n%pus zb6TH#aR9$OZ~*jLJH6fwD*8bwL3I&<5v{aJ-r%4%#{TXK2U2ioR0$&}p^lp|snVku zC1(W#UXKWx@ZMv_n8UCiFeh~Y6pO}lB_?$FEKhj69FGHID8Gzw=VA0lNvnK!Tm2b} zFFb}th8eej+mzNg1hB`|Q`V z@(*>{YXB*A8`4q;0K*++pjkA*XN=?J##8e7z``VsRv#Q zlO#Gwg?HjB14;Lcbd@EsIuXG4pV>?oAXpK!7omgxo3_UW>ywZ9ukXL~8EYYAK0eU3 zoO+RLkeLoa#5Rc@^C1U-|LR%epq?pTq>Ql+ zRmi~zw3vH}@5Xk6idJ?QRnO&Gtr&Z&RD_sIW6Z+b=5-bIS9~U2rwe5Uv;+t!Zl*ev zbHcoHKu3b^H{e7NlF?t8wMb@f*ub&nyTqDX*__OO+~dmF1YH{?KxkYfk+%dwsb=nO zs~hIy;d_%ur#T+U2EJVl(++1J*#}lJ^6W*3Y6BhPVZamfIi_~vSNJY5o&?epET`vT$GU6w6i;4QaPe!paDt>5-P2|2` zIlM9^AG*z{cR!##QW;k_w9V^zKWL0smCy^_;Y+?BvQ(@}njP8^Y`V`FwhymL*@f=D zTf84}8>vdW9oiLtydOp2RcD|p?0ukq7zT9uEU3hj}NnL zc(uQODjbg(3A6bYz&X0xHR_7}mTj@P6%#GC6)(s!qc|I<#?WjTiYac%a z@c&{m_J2StdU|^6>i&1hn3$NvGhLytZ~R|q#eazv&*$0yo{T+rWY2WPb1e2;ianop z|4XmY22im)9G$KGa8^?7O?IsI{$Am<95h;g;5b2hc5Gt zUk~o{*LlmbDR6k6TH1aPaPhoI?yaQ@7yVyjbZjyq8OYfa0Q{9FB3|MjFkuz(b3LE_ z`wxzM2oQIx4hTY3fk05~rv0fUFxeBdT%Wg*w*{cre9}Te)h9!E4*P#va$psYFhT)G zu#f#9OoO^OmXJhZ<;)8so+3bm6wEz@J zalb9Z_d+4%fLT_#1QfKs?HP2?{ae8I&qUgk`7c$i_5lei90aJ}&p%0c%Q?EU#*Sek z{kA|POayGnb`3#?pn8ssUBqe&1-ttIp`WF**MjbHVIkUYtCBqFBTkcW6fi32j0tOigf7k82!BSo80wrEj?DpWa+`%`VncR-;7}}h^A)5A|9dUJV-!V&Y`{AuH>tP1u@dkWumvR z<*j5d-W|V`^&59b00(;V^%Thi`Q@yP8TUc8<+NDl~3_bwB!rgtmuJ#lg4 zn8yni55V`ud10w#s}>2#UrM+adrjz%OWhc9Ybsf&kr z!`w`Lh3H~m#+kmp-?->PvC|GXd#C<5RrjW=xykPJ+|x;K+>O?~6O5|$LkIry-xiuT zacsVX)RE_CzAXLqz22%-eNP~Q_Ym7t6E3S&2>Xv}KgPH7?TWjd0GblRfyeLYJpOIN0$|D(Nlu1Z+t&YN8R z;nHt6lS3dWKdzTMver{r(avXR*;NwZh;Iv_D7hcS0`<>Ls?K$3^=XrB)Sfo%%wjtVPZpvIifqbuM~0N4 z@fYsZjl&=Nxc|?Rv0*FS%l|PMTf@o!@5vZ6|D6B`@qg-3Sc;-B{Er@mH&J!w|K?F_ zTxOE}uO7t+aW>t5c@zlW>i_0Z$a+k~K6@0eY%S%@{^L=or@v__RCFzxOu2aWC^QO{ z{^LPkEU#pb!LFp~lHg7d2KD-ZcZzo`W(R?dxLM?jkXXSJPPb8X#i@ zYvpcTC8B!bB9&N#{w^mFx8S<$CG1f2qp=nMfhb0x+Mu1u&BYvOq>itwjMu86I4STLh^{vbK{(OLdk4bFB1zu@*>e>Jkag8mw zit^DJ$N~RkA&%(wi`#s$=^_4Y#*2j-UjJs&;0wF}ZkNR;^Bcfdr_X+-#fW)~zSKKm znDaYorN*{5Z*VR10p{wo=r&q_aUBRKW^n{1u&;fGXU}93+vfnQ4IHNN5-$6=;$gZ6 zJePU}m~ce61YXL3}fkbd$s_(4fGQ78)t1A)}vLTx+k z;sQ94&gcAX255O60((3d(#Y>lM9rSk+?X%{fmwW6ubj5oGljGsu4jVZZjMxE+@<=x zex1AMUc#xhTXYR7cUrn{5jlaC~kZ>lcCzPOJ4qlwi-s#M-~}0_Rjj zH3mSEjJWOeBAQ_5Aw=r5#Pv8?BW*hm`W(6D7&y^a(UbdiH`|WI%NA*dQtYp-Qpd*qQ__0!Oi8i$q!!PNoQqjO(-@ZLsb@c8SVJ< zA$md8ySTPuM1x@NO?^^iW2r;gDPc`jh-TK&7?9x9jm*`qZ`;V3Q*wH6T;iyjUDCXR$mgnaVDn{7^TciWyy2Bw@OYC}4N)ndQ6hFXsR-Xi1eY^yq5PXSTRN&So68UiZ9kl0-qK)8#Au#Hdfe8p4Hx1l7gfyBD z(VP;tkPoW14;edxBEjdb&W8r$JKden9=R{moHvWM@8qodVGQZ5lP|lF2k)J)2Kf)Z zyD$9OKk{6D2+4Sh^!dD-`!RU^1G1;{EK&Gr_({8I$t3u}Yy6;h8j5#*8l?WJ_3G-{ z{wDtZ7;gT$GV1zs{*Y^b<32T0p#U*wLkk&=hkUar0i6?dfIw7$rll%)ETF2+tuL0` zF~J|nqI1Dw15!2=j0J4e{6s-j{^n*3K!c$$SdJnkjDe)yPb7>1Zh_tfZXtDnp?6;$ zH7!Bc?hjHyv3H6G@qvkM!J+jWpLc^$5xkuObvme7hIZjuY;hd3+y$FtA z^oDB(i=de#6#zQWm8>EuW3B@M5UNrP3Qq~;LCYRsYWIQf(_-CPg8nl=I%8h5t zK{YUdJ+y+|xg!sjz^-IPXr|s1dM{wKLS(j~L90&2`c~5r4PqSF0kBmu{MTm&$_mc5 z41e!Jo|PYd)8!CI67O%9IGAr}ljHV^*~G64xc zHV>|3vxyj|#DQ4i6GY=O81hX7liyNBBjY2b^63)^^;kKWDqyT_>_5@=y|8qYK7O+F z*L081giZLE63wfozqKp(0e~WOLZglUsMAUd2cQWnarTGryK$$YQa%$Co0g9Ln$~Y? z-QkQnxmPrJwdBw(QO0`ZhrE_wI7A>r8T6P(UZ8UpL&a!#xEnHf114d1yWb?K)+w&=Jkw zGhH~fs?Y>;K7LL|_2IcYI|J||KLEDo0gD(x%lj$^p8;~F0JTb{dcvPP81y%OBUZ&Ofj|9Ojb2@4_DU z(fQJe4pUAE2MhMI&<^d4246YLlr@;-K`HrTn!g(h>Tk~*5;Ko{fPVpiJnrX~#+%h# zZlWE;+~v zs(os7-p^jmmqahDwqhySZ)iZijH$7f{F`p$bBcF;p(6=oU}w{R4hg4M?hAckjcDF9 zFT#u16Ym5v*|amYoZ&La&zUeXHoG1BG)t2#HfQyC%2z!O2}NI`9+aJREzjr3YR=sQ zJM!Oge158rd0+PnbU-;?bn^6$Je=Z*1SLrB`G3Esf>%mN2^9|9y+xZ?mRW3jL zHjscL690_KbT-GkC=#zNvO>cFA7ac%84~Fe732~0<+&jLly{dC8qr=1xG7F%jII_a zp?jC#695D_xA; zz=0M4XOS;X-^s*99>%16q>38QfivYc6ud~)j@$B&D@q{qvxHY_8h#04(G)gr>VpLf zT2b)nP%!0<<$a6yPiS&wS;EXh8-RcJF~B)2KF;CzwGaQ2nGp`J@R@h_zbCg!s>V;M ztzdl{NgfH4Hv~#W?B;*-!T4#%26#i9_7tgvGo_jauTPczLh;TB1y&>INb-M!xkj8_?`gy;jUZS(NzU7R?lZ>M3f+TQ4 zvxD$XX{#{aUPvFm#2Bm`c;xO09@F6rrLsd~SS5{kdQ^#Wb2)lMV2q_h7V3Wpw&hE= zQa-hTUH&i&ciY;;s^PG1%cr&SC8o^Ops)r3MSV;}mCzwQCoJS&MSBq`X6)RsKWHXz zk-E%$I3|aI-Tt!5dMW9zAG!(HyRIzyli)q$E`8O4?$|>BH^NRk9Pe0=w^m_C^DZ!r zO0uw#U5W7s`7jon|L?DV#@_0*EV-sYeJEMUO*oTX8C&sH`_5Q;0P!kX@Ij_Rzz~hf zZ_(6CoYd>3l-rV&d#{v7{gl6=DM*|Y;BhiaOEPL$GP*%Brou>laucpX0Dg-a_TMDp z;Uv=3By#U0N`oY7o+Mh_Bmhh&DtCmi6p;k`Ne5L&?kjP5Du#wf{AGM z02hi;{@kqSzyczw>IW78WC!r3w2>qsBGBUJP4>8L)yD1FzdwnSsC$B=%*%Ep zdDpL}x@mt-${fPUKHI!Wwq{JL`_V&}W#LdBS@k0+WVb!&bSqo{`2;r&uVS)UhS*wK zjqhpAP!dT8R*>#Roh;YX@Oxbj`#h1a1h9+6Q{vZz0J9U+@TMnh6Tb8b_K`AvU5<4T z(RULmcOT4-xZi6IKJ;-qoKTq}@V3Gh?b{X^*`EztPc?gyP#?MI%QkcbxIJn+8DrWm zVNLzcoZCK2_kB@OjXo@XnNDrhFlf^-e4Y1X7f)@w_2;2t;{J)M2ddqL`0175>CJyj z#vWQvpH}``GDd#}dVdBq`twHa3?uyv>p2-)J;OtuUC=cSU_U+0^E~5Nz2HS&(EYh!LE>NX8(j*PpYbUvz5R13x_Wt+ zcKKuH^27TpLHsL8=#@8$SW;Odf+I;#a zGv6@yXE>rfj#Okl$M)7VI@SKZhq4N!ApkKx_h|N~`Io08`DY=TH@1IHd)#hr z#ox5t3jf6$U4w`A>vPzT9>1YKqkRR8F}im*Iz4`4^5^8#JN@kY=xHm_-NQ<7%cr}` zaB@tqk-sNxW3xB6)Y-q%zY)`)(J0@?jskDQ9S?e931-~a$N>MYqrB-CaE~I_;^&;VHlK-5(rG0qBdzynitObzAn+$jvJzSh{-5}|1d)dxdjUQu-Kn;I5_=)pk!e7Z- zt+q)x#Ed}3+HL&u=;JDuuU4NPcnMhDTgQLVFW_GTOfnE4AQlz_L4@-9hX71`b_`<( z!Uuqb$4>%)Y)Xeyp*Vpt0h=-btZXS#I-pHJ91k%@0mXPb00h)#gRZr;`2ev!&E?bpB1HfZ9!riw zM|(-&%=s}?yWIm#LHF?{Aa69v@VGY1+W)s$61i-$WFP^J{VgjbqhhiLKWma?Z4x-u zCQ46X90UY=yo0b8CWVyoq*D>dy_@dqEI=I9XWKWSusq<21{%s4H`fnEmzH7GIF`IX z^Rq5|T~5MagNS##kb7OLJAl`o$;M>E*40ya!iD;Q`gLE$ToQ%=wpubqM<_%}sjrE_ zsvzrk1~^3SMR}KM$vW06M-ea)gkk7a`x8Me!S(>3g21e~S`&Z*;Mf${g%jNvFedUQ zNASNTV~<@`{qLWXF=a_)H`Y|M zF9%FEANFbjcL7&8~<=8;0fTsWzRYu~;j+=KZC zlp?Zh(J;S6##u0kr(wzf0AR?<+)nGKZ~Z4=qE{(^GT^Iw`w~b%kUThW$KwoyrL>@x z21k?S0S~px*qQH_4W49)?8&pN zyKwT$?sF<_m1h_WngRVUas{^i?Fa^eZqtRPC4B6Fx3 z;L&2Y4Y@@4#ooa;MN2%lE*t`8naGX>5b(84O30hh$Y;z$$k>`3!o2s)biL~N+qLF- zpfNw@B?9Bb+u&sk3|uP{{JZ=_NxP2d>{#8}1*IUokRTcTRg~zwitzqZTbpr4gGN`t zy~BwE=(Vo&)W+MtxI8e2^f%x_W8}@x7OiuYADNqG9vgk-!kU8&w;`FO zc0zqeq?NkF%owyB9eu!eQBm>oMaDuqwP%yDjWXi%1_mrcYD zS#5K{8cKC$_zWwmVZ2IguMMsz;&sM5DT?FM)UErTZ@vle3%Qyi2lol$+ zuov5}{7f+ScQp|m1YAIVMV8FnQg(HwC$Oz24L!qRJ_dB2<*1gg6o*=+i`B2IH*Dro3t_NFBkk z;LVzHWKtzwulE1UJRB@j+DYcu7Rsc?Iv`~+qwZ^Clfj(0AIPvk@W?LpPJ$Ivmy}Pv zhJ=j~*B)XPI1}5A!w(eKfv8hgBZyIr%U|dQDz`r4rbNaP2pGVG4pK0*w#AA>k^n|I zC&|Emc@6ppRP)Pg*x!AI!MclPdQT?R`LcItRNS%>dWBw=hU`Onp$}U zl>rTc-~J0L^4T(8=H)l`!uSl-YbbHk?7(`B@xkshMTiqg<~nJl_GJ=ZCLZ9oXeO2B zKrsQy9|zzetPO=qIsNHKhTVF2&(y*zI`xB7m6%JLMYa>--btpHKMi^8JHNH$hvCPl z{i9!#wu9uSWluXGH|c|P96^G24b*v^OhG;*@_BHobQR`cwVI^DfDNla)$9wK#i2x> zY5*D%jzE1o0b=E@EM>NGshb{zwIuUOV!<|=oQqpYLhiXdVK$$daEMF1raPejekUe1 zjJzsOHSnGD z@#>efsnue8gr+{CS>Arg`e7%IzsB_8^^jv&R&*+_;5!l>2ps9!xIt(lIFKovu1h##uT{|BvWHpeAsYjEu9UEbmc=a(iCB~~4DG zY2Cw%jT={P6G7pL6nPeB4BVj zO1Gn?y)8S8Q0yM`oOqb&PP@+f`Ss|tk=opN>kU_7>hYKIh{bhr*9jK&Os8H?-7Jok z=39e2mp@Ibs7g+4=-%Qrh{S^DePVcEaBJNL{hLj0Uh`2pV(PS{m60<2S z(;6|*g6Z6hCkPxl<6&llI4UK9c^WwCe z$$`uO3LJ=nj2C#U%$s2g5+jm*HI+C^`fq8OFW2dof8UdaiA&5%-)Em{9Dyds~eeE z$Pl5DTA7Ijb;^i@C(EnTioc_8)9GZ`l9btsiU9#=J6b#m3v1wMQj65{a|x{T9kb^Y zAD06EC4V$|rq77q127m$`uKJal1Z4E1IIE?Dj-pOJ&_X9Kv0^I7v?2)rp8r!jI6}x zkYBxt{4>NAok zQdl_x!-cxr_Wx)#w=c=cq^l+Ez{$}UG1#%%ktGbwE+q`XfWF1VKL~w1rku(U8cA!} zzVA4_3M6e>Td%hJvnvwYs6g55Uuj~hJ4fIQJ%w09>m94@vSI7LC6B0aY{(s1q<$&W zK4te21XHF|h(9g93L?gg%1SozqsCt7y6fx3;FEe0I`Ej-mYp z!P{YYwkv)vM!_RR3GWLR@5^c z+09`RRVg~C9(nG?j;2PAw?|?>ynYRcqKQt4q89mZTC zH~Z|=xZ_o_Dx@{ik0ePUOUm>t+&dLb}6AW(L^!l~bZR zOcl$<_92ZVZcaa(_N$cs_ZbQ9idpbs2j?sFd86CMDmJ zI>8-d(}zYZOr17Ya%%4tDJUgXM&_V@;hS`5#^mbFZewXpxrqTys_nN_3K2=2wU2o%gjLj^@dhb<%Kko$hsXXmvM4bvvO8_8hu5 zHo80p3tOSOyD7RwrMkWix?kLN_m>uSj}~^GbhmNzwrCbNxEI$&_12(ztNM$}HhN25 ziwmK8^C^q7C3-W>i&H~-lS_-^M|xvViz7Jt!!-Is+)D$Z`u)(QUVZ%@o25>#rKJ%4 zh2*8VV*R)`kq0OAY@v z8)^@%{97^v985t$6^ywLGLRa0rjL^f3u2YR3N>{s@R$B&F zt(J`%ERA{}jSR5XwvUXiN>+gZBcN7g)K*cnZ(`_*nMrKx>~S_TGF~o(K^y^on~i^x=f7r#+csF zgrU}$j!l^{&zNb4g~d>rInJ21hvkLh7_EgVMu4eYk!fFwDSGRM#Db}1?8eJ~Ca>^J zU(;=HTdW~@<22uB8uPno@`;-Y@EQx2u?U?Q36IRY>e+lfvdQPN$(FbIx5jJ|bIWdd z^X-Wl=fBM-+AV7_a~{R5M}w^|-diYXTlZmGVv*!msBeWf3vKlHrPu%=I**^<8XVOnEW5mJU2`eJ4&l z@l|GWbO1a)&M4@0D54ho)qZj)9_{FbS~vyX2?%c1jB(+aSnouow7ydVjEnsi$qCDeoXj(6}AAABRUgcSn>h8pcYwUuGgXv))ZWBB#5Q657XrO~P2S zMpTkVUxJ`B@;Qh}JMjLt--Z^q6>(s)Zq;;$QEq5FmjK~SNipKb|H064zE2}cL>$>@J{4{ApQBAOvzCua?* zn`emqZW4TM0uN_e7A%tL7{4IB4#q8lxg&tm3NV|BXNA4Db5u$Ntuw|#8|ABQvl@~a$S3P9(1bJeI-rj_bXRJI zXSp1otYG}&xHSD=0=fW2|IKH^kgQj!o}NYzFx8!hiyw$bPb1a!de`+#%S}sCNT5q*{8?L;r^2hZwB zH}!gvX+%%;R{!dn!LCbca|3$k;#nzy{%v!5=sJPuV!8B*E@)my=Y+=JZpLO=7jxL~ yW)2MJhskGjKId|tZ96*c6f);pmv+-*W<{3f?S*EPRc1uqc1Z5_>"$LOG_PATH" 2>&1 - -# Requires: pip install Pillow -python3 "$ROOT_DIR/scripts/frames-to-gif.py" "$FRAMES_DIR" "$GIF_PATH" -echo "Wrote $GIF_PATH" diff --git a/packages/web-shell/scripts/frames-to-gif.py b/packages/web-shell/scripts/frames-to-gif.py deleted file mode 100755 index 646f0463dff..00000000000 --- a/packages/web-shell/scripts/frames-to-gif.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python3 -"""Assemble labeled demo GIF frames for the collapsed-groups persistence story. - -Requires: pip install Pillow -""" -from __future__ import annotations - -import sys -from pathlib import Path - -# Requires: pip install Pillow -from PIL import Image, ImageDraw, ImageFont - - -CAPTIONS = { - "01-expanded.png": "1. Backend is expanded", - "02-collapsed.png": "2. Collapse Backend", - "03-after-reload.png": "4. After reload: still collapsed", -} - - -def load_font(size: int) -> ImageFont.ImageFont: - for candidate in ( - "/System/Library/Fonts/Supplemental/Arial Bold.ttf", - "/System/Library/Fonts/Supplemental/Arial.ttf", - "/Library/Fonts/Arial.ttf", - "/System/Library/Fonts/Helvetica.ttc", - ): - path = Path(candidate) - if path.exists(): - try: - return ImageFont.truetype(str(path), size=size) - except OSError: - continue - return ImageFont.load_default() - - -def annotate(image: Image.Image, caption: str) -> Image.Image: - """Pad a caption bar above the sidebar crop so the demo story is readable.""" - bar_height = 56 - canvas = Image.new("RGBA", (image.width, image.height + bar_height), (18, 18, 18, 255)) - canvas.paste(image, (0, bar_height)) - draw = ImageDraw.Draw(canvas) - font = load_font(22) - draw.rectangle((0, 0, canvas.width, bar_height), fill=(32, 32, 36, 255)) - draw.text((16, 16), caption, fill=(245, 245, 247, 255), font=font) - return canvas - - -def make_reload_frame(width: int, height: int) -> Image.Image: - """Explicit reload beat so viewers don't mistake remount for a no-op.""" - canvas = Image.new("RGBA", (width, height), (12, 12, 14, 255)) - draw = ImageDraw.Draw(canvas) - font = load_font(24) - caption = "3. Reload the page..." - bbox = draw.textbbox((0, 0), caption, font=font) - text_width = bbox[2] - bbox[0] - text_height = bbox[3] - bbox[1] - draw.text( - ((width - text_width) / 2, (height - text_height) / 2), - caption, - fill=(245, 245, 247, 255), - font=font, - ) - return canvas - - -def main() -> int: - if len(sys.argv) != 3: - print("usage: frames-to-gif.py ", file=sys.stderr) - return 1 - - frames_dir = Path(sys.argv[1]) - output_path = Path(sys.argv[2]) - ordered_names = ["01-expanded.png", "02-collapsed.png", "03-after-reload.png"] - frame_paths = [frames_dir / name for name in ordered_names if (frames_dir / name).exists()] - if len(frame_paths) < 3: - print(f"Expected 01/02/03 PNG frames in {frames_dir}", file=sys.stderr) - return 1 - - annotated = [ - annotate(Image.open(path).convert("RGBA"), CAPTIONS[path.name]) - for path in frame_paths - ] - width = min(image.width for image in annotated) - height = min(image.height for image in annotated) - cropped = [image.crop((0, 0, width, height)) for image in annotated] - reload_frame = make_reload_frame(width, height) - - # Story: expanded → collapsed → reload beat → still collapsed after reload. - frames = [cropped[0], cropped[1], reload_frame, cropped[2]] - durations_ms = [2200, 2200, 1400, 2800] - - output_path.parent.mkdir(parents=True, exist_ok=True) - first, *rest = frames - first.save( - output_path, - save_all=True, - append_images=rest, - duration=durations_ms, - loop=0, - disposal=2, - optimize=False, - ) - print(f"Wrote {output_path} from {len(frames)} frame(s)") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) From dee8fc61a95acd9bc7822a2f57957dacbab603db Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 17:19:26 +0000 Subject: [PATCH 6/6] fix(web-shell): keep collapse latch armed until catalogs settle successfully Two paths could consume the first-sync latch against a partial catalog and then auto-collapse (and persist over) the user's restored expansions: a failed initial sessions/groups request counted as settled, and a mid-session organization_enabled flip let the auto-collapse effect run one commit before the groups gate closed. Errors no longer settle either readiness gate, and the gate now closes during the flip render itself. Also drop the WorkspaceSection reload effect and exhaustive-deps suppression that defended a workspace.id change which cannot happen (the render site keys the component by workspace id), and import the storage key in tests from collapsedSessionSections directly instead of re-exporting it through WebShellSidebar. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_014KuV2hx2ZqEqdRwsJz4KJH --- .../WebShellSidebar.collapse-persist.test.tsx | 6 ++-- .../components/sidebar/WebShellSidebar.tsx | 28 +++++++++++++------ .../components/sidebar/WorkspaceSection.tsx | 15 ++-------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx index 20d15deedd7..1a9cd76bb22 100644 --- a/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.collapse-persist.test.tsx @@ -91,8 +91,10 @@ vi.mock('@qwen-code/webui/daemon-react-sdk', () => ({ })); const { I18nProvider } = await import('../../i18n'); -const { COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, WebShellSidebar } = - await import('./WebShellSidebar'); +const { WebShellSidebar } = await import('./WebShellSidebar'); +const { COLLAPSED_SESSION_SECTIONS_STORAGE_KEY } = await import( + './collapsedSessionSections' +); globalThis.IS_REACT_ACT_ENVIRONMENT = true; if (!globalThis.PointerEvent) { diff --git a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx index 365fb55e1cc..d58bcaeda88 100644 --- a/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx +++ b/packages/web-shell/client/components/sidebar/WebShellSidebar.tsx @@ -81,7 +81,6 @@ import { AddWorkspaceDialog } from '../dialogs/AddWorkspaceDialog'; import { WorkspaceSection } from './WorkspaceSection'; import { SessionGroupSection } from './SessionGroupSection'; import { - COLLAPSED_SESSION_SECTIONS_STORAGE_KEY, isPrimaryCollapsedSectionId, readCollapsedSessionSectionIds, replaceOwnedCollapsedSessionSectionIds, @@ -93,7 +92,6 @@ import { import styles from './WebShellSidebar.module.css'; const SIDEBAR_WIDTH_STORAGE_KEY = 'qwen-code-web-shell-sidebar-width'; -export { COLLAPSED_SESSION_SECTIONS_STORAGE_KEY }; const SIDEBAR_DEFAULT_WIDTH = 260; const SIDEBAR_MIN_WIDTH = 220; const SIDEBAR_MAX_WIDTH = 420; @@ -463,13 +461,15 @@ export function WebShellSidebar({ : {}), }); // useDaemonResource starts with loading=false before autoLoad runs, so - // !loading is not “settled”. Treat the first data/error as the ready signal - // (empty lists are still defined data) so the initial-catalog latch waits. + // !loading is not “settled”. Treat the first data as the ready signal (empty + // lists are still defined data) so the initial-catalog latch waits. Errors + // must NOT settle it: a latch consumed against a failed request would treat + // every section from the eventual successful reload as brand-new, + // auto-collapsing and persisting over the user's restored expansions. const sessionsCatalogReady = !organizationEnabled || !includePrimaryWorkspaceSessions || - sessionsPage !== undefined || - Boolean(error); + sessionsPage !== undefined; const { sessions: primaryPinnedSessions, reload: reloadPinnedSessions } = useSessions({ autoLoad: organizationEnabled, @@ -556,6 +556,16 @@ export function WebShellSidebar({ const awaitingInitialSessionCatalogRef = useRef(true); const [groupsCatalogReady, setGroupsCatalogReady] = useState(!organizationEnabled); + // organizationEnabled can flip true mid-session (capabilities can land after + // the flat sessions request settles). Close the gate during that same render: + // deferring to the reload effect would let the auto-collapse effect consume + // the first-sync latch against the stale pre-organized catalog first. + const [prevOrganizationEnabled, setPrevOrganizationEnabled] = + useState(organizationEnabled); + if (prevOrganizationEnabled !== organizationEnabled) { + setPrevOrganizationEnabled(organizationEnabled); + setGroupsCatalogReady(!organizationEnabled); + } const [sidebarWidth, setSidebarWidth] = useState(readSidebarWidth); const [projectExpanded, setProjectExpanded] = useState(false); const [projectsExpanded, setProjectsExpanded] = useState(true); @@ -875,12 +885,12 @@ export function WebShellSidebar({ setGroups(catalog.groups); setMenuGroups(catalog.groups); setColorOptions(catalog.colorOptions); - } catch (err) { - onError(err, t('sidebar.groupsLoadFailed')); - } finally { // Empty catalogs still settle the latch — sessions/groups hydrate on // independent requests, so readiness cannot wait for a non-empty list. + // Failures must not settle it (see sessionsCatalogReady above). setGroupsCatalogReady(true); + } catch (err) { + onError(err, t('sidebar.groupsLoadFailed')); } }, [onError, organizationEnabled, t, workspaceActions]); diff --git a/packages/web-shell/client/components/sidebar/WorkspaceSection.tsx b/packages/web-shell/client/components/sidebar/WorkspaceSection.tsx index 3ee153647ea..f35c5ae1878 100644 --- a/packages/web-shell/client/components/sidebar/WorkspaceSection.tsx +++ b/packages/web-shell/client/components/sidebar/WorkspaceSection.tsx @@ -126,20 +126,11 @@ export function WorkspaceSection({ if (controlledExpanded === undefined) setInternalExpanded(false); }, [controlledExpanded, workspace.id]); - // Remounted workspace folders must reload their own collapse prefs from the - // shared key (ids are namespaced as `ws:|…` so they cannot collide with - // the primary catalog or another workspace). + // The render site keys this component by workspace id, so an id change + // always remounts and the lazy useState initializer re-reads storage. useEffect(() => { - setCollapsedGroupIds(readWorkspaceCollapsedGroupIds(workspace.id)); - }, [workspace.id]); - - useEffect(() => { - // Depend on collapsedGroupIds only. Including workspace.id would briefly - // write the previous folder's local ids under the new workspace key before - // the reload effect above replaces state. writeWorkspaceCollapsedGroupIds(workspace.id, collapsedGroupIds); - // eslint-disable-next-line react-hooks/exhaustive-deps -- see comment above - }, [collapsedGroupIds]); + }, [collapsedGroupIds, workspace.id]); useEffect(() => { if (controlledExpanded === undefined && autoExpandKey) {