From c5f8e0d0bf88190dcc7c106558f865c4badca46f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 07:57:00 +0000 Subject: [PATCH 01/10] feat(tui): add t1chat mode matching t3erchat UI - Added `t1chat.js` bin entry point which sets `T1CODE_CHAT_MODE=1` - Updated `apps/tui/src/ui.tsx` to read `isChatMode` - Implemented welcome screen mirroring `t3erchat` layout (categories, suggestions) - Added "Temp chat" toggle inside the composer toolbar for chat mode - Hidden git and diff features from the toolbar when in chat mode Co-authored-by: ahzs645 <31978381+ahzs645@users.noreply.github.com> --- apps/tui/bin/t1chat.js | 45 +++++++++++++++++ apps/tui/package.json | 3 +- apps/tui/src/ui.tsx | 107 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 151 insertions(+), 4 deletions(-) create mode 100755 apps/tui/bin/t1chat.js diff --git a/apps/tui/bin/t1chat.js b/apps/tui/bin/t1chat.js new file mode 100755 index 00000000..0b76b92a --- /dev/null +++ b/apps/tui/bin/t1chat.js @@ -0,0 +1,45 @@ +#!/usr/bin/env bun + +import { spawn } from "node:child_process"; +import { fileURLToPath } from "node:url"; + +const entryPath = fileURLToPath(new URL("../dist/index.mjs", import.meta.url)); + +function printError(error) { + process.stderr.write( + `${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`, + ); +} + +process.env.T1CODE_CHAT_MODE = "1"; + +if (process.versions.bun === undefined) { + const bunBin = process.env.T1CODE_BUN_BIN?.trim() || "bun"; + const child = spawn(bunBin, [entryPath, ...process.argv.slice(2)], { + stdio: "inherit", + env: process.env, + }); + + child.once("exit", (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 1); + }); + + child.once("error", (error) => { + if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") { + printError("t1code requires Bun on your PATH to launch the TUI runtime."); + process.exit(1); + return; + } + printError(error); + process.exit(1); + }); +} else { + import("../dist/index.mjs").catch((error) => { + printError(error); + process.exit(1); + }); +} diff --git a/apps/tui/package.json b/apps/tui/package.json index f18b9008..c7ac48a9 100644 --- a/apps/tui/package.json +++ b/apps/tui/package.json @@ -14,7 +14,8 @@ }, "bin": { "t1": "./bin/t1code.js", - "t1code": "./bin/t1code.js" + "t1code": "./bin/t1code.js", + "t1chat": "./bin/t1chat.js" }, "files": [ "README.md", diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index bc516814..0e9e1037 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -2800,6 +2800,9 @@ export function App({ const [showScrollToBottom, setShowScrollToBottom] = useState(false); const previewAttachmentCacheRef = useRef>(new Map()); const composerDraftsByThreadIdRef = useRef>>({}); + + const isChatMode = process.env.T1CODE_CHAT_MODE === "1"; + const [tempChatMode, setTempChatMode] = useState(false); const updateAppSettings = useCallback((patch: Partial) => { setAppSettings((current) => normalizeAppSettings({ ...current, ...patch })); }, []); @@ -8120,7 +8123,7 @@ export function App({ marginRight={1} onPress={() => restoreDefaultSettings()} /> - ) : mainView === "keybindings" ? null : ( + ) : mainView === "keybindings" ? null : isChatMode ? null : ( <> - {!activeProject && !activeThread && !activeDraftThread ? ( + {isChatMode && (!activeThread || activeThread.messages.length === 0) ? ( + + + {tempChatMode ? ( + + + + + ) : ( + + )} + + + + {[ + { icon: "✨", label: "Create" }, + { icon: "📰", label: "Explore" }, + { icon: "💻", label: "Code" }, + { icon: "🎓", label: "Learn" }, + ].map((item) => ( + { + e.preventDefault(); + e.stopPropagation(); + syncComposerValueRefSoon(); + setComposer(`${item.label} `); + setTimeout(() => composerRef.current?.focus(), 0); + }} + style={{ + backgroundColor: PALETTE.surfaceAlt, + paddingLeft: 2, + paddingRight: 2, + paddingTop: 0, + paddingBottom: 0, + marginRight: 1, + marginBottom: 1, + flexDirection: "row", + alignItems: "center", + border: true, + borderStyle: "rounded", + borderColor: PALETTE.border, + }} + > + + + + ))} + + + + {[ + "How does AI work?", + "Are black holes real?", + 'How many Rs are in the word "strawberry"?', + "What is the meaning of life?", + ].map((q) => ( + { + e.preventDefault(); + e.stopPropagation(); + syncComposerValueRefSoon(); + setComposer(q); + setTimeout(() => composerRef.current?.focus(), 0); + }} + style={{ + border: ["top"], + borderColor: PALETTE.border, + paddingTop: 1, + paddingBottom: 1, + width: "100%", + }} + > + + + ))} + + + ) : !activeProject && !activeThread && !activeDraftThread ? ( + {isChatMode ? ( + + setTempChatMode((prev) => !prev)} + /> + + ) : null} {activePendingApproval ? ( <> @@ -9852,7 +9953,7 @@ export function App({ )} - {activeProjectId && isGitRepo ? ( + {activeProjectId && isGitRepo && !isChatMode ? ( Date: Tue, 7 Apr 2026 13:38:51 +0000 Subject: [PATCH 02/10] feat(tui): add t1chat mode matching t3erchat UI and colors - Added `t1chat.js` bin entry point which sets `T1CODE_CHAT_MODE=1` - Updated `apps/tui/src/ui.tsx` to read `isChatMode` - Implemented welcome screen mirroring `t3erchat` layout (categories, suggestions) - Added "Temp chat" toggle inside the composer toolbar for chat mode - Hidden git and diff features from the toolbar when in chat mode - Used exact hex color `#a23b67` for `t1chat` category buttons and UI elements to match `t3erchat` - Fixed React hook violation by extracting `ChatCategoryButton` component Co-authored-by: ahzs645 <31978381+ahzs645@users.noreply.github.com> --- apps/tui/src/ui.tsx | 66 ++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index 0e9e1037..7051554c 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -1315,6 +1315,41 @@ function AttachmentPill({ ); } +function ChatCategoryButton(props: { + icon: string; + label: string; + onPress: () => void; +}) { + const [hoveredCategory, setHoveredCategory] = useState(false); + return ( + setHoveredCategory(true)} + onMouseOut={() => setHoveredCategory(false)} + onMouseDown={props.onPress} + style={{ + backgroundColor: hoveredCategory ? RGBA.fromHex("#a23b67") : PALETTE.surfaceAlt, + paddingLeft: 2, + paddingRight: 2, + paddingTop: 0, + paddingBottom: 0, + marginRight: 1, + marginBottom: 1, + flexDirection: "row", + alignItems: "center", + border: true, + borderStyle: "rounded", + borderColor: hoveredCategory ? RGBA.fromHex("#a23b67") : PALETTE.border, + }} + > + + + + ); +} + function PathSuggestionRow(props: { entry: ProjectEntry; active?: boolean; @@ -8952,8 +8987,8 @@ export function App({ {tempChatMode ? ( - - + + ) : ( @@ -8967,33 +9002,16 @@ export function App({ { icon: "💻", label: "Code" }, { icon: "🎓", label: "Learn" }, ].map((item) => ( - { - e.preventDefault(); - e.stopPropagation(); + icon={item.icon} + label={item.label} + onPress={() => { syncComposerValueRefSoon(); setComposer(`${item.label} `); setTimeout(() => composerRef.current?.focus(), 0); }} - style={{ - backgroundColor: PALETTE.surfaceAlt, - paddingLeft: 2, - paddingRight: 2, - paddingTop: 0, - paddingBottom: 0, - marginRight: 1, - marginBottom: 1, - flexDirection: "row", - alignItems: "center", - border: true, - borderStyle: "rounded", - borderColor: PALETTE.border, - }} - > - - - + /> ))} From 8e07d75f60e8e58fc84699daf02589d9eb544087 Mon Sep 17 00:00:00 2001 From: Ahmad Jalil Date: Tue, 7 Apr 2026 07:20:42 -0700 Subject: [PATCH 03/10] feat(tui): enhance chat mode support with responsive layout and theme updates --- apps/tui/src/responsiveLayout.ts | 3 +- apps/tui/src/theme.ts | 126 +++++++++++----------- apps/tui/src/ui.tsx | 179 +++++++++++++++++++++++++++++-- bun.lock | 10 +- scripts/dev-tui.ts | 1 + 5 files changed, 239 insertions(+), 80 deletions(-) diff --git a/apps/tui/src/responsiveLayout.ts b/apps/tui/src/responsiveLayout.ts index 7d6ad5b6..59d377cd 100644 --- a/apps/tui/src/responsiveLayout.ts +++ b/apps/tui/src/responsiveLayout.ts @@ -27,6 +27,7 @@ export type TuiResponsiveLayout = Readonly<{ export function resolveTuiResponsiveLayout(input: { viewportColumns: number; sidebarCollapsedPreference: boolean; + isChatMode?: boolean; }): TuiResponsiveLayout { const openSidebarMainPanelColumns = input.viewportColumns - TUI_SIDEBAR_WIDTH - 1; const showSidebarToggle = @@ -53,7 +54,7 @@ export function resolveTuiResponsiveLayout(input: { // should track sidebar visibility rather than the overall terminal width. showWindowDots: showSidebar, showSidebarAlphaBadge: showSidebar, - sidebarTitle: showSidebar ? "T1 Code" : "T1", + sidebarTitle: showSidebar ? (input.isChatMode ? "T1 Chat" : "T1 Code") : "T1", showHeaderProjectBadge: input.viewportColumns >= 144, showComposerModeLabels, showComposerModelLabel, diff --git a/apps/tui/src/theme.ts b/apps/tui/src/theme.ts index aa7676bc..781c4d4a 100644 --- a/apps/tui/src/theme.ts +++ b/apps/tui/src/theme.ts @@ -55,44 +55,44 @@ export interface ResolveTuiThemeOptions { } const DEFAULT_DARK_PALETTE = { - canvas: "#171717", - sidebar: "#151515", - main: "#171717", - surface: "#1b1b1b", - surfaceAlt: "#1f1f1f", - input: "#111111", - surfaceUser: "#202020", + canvas: "#21141e", + sidebar: "#1a0f18", + main: "#21141e", + surface: "#2a1825", + surfaceAlt: "#311e2c", + input: "#1a0f18", + surfaceUser: "#2a1825", surfacePlan: "#1f221c", surfaceWarn: "#262016", - surfaceInfo: "#1d2026", - footer: "#171717", - diff: "#1b1b1b", - popup: "#1c1c1c", + surfaceInfo: "#261a2e", + footer: "#21141e", + diff: "#2a1825", + popup: "#2a1825", scrim: "#00000099", - border: "#252525", - divider: "#2d2d2d", + border: "#3d2438", + divider: "#3d2438", control: "transparent", - controlHover: "#202020", - controlActive: "#292929", - controlActiveStrong: "#1e1e1e", - controlInset: "#141414", - controlInsetHover: "#1a1a1a", - composerPanel: "#1a1a1a", - composerBorder: "#2a3f95", - composerBorderMuted: "#313131", - composerSend: "#2f438e", - composerSendHover: "#3c57ba", + controlHover: "#311e2c", + controlActive: "#3d2438", + controlActiveStrong: "#2a1825", + controlInset: "#1a0f18", + controlInsetHover: "#21141e", + composerPanel: "#2a1825", + composerBorder: "#a3004c", + composerBorderMuted: "#3d2438", + composerSend: "#a3004c", + composerSendHover: "#e33f86", composerStop: "#dc2626", composerStopHover: "#ef4444", - accent: "#7c87ff", + accent: "#e33f86", cursor: "#d4d4d4", - selection: "#1f4f95", - selectionActive: "#2b61b0", - text: "#f5f5f5", - muted: "#a3a3a3", - subtle: "#737373", + selection: "#5c1a3e", + selectionActive: "#7a2450", + text: "#f9f8fb", + muted: "#b89eb5", + subtle: "#8a6b87", success: "#10b981", - info: "#3b82f6", + info: "#c074b2", warning: "#f59e0b", claude: "#d97757", macRed: "#ff5f57", @@ -105,23 +105,23 @@ export type TuiPalette = { [Key in keyof TuiPaletteShape]: TuiColor }; const DEFAULT_THEME_DETAILS = { attachmentPillTones: [ - { backgroundColor: "#1d2026", textColor: "#3b82f6" }, - { backgroundColor: "#241b2f", textColor: "#a78bfa" }, + { backgroundColor: "#2e1528", textColor: "#e33f86" }, + { backgroundColor: "#241b2f", textColor: "#c074b2" }, { backgroundColor: "#2a2417", textColor: "#facc15" }, { backgroundColor: "#2a1b1b", textColor: "#f87171" }, { backgroundColor: "#1c2721", textColor: "#34d399" }, { backgroundColor: "#272019", textColor: "#fb923c" }, ], codeBlock: { - background: "#101010", - language: "#8a8a8a", - copyIcon: "#9a9a9a", + background: "#1a0f18", + language: "#8a6b87", + copyIcon: "#b89eb5", }, status: { - awaitingInput: "#818cf8", - working: "#7dd3fc", + awaitingInput: "#e33f86", + working: "#c074b2", planReady: "#a78bfa", - pulse: "#3b82f6", + pulse: "#e33f86", }, diffViewer: { addedBg: "#173124", @@ -157,41 +157,41 @@ const DEFAULT_DARK_THEME: TuiTheme = { const DEFAULT_LIGHT_PALETTE: TuiPalette = { ...DEFAULT_DARK_PALETTE, - canvas: "#f5f5f5", - sidebar: "#eeeeee", - main: "#f7f7f7", + canvas: "#f2e1f4", + sidebar: "#ead0ef", + main: "#fdf7fd", surface: "#ffffff", - surfaceAlt: "#f1f1f1", + surfaceAlt: "#f5eaf6", input: "#ffffff", - surfaceUser: "#ececec", + surfaceUser: "#f0ddf2", surfacePlan: "#eef6ec", surfaceWarn: "#fff5e6", - surfaceInfo: "#eef4ff", - footer: "#f7f7f7", + surfaceInfo: "#f5eaff", + footer: "#fdf7fd", diff: "#fafafa", popup: "#ffffff", scrim: "#00000022", - border: "#dddddd", - divider: "#d8d8d8", - controlHover: "#ebebeb", - controlActive: "#e2e2e2", - controlActiveStrong: "#cdcdcd", - controlInset: "#e7e7e7", - controlInsetHover: "#dddddd", + border: "#efbdeb", + divider: "#e0b8dc", + controlHover: "#f0ddf2", + controlActive: "#e6cce9", + controlActiveStrong: "#d9b8dd", + controlInset: "#ead0ef", + controlInsetHover: "#e0c2e4", composerPanel: "#ffffff", - composerBorder: "#0891b2", - composerBorderMuted: "#d0d0d0", - composerSend: "#60a5fa", - composerSendHover: "#3b82f6", - accent: "#0891b2", + composerBorder: "#e33f86", + composerBorderMuted: "#e0b8dc", + composerSend: "#e33f86", + composerSendHover: "#ca0277", + accent: "#ca0277", cursor: "#a3a3a3", - selection: "#dbeafe", - selectionActive: "#bfdbfe", - text: "#171717", - muted: "#666666", - subtle: "#8a8a8a", + selection: "#f5d0e8", + selectionActive: "#f0b8dd", + text: "#501854", + muted: "#7a3f7e", + subtle: "#9a6b9e", success: "#059669", - info: "#2563eb", + info: "#8b3fa0", warning: "#d97706", claude: "#c96d4d", }; diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index 7051554c..b57564fe 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -3408,6 +3408,7 @@ export function App({ const responsiveLayout = resolveTuiResponsiveLayout({ viewportColumns: totalColumns, sidebarCollapsedPreference, + isChatMode, }); const showSidebarOverlay = !responsiveLayout.showSidebar && sidebarOverlayOpen; const showFullDiffView = mainView === "thread" && diffOpen; @@ -4091,7 +4092,7 @@ export function App({ const requestAppExit = useCallback(() => { setConfirmDialog({ - title: "Quit T1 Code?", + title: isChatMode ? "Quit T1 Chat?" : "Quit T1 Code?", body: "Press Ctrl-C again or Enter to quit. Press Escape to stay in the session.", confirmLabel: "Quit", escapeBehavior: "cancel", @@ -7760,7 +7761,7 @@ export function App({ style={{ width: responsiveLayout.showSidebar ? responsiveLayout.sidebarWidth : TUI_SIDEBAR_WIDTH, backgroundColor: sidebarBg, - border: ["right"], + border: isChatMode ? [] : ["right"], borderColor: PALETTE.divider, flexDirection: "column", ...(showSidebarOverlay @@ -7795,6 +7796,43 @@ export function App({ + {isChatMode ? ( + + { + if (activeProjectId) { + openDraftThread(activeProjectId); + } + }} + style={{ + backgroundColor: RGBA.fromHex("#a23b67"), + height: 1, + justifyContent: "center", + alignItems: "center", + paddingTop: 0, + paddingBottom: 0, + }} + > + + + + + + + + + + + ) : null} + - + />} - {projects.length === 0 ? ( + {!isChatMode && projects.length === 0 ? ( ) : null} - {sortedProjects.map((project) => { + {isChatMode ? (() => { + const allThreads = sortedProjects.flatMap((project) => { + const projectThreads = threadsByProject.get(project.id) ?? []; + return projectThreads.map((thread) => ({ ...thread, projectId: project.id })); + }); + allThreads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); + + const now = new Date(); + const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const yesterdayStart = new Date(todayStart.getTime() - 86400000); + const weekStart = new Date(todayStart.getTime() - 7 * 86400000); + const monthStart = new Date(todayStart.getTime() - 30 * 86400000); + + type TimeGroup = { label: string; threads: typeof allThreads }; + const groups: TimeGroup[] = [ + { label: "Today", threads: [] }, + { label: "Yesterday", threads: [] }, + { label: "Last 7 Days", threads: [] }, + { label: "Last 30 Days", threads: [] }, + { label: "Older", threads: [] }, + ]; + + for (const thread of allThreads) { + const date = new Date(thread.updatedAt); + if (date >= todayStart) groups[0]!.threads.push(thread); + else if (date >= yesterdayStart) groups[1]!.threads.push(thread); + else if (date >= weekStart) groups[2]!.threads.push(thread); + else if (date >= monthStart) groups[3]!.threads.push(thread); + else groups[4]!.threads.push(thread); + } + + return groups.filter((g) => g.threads.length > 0).map((group) => ( + + + {group.threads.map((thread) => { + const isActive = thread.id === activeThreadId; + const isSelected = selectedThreadIds.has(thread.id); + const status = threadStatus(thread, { + forceUnread: locallyUnreadThreadIds.has(thread.id), + locallyVisitedAt: locallyVisitedThreads[thread.id], + }); + return ( + { + closeSidebarContextMenu(); + handleThreadClick( + event, + thread.projectId, + thread.id, + allThreads.map((t) => t.id), + ); + }} + onSecondaryPress={(event) => { + openThreadContextMenu(thread.projectId, thread.id, event); + }} + > + + {status ? ( + + ) : null} + + + + + + ); + })} + + )); + })() : null} + + {!isChatMode ? sortedProjects.map((project) => { const projectThreads = threadsByProject.get(project.id) ?? []; const orderedProjectThreadIds = projectThreads.map((thread) => thread.id); const isProjectExpanded = expandedProjectIds.has(project.id); @@ -8034,7 +8181,7 @@ export function App({ ) : null} ); - })} + }) : null} + {isChatMode && responsiveLayout.showSidebar ? ( + + + + + + ) : null} - + ) : ( - + )} diff --git a/bun.lock b/bun.lock index 8ba9b2ce..94ed3dab 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ }, "apps/desktop": { "name": "@t3tools/desktop", - "version": "0.0.16", + "version": "0.0.21", "dependencies": { "effect": "catalog:", "electron": "40.6.0", @@ -43,7 +43,7 @@ }, "apps/server": { "name": "t3", - "version": "0.0.16", + "version": "0.0.21", "bin": { "t3": "./dist/index.mjs", }, @@ -73,7 +73,7 @@ }, "apps/tui": { "name": "@maria_rcks/t1code", - "version": "0.0.19", + "version": "0.0.21", "bin": { "t1code": "./bin/t1code.js", }, @@ -107,7 +107,7 @@ }, "apps/web": { "name": "@t3tools/web", - "version": "0.0.16", + "version": "0.0.21", "dependencies": { "@base-ui/react": "^1.2.0", "@dnd-kit/core": "^6.3.1", @@ -173,7 +173,7 @@ }, "packages/contracts": { "name": "@t3tools/contracts", - "version": "0.0.16", + "version": "0.0.21", "dependencies": { "effect": "catalog:", }, diff --git a/scripts/dev-tui.ts b/scripts/dev-tui.ts index c7bbedbf..1722252c 100644 --- a/scripts/dev-tui.ts +++ b/scripts/dev-tui.ts @@ -231,6 +231,7 @@ const tui = spawn("bun", ["--silent", "--watch", "run", TUI_ENTRY], { T1CODE_PORT: String(port), T1CODE_AUTH_TOKEN: authToken, T1CODE_TUI_ATTACH_ONLY: "1", + ...(process.env.T1CODE_CHAT_MODE ? { T1CODE_CHAT_MODE: process.env.T1CODE_CHAT_MODE } : {}), }, stdio: "inherit", }); From 355bd1b2e191ed94982d111759d3d18b1c1efd24 Mon Sep 17 00:00:00 2001 From: Ahmad Jalil Date: Tue, 7 Apr 2026 07:46:09 -0700 Subject: [PATCH 04/10] feat(tui): implement sidebar search functionality for threads --- apps/tui/src/ui.tsx | 138 +++++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 35 deletions(-) diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index b57564fe..d36c68ac 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -2838,6 +2838,7 @@ export function App({ const isChatMode = process.env.T1CODE_CHAT_MODE === "1"; const [tempChatMode, setTempChatMode] = useState(false); + const [sidebarSearchQuery, setSidebarSearchQuery] = useState(""); const updateAppSettings = useCallback((patch: Partial) => { setAppSettings((current) => normalizeAppSettings({ ...current, ...patch })); }, []); @@ -7816,19 +7817,39 @@ export function App({ setFocusArea("projects")} style={{ height: 1, flexDirection: "row", alignItems: "center", marginTop: 1, paddingBottom: 0, + position: "relative", }} > - - + + {sidebarSearchQuery ? null : ( + + )} + setSidebarSearchQuery(value)} + style={{ + flexGrow: 1, + backgroundColor: sidebarBg, + textColor: PALETTE.muted, + focusedTextColor: PALETTE.muted, + focusedBackgroundColor: sidebarBg, + }} + /> - - + + ) : null} @@ -7883,6 +7904,10 @@ export function App({ return projectThreads.map((thread) => ({ ...thread, projectId: project.id })); }); allThreads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); + const searchLower = sidebarSearchQuery.toLowerCase().trim(); + const filteredThreads = searchLower + ? allThreads.filter((t) => t.title.toLowerCase().includes(searchLower)) + : allThreads; const now = new Date(); const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); @@ -7899,7 +7924,7 @@ export function App({ { label: "Older", threads: [] }, ]; - for (const thread of allThreads) { + for (const thread of filteredThreads) { const date = new Date(thread.updatedAt); if (date >= todayStart) groups[0]!.threads.push(thread); else if (date >= yesterdayStart) groups[1]!.threads.push(thread); @@ -8248,29 +8273,54 @@ export function App({ }} > {isChatMode && responsiveLayout.showSidebar ? ( - - - - - + <> + {/* Row 1: top bar, full sidebar bg connects to sidebar */} + + {/* Row 2: icons row, main bg on left, sidebar bg with icons on right */} + + + + setTempChatMode((prev) => !prev)} + /> + { + openMainView("settings"); + }} + /> + + + ) : null} + {isChatMode && responsiveLayout.showSidebar ? null : ( + {isChatMode && responsiveLayout.showSidebar ? null : ( ) : null} + )} + {isChatMode && responsiveLayout.showSidebar ? null : ( restoreDefaultSettings()} /> - ) : mainView === "keybindings" ? null : isChatMode ? null : ( + ) : mainView === "keybindings" ? null : isChatMode ? ( + <> + setTempChatMode((prev) => !prev)} + /> + { + openMainView("settings"); + }} + /> + + ) : ( <> )} + )} + )} ) : ( - + )} - {isChatMode ? ( - - setTempChatMode((prev) => !prev)} - /> - - ) : null} + {null} {activePendingApproval ? ( <> @@ -10045,8 +10113,8 @@ export function App({ /> ) : null} - {responsiveLayout.showComposerDividers ? : null} - : null} + {!isChatMode ? - {responsiveLayout.showComposerDividers ? : null} - : null} + {!isChatMode && responsiveLayout.showComposerDividers ? : null} + {!isChatMode ? + /> : null} {activePendingProgress ? ( <> From ca1721fc279ab7f1b9f4c5677b6bdb6458f44ea2 Mon Sep 17 00:00:00 2001 From: Ahmad Jalil Date: Tue, 7 Apr 2026 07:51:52 -0700 Subject: [PATCH 05/10] feat(tui): add bottom padding and sidebar background to match icon area --- apps/tui/src/ui.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index d36c68ac..6210eb06 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -7848,8 +7848,8 @@ export function App({ }} /> - - + + ) : null} @@ -7861,6 +7861,7 @@ export function App({ ...themedScrollboxStyle(sidebarBg), paddingLeft: 1, paddingRight: 1, + paddingTop: isChatMode ? 1 : 0, }} > {isChatMode ? null : + {/* Row 3: bottom padding, sidebar bg only on right to match icon area */} + + + + ) : null} {isChatMode && responsiveLayout.showSidebar ? null : ( From 69f44599c51389346a982803aa83132ab506a53c Mon Sep 17 00:00:00 2001 From: Ahmad Jalil Date: Tue, 7 Apr 2026 07:55:45 -0700 Subject: [PATCH 06/10] feat(tui): update composer placeholder and adjust layout for chat mode --- apps/tui/src/ui.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index 6210eb06..cd52e0d5 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -7147,7 +7147,7 @@ export function App({ ? activeDraftThread ? "Start a new thread with a prompt" : "Ask for follow-up changes or attach images" - : COMPOSER_PLACEHOLDER; + : isChatMode ? "Type your message here..." : COMPOSER_PLACEHOLDER; const composerPathTrigger = detectTrailingComposerPathTrigger(composer); const showPathSuggestions = composerIsFocused && @@ -7785,8 +7785,8 @@ export function App({ paddingRight: 2, }} > - - {responsiveLayout.showWindowDots ? : null} + + {responsiveLayout.showWindowDots && !isChatMode ? : null} Date: Tue, 7 Apr 2026 08:02:51 -0700 Subject: [PATCH 07/10] feat(tui): update chat UI elements for improved layout and styling --- apps/tui/src/ui.tsx | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index cd52e0d5..6589fbbb 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -1341,7 +1341,7 @@ function ChatCategoryButton(props: { borderColor: hoveredCategory ? RGBA.fromHex("#a23b67") : PALETTE.border, }} > - + @@ -9220,9 +9220,11 @@ export function App({ style={{ flexGrow: 1, flexDirection: "column", - justifyContent: "center", - alignItems: "center", - padding: 2, + justifyContent: "flex-start", + alignItems: "flex-start", + paddingLeft: 2, + paddingRight: 2, + paddingTop: 4, }} > @@ -9232,16 +9234,16 @@ export function App({ ) : ( - + )} - + {[ - { icon: "✨", label: "Create" }, - { icon: "📰", label: "Explore" }, - { icon: "💻", label: "Code" }, - { icon: "🎓", label: "Learn" }, + { icon: "󰛕", label: "Create" }, + { icon: "󰎕", label: "Explore" }, + { icon: "󰅪", label: "Code" }, + { icon: "󰑴", label: "Learn" }, ].map((item) => ( - + {[ "How does AI work?", "Are black holes real?", 'How many Rs are in the word "strawberry"?', "What is the meaning of life?", - ].map((q) => ( + ].map((q, i) => ( { @@ -9273,10 +9275,12 @@ export function App({ setTimeout(() => composerRef.current?.focus(), 0); }} style={{ - border: ["top"], - borderColor: PALETTE.border, - paddingTop: 1, - paddingBottom: 1, + border: i > 0 ? ["top"] : [], + borderColor: PALETTE.divider, + paddingTop: 0, + paddingBottom: 0, + height: 2, + alignItems: "center", width: "100%", }} > @@ -9716,7 +9720,7 @@ export function App({ position: "relative", zIndex: 20, backgroundColor: PALETTE.composerPanel, - border: true, + border: isChatMode ? ["top", "left", "right"] : true, borderStyle: "rounded", borderColor: activePendingProgress ? focusArea === "composer" @@ -9726,7 +9730,7 @@ export function App({ ? PALETTE.composerBorder : PALETTE.composerBorderMuted, paddingTop: activePendingProgress ? 0 : 1, - paddingBottom: isChatMode ? 0 : 1, + paddingBottom: 1, paddingLeft: 1, paddingRight: 1, flexDirection: "column", @@ -9750,7 +9754,7 @@ export function App({ Date: Tue, 7 Apr 2026 08:26:19 -0700 Subject: [PATCH 08/10] feat(tui): add profile management for chat mode with creation and reordering functionality --- README.md | 43 +++++++++- apps/tui/src/profiles.ts | 72 ++++++++++++++++ apps/tui/src/ui.tsx | 179 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 287 insertions(+), 7 deletions(-) create mode 100644 apps/tui/src/profiles.ts diff --git a/README.md b/README.md index 1bb6c21c..29c48a02 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![License](https://img.shields.io/badge/license-MIT-111111?style=flat-square)](./LICENSE) [![npm](https://img.shields.io/npm/v/%40maria__rcks%2Ft1code?color=111111&label=npm&style=flat-square)](https://www.npmjs.com/package/@maria_rcks/t1code) -[![GitHub](https://img.shields.io/badge/github-maria--rcks%2Ft1code-111111?style=flat-square&logo=github)](https://github.com/maria-rcks/t1code) +[![GitHub](https://img.shields.io/badge/github-ahzs645%2Ft1chat-111111?style=flat-square&logo=github)](https://github.com/ahzs645/t1chat) t1code terminal UI screenshot @@ -12,6 +12,8 @@ _T3Code, but in your terminal._ +## t1code (code mode) + Run instantly: ```bash @@ -27,10 +29,47 @@ bun add -g @maria_rcks/t1code Develop from source: ```bash -git clone https://github.com/maria-rcks/t1code.git +git clone https://github.com/ahzs645/t1chat.git cd t1code bun install bun dev:tui ``` +## t1chat (chat mode) + +t1chat is a chat-focused mode that transforms the TUI into a conversational interface inspired by [T3 Chat](https://t3.chat). It features a pink/magenta/lavender theme, a flat thread list grouped by time, and a streamlined UI without code-specific tools. + +### What changes in chat mode + +- Sidebar shows a flat thread list grouped by time (Today, Yesterday, Last 7 Days, etc.) instead of nested projects +- "New Chat" button and thread search in the sidebar +- Title shows "T1 Chat" instead of "T1 Code" +- Git tools, diff viewer, Chat/Plan toggle, and Full access button are hidden +- Settings and temp chat toggle in the top-right corner +- Composer placeholder says "Type your message here..." +- Pink/magenta/lavender color scheme matching T3 Chat + +### Run chat mode + +If installed globally: + +```bash +t1chat +``` + +Run instantly: + +```bash +bunx @maria_rcks/t1code t1chat +``` + +Develop from source: + +```bash +git clone https://github.com/ahzs645/t1chat.git +cd t1code +bun install +T1CODE_CHAT_MODE=1 bun dev:tui +``` + Based on T3 Code by [@t3dotgg](https://github.com/t3dotgg) and [@juliusmarminge](https://github.com/juliusmarminge). diff --git a/apps/tui/src/profiles.ts b/apps/tui/src/profiles.ts new file mode 100644 index 00000000..1fbe56d8 --- /dev/null +++ b/apps/tui/src/profiles.ts @@ -0,0 +1,72 @@ +/** + * Profile management for t1chat mode. + * + * Profiles let users organize conversations under different personas. + * Each profile has a name, icon, and unique ID. Threads can be + * associated with a profile so switching profiles filters the sidebar. + */ + +export interface Profile { + id: string; + name: string; + icon: string; +} + +/** Nerd Font icons available for profile selection. */ +export const PROFILE_ICON_OPTIONS: { icon: string; label: string }[] = [ + { icon: "󰭹", label: "Chat" }, + { icon: "󰫢", label: "Star" }, + { icon: "󰃀", label: "Bookmark" }, + { icon: "󰋑", label: "Heart" }, + { icon: "󰈻", label: "Flag" }, + { icon: "󱐋", label: "Lightning" }, + { icon: "󰐊", label: "Play" }, + { icon: "󰛕", label: "Sparkles" }, + { icon: "󰂞", label: "Bell" }, + { icon: "󰛨", label: "Bulb" }, + { icon: "󰋜", label: "Home" }, + { icon: "󰉋", label: "Folder" }, + { icon: "󰃭", label: "Calendar" }, + { icon: "󰇮", label: "Mail" }, + { icon: "󰈙", label: "File" }, + { icon: "󰂺", label: "Book" }, + { icon: "󰊗", label: "Briefcase" }, + { icon: "󰆼", label: "Database" }, + { icon: "󰳗", label: "Cube" }, + { icon: "󰕮", label: "Music" }, + { icon: "󰄀", label: "Camera" }, + { icon: "󰈈", label: "Eye" }, + { icon: "󰟃", label: "Globe" }, + { icon: "󰑴", label: "Graduate" }, +]; + +export const DEFAULT_PROFILE: Profile = { + id: "default", + name: "Default", + icon: "󰭹", +}; + +export function createProfile(name: string, icon: string): Profile { + const slug = name + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-|-$/g, ""); + return { + id: `${slug}-${Date.now()}`, + name, + icon, + }; +} + +export function reorderProfiles( + profiles: Profile[], + fromIndex: number, + toIndex: number, +): Profile[] { + const result = [...profiles]; + const [moved] = result.splice(fromIndex, 1); + if (moved) { + result.splice(toIndex, 0, moved); + } + return result; +} diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index 6589fbbb..f14f2bef 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -194,6 +194,7 @@ import { shouldClearPendingCreatedThread, } from "./threadSelection"; import { resolveWorkEntryIcon } from "./workEntryIcons"; +import { createProfile, PROFILE_ICON_OPTIONS } from "./profiles"; type FocusArea = | "projects" @@ -2839,6 +2840,16 @@ export function App({ const isChatMode = process.env.T1CODE_CHAT_MODE === "1"; const [tempChatMode, setTempChatMode] = useState(false); const [sidebarSearchQuery, setSidebarSearchQuery] = useState(""); + const [chatProfiles, setChatProfiles] = useState([ + { id: "default", name: "Default", icon: "󰭹" }, + ]); + const [activeProfileId, setActiveProfileId] = useState("default"); + const [threadProfileMap, setThreadProfileMap] = useState>({}); + const [showProfileCreate, setShowProfileCreate] = useState(false); + const [newProfileName, setNewProfileName] = useState(""); + const [newProfileIconIndex, setNewProfileIconIndex] = useState(0); + const [profileNameFocused, setProfileNameFocused] = useState(false); + const [profileIconFocused, setProfileIconFocused] = useState(false); const updateAppSettings = useCallback((patch: Partial) => { setAppSettings((current) => normalizeAppSettings({ ...current, ...patch })); }, []); @@ -5784,6 +5795,9 @@ export function App({ setExpandedProjectIds((current) => ensureProjectExpanded(current, projectId)); setFocusArea("composer"); setStatus("New thread"); + if (isChatMode) { + setThreadProfileMap((current) => ({ ...current, [existingDraft.id]: activeProfileId })); + } setTimeout(() => { composerRef.current?.focus(); }, 0); @@ -7905,10 +7919,15 @@ export function App({ return projectThreads.map((thread) => ({ ...thread, projectId: project.id })); }); allThreads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); + const profileThreads = allThreads.filter((t) => { + const threadProfile = threadProfileMap[t.id]; + if (!threadProfile) return activeProfileId === "default"; + return threadProfile === activeProfileId; + }); const searchLower = sidebarSearchQuery.toLowerCase().trim(); const filteredThreads = searchLower - ? allThreads.filter((t) => t.title.toLowerCase().includes(searchLower)) - : allThreads; + ? profileThreads.filter((t) => t.title.toLowerCase().includes(searchLower)) + : profileThreads; const now = new Date(); const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); @@ -8210,6 +8229,154 @@ export function App({ }) : null} + {isChatMode ? ( + + {showProfileCreate ? ( + + + + + { + setNewProfileIconIndex((i) => (i > 0 ? i - 1 : PROFILE_ICON_OPTIONS.length - 1)); + setProfileIconFocused(true); + setProfileNameFocused(false); + }} + style={{ + border: true, + borderStyle: "rounded", + borderColor: profileIconFocused ? RGBA.fromHex("#db2777") : PALETTE.border, + paddingLeft: 1, + paddingRight: 1, + marginRight: 1, + backgroundColor: RGBA.fromHex("#eaa7cb"), + justifyContent: "center", + alignItems: "center", + }} + > + + + { setProfileNameFocused(true); setProfileIconFocused(false); }} + style={{ flexGrow: 1, border: true, borderStyle: "rounded", borderColor: profileNameFocused ? RGBA.fromHex("#db2777") : PALETTE.border, backgroundColor: RGBA.fromHex("#eaa7cb"), justifyContent: "center", paddingLeft: 1 }} + > + { + setNewProfileName(value.slice(0, 50)); + setProfileNameFocused(true); + }} + style={{ + flexGrow: 1, + backgroundColor: RGBA.fromHex("#eaa7cb"), + textColor: PALETTE.text, + focusedTextColor: PALETTE.text, + focusedBackgroundColor: RGBA.fromHex("#eaa7cb"), + }} + /> + + + { + if (newProfileName.trim()) { + const icon = PROFILE_ICON_OPTIONS[newProfileIconIndex]?.icon ?? "󰭹"; + const profile = createProfile(newProfileName.trim(), icon); + setChatProfiles((prev) => [...prev, profile]); + setActiveProfileId(profile.id); + setNewProfileName(""); + setNewProfileIconIndex(0); + setProfileNameFocused(false); + setProfileIconFocused(false); + setShowProfileCreate(false); + } + }} + style={{ + backgroundColor: RGBA.fromHex(newProfileName.trim() ? "#e33f86" : "#f19dc5"), + height: 1, + justifyContent: "center", + alignItems: "center", + }} + > + + + + ) : null} + + + {chatProfiles.map((profile) => ( + setActiveProfileId(profile.id)} + style={{ + marginRight: 1, + width: 3, + height: 3, + justifyContent: "center", + alignItems: "center", + backgroundColor: + profile.id === activeProfileId ? PALETTE.controlActive : "transparent", + }} + > + + + ))} + + { + setShowProfileCreate((v) => !v); + if (showProfileCreate) { + setNewProfileName(""); + setNewProfileIconIndex(0); + setProfileNameFocused(false); + } + }} + style={{ + width: 3, + height: 3, + justifyContent: "center", + alignItems: "center", + }} + > + + + + + ) : ( + )} ) : null} @@ -9238,7 +9406,7 @@ export function App({ )} - + {[ { icon: "󰛕", label: "Create" }, { icon: "󰎕", label: "Explore" }, @@ -9279,8 +9447,9 @@ export function App({ borderColor: PALETTE.divider, paddingTop: 0, paddingBottom: 0, - height: 2, - alignItems: "center", + height: i === 0 ? 1 : 2, + alignItems: "flex-start", + justifyContent: "center", width: "100%", }} > From 2bbb929eaf9fdb2f6a216eda4e091c9a9ce4ea3f Mon Sep 17 00:00:00 2001 From: Ahmad Jalil Date: Tue, 7 Apr 2026 08:36:30 -0700 Subject: [PATCH 09/10] feat(tui): add boring theme support and chat settings overlay --- apps/tui/src/theme.ts | 84 +++++++++++++++++++++++++++++- apps/tui/src/ui.tsx | 117 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 190 insertions(+), 11 deletions(-) diff --git a/apps/tui/src/theme.ts b/apps/tui/src/theme.ts index 781c4d4a..43971d07 100644 --- a/apps/tui/src/theme.ts +++ b/apps/tui/src/theme.ts @@ -16,12 +16,14 @@ export interface TerminalColors { export type TuiColor = string; export const TERMINAL_MATCH_THEME_ID = "terminal-match" as const; -export const TUI_THEME_IDS = ["default", TERMINAL_MATCH_THEME_ID] as const; +export const BORING_THEME_ID = "boring" as const; +export const TUI_THEME_IDS = ["default", TERMINAL_MATCH_THEME_ID, BORING_THEME_ID] as const; export type TuiThemeId = (typeof TUI_THEME_IDS)[number]; export const DEFAULT_TUI_THEME_ID = "default" as const; export const TUI_THEME_LABELS: Record = { default: "Default", [TERMINAL_MATCH_THEME_ID]: "Terminal Match", + [BORING_THEME_ID]: "Boring", }; export type TuiThemeMode = "light" | "dark"; @@ -207,6 +209,84 @@ const DEFAULT_LIGHT_THEME: TuiTheme = { }, }; +const BORING_DARK_PALETTE: TuiPalette = { + ...DEFAULT_DARK_PALETTE, + canvas: "#151515", + sidebar: "#1a1a1a", + main: "#151515", + surface: "#1e1e1e", + surfaceAlt: "#222222", + input: "#1a1a1a", + surfaceUser: "#1e1e1e", + footer: "#151515", + popup: "#1e1e1e", + border: "#282828", + divider: "#282828", + controlHover: "#252525", + controlActive: "#2a2a2a", + controlActiveStrong: "#222222", + controlInset: "#1a1a1a", + controlInsetHover: "#202020", + composerPanel: "#1e1e1e", + composerBorder: "#763750", + composerBorderMuted: "#333333", + composerSend: "#763750", + composerSendHover: "#ad5273", + accent: "#ad5273", + selection: "#3a2030", + selectionActive: "#4a2840", + text: "#e6e6e6", + muted: "#b0b0b0", + subtle: "#707070", + info: "#888888", +}; + +const BORING_LIGHT_PALETTE: TuiPalette = { + ...DEFAULT_LIGHT_PALETTE, + canvas: "#ebebeb", + sidebar: "#e0e0e0", + main: "#f0f0f0", + surface: "#ffffff", + surfaceAlt: "#e8e8e8", + input: "#ffffff", + surfaceUser: "#e0e0e0", + footer: "#f0f0f0", + popup: "#ffffff", + border: "#d4d4d4", + divider: "#d0d0d0", + controlHover: "#e0e0e0", + controlActive: "#d4d4d4", + controlActiveStrong: "#c8c8c8", + controlInset: "#e0e0e0", + controlInsetHover: "#d8d8d8", + composerPanel: "#ffffff", + composerBorder: "#ad5273", + composerBorderMuted: "#c9c9c9", + composerSend: "#ad5273", + composerSendHover: "#8a3f5c", + accent: "#ad5273", + selection: "#e8d0dd", + selectionActive: "#ddbece", + text: "#171717", + muted: "#616161", + subtle: "#8a8a8a", + info: "#666666", +}; + +const BORING_DARK_THEME: TuiTheme = { + ...DEFAULT_DARK_THEME, + id: BORING_THEME_ID, + mode: "dark", + palette: BORING_DARK_PALETTE, +}; + +const BORING_LIGHT_THEME: TuiTheme = { + ...DEFAULT_LIGHT_THEME, + id: BORING_THEME_ID, + mode: "light", + palette: BORING_LIGHT_PALETTE, +}; + export const DEFAULT_TUI_THEME = DEFAULT_DARK_THEME; function defaultThemeForMode(mode: TuiThemeMode): TuiTheme { @@ -714,6 +794,8 @@ export function resolveTuiThemeMode( const THEME_CACHE = new Map([ [`${DEFAULT_TUI_THEME_ID}:dark`, DEFAULT_DARK_THEME], [`${DEFAULT_TUI_THEME_ID}:light`, DEFAULT_LIGHT_THEME], + [`${BORING_THEME_ID}:dark`, BORING_DARK_THEME], + [`${BORING_THEME_ID}:light`, BORING_LIGHT_THEME], ]); export function resolveTuiTheme( diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index f14f2bef..5173ab2c 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -195,6 +195,7 @@ import { } from "./threadSelection"; import { resolveWorkEntryIcon } from "./workEntryIcons"; import { createProfile, PROFILE_ICON_OPTIONS } from "./profiles"; +import { BORING_THEME_ID } from "./theme"; type FocusArea = | "projects" @@ -214,7 +215,8 @@ type OverlayMenu = | "sidebar-sort" | "git-actions" | "composer-env" - | "composer-branch"; + | "composer-branch" + | "chat-settings"; type SettingsSelectKind = | "theme" | "theme-preset" @@ -8247,7 +8249,7 @@ export function App({ bottom: 4, left: 0, right: 0, - backgroundColor: RGBA.fromHex("#eaa7cb"), + backgroundColor: PALETTE.surfaceAlt, border: ["top"], borderColor: PALETTE.divider, flexDirection: "column", @@ -8267,11 +8269,11 @@ export function App({ style={{ border: true, borderStyle: "rounded", - borderColor: profileIconFocused ? RGBA.fromHex("#db2777") : PALETTE.border, + borderColor: profileIconFocused ? PALETTE.composerBorder : PALETTE.border, paddingLeft: 1, paddingRight: 1, marginRight: 1, - backgroundColor: RGBA.fromHex("#eaa7cb"), + backgroundColor: PALETTE.surfaceAlt, justifyContent: "center", alignItems: "center", }} @@ -8283,7 +8285,7 @@ export function App({ { setProfileNameFocused(true); setProfileIconFocused(false); }} - style={{ flexGrow: 1, border: true, borderStyle: "rounded", borderColor: profileNameFocused ? RGBA.fromHex("#db2777") : PALETTE.border, backgroundColor: RGBA.fromHex("#eaa7cb"), justifyContent: "center", paddingLeft: 1 }} + style={{ flexGrow: 1, border: true, borderStyle: "rounded", borderColor: profileNameFocused ? PALETTE.composerBorder : PALETTE.border, backgroundColor: PALETTE.surfaceAlt, justifyContent: "center", paddingLeft: 1 }} > @@ -8318,7 +8320,7 @@ export function App({ } }} style={{ - backgroundColor: RGBA.fromHex(newProfileName.trim() ? "#e33f86" : "#f19dc5"), + backgroundColor: newProfileName.trim() ? PALETTE.composerSend : PALETTE.controlActive, height: 1, justifyContent: "center", alignItems: "center", @@ -8465,9 +8467,10 @@ export function App({ chrome="bare" width={4} justifyContent="flex-end" - iconColor={PALETTE.muted} + iconColor={overlayMenu === "chat-settings" ? PALETTE.text : PALETTE.muted} + active={overlayMenu === "chat-settings"} onPress={() => { - openMainView("settings"); + setOverlayMenu((current) => current === "chat-settings" ? null : "chat-settings"); }} /> @@ -11027,6 +11030,100 @@ export function App({ ) : null} + {overlayMenu === "chat-settings" ? ( + { + event.preventDefault(); + event.stopPropagation?.(); + }} + > + + + { + updateAppSettings({ theme: "light" }); + setOverlayMenu(null); + }} + style={{ + paddingLeft: 1, + paddingRight: 1, + backgroundColor: appSettings.theme === "light" ? PALETTE.controlActive : "transparent", + }} + > + + + { + updateAppSettings({ theme: "system" }); + setOverlayMenu(null); + }} + style={{ + paddingLeft: 1, + paddingRight: 1, + backgroundColor: appSettings.theme === "system" || !appSettings.theme ? PALETTE.controlActive : "transparent", + }} + > + + + { + updateAppSettings({ theme: "dark" }); + setOverlayMenu(null); + }} + style={{ + paddingLeft: 1, + paddingRight: 1, + backgroundColor: appSettings.theme === "dark" ? PALETTE.controlActive : "transparent", + }} + > + + + + { + setTuiThemeId((current) => current === BORING_THEME_ID ? "default" : BORING_THEME_ID); + }} + style={{ height: 1, flexDirection: "row", alignItems: "center" }} + > + + + + + + + + { + openMainView("settings"); + setOverlayMenu(null); + }} + style={{ height: 1, flexDirection: "row", alignItems: "center" }} + > + + + + + ) : null} + {confirmDialog ? ( Date: Tue, 7 Apr 2026 08:40:51 -0700 Subject: [PATCH 10/10] Refactor code structure for improved readability and maintainability --- README.md | 4 ++++ apps/tui/src/ui.tsx | 13 ++++++++++++- assets/repo/t1chat-preview.png | Bin 0 -> 49956 bytes 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 assets/repo/t1chat-preview.png diff --git a/README.md b/README.md index 29c48a02..d3559803 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,10 @@ bun dev:tui ## t1chat (chat mode) +
+t1chat terminal UI screenshot +
+ t1chat is a chat-focused mode that transforms the TUI into a conversational interface inspired by [T3 Chat](https://t3.chat). It features a pink/magenta/lavender theme, a flat thread list grouped by time, and a streamlined UI without code-specific tools. ### What changes in chat mode diff --git a/apps/tui/src/ui.tsx b/apps/tui/src/ui.tsx index 5173ab2c..619334f7 100644 --- a/apps/tui/src/ui.tsx +++ b/apps/tui/src/ui.tsx @@ -2839,7 +2839,7 @@ export function App({ const previewAttachmentCacheRef = useRef>(new Map()); const composerDraftsByThreadIdRef = useRef>>({}); - const isChatMode = process.env.T1CODE_CHAT_MODE === "1"; + const [isChatMode, setIsChatMode] = useState(process.env.T1CODE_CHAT_MODE === "1"); const [tempChatMode, setTempChatMode] = useState(false); const [sidebarSearchQuery, setSidebarSearchQuery] = useState(""); const [chatProfiles, setChatProfiles] = useState([ @@ -8636,6 +8636,17 @@ export function App({ {mainView === "settings" ? ( <> + setIsChatMode((v) => !v)} + /> + } + /> FJ?f_6`OH&I<+xRuLHy zD&hX=lMMrdT5cgKswgEYN~Y*&XJ%n-3Ip@@b6f(FV$2L~&*8bxb`UPIxb&7>voOr7 ztPpTYL^M1yb+@o$6+@VerjnSLu#U50`O;fd2ch<6CW{)m~dPcxkw2`JRw zzJ(FC>AqhOJcW5by2zC>2svE5SC82|W^aTMCa0r4Vr$%{%MhF5QK$L=55pU}(>%Z~ z9^;K6Yi8Fzw-68-k}Nhi36eQC%NF`E10)~imR-IG8hnimzwD2W zUw95b*-oOHMTQt?@=M)BUa#A@=zX3>bZly$LDtJ2^U5HdlcuLuGFJRBG}mrdo5u%W z75z8P-%8^fUhW&Q(Hog_W&mJ_l|<(@j!X0PBkDIqVSZdWQLmI*ptE0oK%gQ+!~Q@& z0^i(@oS0QN^MrN@vDKdx84;5Vml07X`swHgd`6^_4#a3Q)ONK>xY&Iz~{GzRw7!%Xt&EIB3yT3 z9#Qb1=pmYS@*d$fJ}nQx=!`f*xZv}~JNT&oEeBb+7y0E+iNWVID<9w~9o|w&uC^E-};3sFBhJ6XjymqH@$D*u4hIRLT6c2{m?7sMa#Fkf*xZaE4$-aKwN`goxL&n6OPAa_Lb9WyX6BJkZe z@^8G~n7%oE!&izdRB)z3#Pawa_nl(pi4oc{qC+%~#8+B^uc=a)x!9wK?+A+0oj;#` zI*pX2*^PeLn`QFE1JD%i9qS!5uW%=;l$od~K!^6~ML(`IdS0Ak#;-`}JVM2VLWgnL z9b8K$2n7TM67y89TPK*QGe(nEnS6}SNoJ#tQAtfdp1MVYOS@j>EN~BfM%_`27mgQ$ zH*#L93AJg=5$Krni^GHA(%{nh^3A2_FZJ~h>?2e}R6SIIAmX4~R6HES_sDVy8zQTt zTtA9B&-N~VJhT}^~mte^zEjS-( zqqCx`iM{Oi=!f(-#0nJ;Dmf@Gm#md=6wa$&X7?*X-pQ#h|_X`ttrW#bmt>)1ri zQrD?FVQ=ybE&u4taPs~E=c2J729WiMyYhv|LzK|v&`Ht1bTdbU5ms<)C)*~!N~TWU zO2jWwiN_j z;imDXYUY!6^4u7&7vFzbgun#+v_<_?s&aEx#Kh&q#%vrb1&dhz@-`Nk|hwQEHGwgdMg$_P9 zBR>g+o9tLHOzR{F8IE*~Op5GzdGE;PB<;et#=)^@FX(so;S%l#-w)zVpC3NVPZQh| zGIXzW>U4R_Rm&9?oEO|}Cv1l`yGyYr4JWPKWZbH*U!6yhM3PtuXaO)4NQb?LnOP7j z-c~qP(1Q8EQeYP_rC_~aiMN(d73dB$=j}B^XwBRb4_GljZ0N37$XFpg&_B56Xr~e; z9(jR`bcJAx;f%^r>ik&<$ZI8ahfDvJb8qPCZ{KWhyR+>PGP* z1o`|^_NS095?{Xd*3-=t1}W7kk>&t$!g8*S7>y>s>qM!B;e6-nSzgUvDPEPpb7z{a zGO*9u|27{42z!cGrs3>THM>&VWuDN>yv)dp85I!~DTFQb17$}+UB@TsB2jf8g zKqj52B9ME~Z3FKlgO-mLm-ZPoFRhINp}eacvz#yW(eqd|aPCF^p4??3VX9UP-xu7p z$!PhvQ3^s#D$ny#=MpTLb*pN%P3Ze^AWTj=^~N)wruB8wo|rQBzyF~khLk7&`O6xT zlwZ7zn)qbFy3&9`X|z7M&d;V%6H{>i<_vhIt@K;zO1Y(W8PKzH=Z>?K^Lp})71m^Z z*_duZjjcU!`069kTcUT|H}yXqjgPy}Q?j}1*50p`4ij8-Y=$)JBJ7@=Kd(}7D@@p{a zr|Jq+0_?qvQF5r-)@~!I`OD*fFf6ph)GAlkR4`_idL)3$%UZezVg?S?8r3;eiq$t% zedmYfe5|78(yxGoUT9~&mzqZ>Kp(+t!jh-O(&wvZ=KFrGWIMt=EQ9)%B@@MH#ch+w zR#(r0Us_kxRg?j8eG3bpmB);ulj^x&Y+w#k4nfQy*MwW>2$CI2eLwMfR-z!p=R#oD z0FUgu+`GPhONz^OsUPPcL2__v=jph~~Ca*=C*Sq^Nl?FvJd|ab$Fqx3xZ9GB31#U+QghdbOr+>*o^rJ^U#M zvn|P8`$8U~J9O=FI42=0k>UOJ{`DE_eaue5$Bd*5evp`-hhV)ov5?-`#D&!W_q|;d z%_uF67%{84CARPg0xb>fQ9UAzB_m923QRPv?C_K_qTD@gBE?aLyC-4>o3kTtc=L#w zf8-08@k}rb#IX=VBnX)sW^VfJ)F7h(XM=(b9?Ww;Yc?yt-2uoFDHmYOPyJj75N&pw za*wA~jH($wltKsvuxh3nQf6{;F!azeGR%_@3mAB4=?V1b;VBgBehr2}g8qI1{SnKA z{m)Z4uS~fAlwlPgUKCLlm6C$~RyKAtHMMnmZ|7XMa@PZGYSKbQ!&yU4me1JEhS|`> z&d8M6-Nyc*3XFg|AGBy=>TF2nZewlh#ODs6`0EWmX!$|RLP7S|E6!E`3Jp0$GEqB6 zQ!*~**UYae1X0Pz$OIfs%=q4kN&HhC`WJxWy|c4D9}A0{n;WwmJF}gmISVTYXP9C<-hVD$ZPLzK)@}G9ZOr4A!E$p2w>}<&%+BGz?b8!YxP&{<>-+zCP z)70JK-=1up{+SkZf-DbDSXh}~v;4PhXjOp+DW9T+yQ#INn1u~=d7ym=a`LhX{Pq6- z_T=9lAJx?Ow*Jb_p8Q!;)ydRR)XoOlrL*9_JM&NF#}EIhD8TYC^G7T3_cH$_ zg|4$8ssPJ>_e>Dgn4uDSE`(vE#6(owpX@Cl31j!nAs$I0h!>+yEA3@}0-l54ajP{hXYyl+&$8iTk~LnuwkZSGX(S8~0(f_Tlq`XS_?#GNdBs z*!cOciG;x^Gc#|BoLkSEH%6^NMSocAOc-fIRj~y=a=PyZQH$u%zyEIRuSW=| zk^Zly8FcKKKdZli_VBmxug?5oX`Uc@(V&2oe-mQU3Bx`;Lsc~X_3oe7{?lS2JpNu1 z)rI`;&1Ax$yq;zdj_8xg`mJL+gBJ#s=3kfiACHDk_ya6eyFmpECk;!`!_fbI=r85p zkuDW*_?r+00W})tBM^2xu@(FO=fT^vGaaSDHqa|Ji!GIr?7Zknlm6uP*>gjkIt+&? zS-)Vz6RqbDxqt}h=Q1H~hR0D6^xj3|~!Vt~mp{qk-|CMwPe zcKgr}dX5lbj-I~Z))))_>HJLCTVhYksaMu^<0RwCU)oxIGmeRT)3_Ei7TfIFK+$eO z-lP?+$Mh23H#1DS)6uk~l4&D+TO-vklP9eL!|<4w((b(u)pf!?sbdCz} zcnE#42c{T1t5$nXt!8%eqHR3l2&e2|N$vE<(NL6up4*0+P1Wm|OQin4c7SFOqBQ9S zE!t0b%P_h9y)q}@l zCGf}n@il4jQbl=J704Hq_-jBVkQ2ExqwV%Xg+yd`o=k#sI)!-X(ge4@=rF%0B_}72 z(|vM;gMLI;U98pniF{6p2%?B=OA2&-ef`q8Y9&%EpHuvU!?EEN;-;o|)Q$9!#Qddg zNhK=fp<@L#+{b;K6VKGkb$(Wumc(bPhH=p;H$OF6HQFzBl^?d7l1lvi@AYe^hHnM7 zlKIt~Z%;_JMWr4gv;u)&Qd3iRAqm0|nd(uj&p8-Zy6jo^wHKXztfJ z+zzal*%of~iv)Y7DU2s@(7mMe>GA8(+i0o=?;LDI4kXxx4+ET!p0Amv^EvPNSLyo( zkQa}BmV)zmUoc87wk=NtTm znk6*pKHDBZMx^yF?rM+QkE;G|TSpzI1}u%J6cpTvhd(AAXV}(HF-+eW1uXRN{9#-0T|YKA3TJ7 zcBczOWQb?(nW-^{++8}|NmTHA?HU|9dkf&Q=mZ7^I=NG;x+L*Sa_ry7Sq^sZZ-|b(Jjtev@s04i@G(zSSY45h9jz#{bhL@R63h71O`9V$x~Ws` zhyuDWpqdYDB$vKrYB5<~A)W~8(2KnIb++^o=@}tyEP3KSUp@DvY2r54us~jjdp|9F zTnYT@5&1y62&I!C<3&kT4_o-Digzk#xI8fgNjmPAddD_Fixnl%W3Op1Ug}-@{py8bR9Bs&p)YY>O`A-y;4 z(`U7FB$A&U;0{Wpr-LVWU!VJ3VfV+qin8Hv*m#dzfZcB}oawS3#L^x9Ca*v4^<|4x zV{SLW0MJViBq;BsG)m1ZvgM=z%G_Pcw|!Igu1UH+02q-nX*@Kms;M`|JK^MUI*kcz zE8Y?3z&ImzxMNfpeEG=gFw>5JRFRzDg4@msUMHHR;g>9K4a$dFr}9<1hd*taKZ2js(1BeTA7fFz+5Ke69yL zeJ$67G_3Yjfr8IvJDir(gP1K<`CVssL@NC>n%eo+e(3DyG|R2L+>vHOWVI)nYw>cP zfQGiU^Gm1eD-FR%hVmAcDW&$!BL9KX2XF$9=glXCzW6i~Q*R3nAfazB(E<{}${-o_Vnwaa)>)kq7t_+ulpSnZ&9)ZZsbm`|Mjy-! zxF2=gBB|OX6p-w^a)G?7L0?xbF7`aD-=Bu{c$?R1qUm|YMuQQx^4c#cKWiwFrDgXl zU8rT9P3X~#c2UO52?FGb$2zdL`f!Lp3x*HPag~IWZQguAsu#OU7Q(_~p*Hp!f5z#2 z8hb~icd!^!d%mOxq5G7>Q%Yl_iHB)hLRWGPy?SvycWH|XX@QeAbNEQsb1lj&4c6nb z0Qu$IDnLn)zV8hIIw9BhY#bRCW4IIwisPA(43oIq{C#5ypP+l6BW4oi;az^Gjnac< z25shB*5~89 z5VoO0P1%L9^8=zoA`g@0>sO0~cBh@VbJZOPJj&f`Gp0D z7!4i^c=m+jM*4!Bm7G<~q_3 zAlAF{vQf|6pQ7(@ihV_G9*n7|$Vfh7c_H*8(Fnw=o?uF~9QMW9}rJ0!`BEyK*?C4E$8uaYrzuWLHzG#!XW&TF_@T*vs{Te{o+or``nMu zPG~Cu+;Y4a)u(|TQtE_XAd0Vv{I~|)5E!{^b3#LjHDdDWf{ zoU5LB>GiXE{J&|(=>ZknKf+xZ>#V%rD8d<00+ zRX=?M1s;m>H0=h%eT0D%zr!@TC1fa2j#M-u_-#;7n<5K^tTCL(C8EDW;&x0Zf+o)D zWqYKpABsFl(0*yHo~wmC9zAxZKP41d^S=3F`sX;=p#92El9fLDV`7rXucw^?DFzQk!&Ln2OK@v46VsXGgBxF*<^Du9!?+N zz+sU7V>G_d*`8^$(*7~o1Wf4QDdb$HdJWwjJMbHEoTfOR0c&0!+}z{|l=v8Ob36N7 zD+vJYiPQBsNPHyPo?_^;tlqeJU%?UzP7ggm->-4~StmqM^Zi)@!M{zZm#p1@1@04j zMZT_r`1%IqS6(RO+9klRj_l^wkyqmAp^J_jC}46Jd^pnVe&LZLJ#f<`7@~)amh+go zU?ER)iiBZ}$EYg8_B8B?Kq4a?>>(Sn04;$@q`H=dk?S)?ttd#$DD5Qj_6(t5o{Zt@ zc-zl|yt}tDa-GQzZP`lq2X6&q0s&!DrdsKj-PJB7t4sYujrKdGU}C{z1$Y$sSy-*KR>DmU#(@_|+fuhls z6JQ?$30!r!q8`Y{9xr{ybz2L=e&d#FULfm7r*wKMuuRcorV8dIoaD2I*@ zoivcA?tbfPpT}#BN-gRZCof7zayRxZSLgHgm0C;+HnaBaIAD>zLHoAi11YOK@lBJW=^JzFi-@)u`}@k`GTNObl98^GG_~CNI@HE zXrW>90@#Nb{mO-PhvtLsW#tY}$-&Q<<#-&Taj|bo*B^mAIB(Ug#$c!e`U^9M@gRr$ zh32Ci@&+~{5Rz`vaEVLL2?#Ji(DW8kno58nr3i6Ujv0oZEDkraRFij8hzi$id_;Aj zT6`L6ivavtsjpI=t~07=$TeBSQxvcS4%S|du#m1?O<3_XoshW>X&bM-4UZf<-=;1+ z&(G5j5_&`^bZWe9yT5Ji&>t1ol}RBnR}=j zp17Ug7J#+wnuj=@mKW#@#7X10Iw@myU@Jw5H#*9!@{&kC zJ|KH2N9n*l4_59!)%b%AdNK4}`rYsDrrvaa-A*2xg_;A_0&B0-JCrT-7*KU=eb%A1%5e?6jp50_=_Bh-!LExh^aPkB95K96g5`S?CV&FU`^ z{|-ID%#+wZu4EjWOdlWtS-|2(|bFNY3eB|B&0FLFG1v@d}T&09qs?gI$T$7!!qm~LNolCDsk0X2fAq{|))q2;2@^u8hGHLsZotMT^2svP#RkQ&|l z8WzD2DItZv+Vc6gzfEr%w~}Exru|@bSV-J`%~Rm+G$yg-z2IFUJC+_8Tt9rIAGsL< zLe|$8;;2}Jw@I!SgX~Nke`i;i@eyF((8-MTlXBh$b8aG3!T+|6?b`@@sAs_f$N00g zR$-B4v&#o0Zkd`I#;E`=TuRW*`~e!HI*6FdcG}-*^9T8ziU5hH0omQnzFXL;zR#|U z^>njTo1WJ$dYF%RD0TFxQ9pVnHf>+M;p5bZv#P1c^lt3d-Eu^w%oAH zR#P>WUD8k8-;*^v?-ob#I&C~PGB%13HCcS=w(!|-xGgY2AHO&_4E$N-BPrY2c~b*R zE=i}pC&3{Q^6X&u9%j~U%V00TzeICjgC`?4u!occ*3ZvR;Bs%Q`|g^gl{88~5I#qU zH0U09%;mW-L*#S4V7!+Q^z0@7(v-#6@vpE>`@`GYks>Seuv^VC9Ueatq;t~mx<1$V znx3ZzMGd;G_9AvM22!w%N|X{SGO8gTFfgi-4`5+om-g39i4SwidxY8-H5y!eB)VmB z&-NEW&cWVD%Ak}$nFQu-Q%%zAb^NgT$@0a?OQ%_1-DWq@46m#A*OyM4Sus~&HU;Q- z!bU1R@tyBsZbd{^^X15F`u_uy|Gs`E%m(VY8)d;(-8~ugbjzivleygajphzG25Ak` z2{ZlkZ5x@6_OC(L$4FE~{(ChTLP{Sdo>E$Q;k>`SoFhfPcPj&P?M5ZNpQ-}*CSBF& z-Mc{uOEsEx&guGtp}wifCv#Be8pQYVYkYF3UAMyc?xNqxOF_cp&s7z2*Y(kSy}VR7 z3|iQ7+8o?x1K-NiS}M0P#69N%JkUl|5y)P+PsfKL+D2S}@m zO`xQS%UpquW+@ zvRQk}SJLizpzl;=c`2=1_biS7TSZ`o(vT%JlI&;r2f;f>yzkCx{Y?7|ru@EqsEnCER*;)m1 zx_&m8#yK~wFCxc`Rppyg#VnHcs*>GCzGYt9|+(kQs=Gtq$EvXq8I*OT8Na4(sGdL_5*DZXslaZU4-HDF`t zmWWBaZoFgQSKh!Wz#=~@l%W5Ivud#I-O>IzPericnb(qFVgMiso`#hbvtVWA)ZrKN z`B~B^p0R`oY(O6yHY+ay;BX7QDT<-Qnf?G<@R|;oz*Hg-InUfliN~bziT@fL5!0h! zaQW4=(fv5vA#~7;Wym_k!9OCX6$A>O=yUg#Y|!sMVM@KnH~{8gG#6&Ia#~G{W5ENt zta7Ba4DZ;$qV*rvCd=ZG`}_SIibzaVJ+-|>xky+ZO1&)eeVcP zwjLVXvzkiW} zJ!Peo-0hKh`@wB*o@mtmgN%JoBR->iPTH@wN;!Sn$9|n# zF#1y}K+_{~(MmMLc&KqHTAF=SG&q~x9YT!ld;dSDg6u8d7}Ia2&FXD7wXNX0A7)$^<`uO zlXt1`o{leHy?S-E-fP7cM~#urQXIfLwn`~GH?+8ZGx@dbb~4f>*oW6f$m&D7P6idyikhp!;5LT-EHkk7aL6^m6L&>K+QqmC1&ok<=3- zr|WsI?6bAASC2j8e>K+v4~>@MVX;3N`vYOHXMz%g@(I0Crr#0wlNKmap6UAjXHPmZ zP@Zd5hiCPe{X)@ZJ2SK$S60^F)tK;;DQL8NQ)|C#_76f*`OuE8?BMSPUa`Fn+HI4X zS5^IQ1`SH(9-`*|GyMNq{f9&Qf6U>rcM@}flZoqw174YHjfZ=T+0!gL=Uq?k{q=A!IE`v&%%Rih}1IPYtrQ4N`0O%fY8&z5_H%rdRglh#tGUojl=4BeE62oGsS5p&R@vR^;aDw>_n$+q1&j5Eb{k&XOdcNC9=xBa2tvhq zQzBt=q?jT3dh4JgdKC>%zQR}OJiB?&|Lcct^1PcH2-i%{gNj+oTkfp%Ua-9NTwYIj z8tscQk2H4WMS4#PzHB=`1<_{s5c(XpO!b;uEngb47b4 z8VpYZSzou^gwW2Q)FK<}a;vxu;h*GL z;7pb4b`~}A5DU5^FGgm38apodnd_nauTQj`ekmFn4hpN#&LMeIJkix{vpZo0x1&}O z!Z)!?7@~$!97@>1S;gp$@{D95svjpH!lzoXL>jqRrL_3GB2we?tmcDB9Ul=96yHduWtUF#F-XwEC|5*4irnk`86$bx4DTf#hC4R! zi<<9@E;5}bTt=OF+Xg9eU$Rbm!r|hA90*c~TgVn+fD~P50rgZ7ytH%bcT9buK{r&8 z|LLB<^(flan}vmi2k^R@Jy&lRGUNB-TWXTuw#|$2I;A3GuL?rqr9JDWS z!wEFnfjD~g?yQeU5p1PQwYF2ZCmTOH_5B9NXV%;jJcbo7lIt|;yb*q+0SeCV+3f^9 zPj^1!$RrSTN$iJy8x;?OT&?)wW+_iGUzmeojs0v{5;OdM1_a@5+RcH=n(H+++PHiY zH>}s`eA#`|ZQbh5B=$R?=x_Vx7r4?vv?VHy-HK}sEIbDIr0eC}IZm37-=l4%PZ<*t z#AZ6M1yg&j>t=A0|8{S_sK$J><8VdK5kzEOLngQ zD;E%4w=Xl)a3E+e4eQ_g!y|k(xiIZ}M;(DyuF~`(o>9l7^l+{PgGsAleD`NhWLEf2 z-z)vBfPGso%xfwQxN*a}6G6fRqi{)EgS?8d(C6U)Tahg7Whx1MzG8jUc12iZF-e5J*I*5s?r^I z`}>GptP|*&G>wDg?+@UzbV*VQiU6++d2$YNO4NXS@i%z*GGC2_Eg(6PW?Do&AJ(vVlQuXfbF7zMA^Bu zsH;FHl=q{hP`$vDc^DTnLx6?#!oYXfp?kHa=5D(XGuq!`C->D`c~Ia1(6ha#hn8P_ z%}TFIT2@x+S;L_R`sTYaDY}@R-Adryv(Gg0NAglGYcRoz*$4oKHBFIPr#jo>)T$ z?B5Qv8vTM=W@V*|sO>)Dr5f%=vECfU1Ix~lR0S}?v1#uU;SrU1QrM;H?@-JQj%~=L z{G_8A^u|i~$0g6qqR3NG4$WyoIl2zvdL?==HIPX4JeC}q6$m>H^b27>*x-XY7gA8IKhKBrU(W}H z?tlfh4O>=vo29fE{UWsRURer#R-Sqm7?0#+FW*%ru|2c2o?vvVJ7AA6;9epoJqEYm42h;47g6DfKQ=2!(yn_|O zp6dLQq|lftoDSKunI*{bT0xwLM4Ryu`yhX584c0tLrk*IGis7{Ujz4?0^NIzbI8vx zliqzli+I0~%d|6PTS#lcTMo;wP?P?qkJe8`yoW^ju;}U-Bj5NTyK^24%a#V3->fsZ zq`37my8N1V;k(j+*#D(7L=oyQdAZdm8-b%L?7HQD3-EgIe$Ezi+uc=*UtPN1mur1o z%pZE)Pb%ce2zEcW0X*r|+DM);1ma|GyZqn$$Pgx|% zI$H9oRJHsUv53d_K#%GP&?t!&e`(jChf{R^opvV87>a%hc)*grz$)s%-F6Gj(kNqn z8p!Um@_J10KXhA1HB<5J`_)>fpFJm~ccUjX_tW|L`DUK-9-#C(ul>-XtaLU_88fMa zpZN1`pFATc-w}uLCTrq8TwP*DMOg?Jx7S0>zjPO9s?!FK3=FRi9-Qnr`tXQw88n?R+r z%wt%Nl>ZoEDFlHDCI**@_lW|#Gwi&*1=W*Z>x2~|2k1v)d_;=P74OKR!2p^EHxvQF zyLXOmqcpD;XL(J&VyVkVA_v-1k6_iP*=ThF3;8EQ3WV;+*(I&czAjGsO6usFK~}i_ z_FoD6qO5sTCZynbvR+c$X#$1)95Yfj4QKwsum)}IP}KfGpjD7|d%Q^5{jSfPM@)$J zlR*4mVJTbw53o3UP%Gv$$4SPL2$lKQpjpr2e_5qBb9<9OoDq%iz-pj=KU9C=^r3D| zm{NC?KlrbX1e7oTzs(L|lj{oolXvSw+qqR6{38I?Zu-ExWtFS4|LAuQ56ZjSaGd^V z#-0F5_nF63O6mS^8(c#JX)|_Rsed%{f9LSy^nO~ZT4p-6Ff*%JsU`fi#s5iB=(T#% z&?;!Cw&}#M5-f@MR3xXeTbO)lFBUHBKzD>4LB3Ooi?* z!rnih6()5fzoaKEF$iIm1+(9}6z09%{1^R|&wQ+KIVdHNleyJhO`KJ&c%c1h*UvvbcFS>x)1OHiXxr6%O% zJzHlTMW|}EWc`h|)?xb0ls$=rE^Yt|8{MB9 zq4E5G^a%8j(PG*srvn-&#O)^-kG_zR8x2T&l)A%DATugQCEK`TN7~maa>1cW{2JAO zAs`?iTf*1)>+Fnk5;$>3gPyXo=u*OJdS!ycQU69G?NqNdrmBb4c!PsczYaS=^@w)h zS906?bO&!&ykZ3P&b~yfXkmqSVKilmng|s+w2|M`vDyOR8_H7Zs#>O*__=oZq;79- zq`}Uqb-OLEoM?zjCx%H#F>szw6sn;mFw+`;!!TaXYi)7avrR4XSdI5g+>PBU*WZ6`oT%{L#$l~0?^iUudUqf;><*K7{p^U5~!=$?2?=~M&yjfYX z!*rvZFhy5N9$%_u6g%`=0EZ{Ve*oSqZ-VqryP*KdGQj-}BspljFpX(AvGJ5V$WifT z+a2d=pxLgwcGAMcYsGEdQeAdg9@H z$8RYJy-ZX}0h6#XA7bJYgJz0SAoJ{R-|lCS6?6j{Av8 zneW%(*}F1+?$s=yMudn#nX(p~$ zWi4WwF%EkOlvOa+_&9T`g2gAeO47e8^myl+6T}%NcIvy!q3_GK-H%Wh3zqRT`?B3Q zZY*vC4IHJS&PcO^btgkkxB3*vDSMs~sV65V7kjCelz81485!G4BYaS$R~M~Lo*3tN zI@z1r72xfs^X?KW=EbSi;Rk?5aqzlX9NQ#|F(~|c9V~xYnWdRy=Gi?BJqWA)aSQ|) zt58(`@9j^Ocu?%jMd=wYK)#5wq7Y<6kJygbdyaVy8gu}a}2g3EtHa!CcygPj(yVZz!`YbTP50z3g9~z>#US04zbhK zQ|a5_b*%Aaqhha>$ET4`_Fu4s@}S}lRVDd_O>^nd5}Ho7hW*)RQAS;g74iT;>sYNm z_;%29r&RzLL7r8u01|pu0^^vJj8EGzdesYY-@5DI$@8R8FV&0JE--9*3?J-2`H!9M zz2x)6M^`ygu6O3WD9ThIA!cUhO#Uk1R?*}M((!L@U>IrRNG>6X7nK}?qwS*vTK)rSqssKq z&lC<0EPMCHpkL9XR4rnnDP6nn5@>(yM>3Qgca_BM`z=X2+6q>FtfoO)E>|hf5vTQf z_L{B~HUKUJqtMXb4<8R?ur1K*d`{YW{ADor1N3{4qCR0<{A;(vna>NB?_wMDJ=6~q zPy|fgMepUFSgQ42hTV0eDS!$+zueesAjFVHscp6d)fx@43EV6h3~KU70`!)PJ!RXy z11b;Zp`ZbDq(3YH#;e{UL(EUb_WXR0iFrdB=lKS7x?lXCpEk; z_q1Js+ZqLh?R?>nTJXvt1J>kY7kHkn2?9{g)|8Mw(38cQp46(#*;8Uvu;d){%vE_I zaoNq8?CDiHgVOs>V(xtqJ>a`Ph+bFmdB;604col)Iaqh+=kub=U#X&!I&hB@yc>P~ z8x5SdDA(`=hBo5#{qm)oVHqxBKZLMXw3gj&c$^q(!X~M3TIJP%598(_C#R<>=PT5k zpY+lj+}15O&Ig)W@Fr?3^^~7v1JyD%+7t3Qf^Q2WSA{a zsX-q3!AO`m?O$>BGL)FuWDpx$`KhgMOt^ZOrLY&wm+Q;J0jmC@A){f*+C}nZ&6dcv zm=O1n!g)S~fxO32zE1emjdVbWE+$&GFf$4j6^+r#S_S0w4;?;)us>fcHy<`s?+&0_ zGWdYS`tup{m#)V#J+kwM6t{FH8E7ixFX8s#lWt1wUrWvcK_3xb#9)=h_ny`Dum0kn zx%;sYXid6lOR8O0GvQkwxp@~r-fozRkn_?5=ke`>PMi_iqNct5Z((ZeOyQ2__Q?i^ z>*gWGH=9@!a8+Xk4$yRP%3#4;>2&%5r<>rX?u=tjpo=v{=fiRKF`vt-D)R*JRg%-C zgKc`uV_l1F@B5?x zV{eLY+~q^H0%P5?oI6WDKZq`EY#>&wq~jmoy#?-`{{4B$D|@Kvm&b`paIv)igCnhO>vtEolfKHt1}Q%#4fi) za`Aoe5D}|i-(q*?>bl4Zuo?V;i)nVLeHrf4XMjcQz;BlIFM66u5`mc5R!u3#3-oiK3R5Y3y+j4B_;ebd2_5#Dp z;=IPv0B+6rsT=VVXWo34Eb^=iw*^btBtZhEfa9 zGGn)~tV%V%zVTKE84hHd%>ML%?V9`?)sx9W?G`B1yB!IBKiSwM=(aW~SEx=qINkIA zIDQstY6LIUC=HE;l#q9mWxDE+ks#yYyM-&lN%2zAmvuJlvpE0Y6*`r|=kp8mE-Xom zNQm$hG(TK1G)6C}tQV>f@L2T3rXm+k9VO^AqXZqEYP(ubws6@&;nqxk8Mi0-*yy_| z`xzn6`%#=Q)+LY#6b5RPwCzRBHlXpl+I0qn;hErk(WV#?gZzSm4%Lc~33SoGo&7n2 zI?;-d;PEj)glCZcBa``1GSB1NUn|LkbCNR>_2G*;0 z*1Wj7*jc||FHp=Dle#F$N;Sv&vExmBEB#G!U#K8hm5;vb`vU$~k1OX^R4!4b&`2nx znT#S4$_}VO?{2QUlF46_=_{tKM50GR*fpq=6}I?ZOg0(2cHx6`maL}woam!v$3(cx zgR^=Y);4HiNe`NUccF|QH0~#R$qra08Va8i}rSXfx#uCGmmJ|#`NJkRLfE}hV41I+p%!>R~3 z$f8pN9U-AGsUy7q`M&@_ZVJJ{4|x%1Xm80B$II}`^;!aUi>9atEAI_9263KTE~w|) zV=gc&R_V#Z3f9||b10SL_MZ)_f{F2dob|C9f^e65&jzN@mZ46c|Ha;WMm5#+eWOZI z5djqiK`APTbOizFDAJoq6OaU@_YP76qN374>Ai*Cdkcuv0HL?gr1wx0LP$6p%l%x} zbDp)%`+hp>ob`Um(h!F1nb|YHHviqU3S_aipKVR$Y2E!X_nmt9IlQHv(g-+*OoLS6 z9LUpFqp+E*v=SBja$N&cK0MeWs)F0YP3hq?<@hzH{Dm9_NH(-vgjuyH3com-Hcz_? z)DOZ3;nkNkin2=l$N~KYh{8+pvYjQRW8h>yTV+dieMKSEN_9du#0)1=>Frqes1jmJ zzMSt>sn&bEi!+z21p9!=5y_rJsz82emCxk9*(QC3O5PEEyWiiuKZEty3qMqLIJq-4 z3+=Cv7P^aI7g#8ft%$Q+F$!WNyi{Valh_#OFgJM;!kv3BfQ&LX4Hd9=f9W-=8`@Pj z54JBMoBZkHzOv#h?Q;$(%tmbT>*cLY^J^pW=e;r&qn>iR6aYaC-%Q_2Q(qay?=4e@ zxrU=V72e>Mp7BCLO>oOau!6EOZ)-APjE`J-vJ?2WRW$Wk==ykM;CL!g{CAF(hic?e zy#YWNzw<;wx~szKvAwyZc0dtd>5rTw@ftNXqRP2kIj-lV%B2a3&Y+$(VMzbX+RjDZ z?}&D)fKbNo?dg(Ts#kQEDzr}A1DECq5>&t$eICn*sJ*cUp^L1?iZ>QzY?1H7^jx;F z=hK}-7a0Y@RV-YC&{O6wc(4${M_(9olS7?D|>ps?g_Ss<45xjV~whVUc!YJkdp21 zYxK?_*>v2U8n*Q|M&Y+Pk`fEI?weYM*sU^v(v+JbmwUQ zTLAyswX@OjDVZrvdm_NvG~R=U^lM%CIwW_=&v*q{gD*Z zT(~J}Z-|m8U+vRwKvf}|r=}QAgm#UN>%7HBw#IzzdMsJpacTK)c6IK|WZA`t{iK}_5 zsy$v-fsai^=zi5N^s2FDKy;6ge!0d@gq4z;?$OXj_Uy$(G2r*a4?E>{7Jn2ex9*Ai zOQo9z_W`-nOZsIXiUKfIUe!mlL_g2_&zner-IMn>f(Y%fxq|(^wYlhD+;@a^v`zX7 zB%N&iU_Qy8@t$a-rn|%r9Drl< zQ>{@A8*R`95ts&jT7DGWyMfct&f%1We% zM&KdPrH#FmaHG|a-?q^^Z6O87hYNnqZRl>AgrCczWggedZM3X3TgY3$15A~j6*kEF z>qe3%7m-!goaWJ8>#4rdq`-0MzzNuSm79W{d}HfK9H`uSM3Fc@^g?^SJO6>>ZxKpz zi6OwmDn0RUgC-K&(j%9#*G%5cn)Rj5d)iGstUYv3|eS{emDLpFjOD} zkxPhp;z9L)$3oSGM1~5x4-P;ED5-cF8~fSqvp;?$4I!}ryzCG?8#>2$DVq3yAd zyn;dr?#IDRfu;xuP2P0#WAARAyRzw~@+$L(5*Un3%e~;sP@>>ERNeXTn|Cy4w*}Hd z8Tog`L^p)@w>+ak;0HK6WC^_BhKoV98x0->#~k59Jw|8Q7}YliF7b-*)E{9uv@q3-laR~1 z-UC1)<9!i<7S(_bv4!R3zH&EEsxbv^sKC`4_Ahx%c?V2OoD|g@+D=x<_NtaFho)HJ>D9g-JI2(OqpW-4L?$wx26M?`9c{`&2DiZDR5gIz|xD zLD`QTw;#}F&+NE7T{lcR!sGFWb`F4#XgwNv{`ssniS6;wWLH0>gei(}e<&ZMe_oI- z&~`M}pOS>_gpW)kOXUT;hgn*jmDS*J=$Gs7_8qZ~*M}Ts8g6N(UcDN5^`NwZm(3{D zsIA-LWuqlKPu{Fb)S>{SUo5cYsr1twRPAsKpEsZ0Bb!Xm+bVQoVqel_EA*uh36SDn$E|mgU3I8Dw@`n^LOp=;pj_q-a&qjwj7vAi%Lw$6W5WS#HzDG2V;bB?J zzA&T5#%m+U%=xV+(MeO9XSOAWZyr+CqSrz^aTtCyroK#QA}2NA1ha%YoNGbYFN?dS z%OLlu+7_tf#?k9)gZ4ncxq)k4(hHyHP+zbc97O8;dTfw42UO=->P6wXMjEff19{)o zCpnn+JoV|(4YH*yA0fI>aZJXR@YiW+U7-7~F{~(LMXY(|>Z7(^ia~Bq`(Rz(3*?6q zLN67&t7@FX8kHfuy>~Waigu>P?CcT#c>B=?Z{@mSn4B< zKFDQGg!C5stfsis%hEqp!(3N1k?ci&fTx z7x#vt&G949lSbVKW=XZM{OhK@hZ@V{Hww2%UsaqVTfVw~^kO)0tj5#qxbBTWZ<2(+ z7F_!_`;OR)j~Oc_>9Z#^*58sJLZ0Yjw(p-)E6Rr?d%S%+R=;pI^wJAyFT3O3lZ7d& zol(28eTR5K>+fBeoZFLd#QMQ7T$|p;g5%Ja{!YDd^P7fRpONgG$LjX9%~0<%@A=Bd`I8`(ZM5Kwg# zJ@M*I6s83r)!T)fcIK8Gex=>pBh~`lgyw+rbMR0(`n!dE@c3~-{l*a=SX@^bdXLo| zaOU%T&gTz}>u`ZSx+aQdH~Hj%mT7?#iM|@7p1bWs_lB*8^Bwp%rpw@S6aS%4LioY` z(oFdGQ8s+^>*H9B`|4>d$HUKi%gD@J8Y6sIhr?LG2^uXN4By_&*-Y5=T;%UqBkb| zW!6F^HF7U&s#CRiVGDkGXxI-M4&T)5D!;irj5j4_^1k4*HmnYl{KA~=csYcjQ9UXA zVIZmgqM5@>zwzTatCpbGH6MnwNVN*z&$p+A=YIU57(w>fIumBr+Z;Nh@~|{f+^Owl z)Q2Jo+*KHFOiq>f7*4h*pMCJBw9v<`#OKo1>p9avNB}st;c-8n{iGQm(3fGtKbY%- z=)^?JJbBew>05x#xvY6MUdWcOOFaMM@vxB92@<=k@$msXz_4ssd_X$M{St=4OG+(? z<)vdhTui_it3Mp?jU8ZR*r59~{<3({3W3tGk_5l_b~k|XrhtggMm3?hcdUu@;zH@^ zkU18Jrd7V~!&ALJY4)ax23`1I&yG!_kBTL9R@AfQzNU#VjtScTA1I>ZiJ}h+orK69 zYF}*%NVTu=;+QriNzQVl=x2S50M&TN%+#?-OP}6j$JfIo@B0vb%8_K_WIZPPHPn{E zkQn`h5Xs=3yY17}_SgLqbYLje z`xf93*C(5B{e2b%$CLC)h?wod4ZJ66*bUSkVAlXy*Rg34s)K>;juK-%%C?oZ*p#&VH-m><+Ph(2wE(UCDc270utMcTP z%Qwu2iu`NaS@4W?m>`=Gn%>+d+fj7l;pQ^fuInC5()h+$9)hB!Z9CWllAUYi`cB+x zamM*O(zs=A+k{5P1Kk}wJMktUk+Y}~M?aGH+G7CJGUIdIj>5BLuHyxP&{iuJHUGk~ zw8XX~B?J+ZfhU4b9uh9zXGoE_^f>XMe$=}4375sj#Ow8Cq&AKvN$I`vHDg7BrR(#( zA85Zgx~f}8Uap}@0xlc0=?AEx(HU>z;5dQK{vTX();*(FVBj8P1pHbD)mdmjEXQn=@<7i7^N-AE6R9C^_mPgVDb zPfs_@C@@!-*WW*P1@}|zi~GyY#0CSPhZ{os+0tAP8OLwP0G-|w-Hj-A!ni)VXq7?S z_<~g_HL6w{NQW?^NZaysXOznpCOpxJ9M2=q6ghq~#7|ZdEFGh*MmLgsoR%v$J+fMX zNY~Wfe&-5e|1R?aAPz`p1>5yir&6+X<&3kKX!iywe7w?`583U0T!U#HTyG1I8|gy4 z?AvbTX-ZXOOndnQ-R8FvY4Z~KleN4Tzn%HKDcN+|o zY%%C7ft@qTPlpPpENSNTZb)KpD&w8vDEBjjyeDbFtFvl8AQk?McwB6hilk7M+aMxT z0PG2Y#q~D3U3{GGJ9w}`Hb8lO>{{>k>f*c1z1#dYKD#hPSXvgfw$7~o8! zzZf5_(HDh5O%*XZp7>vBiYKgMZ3s*pVX81aZ2EQ+2(C}gC(_aIY?m+7Xsv_}<65VG ze%P)abMIcRr}+HI&5yXLvCnVk@l`v%wQjsv7-Ri~1M`pJ)QwLo;)YAWiSS3dp*dpi zCv)jn-YQIi54(^>t3A6NzT8*B57oUPBszItW`={_zc7hCv$TA257$p;<5iQ0gnTQF zVAho|H+Y0<^Da$2Xc;P_5ZAQB2798)-jx5seNo(cUw|lf-tv7O%?3@m+cV_fhP>0W zoJFfbJEa;C=VwIa5qh}pzUBS%I(!L)q8T$f$-1o9qgh7XGmHL(i&ye`gjVGc@->xS zn^?T1`U9QQ>N_TQEG>^0LkR_Ijp6z#eEk9S`S>7x&bpX5ffq?F zvBS_&KL2E8I!IopHnvKyaJBlcyyIBsZ8CA3Yl-g4&vJ>bg?s-D7|LMVo%}v25B3(K z3AcO~k_@@qeYROC85wF%|tyHTGdfbI5 z5Xj5FRA5uJ7(X~uG}62I$-8P7H+*H;gszu!HfM=GTc3?Y6)3C~%+0!(TQQ{yLulh| z@K-vd5UcSq*SS)NJcpkv#M1GwDKPRzCI*z4iA7ha6|&l?5{U~>YhxpzDXe5cXc=8p z1srbGdstt!ks!)8qwko6EWww}TX7Z@B-`n48gagTjmxO=-d@t#D$sb#xv(SX#Jy$Cd5br-tWI%P21Bxg(mEVW|Q~3f+t67rMa!M&zUjBFe zKZNF=hEVy!>R`2-`P3EZWS`dlQLBo2N0YAC9lq^+J$rMJ()|q@2xZJ-ds~u$$2_(j z+#Ra1_wUr0FZqSXD#dKtIip2)^HuR)MtkVpCojzjiIOus32gta^l>r4SEHwmt^z_8 zCusc+P=8{dVm7^b^#rXlF$Mp#29k4rHtTg;zQxLT4eeC~+a0g@31v9HP5vJlL^5MA zubNG^Uh)M>#QPp2IOyi1-Jl0I9d=6E zc60$Y(n0Kv`)vq~P^nD+jRk3);Zk7Cn$BBiFds~#pu8pF8#5N^AqoR@e<6~Au#ZpI zB;Cy%dCtm}lOsoKCLW1ayvCWyaiaE0y?3W4!_AzZ;e<;ixZpJ;r3xCg6s?))CIhja*4O`whSB_6tAl9eo|{$A{QJ)3Zm`ElSc&Kf*ag>CtkboRQrUbm z4US)TlCzz9@8R-2$omw=43G{CyifeeM(c&vp00R{Zfxr`;nwL(;x`TBK*YpLYzN%*>hgTla<0V0;jt_se} zZX7h3c;e?kJgvH}W*x+*OL^(SGv$O$#^Q`Ug|S*Aavo6qr{7%i`EM?{ijbbD7k_cy zEWUDj<-=FWXEZ!GIi_0Y);E``?ZVW9PN_z=cU`d&M(|bjTi}Ft4J(m~ChrW|K>xtw z<g-_wD4zaHcy0JUT@N zo$A4WXEO(n_8%u;zuRII#wA=egiU1SDw8!p%$$pt&q6PvS)`B|9tCj%wjZZFU3Fg> zra)5YO;zT*NIH zstGihd-09YbMET6l3w8EmTG;Q!_lR2Qb+4B+Wg^Iy5lR1h;RRe#Dz#HwJJ5r+!l1t zS&#$+;Nq~o6;*?yOQ^dP%7jbt3Hw)dxz7V5SIjnA_D}oC{eQ!L!aAWs{+Le!fVF4r zQGRlnB-__EmR`|gaqYS{D(Mo132<(ql#Ti96IK5&8;RjHdoO%98qU4)=Jr=02g4}gdIP8|TmjT(3I|nqSenmQ8X$kF z=f~7S3z8V8Ehds@Q2&qk+~hy_{M14GmA-2_sd8i5cDaU0Yd=|Qjbc@*ob>!w$E_2@ z-c~K8OmTVkDbf4)EE!>rHMCo-yti+EI3v_~mnAG(&Ue!7w$z#P^F%Qro_5v92%Aoo zsCet^>phTeVTp*$De-}D7wu&sY{x{IsHo^(V!A!S%+&0P6!SCYTpDw-Atk6I<%ZCM z?E%7cqf=fGllT)pq;Z_t4r}Kq%q#SjPv?+flDBD>5ljN#Gt}PTdNhB=$!Db;ZYUc- zAucqY?=fDc&+94q%2F{`dTX0fco;0lMD9xaDMZ?q}A=YQo!3c}AP z*E4m$u$D#dONdj94Rn8c=}G-y)%Edew(js=Pfc0({Po7Wv2?Me-phhmEVU##^pnS4 z?eIym(@faSq8f#)S7NPP1CE*_kjf{ed}Z$zfzl3|yXP)NfKL`4ioU7o=Q578+FHMR z`yb!y43P`jk41{kA|Cep`Rgw=x0DnWD}``6alVMq&H}>f>gp1xh5A5HCAkWU*$=<` ziRNsBT&1vvsfVc(D9K9bMausBy?KX~8CfDZ?CL(m+3hl(dI~?UEm}oz5>1{b(8#00 zIQ`ErDrkmm2={Aieida1IWn?6aJ#r)?m*(9KGUjs`7Ft0sWZP`{IV~9>heUVJ^iTo z&)z#jBtgSGw&$>{?((0Ho_;<>;_Qsde16gG4Y@OZW7lSr-bYQL_U+(X;Hr`S6Wgaf zo(TjC5Tr1~m-ehIy8eFn`TzP;z`wR*&on0?M1t1t{HsSmBp3eAXMrC1KmG9g_Wl2N zKU{4vzC#6E=;aTUfIzQgb|)BY^JFf1m?`tTPFq1x5plCcU7yID}d3y4?$U zIrcZEm3*90c*oqdR~O0MI<4#)?jc>RbNG2hul&0IKe3*D*YI-9X5z~kqKBM)x2qz* zCX28-?CcDrddsm-gRhmpb(-di#ck<3wrB};t`0&Mzd;BF9Go2f?djg>h8Q0ASu>21 zrD9yr4Zf$1_zF~)bg0;Uxlo4B=v)oS-%CfFrmta z2KViHlTE7Ff@1HF>yD!jR7R(*{S#p@LK?6`JQ8-?|HP#Sq?)KBA|p+X7C4QM$H==s z4QR7-OndeU9~el6?c}bGc8neGpcm&%$gP6jWM*b{4(q()YzYXtmP4akV_H)$vTg#b z_7@Vij_SZDpHdeb&1a!z5V(wienU%B^AkZ+Tf1v@ZSaM*_L3fm*L@~P8VAFM(GBMK%%8xVnXacU5wJrYI)OXjd%>{#FWX|&=QH%9w{{o!Vc*zJj`dey!vu))#+;^O;Ai(xY)_bHl-pS{FG!D&h$-7juF$Z7If1~L5#cOBn=0eK{ zrZK4^*Q}rsn+zLMeRg(nP2A<8KXXSISmECk!Yv*Uaccm>YSy*St~1ug+Q!xq7i{&8 zf?*62#szBZBdqkQGKeZAjbm0LLVH%!!^`2l;nmfT6_l0B2XDF5d8>-DPw3p@YOyeq zpT4Zy=$Mm#VqMT9__oPVK}m@!Y0lZZ=c|xm7}uk8_xMWd@?|dF%9rQjyy^SQ*KCOz zRP-exzl(oB9~eR24u$lM(=wj)_t)2;dLM2EMO!Q-waK&c`q*xc)+Q7dMqyDs6~lVl zKaq~Z7 zQwOe6Z?k?rM_Ir>5tH&Ax$<1qRdl@G#;N!?&0EIRFRF2B#A6#aDj;bDQ+wh7G+6&>tohN{w?Ya zl_;I=eQk=3gK6ojzt`Oo9;ss5b<~-OP$}{&k6$`Iuk>joIt6J~B%b%a3q!w&ct92W z-%+!LAU)f@1O`4s!PdtMs=mg0R}g=3y0}x@^7E+jf>V9|#`^}j=iv2*cc~ikI)w(e zMdE~<`yQ;CXT?psAux+}ys*ocY^QSjtK1-;)#Sr)usxJuo-KwScjwB6`z|mu3(l?J z-HH%}HCMCIt*ac=O|dhp7G}iQJ`H$}WdaIP%Ir*w{0;Sfl%Ht^DZ5Dq%P|X*JbK$@ z9Z6?xZ5q$TE`TG-c?&7I#HYbKYWtECsDZ=xcGi%c4_BXMbnk&ORcvMK6+RBANwonu z6vZ`m8|D4VEhP<8zIsiyCtZOyl+IK;@lufSL?Y$MinxbJX^r)8EUD6Nz%mktcBZ#t ziHJ|u$2APSo?i-f?T?Fus&-N6%&>%t?Gzitu-b1`Ch9K+P)*Uk?X>#?p&k%%^?gSI zxJ$3TDI^hT5~M4|+CCXS+N&IU%Vul+V&b`)nf#-%_QK~iqxmm%x$sAnVcv|WQH}b8 zo1zX^(HIA6%>%NyE9$?`Q-lya@aoWvbZ@yz&^HZ>9_?RUo zC+R31bBj^L`N7*s8)?+2eyLYUzAmhbuu@qm6c!N?v2jhI!Au<*x#p};5HPy1v^?Td zG}`awG@Z!(Ijb>Mq88@V&ckQa^=)Z~Jm)Cl{U2;C>l-dUeeMEt-yG=@&s(K^iR$x^ zZ6oA}z`FK%TH<~NoU-reND%sZ{25*?l$lk$ku17bBA}PTymiflFJ(pwJMIq)!)N6q zy5DeY_INw`Ex6*{O@CganDy~GX+iItKl=1OlqOL(K?WXUmgMI)THppxm-L@AY6~AE= z+`n>#7e0>DF zy5$~g8U&>K02AXT^31`YVc73plgcO0aO?Qi?7w?sJn%q>Xj6_kiX=C8jWX?y$?Ub2 zRe{Mu6ixPu)5>webwhfyZdY-N-`(}+-6$@p;?W4t=*oX`FRAW}mp59iM+fcy z+>yUPka;JtymQyMS={^&c?5d+M?SEu{J-5SOPe*^;xW;M^2dI1j7JqVgQwtcZ=%v} z(!I!fx>jM@2Qn=@nkc+!8S671G)p)+ZY6fiUUDF=@40~i6{KTh_LX{caesYEtj17f{WR8RA91gOQBUow7mNFe}+U@JirViQQ?4h4TlCwMAe`gSr<(+ zplPMw37T43q(xeu((7VQN5g!M9r8H<1OOiB*OlOArnDlg^NK)9C+YDnL9jiV3(T@N z6VJK0xL6TbS|mr=6KxR*g#(rS^z4!**s`FRaB(*;rCPW3+kw$MU&r@OEY_JiFduSs zI%AF<(3y-7CjwX9W~O&sf2R<)fJ$NGoF1KobB26HN0$J6aafmEU&!-HG=v@rQzd{V zpm@#Sh>^BC{PR4U!VCy++!|-XBe4A7clWDsHi>(xtShZvV75;pu`aGM%8Qk#(sCAk zl$n(gM>XS}xnUeDhTqT_sMY%T%5`V4nTwt)h+a@J$#X~7ozM#F%D#f-vpAM%o(Bnj zT%ZWQo)^_M1M=!UCnJ51V$U(Ihu?JJC7tQ64SM@_`mZFo{S?s(Vn3-0s z{@HW_Mh%OTB*@89GO!xSC6X!o$4bofrR)Xj%rGnDo(t4wqKKGD5LGfo#GAZ}B{%sV^SOE6_NHy4k!*0JtwAk35^=1Zbi8Mm=@vRI zSnR?nWM5x`UteOX4vC(jD?rL4yDMM3pdZhv1r@3={=QKy$sd&J=)WJmz&xvTmZ2ve zw{`1tU|_UGw1I=_P3LFIaxIUz54UY21Zve*qInc7(ITyMn@(R@mz|K<3DMe4O#klKyLnO&?on*RL`oFN( z{38H+;|+&M0+b{QtLpoRZ5yXlQmQQAW@#>c`(sRu6?e;1O{#5#vLI8(Q-uZXWqf4| z1LVB?;A=sZ;Kv&rldFr5c7G$#z6@ZAQt49q3Csrx5=Z3Xgy$TZ2KBHRl#r2`rC67M zcAT9h%q~hpt=Gi0;o3Z8j8@50s#mG6E*`}Sjd?RTiYmsB(4EePFX+Sux$W6z;(LT!=fCLGOe(fO5=`#~x&>DyrK6P>XCFnr1WntoJz*$~uDUo?4Qt9XiVYYk+zyOXj+eLl*!0 zdp;@x_KCeCCqO6r5?g?5a+4YHNi& z?SjRz&-2ZrsW%;7@XHURSadYq-tBgRTuoHX&M^xG4LoES6SXDk7qsE?Z*^WBlJRg} zx5%k1=ajHb@?NNkvBx`2!`L>F{yeSFs zt))E7B!Ff&iB+P(d@3JfwYXj0^Iq*-Jpvs|uRqtp?H~RHsd3+Ie&17%sH1tx4K;V0GsmQM}EN(IEA0pXQ97GlqjPSVq=t@%#aNfQ) zpY^uACl$#2o`G=L5Lc_D_4WnPsTa^!YaC%BcdC#iBHkY83XSJZ6H8-S!qSMT5~uhe z#)q;K@i{rGS;u)r)xARkoR5_3^>ucXl@%}65t@nC#>PsV>VoKmWgSbMG7{KopWy>gDj?+g*e^}aJ11-iG4kc9=!cbrAp@(L)Ee^Q|xtr=b<+| zJ|qNWmX;1oGhzDgpaRQQJ_yAIhm&UYKIs8U9M3;Xd?zU6J?Ku+XlII^d^`1rpjGOdY zj&0YRbXlL;T~TAIN)NB=_V08VM#!^Pt=D1Cuc?E9!M5kWDs0qs-BmsD-4v;x#$oU; zASqRL@vj~Y#I)@l0F|~#O3|?t!U@h>QZNM;SC2=tR_2O=j0fT;y#>nNBU5DjCS{Eo z>7bn*hqSW>PAmDbZcQGaz=z=$lmR$OJWM~a{CH{YW>u{NAOHa{ zuaaaybfIhZiTn2skoAFRfy|k+=l$fpp4*wHK!+VK?(e_V(#pj47c41w;M-V)z}}Q^ zH13^VMcr^ufmhX@aLDhkok3b{#ygPqHQ?8O|Gqr)18&D_&O};&^zA>m_|7Tduu$)k z{uf$Klkzp>y(j{l1N{p*f8EpJfb<<$et+?w(D4kBIy2A&X`iLi@z6%KkZ9pkX}IF;Z}72_q*3F08Kn$drta$QvPX)d4TA1I8WGV z!+v}0eZqNgZ2hg#r++WN81Zwe@j0N;%Lu6}kX+7IYvMgI(+A_nL;rTgqi<&hAIB?3 zIsVpb{&a|+={4ptD_Jj|-=qJJ4>yA}I$!Yo69Y5%1p)g`&nHCYznjRq3N*o?+XW2W zKfZFDI-v8>?bJN_cN3bYTNkTR&A+diA3)*)cO|0#p!h%S#3=$zcIuS}(fzT(>%=(sNb$MZRS6ijH zvK@+y2n{WFf=~Jh$46|TaDysu1HCHCskxW36o_bxEEF|0aLS-Eq$Y8pGQRPZSi{KiuX?sw0ky|VQ3ZbZT>vH-)+L68%^o<%BRYlh-_eH&Pv8(eRc08_ z$nj3{iE86XFq0xG$@^wX6Fb0@UjT!yXNef#Ii3B_3`Zt~7S5kw#j!mo2)l!WgJXLE zm_m4hPY~=g&+0z=6Y{OAtLu}Hy#M@d_m(>#Tc!KOrE5WMCR*P}D)YKsACt`-RS;zz zU}-j{S*eaUe6Yh1@jL5+h2Ku0c0_-z3ZB%FMdn>Fr0p70FfdB63tq3+wD|5p z5(>8FGRYTvuw@(Q_YbLAMLCg;jjI#!i~FmCyO>e%S2%C%J-HCZa2dFSi}f3A)5=M2 zJu@2 za`};+T`hkqqfXTwTrQZ|z@B{F{o<&gqVf%boA%%B2A|xo!)#S9uHNquck|Kk!Tvxj zF62ap9FX4{IwQZghQMXnVy0b+D2~b{EOIOy53Y}PZKvLq$(7MA)a5Da_0Z+u;3R^I z5?rljemF#0BPp49!pV%-y0QwAJLB&iPM)%b$-FG^U8B8r=!Bt114JY+SnJmx_9XnWi(3gWKHCk^jrx|ZJ0Z%$HI|Q*k za1z;CL?L^0HkCrZej%uxNSdW#^;eL2$@wqKxSo6a-1U{Z1R!GCmPEC|E1#~P#TREj zX*<=FH{8p*15(LlBuOkqEZ_O_B-TxA_A-bljjEbe*12w(zH4|-We-^F*!8W{-aIaknc&=h38}jY7|HsZfUU4?wSSiQJ7&6yd{gla(0zQ@* zjQ=RjB1*D)ezLG+y)sAR1q|mMSOtpxOC9 zJd)=f}?K5JB_ho}Q8Xxo|#0O9~y=gl!6e_3zm)`F+% zlAb7_QJ-#_syD-Ybi&pd z`BTLrFYA-a0HT8P03Q0sH{><;s`02>Xb*S_ZxpX`GJ6s1fXdVBaQ0_u6A9B#77D>K zW#+GuQ>y2vZg4?7u^(nv|L~T|>Ds*#Niq6Mn*Oo31nbLD<#hPilj&FbUGJ!7qHgxw zw`5+7m9j{s>p_;Aq)$~CkH4WaM|)Rq`%BTZ&v*u6J`uh~Cr2nyuTfr2^h&%a>9O%C zUl8v&_roEl-u%l{At%Hf%Q>2#-46-2NRGxp2XUg$b=V*j&QbCGO9#mX72^l^a8p^1 z76nPjF|>Ij6Z4nY+q$?Kyh#~=VUeJ8F0??E*apGVFdO(>lR539lLPj4vE zp-0a8@R1A%G8*(Y7Mo^V&X5S`{CnMJ2{LE=VrYRK8(RfDh`#)ipIWrnn|C!bl@I5; zvo!&@{*D4J;D@xNmLiP^xp!%Wzo}8oe%qIh!qN)rxTdL`SO{fG&@(G9@za}4_;?%= z$nSJVg@co`?5^&qpLbaOw$mn>^GLw)Zvf%nvuz>$v``wbC3pZDHsba2x@O-z!=m#ZuQEE_( zTMDHr;fS4F{(799j~Cx)n=q7lH|3&orTG^z`J{;cOs);P2|xrCmfT(UF0X>N^L zuhoDjMmsj=3${g0>2nQAMN%x8Fa71uj73wAX(S3!hS9Llr}a8i-QEWZ295QLF{o^q zXO*7%3Y#5Z=C_ol`DS+~(W`iwB7_9LqD9~%Z*^_g@6&g4iVi>uyc*`~IjZ}cw$E^4 z4U!DgGcKLN#Y&~?GN6Gi<^C>v#G-#>6^glT>4tU^l{TChvHZa zj`3@yDND-2_7nY}ki*qSKN3krH>?a2Ovl2mG>5-{TFIn&Pe^)fV(9yMpdx~plD103 zCVmwS?1v{0_gX*lJfN1RyTYht(~aYN4n6YmbU&%VT8!It%lj}_@NpL9DliLFo)9fQ z#@lYpVE*vHWoNRG+rCmk7k{*y*I58s;E%9Vr=sT$yHPjZQh13_?`m2S*)NdAz9p3R z6umlyP%fCXg6g38H5f4N^y4-oUc&S`9L?as1lu#+`s-;@BXUTNZJ&DNDcnIu4 zch)6fRk^N=yhR7omE6dxj#k=Nx4M1c6jhxCOKi=`nL|tsP<5U7{fImL5{Y~wBV1S0 z&(=t2C^L~ibRW3J8(ro8C`O*2(`4<-7m+V{$Rp~lvgrh#m*sXv8FA_}TL$Xj?8|4) z2n;Jcd!p$%QF;&u(lA0UqY2KQkh)=~2Ea*|iys`+ALl)Lbg+eTReagrP!FSDTVKR4 zFF}J>?M#VRXqgLwnjbi+jx{2UW}gwUwEKYoa7ZsnlMEL8Op{Ae2 zR-_iV`B%+v3fUA7s@}xis-fy$neG7})8eW>fBAAT)+{O(wmwp~*qJ*N&02P7bdcCP`=FK5ANDT=X=Z$SJ%>FAu|ZQoHV{Eje8Gkkn*} z;3-Vs9FhYW$6cp#iwW)|JF$oS9f$(z4ecDc{jhj`J9itrR25BmTZHR6C#lNqiE zng$2&xOkpUBvB!oDHe>^BBRAjqd(dHNUY}@9g_H&F#JCw3VwBAqOP01zm9TID&1L zlp3z81jWnU!VavQP!mzQM-WVSpAn8H z2f1>3ac2P+xoO6Yd_yztz0}tW_6X2dXOiJldX_+fzV>ixx--D&!lAA&%uW)+=px5l zOA44qe_HxW*8t(%ACTzjf3*E!Kz|vg^C=y5J9#ep4`K!=XioqVM)79;kL>Z6Vb%i7 zB&`mT&G2u=#thJ{b0rRcOyNJg?eY}RME{iJ5aqwQSs1Vq`j0&ZoPs~Co^nFq>KStx zr|{C7m!+Cy0AGhT`qr)gP`x{xr@l@H&DgWdD;ok=xLWV{Hes)v`Z`HW6ov2pUSk4a z>@4e&SN@}2U=q>X1&p0HwGkix&DqU>OX<}s{_yvgfBm%l25==bgWnzjeCQvoJm!=t zb{l!e{m=KLQd$8PUJ+B6ar<9HwMiFfqKwA+-&z2-EWlG+omnaV?Y{K_q&Bg_ko;eN z&HN$Iaoky)78i&vJgbvJF?ugVe2$j&6!6xEy~G3`pmFG6rB;Ao``x=HZ~eWLogVmg zb4S1U!Uh?|&|6W$sP3pbTU;Cq8|x`bpW(x%u@3+l$ol<>lMbP+dL2Q#DHc1JWKr|M zY7A0=2j-kBb}|RP`Km4ZCpJRzO^{dligZqXzO}uuOX7+qOuigeXeEnm%-Rorrq8BQ z{Sv_0juv*)(OS^F1OQK|)X_=yEDra)qYjhRP90aXQ=DlN zYnf3@CMC)qw4LisKwf*B{$wQ^~y@ZLGzV&rda#vKuc^_9;Mpj1W zr`vZIkYHS)*nWq@RuvR77u2c5v$p0dhT|xq63LLGGrKGbUF8OOE0c*!T zO7yvH-Hs~|YKhRcu-M%u4jpr%WDvQpcD(bjZWE0t@8n+ z`~Res9y-kaF^ZIAOk{$+TpG6ZfGCnx&0o7K`enU!YW>6Iidr7G-p2fH>l$2T-QshL z&kIZC(O48B+N!7?BysrGuiM}OTg*(37aN3o77#PIWLjkgpgJ=mfI>04>BT^iB;sEC z3UJlj5gQSkqLmZsKTmuXKN#Yt1REb5aL`3c_L>f2M&tL)h99$X7ktZzS-%$7l(S*1 z8*HS?yDy$1c?8Pmt*HPboa!1iPF5;@Q&gODnmaFMb68C~UsoIaZS0o_b(s~I`RW5# zG>OlW(BFIh=3P*Jkh18mIuM$I0#9L&BkKY}vl%SLwaApS{tE?FW~YH%-V?`pn^HK* zJHSoB7K@zPRDwV~Wx%J3NJ!ukg6x2;Q>tBCEXVcB%yNQ|>CofVk<2!wP%<=n>`Y0aVNo5~zMu`ZTN%Y!E+dH!klQp*k6wRU zkgS2*Q)aIJW50nMJ4;*n`Zo5iZmg1hTOyQAd6IW$Yx!x$Gs_lzvzC2|G&b&hO?~aO z2bmsRC^*Tj;I!ILjxUP(=}ZXolRiyrl$zo#J!_hY&nYQdTFwp>!p@_aSOTMY%O&G# zG}?P3%+MCaZpViXSmRDMSK}HhXkn>2_D)WA_NNt15ESiM)2M%99={KdRN}2ymJPmP zvo(segmLxbf9-{SE2moT@6C1gj{R8BMn$LjT_8{u+y9Zj*#Mb;%VJ7II^>r4EwMUt zu)002DU}%A^43sJS2xz+ar&ui#zf0ov+0=m7UD2#A9(`$kLT6;Mg!4$k9iEpY&|Pl zAM!DH!1D06dSSA=e}iejWXM3?7dKf?z!Af-W07O z4eYNMmq@0+HMv?GZZ++hr*ei~USvY#G-0+|jg6SS%l|ZQ(4Z*EA(-otANcX$V`3A* z{i^8ox`R-VDDqgvz~iT}6JixEm_2+&|JQCVwSNi^AWZ7gB$w?k_i1sf#TXU@#42W= z)k}z#8?A8CDMP(aQ!P0(v95_aoq2Ick>9>(Lc~(Fp+t@gdPp=eC#x1%FOB`vB6117!ysa0gJ6*cQINy^0n!(G{a9(CIx@d z2DGQC`nN23)kQh~laWpRjX6NM+)c7+4C!Lv6y1t_ii8e+Bvzn3&w&@Sr`!k}vTK0y z4Rq!}h(;$Wo$`*iQ*lJR*k3yNEVW-5*yJeVyCyZTJ7;sJa6Vb{ei1;)^a%6moBSxx z)CSSrioM|jJtqHR*$s~0~a7qs^x4YOXXh1vw_Zt^D##T>1E?JqG6zd`XbqPxKr@Y*r~ zuZ?VZH^e=$3$uoDJ}U%kbfW;;z z7mu5`BTk`1%fumo38lO#cIS}Luca&?P>EN?Q=6MDhZq>QedXM&Aj182g)ZZXKKFxV zEI$Ew>M{AseNg<90+kfEv1!jJ8*TDeOAf~WS9{+X)ztESeMP_mTnix5M5zMOivm&< zkRBum7`jNW(t>mm6_wt53%x777tu@ay+);%fRrRs0{;n?pWM&yTJM+32UgYuCUfRY zX7+xby`Q71lw_m1RQoHXHF=xnQ5q~;M$YVRIQKMSXRT^0KFmUsdGgV{4_9z?TaG;h z3*J4-9fL!{CzozYx_+=dSSs=^C8_Up$8uHurZuBTlc>XF@uK;DoR8zm-c-zPm%B?4 z8Lu~98un;iv>)Bah>et5$x}k%Z0FoJiu6nut!^TFYn^SVR;MkJXtB=#>1A=ptO*BC zT)ygdm^mWo(xijHib#C-WtdPOj`?hV;O7%sEH@{y5=C1c(ls4ltsX;t}DU1r+hB>(v893raVB8+|904t& zGR1`o3JUN@`MPt(>|r`PT_^_gt)qZ1=2DEDcrEql+)_nQ`?BxF#H2S1IVq;?v%`{Z zx>^@mj*zBIgOr<}F7@r}ssQ^R)FWOU2|?lndIwmOdNgQO7;To%ZQ9P=%sPxNtofwe zY;DFc5o%oD*sE6hL|sE;fUxb$a9Zll)NTvvsdQ1tkA#y78-UJEpT9xGZz2q!Ue#aPBXnc-rC2~=!ik&NoJD~@bQEs#N5bH$O${2d>zOf_|{MS z8d~XXpXFuC98o=XHcp75tBGs4P>5ksn>qXC6NDcqd!>pr6s<(OmSY|~(0F~oaP4$Ip`T}e%FEM>RO4n6 z>vSaoadFEf=di0~Ava08q*7Q;FAl2E_x;b83z9g=?%P|c>MYOeGQ%=-3D2L_k;)R} zbr!G|hWy$dlFu$KBxUNR0jWur9ck`y{miX1=T}9Ro{%2LGdTdysEP$y{+6^KAr%Nf zE)Y?=<60yS4uF#eg3`wS05c$R91eO2|GVXn=p`6VG5|z7r8C(-#o9mr<1;hpA-x#{ zpMQ-QylTwC!z9$L|Azbjbaok;dnMSkh%xjOsr}c9f82BzV90P`asW52hv*_X`E1xm zbQ{;mGE35+9Mm3{Lf@UJnQ?%w57LH_>Xp}t!g?$Jq5 zn}dov!+0_Z)no~qVC;&Jl*5E`2yl@pA(LL&$)!r6vQtZ4CWk~I8^K@fq3mO)ta^A}|5$(81rdv|KyJCM+l75F? z)bItW#&h1&;rr+psrQ8qb>B&;Z|UyFV_#>6&#{?c#Ow$=0$-@Ud*>!RId0Hn(KNMt zAmWiO<4__d--1arcCke+xkX)QMjAxli z3#)>vLXny-vxgs8-6ajmJL;co^Ebxx^0U~YjTs^Vrk7q zcbK4GQDW@We2rB$=KU#f1V&x2G#pkJ)dsY1vs0?`Z6Q9*tH|7W`|5fvNiILU?dl`q zFQ%Wi&CBGyp_M?swCd?TM>$t)GzA^ACSXtz71elW79A0<)-W)CGT(Lt0yHY}kJM5V z_V-IKpc z({v*1Hx#!RiOc9nqC-v0h3YOS$Vz+Gl^Nza3GWHpOk9@>ic(w|riObzk0r=UlZoT+ zl%VHwQqrGb&~DJ9dGc~lwMZ?w=!#=5r~tjSVpLB!3agw7c!(Na75=8IpbUc7hfg>9 zp2>J~bVZ-RxhI}MoSeJY&URm4uVHH3U__)08yRthziAp&`9dB7$}lgSpmE|ih_W7gKUOcnl_lFD`M|9=h;GIG&U%iDl8S&qJ6(nAa!tKf15M-{ z5q^J>2ImLg;Fut#&qK;y_jJwpP*HkV--Y}>=@tc}y9n#i;%V|X#IIdV(+l+`;KHMA zygoCbX>yhc?z2xVzM-3%Pob{MKapd)S*wdgUCdxLCt*(19~Z%{E^4dAOw*Uy zl?UG9BVDQur>{>Nq{D1D#G#g%H|B};MGt~5F=Q!Lmm!xX8~KFJFr$*;WniHm^zmFK zP{?`#=N-tJ>W?j=?4AYsWE~hCK3-v@0d_h~_ar#ed0zriXUFDwXDCeVTAlkgnr>C) zvS+IKvx+`KKT*+v0;O{b5amLxcdN5!VD)f|x|U|~^K0m4E(6I2E4TnQQEc5cIHD+>(@(x>`ERLSZLvw+|1#ine(IJDp;Rvt3MfDo7xMEycQAW|q za`O1j-IG=0El-cY#d#3`>RMq?5W1Dgc_sn>Qes1Eyk0L}{>mh)Jw1{-XEUxEzR0@H z<-zu}o>;7n8hy%LzXKgtlv8A&35Wh2Mfa}BTF+TDm&s>dqI}A9+|tM>%t;u#x3vAq zTme)B*%EW!OlM|GM)f!ec=9Gtpd@U8+-G=uaL};axFjUhYw3%S#bJ6}-xsNV8y>7X z@#f0*NP%?BZ^02@2RM!d5P2Vh+T{vzgqOT1_GbR=^SsN88{d>8S+ADdf#jiH+5D(4N#DOELF5KxyL6q zT>oOm>L!CH57D%`TThzpO0Wbmb4Xl&O99SD^cGH&yi6@^&GbE@IfDffy}D%k%>%sT?v zLvY!FBBgMRuHFyM8lN<1wc2M%xGS)E^tU{urrlhkx)9jCz$?O<#As^k=x!cs6OfD9W|_-KT5fCb4Rt`##7$+N5N0@>n*~uC?kv z9%smx7pKMFtcfajKi70AvnsEDRG^k?bvJ}bhKx%s)q94zr%>|(=&#g>ylCQn6#=`l zcu{S-hv&1|JtxN*vZDv=^lBF_Zl&zxcit$8QGAm>&GHj!3bA=`unChxD zb7yiho~ZhGe!Tm+G8$<+Vy7s{gJwD^R8jET6Ps4%H3-t%2;HkJ#=APwxp8q%;cgG% zD|8}GtA^5VH=Kfkw3m#+A|o}DHb=T65)zCxQTf?*d$o18%PV=ot^p|z5^ZC8Tz#__ z?&7a$U;!nxU8klRiLk8D*;~oWmpFXadc&|}n((QD!W*dC7tgb1=CMAZosC;NcY{T2 z6Hk!fK{OfAzJ|Ux&TE-Ac+x(n5wU4 z)_XnnjN2pof?qOGbc?dfxPe|N)glJl5Ur8D9KMmFyxSOj*p*8Ur1dkfyr>`0Jmmtx zN~a*QxF?x!?6nb%hoz%ILlfnK41m>F&jR%YB85Dg;79PWkK?}e+868Y!O0Q3?s7-G znI`2ufmt+xm9;{`Z$7<3|{JWk;lLEM*0L`m^ClRP2_2%ur$v8;an_Be$LB?rVV zf#pXS`uAIEKy^VSq<2U5Pj2y-Pb*+OvfW`}$6K@U+%T>%h4C1bk(vcl#%QMSFTu(n z-u$4Vb^pRK+H%za%uy&v4j*r|#Ls#rXEu;h{2m%OqyD!~{@E%-@J3CYoTJ~+N586$ z#-lKrykdP~jfK|r&(U6eO9&$Di0%}QKYM&8m5v`Qi-O4URto&}s+rxVJU%Qaez0Nb zbjMrY;s={8kbU;}VA;V9l~;iNeY{l`za|E$9}pcQkgp@a4NYfeKVC)YH6!Ta`Z8(m zzt#jWPw+s)BK{Qu2nAhGI1_99YfZ3#8_KTGe!LZd2mEhEeRKbqNASG}1%40z2n~+DJl#&{>y1@L4S%bHH`drh{rRVTqAkeu_%QTf-`w?fDHIsbSJwWn*NPBXtIQ zL8aZTA##T+R8SafPWv7dikS46vEc_rOX6KRFkGAahwhq?q}vt-Nc3D$8_+kbV%j#< zi}MV#YK2!Q*{8K`B#JqQGI;FCIqT2dY}kuj#nc;B0NQ{O#2fL7p|Q*LNqtn|ZNe7dCYdXVGtIa&|+KO;~fm-r4-&gVrL5WtYdF!&E zUdU`xO+EX${i3~)4C^ERCn4NoDNAWK>u{G`#G!IK;qj_DH9<2mS#(XqzO#ao0-5l2v66xSS|P|Q zvMby@`)Ji-H(c)L_YaZsii%+`UdX%&R^p{tYnnEpGT^oeTZ9XRHMs> zR&+%NYCVsz>q@q{7dsukU8?EO_cFS5WPLa4_I?M0lG=v;PH^%;O_$41^+e5eGq_ZIAe4; z$^JrD&ikVyVq&i1_At@Drs*ZKy?2%dYKqdCvnq?XdGGuKlohkBp->U{eoXB_kCw?m zl!{Py9zT*INmPTI#ZSq2TFj9}AT7{owYLvK$t4D3-AzNXYR1){sr}oGCWkoIXRAA6 z?n?SA&3A!0J%j85vuIe68%-CfctuqOP_!zP?H~g>ib9q$$xPMzb(ZQ|S-7x14C2P# znR>$F55JA>+h2U@Ii+C`VP&Ro7OYbLou68Kd6H-3Ia@LCKY}AQpCHzz*!b%xiAhHg z7qqlN{w)R-i7%8VL;lUAyv=&JT6Xhmz^W`%K(QkZK18}S_{mi?{7Nz$P3n4}>K)jx zdB5P*S>U~jGcIipIm!AI=w|doKy@Qvn})`UcxG-d_vwUnZ4-%0p!%0Ki(~t#^DGuW zKQiT*2cK1y%opN@U4Be3!{LhS@@{P*kvzL9vEUL$O_YB+7NhpS`XKF**Rm2Ya)yEU zA?^2}XtqflPC0}oImECbP`BbyBHgV@$$Z9o-WiUuY8!v+@7A6V7ld5qE2ui7?It~o z7PDs=4tp4^pSkpX^w02%joTR%I22YPe&c)2stm*TlmEGt~e z5OMRPewMl)q-n*Of56dYot?Da6zG`%1gIv|j_0WwvDIaA$F4=4z-?O3VOF^!28OaS zB6oBjSk^b%oOxqd#IlW0qGeU7YGrAOZ4T#-mwC6K&~6ax24Ck=>Wp}?pue9-ztcBv zH(5JZy0=LGgrk>ON^2d_5Ltf-@U=WfP8hQY~e#T_C1rF z_}sKkWsAB5|Blkt3Bs#IbvwCa+l5tMwZ~wUN}BoltW17$O2LIwb7fX-5izJeAbfHU2z^jAt4kFxnqQGd56@qD#^JcGXZ~)aO5MyNGKrf_H8Jd* zq?dsNucW>@rvS@_9}-tVGv0GdM60DM&b-L z*9XKNII!LtjFj*CE6Og6*uy{C!*cCq?cl+w-IFD}D(M8`vgXkPkl{iD`t9q&SQut~ zpr?NLnReD*X_qA@i)mjPvjDWP2wnH2a{I|QI^iFupHfPp6wiI>M9%@}#U_2FB?Uit z?bfhZn50*!bdZ4GrOpjZQ*YT-ewj!qpPhNj6zbSf%lUM>iK?jpOhW9mmi2t*il=@Y zaZJVvN!Q@ZhssncwKA{b{<3M?{IuDhcgXa3v!y+7#H$SoPLIM)PqQFp0j!k@zxqk% zkJdpj^Njj*l9#u4w%1KQz6<4_>(3D?_N@LkLmV7IFX^T0wb|-De7&fi$}Jh`R>GeF z;o1h1yWKowO4My{nu}o7t2J}?qHcW3yJ&t22U_~hY{F? z5r)>KQ(=dDvj)mZ*lzE+N~3U+{E2t>HoLMT3VEyCrWD6)o=d8@cM87Lm@k9O(b)uz z=vUXbab9Afs+>i%nZA!u-n&JJ`&|EcA;_=`4xS7k`EFgX!TIC519{_+(9qWRHu8Q} zV;@?*)p=tW>|H|Ubmh`+youqV0G4*K3<`yQh-1vXvusFCr_XDPz_sxQFMU&LXAsdg zjfqrEKIl+~ny-Zpuov{~BPH7-2Tbv`H!fKGbHOn=uU?KICy=^I%wc;q;Eks#$HKD{ zuAqL(Lri>5yz4w4t@0OA^9@@3ZjvGbRNht)+}2Z+@zxjB0X2aPb;;ALS=Upy-<8^uFMX!{*)RnJa|{hW<#55ryq8j7DZ)&DaPu}RM zsQU)kh?OVayq8$~bR%8K=4z3;gIw$a(b8*UloPVT%Md)K4aJa@ZNK=2jUjJix0?s4 zH{&eWusD*{kf@*Mqkq++vBGr9a!cER291L#spQ$qGkB&N{I$y8bRa*Fl{Z|- z&*W#6)FRj(^n#e5;nq8(GLrt{PtIy3`W_##LmLy zdu+v7>^>Xz(MB9@=fgg|BkEE;*RsEX6z4$h?z*J=!^G`3Wuu2~31!&?>6%9Mj-->6 z?r*+No;Y_X#2-tw?!8^;v^FWb*0A2WwYiB*{#*Dam{gJp*^@BYlqn&vr@i~ z;BZuO2|QSr*qw80#mTry|-3EP<56T4-1gvBUAiQeoip_m+Ghl`C3?AywaM9&}|Om za9bz2#O29hlc;36XwvtcCe+@(6E-ON(kI)bRNq&zFIjfu8&lGDc(s}1D{O7In+Mf9 z#T5e;a^B=L4V36Cw5C3u)(jy{qVOS0gu67oAnB5>?9mHU_aA9|^b!Vh%yEUJsw)cm zJ1#obc69UvC5g@jZ}Zh1EDdO&4+>6YrYz4e$1w@X9(pbLSG0b{s%*LrIoV&Y3@6hGVu=OIe?jGOn z%q;HH7jdwd3lLFc7al&=q!?s?s+{mF*JV!P(Xz;N6*hCI@p2?`Rk7G?Lj=#2sL-y| zer%Esb=|(3@-1qS%AB_0DD$VC2^deus+Kn14Dj(qW8w2exP4-b`HeuCBkjjkyn+H%)F{r*9;ja$Y{9pI@S7wBQqj^DQ#=+Q= ze}3}SUIgK4t3Dl*J6Eo{1?jQ34COGDX2CaqMmT;Bx3?JIQSPO+F)P@VqSc?5e07*e za8w{P>0>;_%B9ld$}V7npqbHF5qCdx<_uS++P@4Luboc>ZgAw~GE4A<8uAPyNss@x jcF?nbp0YtA+73^N%*#-oOk%zM7x+<-g~$}%HGKYm14}2O literal 0 HcmV?d00001