@kennyzhao/cgm 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEPLOY-Linux.md +75 -0
- package/LICENSE +21 -0
- package/README.en.md +220 -0
- package/README.md +220 -2
- package/cli.mjs +68 -0
- package/install.cmd +5 -0
- package/install.ps1 +55 -0
- package/install.sh +66 -0
- package/mcp-config.json +8 -0
- package/mcp.mjs +487 -0
- package/package.json +35 -4
- package/public/img/add-provider.png +0 -0
- package/public/img/index.png +0 -0
- package/public/img/repo-show.png +0 -0
- package/public/img/setting.png +0 -0
- package/public/index.html +892 -0
- package/server.mjs +1144 -0
- package/verify-mcp.mjs +207 -0
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>CodeGraph Manager</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHJlY3Qgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0iTTE2LjQgNS44IDguOCA1LjEgNC40IDEyIDguOCAxOC45IDE2LjQgMTguMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjEuOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PGcgZmlsbD0iIzAwMCI+PGNpcmNsZSBjeD0iMTYuNCIgY3k9IjUuOCIgcj0iMi4yIi8+PGNpcmNsZSBjeD0iOC44IiBjeT0iNS4xIiByPSIyLjIiLz48Y2lyY2xlIGN4PSI0LjQiIGN5PSIxMiIgcj0iMi4yIi8+PGNpcmNsZSBjeD0iOC44IiBjeT0iMTguOSIgcj0iMi4yIi8+PGNpcmNsZSBjeD0iMTYuNCIgY3k9IjE4LjIiIHI9IjIuMiIvPjwvZz48L3N2Zz4=">
|
|
8
|
+
<!-- Tailwind Play CDN — 仅开发/轻量部署使用,生产建议换成 CLI 预构建 -->
|
|
9
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
10
|
+
<script>
|
|
11
|
+
tailwind.config = {
|
|
12
|
+
theme: {
|
|
13
|
+
extend: {
|
|
14
|
+
colors: {
|
|
15
|
+
page: '#000000',
|
|
16
|
+
surface: { DEFAULT: '#000000', elevated: '#0A0A0A', hover: '#111111', active: '#161616' },
|
|
17
|
+
text: { primary: '#FFFFFF', secondary: '#D4D4D4', tertiary: '#A3A3A3', quaternary: '#737373' },
|
|
18
|
+
accent: { DEFAULT: '#FFFFFF', hover: '#E5E5E5', dim: 'rgba(255,255,255,0.08)' },
|
|
19
|
+
success: { DEFAULT: '#7DD3A0', dim: 'rgba(125,211,160,0.10)' },
|
|
20
|
+
error: { DEFAULT: '#FF8A72', dim: 'rgba(255,138,114,0.10)' },
|
|
21
|
+
warning: { DEFAULT: '#E5C07B', dim: 'rgba(229,192,123,0.10)' },
|
|
22
|
+
gitlab: '#0A0A0A', gitee: '#0A0A0A', github: '#0A0A0A',
|
|
23
|
+
},
|
|
24
|
+
fontFamily: {
|
|
25
|
+
sans: ['Geist','Inter','-apple-system','BlinkMacSystemFont','"Segoe UI"','system-ui','sans-serif'],
|
|
26
|
+
mono: ['"Geist Mono"','ui-monospace','"SF Mono"','Menlo','monospace'],
|
|
27
|
+
},
|
|
28
|
+
borderRadius: { xs:'0px', sm:'0px', DEFAULT:'0px', md:'0px', lg:'0px', xl:'0px' },
|
|
29
|
+
spacing: { '11':'48px', },
|
|
30
|
+
maxWidth: { '1240':'1240px' },
|
|
31
|
+
boxShadow: {
|
|
32
|
+
'brand':'none','popover':'0 18px 50px rgba(0,0,0,0.85)','modal':'0 24px 64px rgba(0,0,0,0.9)',
|
|
33
|
+
'toast':'0 12px 30px rgba(0,0,0,0.9)','ops':'none','conn':'none',
|
|
34
|
+
},
|
|
35
|
+
keyframes: {
|
|
36
|
+
fadeIn: { '0%':{opacity:'0',transform:'translateY(6px)'}, '100%':{opacity:'1',transform:'translateY(0)'} },
|
|
37
|
+
pulse: { '0%,100%':{opacity:'1'}, '50%':{opacity:'0.3'} },
|
|
38
|
+
},
|
|
39
|
+
animation: { fadeIn:'fadeIn 220ms cubic-bezier(.2,.6,.4,1)', pulse:'pulse 2s infinite' },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
<style>
|
|
45
|
+
/* Tailwind 无法处理的:全局 focus、伪元素 tick、复杂子选择器 */
|
|
46
|
+
html{font-size:15px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}
|
|
47
|
+
body{font-weight:400;letter-spacing:-0.005em;line-height:1.5}
|
|
48
|
+
::selection{background:#5E6AD2;color:#fff}
|
|
49
|
+
:focus-visible{outline:2px solid #5E6AD2;outline-offset:2px;border-radius:4px}
|
|
50
|
+
/* Checkbox tick — Tailwind after: 做不到 transform rotate 的 tick */
|
|
51
|
+
.radio::after{content:'';position:absolute;left:4px;top:1px;width:5px;height:9px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg);opacity:0;transition:opacity 160ms}
|
|
52
|
+
.radio.checked::after{opacity:1}
|
|
53
|
+
/* Search result radio 边框用 1.5px */
|
|
54
|
+
.search-item .radio{border-width:1.5px}
|
|
55
|
+
/* Form select 下拉箭头 */
|
|
56
|
+
.form-select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23707784' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center}
|
|
57
|
+
/* Provider icon 背景色(Tailwind arbitrary 太啰嗦,这里保留) */
|
|
58
|
+
.provider-icon.gitlab{background:#FC6D26}
|
|
59
|
+
.provider-icon.gitee{background:#C71D23}
|
|
60
|
+
.provider-icon.github{background:#24292F}
|
|
61
|
+
/* Ops 列 sticky + shadow */
|
|
62
|
+
th.ops-h,td.ops{position:sticky;right:0;background:#13161C;box-shadow:-12px 0 12px -10px rgba(0,0,0,0.6)}
|
|
63
|
+
th.ops-h{background:#181C23}
|
|
64
|
+
tr:hover td.ops{background:#1E232C}
|
|
65
|
+
/* 表格列宽 */
|
|
66
|
+
.table th:nth-child(2){width:170px}
|
|
67
|
+
.table th:nth-child(3){width:130px}
|
|
68
|
+
.table th.ops-h{width:460px}
|
|
69
|
+
/* hover 状态下 .repo-msg 文字变亮 */
|
|
70
|
+
tr:hover .repo-msg{color:#A8AEB8}
|
|
71
|
+
/* Toast slide-in */
|
|
72
|
+
.toast{transform:translateX(120%);transition:transform 220ms cubic-bezier(.2,.6,.4,1),border-color 160ms}
|
|
73
|
+
.toast.show{transform:translateX(0)}
|
|
74
|
+
/* Modal overlay backdrop */
|
|
75
|
+
.modal-overlay{background:rgba(0,0,0,0.7);backdrop-filter:blur(2px)}
|
|
76
|
+
.wt-row td{background:rgba(94,106,210,0.03);border-top:1px dashed rgba(255,255,255,0.04)}
|
|
77
|
+
.wt-row td.ops{background:rgba(94,106,210,0.03)}
|
|
78
|
+
</style>
|
|
79
|
+
|
|
80
|
+
<style>
|
|
81
|
+
@font-face{font-family:'Geist';src:url(data:font/woff2;base64,d09GMgABAAAAAHLYABQAAAABAQgAAHJiAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGoYFG4GfHhyHTD9IVkFSh1s/TVZBUjwGYD9TVEFUgTgAhRAvfhEICvZo3iMLhEIAMIGoaAE2AiQDiQAEIAWIGgeLSQwHGyLxFWxXY6CewOalNZfPMyrYrmbcDkTU+r1qNsKGjQMg3hwT/X9SUhlDE9iTFqA63e4PtWQjlBo5goYcMo0ZqbViQ0jvqjjmPNf7mgM3sj6o9UXp+5FQOzPJbOE3Jx5hd/ytpmpjm0pYR9BzruG68ZnXxBVYEMSHMTAwnWpYTZ0wG3f74drkjkO9/6wik1904hQkUpdYDWmJLZFY0pNBEcb7IndF19PSj1kEuE38iIQcX4SK1qgisnr6gFERPc4+OgJHpJCEZJaAxtzpl8/CAc8Rzfk/exe9hCSEEEII4fAAIZilNJgFLUVSVAJFvJRHwV/VaP29R8UpddoiNaXiVOXz+nnW8vzTuOd/7TP3wUxgI76kJCdlOHSryZ2cIZquptBglVCzlLbxtmnUViK7SXY3ySbepI23Th21In7G/T/8PXD3nBn2TjlRgXtD5X9y8/sLj1BKDapUxtT7bcyW//zF1njpmb9ZiFmfWWVarEkKASLEBdHC8/y9O39WtzE54I1pQBMnULgfMHXC/yed9r1BDQhtGTCKA5+y+w8Yu+u5KTKSj4v2ymuaz5wFYG/AcWQSNAC4Gc+jnNqMoTBjlTBQYlhwJCcyUAFAfd3JRQxiCQASnZhHqyFanw2SSW3yoCX0NbQNST3t3wtitjLDMjrC5AKDqIA84tpO+X06q19d3aKW2DLteMgws7s+RPtdEF0YdkmaA4IIKEPg+LL735x+fyVLjuNAgT8DDtDyE8VJ5/zdX+xoswDARzSdN21KaWKzLNlCv34uhbKnT9eI6JKKPh395+QWaAu0BXoHtIXfybyEh3836/8kJIGEQAIRQoAECxKklEmpQGl9rGsus5371L9E9JqP7HZnzdqZmk8LlCIJoX/k9iCCRPK2rIWh3PYL4J+AWGsmndQZSiFWlgOPgZDfTBA9mlsoDTKxz8xQv/38r2Up7Xf/esMojtKulKeklnaUQvEVkpgOEEUkKHYzvaO5v32jkPtSnpPkmC+vzpmFSM1ChCEkZmZqQ1ED6jKABowZGnBonv//5Ts7529ae2sUMo7ILxzFmpQ23KS7P9QiUaXLCBVnQXUjMR6jHCDqZv81T3ETF1IgRjKJJB6jsPi8Nst2/GZ9wAogFg0Avz5FhfaXZNmjGdM4u2Ovj9m5AFmWtePzAVIVIKiqtCnTXV2t97qtAh1g0Vb5e1PN9j8EYcVLSyr9y//yvwzHWjacAi9Url26i4tdgNhdENICJO8Wq7QAeeeF4gdFn0BS8hHgRVKekUNMdEofIH3zSZ2sZTgfpHMIoWhCyJ2L0k2fq9pFpXFRea4oO5eRnseb+MCsd5v0rcACzGRv+jFSDL5rR3UVmRIi8/WOe9fUUvNIKU12j6zTMTxCIATCUNraqc9r6/omVq3JfU/2LaGEQYYiEoJIENcL3e/m91MrGjK6RrIPCSoSZQkEqSUm9d3XvQOzYiqtAy27c6EL4OPV3wvhCgBLAZ8AzmKjCObOihAGadQMEAWeVGgw8LQnvyyX5FkeJYJ28yAgeri/vhBImAZti/nf7CJ/usjUXBR6sFgmIo65HKSrbQ4BKwHpaBFUHkZoocuC6HIR7791yCyoIEMB/BLgHMA+wHoIUIDAf9gqBByCQYDnn/XrBzD/O7gICECv+OSXH37/oxc+evGj73/+zW8/+S7Nt6adaVIcEx+U7pSelL/zy7u/PqQt6yn9Gv16/Q79PmPOWDSWjWtMN06aaXPeituIHbfn7AXnJudW53bnbuce5wkX2Q/3V/eUffvB6n58v7hfqtjHOxW5EqykKn2V/ip22DscHG5Vhaq3qla1GjZ5dC1Qk2tKLX1gO3rcQbj+ZWvailucozRIw7SfrtelQ7VRa8jMn4WzT9yE5t35SjPZzLZaEi/Iwl88tn2+T5fb7by+OWCaZMNsXY/r43dlQ+1sjtJRY7XFBSme08lAz4WnUReneIGXOMMFFljhyCK8gjyMhSQjmEbIU9AY6dIlzShlFtN87E/81J/5mU/FzUQrmAVZIISPrxB7yUZST5pJO+mG4zAN52EWFiFTllKqQqwTO/ahfXvSnPT76VC5AUU87a+nfHZt3pxPrrOsJLgEIcDFG7lwf2tccvQTuBYACKDzvG7ablGymjetVNpiyi2hbMcNBVFKqMABAATg70VLW0wZVxVWAlmAUF5kCADnUQEOHqWahWkqYG7/a8tRdlwUmQBWZ9ZXlID43IrcYhBflFmlBSsU8E4Z40S9e97iwlsrtCDreF0gubh4fx8QAIKGLg4FvSio1PWEALOnvaKu9ATayqQSofhoeBACBg15iBU0JAs2hAJXV9fgIWBYNwJheiOAUHhFAhVH1sydmDMy41JSr+zsJRlVf6gY0JA2lkFDCGCjSKFiq/nDeGjwYmALWEcAt6sWsHlgRNWqVvw98jV9EDt/t6dJ4t38UWbZvNuC7zRW2/yOyRe4u3cTvMM85eBusFEccQn2k0cMrF+2bUoEc0Q0Cw35AJgLWuBptqK8NQxQJ79YEpIozULEpYRlx2qsRXMKCGV3QHai2S0OIuvP79huwNbY9k24R9KWqqkMEM061+nLuWQQ+Rm4LD/yqVz8plpK2c6Z7My6LEnXwN+csfSmq6E/Tg+OOoqCRcRH5wXQj76iMnJzL3liryh+gbvj6VbX2GODvYalBhta1eus2XcVyYCpygCPfQYhdpwFmPobyf0m0Q5fQFD2zTkYvxXNiI3Q6YbYubiiP79YtvIZGNag1Yzfw25N44dILrWOE/tGy07v+I7wtizXnozyygvx+XZSeGAY+aRs0Ynvt7pxlYldax4X5FlrG9ti3nrGhgxcWjSWT2mo37fRC6+vHq3cNEk9rWHcOUZi63kT9BnlQEKXX/YbzbHSkOe8du6C6aWTzdruMYlJOzhSYteOXvh3LrehC91O2A2R54sy2nJASFgckLmLiZqYZHoE3M551aqGndjctE57MNyLwb1Fawgyk8FeSauiw2xI7LQqYOaw6Z1QRvjO43xSsKfTaX7INVZ2SiQVWHJy2iTsVALOnv/6TlGOyNy6hNlwLTD8Z9ycKHdGvXW/3b5E9MBzDHoV/OxCnfqhwdWAu359A+AywAnAXsBaq50rhdh2RcwWcvtzgVIXcoBSbLaysi1L6dmTBP/S85VPLebSYKs6Pdh8tH4QvB3sApaqm6yJIDhz8B/rPhVTBf+52l19taC8+esGK1ZEoSUvixIU5WYbbDIvQuRKw/9mq7csbalqSiGGo9NBI48FCUopaNIXEhoXw138VzY86FYMucw285OptCEtqlyLWwukwGQNP5O8LzB3y32AUdjLUjRZWp8ZW3M1fb/czl606sIua1m64BrEsuEoidz0rjJA6/IGmTQqxkk7fH3Gr5oz3YEwT9v+jOhj/q2133BJDQg0R9WGeBLX2G5AGRFtAoRoh4eXaZdFhSI8JbHRocrBuzirxt9kXBVQZgiWpkGf2mu0uvMCjh20D/ssaczOzCZ4VW+wD2fc9SKmUyGkqbP4YC6k2goCp+MzAo4Lt32RWJiAqP8DqEaho3qyn6Cb5TR6pUKILZNAaqj1XJA6yOmhUhwRMWT/hP2vjGHAR9fML4cNG0kSCOZtzH9sYwB+KPe/rvdAE5itjuNwyb+ubUQWFj7flxVrSSYVGN0t27wtF3uCy9FcHWW++jEY8Fb5lvXJbbqUDLX3/P46HusWhwuM1t7424JxnZFwTe4341jYJbKJe7VN+OQ4Jvvq8wSXRAucgEN/t2pvr1+CcroGRq7VSUKnMU+X6l7dL6xitWr7GYTKP6m5P7l5KF5dF+wOx9ewPAD9HCw2sOG4j2pvMdD6jhG7HHhaL2xagjYzwTotcqvkJ5hDAdUYnuVhr/Viuo/do6cRrmptPFKBNc+qbXqeJepuWNYf37sd/2RMAItyJmV62GDjqh0u1dTW4h60pdVyb+75Qcor8mV2FXn8faldbosJTJT5mEPQnen8qeMs7Fa6QfdjHgvg1ZbHNP/kLDhG+RVeN1Oo5629XL4z6JZrDcegt5o+kt+0Soh7jL5r+7snuE7qZQtVm8uXM5Yh6un2HIx5OILHs9/GYu3QG/4xyE6redq069CpS7cevY474aRThgwbc94Fl113x133PfTIE89MeOO9KX/52z/+9Z8ffgoKwhAOiO6NALH/0e4smmndCW4eEWojYu3EUAfR00n4ugium4j0EF29Up+TRF+LmCEiyGlCcZbQDRPPJuAsmYbzZBLeJTPwPpmHj+US/2IEvgJJwtdkAr4hSZglEzBHELhI4nCJIDAv450Wx2EdZAq2SAoae3cABn8/NmbzNmdTttNmbLftuDoLerDH7Al73o4VU/AReWkAeznOOzpOGVOudsJO2pJm8vvqQhLCOEAtNSsjFCALMYBEU7dN+t1EY0R0oU5vIHWXObUdi5iq5Jpy/yp/CP1kTiVMt1wNzk8JkaMAEsgkAkRAQ2p2MD0UIAcqQKKrQ7ffnHGIERzuOgX9IgW0juXZQhGNRwuKe+gic9yPXG8HnOveYhkz36GCHrPiUCqrrpXW1i4aGqTYfSSUUH9gBtKvyk+pu6yjTW3bG7mshTEtVemEZSuCOo0CAyrDMKI1/N1QvnRc0NB8USn7tLRhrnrOmL8lk0oK0dFBZ5TBRXOdn0+yBP95o+fIhLiAtksPzdS6ngKbOdKRcZmnXOdaHXcuF2j68D4nj1iMgyapoQOvujO41Gxdjk5O4mR/Z2Qn7khkO8bvbqTKZU156rI2+lxl+DILdOdJ+TftWubGm6e1rpd3FQhqjgGdgcRLpSlo5lPDkdaypvbK206RbLwXO2et96Qd0U+QuC8k6RqKkXFzVsToa/MioW4uDcF1nZgYhbCo9bcs11Glipm3bVy58p42vIziqHNqZLDuD6rbS5o5jwQkQm4R97iMCvIOBCzUGkxI4f0zRt+f8ESIvRjTkyn/+FGovyoydZ9vAx+OeFVm+H5A9diI46htvsJK/eRpygPEPKK2VGtvSKCjvOotlMsI1J0jozpfaZ37G19ya1wj+1xJjaafAJwT1kdVKsy6jg/J9kAXSjiDbr7BKFxz5jJpajawGoorCce5f2bOCq8YGNnbAz0q2c+rla3hEdQqXGU0tT3Ztwm1QHMsY051861utJY5Mnm17QovFnTmRVCHDp1RJciOzlVYyxbtRxB4RdI+K3R2DsmqhDepv0jNkvAK6Iww4BIujfT7HqtA99av+RBeN+odHZqnxW5gRwlL4xGqr22Z2eBpcp7jLcwAbDBGo+soapNJg++5l9mH5xLD4jbaaZGCUq+9pMp3G7YvlzeaOYKLgISPfISwRysrsj4F1z73hyxzNABOjYken+yiDj5YnurFBWI02F61pRuf0GAESga1QgdeC0upyUWMIrjhRw2QJZyXIfA0ptobkw/rYsYcX2kUjwEuzm0WBic6iuXz3vWYmBie0oALBc5RjNgLvhkIHwR7bDyeNDNekUmpWYvLAhzF+3hK6Lj2uGcmk3xxKqCuIYOBTYBL7VZljrMl8p7zOVk/xwVWEhKq7M0Rj5Tel6LwJDESWGES4DhXsC1+FVAbSaj3L5YiYuohdJalyYWRS07cIAEG9hnMZz2OxSHSaP87LZU6f9f0aGWMa4bwrVk1gxNBWeg9bmN+yLkthL/EiGh01n5HXZr4BUbVHpvtpDivtb40D//FHIqTYeDRZnMQs2xsKx7nYsQwhcExKWEnHDPejyPWp6HL1ei7nqQFVYuLNh4jInTm85tVr9X6xjq/rC/ZV2X0aPWQ/pbxUWr0ES8d4ODau2d9dcU17qmaBR141Z3ABZDgCKDrKT1LeEfCuxAfTjObB5+mlyZ45t8xkItazLxzb/7mdbGWjTFhm/WsT9cQ+njXJMje1a8GCE4F8FgAZkLFeG/6bBZjXhjq4qdrKnQuSk9eMdNKf9BtRfW/M5loPAgG7UicUls1J7OMDi+afUcjOJ0pedfOIz5S3DOXfKO1m4xk7N+4zZ6cdEYEtcBQOsFLXyiXmv5Nz0OGfbo5MUc4VnBQowsij+KnrMWkZNFkvOusfaHBKbpunmwXdJrLpkcGf9yXDles6xbjeQQF1hlurdgl/j+3OY8axbkuvTuld6G7c04VO2ZZ3cUF1Do6kWDT5iuhBNAxV6/u0jWtNXfocZqjcT3iPqyGWmJ54bpRFea3ZEQfkq0b9ybqq3dhZfBRriuhMcVKeLslCzR0ur93Vnrw4I1+snvs5del/7W/UR7bMaetU2x1BpB4/0n4mio8RmjjAKNWMebaC3kMvb9K6+vKnAufB296rlBc20KnBxosjW2IHn1F9Fy5oXtne4400lGrcODR2ncPGVgzqBU68NKskC18ZqlkDbR4odZ2CAfw6wi/1QB66Yd4wujb7PsJ1IzXORoe4ntOucz1Du8I3dJDnzVw6c4Z9tk7eub/tJWiozCOfK1gZ6SwfsX+a/vOuVI+AGIa81fNFK5VXodjdWtmPf2tUX+dhcq/zsQ08PW1oS14YKVBxi2toT20Od79BFjrq1eNpBLWQrqPeArURFYg42gE6sWvMB5de03hyvBfOj7/ls39qYeB8N/zcSvBzYMWhtZfn7o5598XqtJvt9DNqsr/kOiFKB19/eBQIgvg598LTeotJ9Hb/DVH9tpbC6QWe38Esgn0DOU/XpyT/oS8VKJX1SFExdaHcwcHUlK8T4t+fKtecx03pNvwPlO1BcamFzrvskWuTy93xz0rPJhe7ZEJa7xpG73/+iZTwWEzzO83mp035rQLOdOgj8JyaowooDWqzXCqGsyWUafJAsN1r53gcp1sM+p9klurkweeuivfY3eyfgQIgIeoCsENS7wxmClUglY2icpp6+Cg2joC/sMVBY11BVQ6LmNb6rfwKq/cs8zt2gH8rjpQzzOfZBU1dPW/5VrWxVrH3O89kOWZJ+qcc0qF87RL4rKrKqd9z6NLXHMJc6yPjWJTN4r/iFM80GJXBbThkmpLAzsdvRBGFUPFPVpvniab7M2SLcapxx5FaEnrZUCaZT3MQVRtNgvYWg0k18KL6P9HJECYaAgEhIUNp0MHwsGB4xJA9OnDCRlBJIyJSJlgIZlBzNnC2ZFD7NnDKSggDhzgHDnicOKGwZ0HNk+edHnxpsOHDyGlABSBAiFBomDU1PiiRcPEmAlJkkIgVSpEQ0PfLOmoMuTDFChAVagQUqQIplgxqhIlMKVKIVpaXGUq0VWpxlSjhp5a9ZgaNDDQaA6+Zs245moh1aqV2DzzOGjTxkW7ds46dJDr1MlOly5i3bo56dHDVq9eCsedwHPSKYaGDOE57TQjZ51laswYa+edZ+mCC2QuusjEJZeQLrvM3g03OLrjLp577jF3330WHnjA3EMPWXjkEYnHHjP2xBMSTz1l7JkJZt54w8p779mYMsXVX8GBgImFMNAwMdERCDpY2Hh06MBxcBjj0kMnIGBDnz6CkBGMhDEmKSkOEyaESGYw5mzgbNmis2NHSq6Jq/XoXs3EwY3BM39UIfuqGhgFzcWgiIURoBZFeqj4chNBGAQYMVzgZqSpYeYUNRExJ2HGnRsHCi6c2ZPQJy59J2QVuwW66Sr6WSYsWJajQ5YT0C+Z3CUTGskUyZQ1W3Jm32NBFAIBS3PJc/Lpl9ef1lEzNlu2/L0WFYokQviYCFw8uvj02FJwpJaqWKMz/yt2CeP+UBwe8G3MHzcCOfa/aWi7j8AoRr5AV2r7wdG8jJQ/PuqGiMnbywX1UYPgJESoMOEiRIoSLUOmLNly5MqTr0jJiNnMpo0jbiPiXQS3gXADAAAAALCycXuyAAd6084t/bMQBdCVSxPImfy/+o4SBP9vxzGOjcLxeRKpZEnKfBr3fkMeS/8/l8Y0DXNwOEUNMSJapbeBuTaGBPvrfNX8F/m+w1GFvvWGIBJStvslHC1we9zllP1+KZcOiQycKY7yI9pRcRHLf6/vxOz7aOjZYUzyxtTjlJECmJOzVi7+nTjz5tJyqDtLW1YKFcci5VUouKgwPxP64zCWgGYh8bWYS6pLKyD8sm4GgVfhwNc7/zJwMdFR4NGt2lyWTh6iMovRt+dXRuxpjsC77nnGZvultSrlL+Sdh4YNsELBgMBNtjVL0gbwh1CQRHgIz2EuewHfJJhXcdUPL1H05KtwQhwMx2B+OMrTkTABqjZ3vV44gRBzjGZD1xKrcaOqJVZN68fPBOw8dSsi+/O0zsEgv1oOaPj+FBogrBBwCB+ikLgLYZdX2AA4SMr57kZ/wGv7q1UPqILzwveeEYIiKUx/x4jt6PcjxlJnCpqQiWgsTIUsJxFTBPEEyWyiTBQXP9UCPVNCNLSKMxybrSMwzkVMKgsDOWlIUzsovbwHFWZRikzTE8Tq5GLULQhPoKWgAdlYSdbwVsywW5tA4xPbGkBJEg0zyR/jT7Ma5DyAHHdhufErrwpJI+75thxcNMb8EJP13tJLMDsiqPyhPPlzLg9y87iYwE1PZ6np8IfxCBscGsbK8uYIMWttiGj5xywAlwsgJqV/Bh/uAsqj0xYh1PJge9nCWUvEhjwm+Kz+mTLDQHREQzjY0pMolZMqFTQyaClUKlc/+DvPJDdbmSSFEqQplkC9Pld0GILjaqKa9yLZMGAeBhgFSH4ICTc3RSBQQh8SKWoABtzAWmUTDEEOtvrCQkxwvJKnUVmqGdoaAihyNL/nDGJBJwhMML9ZhFI0SeegVh1d8eI+S+drmUqJDXiG7oqaruSr/a6YppEp4ssQAp4LDrgWsA2w/A/+nvHNzLvAae7Nh+14xMJHDT+h62lHn9f/Dux8D65+CCOflfHncPSr/0v3ZxwBzwEC8JZdHTXRbe56zHt44Aj+9Phx51/df6NOD4/YRnVj2NjQueyFJVfMV/uvtV+zXgtcZ35QdUN6S3/LeAu/rRznjgvHpeO6O+DTsLtVd/l3hXc9d4P3Ku/p7ssfFH8FP3Q+an4UfeJ90v+08Dv2s2vPGp9xn41NMH+onmiccE5MPi/8uexl9UvpS/lL02vpa/1rw2vza+i1+3XP69E38AjOD8/GtHu1N3670fjWf+bNW82Hzc/++7D9uHO/89KF8Yv5/YmLN14Sq6XqyctTV45due7KXVfFg56DE9eI9DUNf6PUmLpx1e2+drk9pCf0Qf3+e+GPBh4ClpNRDgwGoG+e5T/32vxv8/0Xf6/S/ohuJ+vGjqN1sAsAPHxeftXaYA0wfwErM6g4HW5h0EcCpYLcBRXk7iQBXB5fvafgHF/G5GlwuyPu8jG75oiASBhCLuTjH///ArQtziGBlwFkYkYBNiOgtfZ8gOUIwHbYtbcXVCEA9I9mMeK+rwIkOU+hEmQoUacNAuBkckC6YirnNkxX/hzbEcsiIM9yG2Y76VbMTdJ150tWLCyydXG13XFBsFgDPaRCerlXYyulmr7STiFRSDjw0mynn+bIyficymhqoSqCU3Ambr5ZQIpJKWlGWpEupDcZSO5cNTNdvLp0wP/pwGaOvG20uzgSdxLgDWecokeKSMmFO5NeXVcY/yGc9InA/2fSkHn+j3kA/33fMQDfP97lnlRZsoGzbz6pYkeZc4AoAdd5agqADzFo9V4eCO92AbcmfeO0Z875MxR4Y8J1N4z5ywmXHXfeSaf85D8/nPUaQmDRwSWgT8iAhJQJkilzduwpOHDkxJ0HT1583HbBeI7BtWRQChQkRBi1aDFiJUmlMUuaDAWKFCuhVaZKtRq1Gtz01a1EYNAZ//N/k6Z88y5LvDfXo0ThQ67iUwA+tS+G/OKtkVzNHi0e22mHXYbRYCgYqOiY2PTw6OIzImJIjMOMNQuWbFj5h4wbZy5ceZMrpTKNL3/T+QkQLEq4CJFmipdghlDpcmTKkifbv3JVKjdbhTqF6tnKz1zE44GH7rjnvrsQYDEMIsBEdCEJctSXvvbyzWFnMeESO0WhCuw0RepFnzMUi492lhL6V8A5SrXOvnaeZBfCLtBM7RzYRVqoF4tdsgl1Z9F5cL65Rf1nDyw8F1h+JMCwEqhXAaDB+riX8HIz6UgSoaq86q6GxqUyATQaHhsLoGk6alKN1cjE1a2cukzw42JQ02px9aXmOnHy0evjUSUjqj52pav6VMUaFpIWr0O5HGXSUTP19KTUNW/is9SXsU6qkSSboV1kcHkiAgqi61MbEUY1JmNiyPPNsbG2WeXOTZJ/xo2goIleZCB0BVqtjrvZqsCl82v22TVlGu9yyCPII0vMYgfcrH337LZTKVImpwPRer2U+1XgI1dSmJ1CczXN4jivNxEhQj/2I54yfroPzYnNxm5na40dwxAw3iAFXs8ZwnJprelWCtPQH9M+s7lPcs+qalZJcnZRLgbpjEUSGbc52qXdlkmUabPZA21JpapONB7sksg+KnQf32eV4R8VX/veHaFDawevYZvzSQWExSavMyAAH/PAgGYvpdnPmRkS/KZOq0ZmSeXzLuggInw4JuKiv7nl34tZKZZR5MJv5OY/t6yWJH8XoBIuxX46dLNzeqZ9VRdUE37Kv96EhodUx6sFxi/MPqXYR96p1+K54zvWVzrrNPSy5zDWs1IcT5ApACPWgGUu32461R+iAl9qzbAOrfWVm4IpqzJ2KXTKr0YvMxObqPnzrFXNNYI83vp2aOQZQx4EEn72bVDlB6J8kISMZsrOiuW3XD2DseyYTg8vIyWpwlfxrO6yBI56hU5ymu8vH2UhDCD51v2TTyX7CVsDnTi4Vd1seWMtj4ppLvhAGu+/K8SWyXkWB/PIDLJ385M9jOsCaEDaAOlSe67WDiL3k6XofjdwGGeGsxq5zU3s+MYnrUTx+jH3JNjm33j9Fwa7YnBd4Z4qF59A/bK50uObRo3iz19umCA/Q5PkVojOdMyfnqL0sIRTDXrL4liNfLmhyD5S9dKM3yrsUbGRQUetYlsHnPApFwUtTEwYzbJ/DZjBzOmBNtGA7Mfk6GvPl10Q64EYgRCS5tiuyyZQ5HKnt/e5l3Pylkq1Pitwzbn4UD78IX0/VuSR8t3XwdE2MyRPHt9U5rPM4/cGw2z8Y8ivlHTmIWruSJL0rT/hVPb9mdPZo+VQkM1lJLKsbxhosyluIO+fiMKJfUd20ByUB62e+68hC5fr1Ur2Wq3vLG90nI/zstRLoJxJ2eJy8LGb5QHJ/zD2XPECErA3XxiZ3+0NIwkbltlZM/k7XcsQjecnUb2VVjLKR9gx5bnACGRoSRdoG5bVT2qIwJGSKXcfTdnmuPogxlR7seLDLPfV7z/fGh5kdVFlFtavaJz8ymNW5h37lVsDbAuZqNJZe2tnTa9yK70SNGt6eQleF9lPNFvbhOzjcZT+9O1WtX/ycSmhSJJikFmKyq3qfK9cftx7hayCwGO+w8AAptjDQBA1vNJxSkh56V8whCwDAsFOV96PglHmO/oX/nzRTwO1jMs/7IKLqd85J11Hd/14sitY0F1Hm1iG6irDu6Pu+5sS0XuVYt2qvxejAdSYnmX5Z3FfOfMv2cPUfe+LFYlu2Eq3k322+6BosLemdZwWvFryufuM0iMtFB89InssR301eXGVe6VuNeUua55CfXP6TJhQ7oEFy73dnDn/Uo3tO4MMrdZyjq26zCua4wup6/PGROa0kFAzPB+jGK1Mr+NcuIptUK2PxIWyQonKxTvvmT3nslPvm3pcaRGrVhGU6rtc7S3P8OmEBjyH3/A0YD++MkCn5Davzx1HZdpUrX9vrTMuc5rmT4L7IJ5KciK+78vYmwc31duK7WFkkJowROhC9BeRGzaXJfarVXNy+Zoz9goYSJ+Lix+NRRxMqHkSlkXfJ9yPoCGpTftLf01+lCIfhgX3u6vgQphBNhBq93bToX9astcbjc57axnt2anlUi8VDCSXKLY5FNWGCCuYU8p9vDtCe1mjAR+kLRKNTX/RWbPzzEnSt+yKR30TGyE6yUPCTHsLOVRi+AvQl8bAnRnclobOwT8LyhmPdxMTKYGa9I8fMLjV9WSSCDVnIB9LNPRsM/uLU+WaWCZ3nBipfZ2RJJnYWqsmDMJScpBNbcOA6i7JFMf3tMP2oLAX9+2T9O6dhGTMds87JpK4Dza8N6qRNv85fzmdL9p3yH414qIEzrYQqc5eYbLK/D+Vkq+ZR6tVjyssr01uaqqz1OAVymIYImnaJ+2ja7DCvdx6NDYp0+V8g+EtU+32dZPPKcE14+7qQspnPNt+8sNdmACFuaq6k6J5jvw04cER/59B9TLmC4XqTb9KiTQVDiTCRhWOPJk9/2gLks52cP0e17n4Szu3u8ur03ixnMZa5YSyyltT2+lASYoAuY00pm0m55HFwYCXPd0KI08NQ8a2rJZadc0f1nlamoVszs0snxGUl1/aHNJh0jixwlQfpD6pT3W+iFV6ZUStlqZC+OWONOdmKiCN1bO6OUf/HlGMoiE+8ozfxlfLdCLkQs6qTKtgJzmbgQ7DAoMzox63o8O6VXvZE22L/NTKuLk6sSLmDuiM44v34Dpz0wh7DEvUVZ3FGIMoRiAW7VDY5JyTRTWMuc2wWqeS3AgizXTroQ32otkQJ4aBXF5XFueRYnEW353E4ebC6zi9LRnHVCLuZ982iCcxUcA+spGBpQXsMyuWtmZhShrIdnvxtbrz2lwuhqz3h87fzu75mWVVy9a5Rb9Kw+azqOw5yfue0zneMPxjowE21YVZHa0D2nq1MUzpUIvuy706K8JfaTkVhUiB/pzhPHlO4afibeqqgzVVx8T8maKZf5tfXHlx7i7nuxud5Rvf4+wBwf2tqhmnBb8uPBQljS9MRoFg2ZnbCfaf9XH6X6mEI28ssvVeDcjQMaCZudK8IpIU8SwYV8aI5LqI7hXdY2e8o5hap1YTRmyG7iO8WoWaUrp/D4qZ8Z1B8vDoKHlkZ3J8zfZf9MvCjGJHdyCEZlDN6nBYsyqDoJ3prkJPmFHodIwuNwPNzFdbnx3c0b/r8Kbe+I0rVmzc1C1KjJcn+Qud+zkaF/11OGHri23vseBja+LWHEsDhczU9jh+pK/PcXRHRso3ZkB7CqEQoxAN+SPuFCla63J3rE47SFNELYnPfcsjyZoZj50+FYDHhCA6409+7vS+ZL/CLHPWHr+jgM2E00b1EAgNMECsLQCh9k7/O/tGt8dEyoQxQ6axa6BCxt/dc9MdSf33gNN3bEG1mKmJ7LNs3epavcGpvBtW3MTStKFh/8zqYDIFypnpXUHX4dFR15FdyemMKBq3+uGSKO3Fc4hqVTisWp1DcYwVjrO+inmgjJnd2ek4MjCIX/GVlqUm9dn0QRr/2Rhn/NWT8Hgqs37wSS+ZtctX017ZmhxOEllcusZLy1Zn7EAzc3+yXZxGdYkhGflEecETFIUgELXzngBV++mz7hgIhlO0+yw9Ntm2OJNxJr/df/K+WlCtY8mdlrwdCcfRbA4/ujM7k0Nz2NFcwvIYJAkMlmyuFJzJtEQdyTni5R5SuiZDbFaaUIOkQ07GBTQzRY4+mbYLFrfhllZ3EMJ1DKjsvYDuWV2h8R1WxT+gCMhgdMH4KyCa6VlisPYzbCFGqgOjOBqppQZiN/3gt6XL3eVdEqFNoVeHe6QYMaSF0nPNIUZYCGMcBewyypp+I0m8NFiR07eRMqMpPCQG5yse0Tz66xFIYnqmTOYJ9zv+mE/rDEATn6yamBRUgDNpD7L3s2lgBL4/ajcuJneRiU89Ptn9vZzdDzDfJF91573aMNCQ80v8XTrvRFOIgqvzvx/L+6tDz+hgM6gPLqsZot+Ph5iNNHjM8nQ5UJR0+ZknmwOMkEl14XTSnGJ8JBrO9I2kvyy9wqzAxQsXsGfEst3dfpLFxhQ4n2dt4x5rTN7cZGkHT1lk2m5TiyBn0+GTIsB0KKr6+aHgHKZjcSSQHhocqtk7YfQk2nnYIZTNQyT1xyoGRMa86bPWiH5Eu5ymtct6UczSY8YnJ1SzJp+mfBY5LD4bopVbx8L/Qy0ZTqdNZyBw4F+g8ol7Z5HOGfqHOF6usqYR1TKfTxR8cnAScfVEoq4cJLGI2AGjnh1EOwCrAO9HdMtpWresH8UNMaHOPR1grMfUSsydi0ahY5Ol21oQTesSYx2ggGVJIYplPh8p+sVjUQTagvfoGSdGhkNiLtlh7mgK6o1NAbOow+rJRSOeHvBJZ5Wkb1XXyGr91hXe+NwyLDZ/nZNa1nIwfnEBNmDQjZKUbiQ0g6mCIhPe5OOtWre1lMSoaMxFhTDMHfH58MwE0gIu/GNog3WI3U7jROP3LOWBs7gpDkSHNKkcg+WpAOPFNwyhTo3WL/BK5EgO0o46nJqRDASrg7I6mvqf0SOYFeFFqDsGYuewCVQosjpjHg8RAzEztM3uA/+31Ao1hJQf1vlog0uJqc0uF64HVCldCvKX4621N5osaL71czv+gVzPqx9+77t7jnlPXs8OTINB0QZ5Fh1ZCBaK8PSZcM3a8ELvFciMb/E7FiAXVjyNNLVpXCJeyOyjEbvKruIkG2CxgTbYJKSOk2QhHQYiwcaH+iTBDQIDoeCG1b7n0ZJKuJRvIJx4kVWADYgT+RTFGNaPYB+tW16wkxg1ISopVzGI3v+4Om/xS1qN3kCmXOIVTxRRVEQKrHV5a04z9n9uic52dqKuRQS3sgMQ1BJAWlvqRf3X0xRf40q4OqJCiopcGpI3+snOz1X+KlZYqgADq1VKRANLLsHXhqjBrOX0vNNrkLVOUI7YGNULX8pbTJx/28G/tSUAQb3CGRHNAwAxpfjsDSPzEdiollMyvvPxNhXgFWCJ9uyQF2CsOmd1/yNPv3UshBV5BXVCWy7cZQ2K2114gFF9QCvXRJprzeinLnaAJxRiwxSIuYYourvkUGIg7UF9fms+2B+WSL3K2Lq+KTNlJmmDWdjrZGnbyDajxaToOGFU2uzQNcC3o5B1Dp3Pvt0UiYvO4g44+C+Qsd6Yjza6lHaNyUbZ9HQptW6GaQ5ZwZ0hXc3DJ3ddwzaceMx6y46vOB6+28uPRye7aHzP/sEBDv9gvpqXmD678WxqYPZfixK7P97zMcjsxw31BxNNDX+fG/3d8RXu+p/Q715RgIwmRi34oeOTH8DB2kn5vVb7Ec1gRXNeQ/1Nm2/2ekJtsmNWZEXR9PwF+kUXC8obmhoTg19prWs5VZ6S/ZMlpV5wvVYdiVGUlAhXJE4qz2kWHJe7nBDYnpTS2xJE71/uff71UPcpicKZ/hUh9PDICHpkRdgfYMWiUf1wie/3RpP7tmjaVtOw0UCKWtEt+T8ZwfHifbie96PJFp0BeodoamKgRzX/5HMWltf+3xHhdy2whRgF2U3LRrGsTBk1KFsIGsG4cWkiy99CAzlzYtVgfiDIYJiDKkTkqr4hDumEXGpabfJIDCqr2ma1KgZDjPnkkkSvPStS+XSwh6KKS/3z01kChePjErDpkUIi8XX0bjAh+Ij/v3k08GA6+iXGEB5iLPPrEUeAdki5downl5ACRlS0Y0eCqZFx39rRcq6tz6gbtNlMg4sNoJ1JDdu129PB+ofNY70wGc9Gy/HQ3OOHfJJS+2c9WLaln2LLrD06bT9GmIaH9TavNRgkJRwbwm0XO7itNomE67AJwfODui+GLxMpj9A3iGB6ZEZxYwrsmc6JDrjT4oqkHCVlf8MjYbU6kJZaLBmpKqCGMed7ZZEl4ZTLAneOdzi9ZNSMGD14KOjFjUjUTERhm1ojs+hQxKqTaWxqUMANDvrLoPCcF67Lk2M7QB9SDWftFkuPQT+EE6bhIb0tNOxW7EmF6h4waXMGk1WCcYQ2mYTrtLVKJRYhxyGRcu0ID7Qy3aMh4+GREeOR0bDbQ0cbDo+OcEJur4/et6UnYf+wT9/+6e4Z+0AhvZE+V/x73CrsD+7xaT8iEjSwUqherf/DM7d9L1nWtzmLP13k7YItkMf2OW8tx2KSX90EVecfcN/N3nbPvR2JuXPhn97Bvr5YjO612SSuvD6rrq3dZkVF9tbwL5LFZpa4wMSi/3wPo/C9OHCubnuL77QeWMPCt/e+orT1PFQm1lWzpciloBU1axGDVKx8ecOTx8uHtHBQ63rykPRblT2f01Xf4Gmt0dTU19RUfb3IpLDscgTSaU23Osw4N+tcmlSUEZyGwy/KDl81flZ6K8PP7NXuBDu1Xla5+VKAy2rZwCELWurZzxmMvvvEe+OC5OmBaqPOoTY5LeCwVkXWucX/4kI2fdr4yamSSObmHwPJkKF2sbSXPK6ltHWrelgTFrBbG4xrFS0PGv64xnYwKwzGqx0W155QbG9xwyqVGu62BuONXMsHjopH2Uca3tUJzxl/5yqNfgMGHbHlWVBHmUqVQjGkUG6PAB5z2wvjcy8k0gfrG8BFO1hxh0nMIoB8MTNAcaBjrUx9JHVPgLi3mmndjDT/F/8rPiU9OM2BtzAs/0PnANVQrZCfy4zs/3C+PRcqp03Z9Z+xXFHEgBOOubYQgwGZ0n0aDErIVBG98KnRY0teV+S+lj1H3/TYEtX1bjnqmnj04WPnVpCOObnZHyd11r7RYnSwkTxYZ67SIpNK2LdCAqenzFN9fwnUC/dKkC2EsHAPeDfvjH0muuECy/Gu6X/tHOY6XA6sB0pHo5Q4oG9gwVLA7jbpIG/H3CGuEyCTXcTHszw8MP3e14973nU0f/pJ8fRPjMUgecBhNFbfeEF3lP198wgMI2Q47vS2Lv+2Kb7p2+V2Xufqi1QTxCxYoIg6IM8XeefNMxxReWCIPAIEj7wRMg8SyJWifT7vi7pCuuI8AXIWoaZC+ShoDCIGLkQvWA++dxBwSpe6FevBmo+Fs+rTtZGHWCB1ZLf+Mfy+Lf/Y2fGx9ar9Nmh1vGeOoQSgOll2hAoy5kcZ84MUF4DyJDPXKf7uwXO7MqVSoRxSKPQUfxYx68Hpfo+vPnEpwpImpabFsb7y+xi6WtkyrL3KeitFnEnELgA1qEXKB8qNo3+k/YcWZ0xHJ5YeghOLjwYifxigOpZ4UZXocPvzUxK/+hYd1S5wKxH5QMxsVuCi/Q/TohjzfYattHKyWqbpi3Va1mz0T3g2qTIz8u2BTnTtuoCYMlf+/KmFzk9ZkYKW3CJh+zuNkSK52AJwbmY8ERHVWPUQOnCF0eiK+t0KjM1FOjRImJQfekeS551bwB76B52Xdr6ZIg2kDKnKe+Zr2t7SIYV16sWBz3SG7HE2/zhX8MAWEEgtWbEmbDRqwjkx+tNMTAO6FBiZkKRITcF+xiqL2iySdFc2mtsJJlLs4QW4bffxm3fNHWRohGPQh3kqR9rHWGGTaBov/lVgAtvLXKWhVkc7/4FdSxtFrQo4ymlUdfS2NFXjpdy6fz1fKXzDagR+V+DcoM5Nqzc4Xer16fQGdaRr/S5Z73KqN6D1HFKXaSOJtpxeL4oqdotOl20jUoUU4hkduohC1mU0ymLwrZMSXIlF78r37RJYrL0nsLmPWkjOfeHziZiQYpQAGJ7XZAlxXJDQaEQhKSHYo1uw66NbuNkc1yoTOp2yO64xm7s0iNsPYqJL2wYLk1pzR2tj3a/ORhM6Jhp/TGZ8o0JwtcEFGDOrYqopU+USk7Kf7nRMHzvG9axTi7hxbXyeiMD51a0hAqG1vV1os/FbqZUvtBnWUhNYSzGH29jc3MjlsADv+/bnPvcfQWQgSkWPsukklSRv3Bhwk2X83hqH5UfwflS3jO4IX70+TqVqkzZdrY5rtaLodbWbfI2Fe3WIxxu6yrW+Ggc/fvVY08SGDbWN6ze01Qa5nayVanZK7TX/vgRJaCm+tB6jJvSWXoYlwEiKbARbLUGrxS1N7/tsPGVkeVzMt8q1qmBGYncOqM3JVUEGJoDsbDnkMEgXfUGY2Ev8FRltKyHVG4MDYvA0E+5VOhOCjW43f5Mr2auETWmNJVVsDjGYZh+lhUOO9jUed/uaEA4bA5EiS4RRgKL9PYZTt/DdC7AND4cfXoABT89ifcI0v6xmBO+eYSKNamUwLtM74mIZOZic83fUsY27+hzH4KtXng8/Emrr//8jATBzBzFDjOwlZghZ87fkiv97PCYeV4wR80lgff/tx93pwcFMbypnk2SAjgurtK6ulJXs2dCzVZj9O+Itt9FLuYtvr3sm9PbniLU84cybRfDRhBQkDvBBhr+Bs3reIC4UnCboYCD93y39H6rzGAW1LymiPba0lvv27t2b9cRH5DsC8Av0bBj/0y1gvXKyqZexp+unwqbAmdIAf9jn94cQ1Bfy+3xhxCDYqnTfKpAdoKf7wOkDyO6/ugUf7CONHkqFYkiUkhffsS9uX68fPunKRIYUCp5/kAfYpRLBhF8SG1zQ7gutikGrgJ0P0kBICq331lvx5ML7ewwOhw0Oi0ALPdNs/geJtwCzgE6TNcq7O6G7dLL0ixtQMstYhUU6ZmmuTF6D86s7iu6YhWVatpxvBK3D9kI7WOrSy14q5ZZWFf1rrbY1v1L8EtesDrTuDwfDYOOX4nUHzds/rBG/WKb3qvhExNyQ9x1P8noVrJpqtwceWCYncgSYe+NnOiQkWDf4cnOB84cNX72yiwCswgsIMCsE/Odg1p4NK9qzYHV7OmxYyoQ/Av3JdeAMt4QDbWkWTuJzFoBOqUfwimUz3NrBDwnXngVd7emwYAkTVgUN2hPg3p7181J7+s8bS5g/p0HJEgLNz5iaDA5mzLnrkq49C463p8OZtFdgwqUbe/S3wd18DvyWOCkSQzrRVulE/dKJDiZeK0dXE6VfOAJe0sPKTAv0fDqZsynYVwcWOJxICnL49ydhuu6V3teH1RS5PejlDrOB4goMMwqEDQXWCM8u/JHcYd6m5CcwnOEQXpTmcJJPZJgzQgTtI2hEeAjm663EtFJ2xlJh2Z6U6dLF+rKafDeTXqpRV5vUFtBxqnXoaUYdBtRZRHZ5l7gk5S9hoHYk8zWZNFrnatPe07X9sXDa/3Y6mtP5q9PVne7ZTs//ud4RN59lN/j/OZV75F65T+6XB+RBHMLzZiCv0nQCpj20l/bRfjpAB+kQBsxO+S136Y8YqJ3L8Ojk/P1ZIvfJSBoeOr4KYBbfhmO6sY4B8wnGTgMXZ4+QnEtFXAL4c2Z/2QNcX6LF7gnbHpJ522sG9Frkt7b21AigV/+pg89NfcYN80IHYhJBLNIhDnEF/zc99+59DI03sfimH3z5Et7tlMJvXY36gacB/o8A5M7qcbtbeLKRh97CO2rnjguw70/Jyox792UUDS/0VZxceE0jfcpCfvrB4+uCknek4OT5/GpA3ySXm3v0G3MMVKJcOE0jPTPjgR9RyKk4uVMuK52e/gayaxfknUWQcflgujEnp3B/9VMC2Nx2KnYL8t7JEYSdxvuoxoj59N9qfIqADXsAB/ArYh4IHww105ABhPGUjDwsE5r7eqxLlSgf1I7RKCWp5SW1JFJiTvm5HnlZP7njbxT2+zIbO46hBLk1o8mIHBfAw7s/C3AxS+vZPPbEpsSb6uwItmE4sYzYsYzlsCo4Z/zTZL0xw60IAF+njZcA1wWAQhBFNzgvQOITm8PleaezlKBiWfWWGXkHQQLaFdD8AOWNZSgEUVSZ0QCJTzThvPW3miCtU90K5M+MOfkm/10Wmi7OpIcZPf1ykHEw2z9lfQrRRauVx42btiPX73DMzguvb58jBmQmT997W4vMT1LIMuFNGJZXrtKnr1iJXBrhFmvMA0/Fi+p4qWbzxarZqnNksD9F3d+ntBjw/2/HRQDkBIBZrLkLkiBbhRYLjWTBZLPucoNHPGdF2WZE0eaBbM+hDAcPX6kyKb8K1co6UP31qYaNTTFqts2zhbTH28MhPZQPx7tF9+qFfapv6iv6tn6sPzZ9LGr1rM+2vo1+yP/52MEYO2aN9QIh/hesjt+JvwqPwoXk7cQriXcQM4k8Yod129/BSmTls6pZ8+zv27+w/2q/Zy9NWJMz9mfIH8VnJvGpOccTHc93vNbxbp0knTydKp3R6UXnPZwlaX3p99xfsv/OfjJTmKFm9FR0OUlPLXxl4a/mf5z/Z/7j/IMUk7LS75b8TnFmNr4acz9X+sVsm5kq+9/qq/n18u/2apzIxbg8V+ZOVNxYcW/FkxWvVvxK/Qf1n9f/XP9//bOFwkJ9QemAldMrHZWBymTlUOWyyk2VSys3VO6owqvKqpqrJFXmqtVVG6reVgdXZ6qfqqHWtNbsq1lbs7nmZM39mu+1tNqmWmktXOuuXVH7RO3/64LqvHXL6p6se1D3ru7vemZ9VT2/XlOP10fql9Y/XP+xwa/B3zDYsLRha8PhhvMNzxu+sUQsHsvI8rEmWDtZO1hjrOeNRONfFwUv6mua37SlmdIsah5u3tC8rHlr82ALvWWw5Tu2L/sAe5T9iP2Jw+LYOTHOUs6DnIOc71yC28Tdw93Lvcp9zv3Ko/CqeFJeiDfNO8+7wyf41XwvP8lfzF/F38Pv4/fzB/hn+Ff49/mv+N8EdDMYgtV5gAFUQLAI8EUDaq51CpAgAAQiAMRUv/RyxL+2cyUmwq7OhEJga3IHGnfsvT33Wcey8Xmu4CGd/pcHcHnx7P3RFm8rQIIVICAiAJw6gHcdKAhhZP8RfWd5kCO7sdZ3Hy2N0LH2d6elLFmBMErhHY8bLwRPfdS8i+xHnoIUHBlYU/XzR+66a4Y/LTMXbxm/A3+Xxi8UVuWVlXf3siYU2jIeeOB06gAIj7sQuhxY2LeAzfhrtzBPK90oH8mOrwm/Pf/mNfDkg//+7xGpBcel4zEzSh4INJdIAidIZTStoEUXoLegdDBCoA6UtZGBSsmJUtKMincx1cQhuog92ivQ07NFPnN9f2nY7nDixEScH0DPiygTI+sxC7fZp55aB1zTRqYBoWhWGVSMilFggQedo0WsbxyeS97SsoUBaSRve79mqZUFyw+5xnLG2Fl4Xl+44Nod7zwn6CC5uGrrrq5BxSjYL+d4SiQG5nCbocgSvtONxGD5Au5tqVf30oynjavM7D72SEYGmhFtWsj/oqo2W89tQr0iwfKXj7/GjoCfRENpEqdMkE53igMahKUwMBUl41iSMv1zvtfTLXsGdBcF7+oYMN84qLInVpJSov0DrNen9N8qC2yHfjy4Gbq9WVujlxGweVwYhZ4C1BYKgM9FXuD0QWGvy/+IC8uZvay3eP96cOS3WdpjrilwGQaLj+P6NZq8+zwyEor1ZsQemYFf8sPwlNIHmn+u4H91Aa/Ee2XAzuy6676VLvJ33GH/QEwByILq2i7ffux/N218U+cwXANXevv7LY+1ABrbI6rBr87VaxmVp5K27LbnVGq1H9/xBJUVK6wR3WpB8LjGvCPxOE2q2w820jkfh80d75KQZW1Uzp3ed3TREgH2WkrPiOUjsvPo0p29NB3RyOjt/EEmM2Epo3b8AQ4MEzL0bg2JU+lwE+qDEILPHJ9S0Fjg0ulTvN0SWlMgBCFeQcnHBe9saupcZVgPvLkZqGsYHZkIysQgTEyKRX6dEqyce3aZ1vrlNrOg+AXs3FCcmyt7lD+pO+CAm6a4jYC50JBZQHowihMKnzzq4EUp5F1ORLhYZyJmREJMSgC3Gh8CpR8XZR8ajRb4kgkSCXSNiqMcc+BRautJ5TNnPP82Kfd1FtnPUxB/6C+hvRNRutR/ouRIkZIO4TISVlbvUwYf+MyEI8xNLMHf3N/PBIoG/y4wDan66YQG89tHU/5f7jcL3M/QMXTY850PKIBLcsGagFKlVOf91M7e3kZAWCowCwPqk3XQj8PwIZAO6nEd+rr/5AVuaem9lYCkcvwiWFsrYnTTbX6v8ZNPOv/v3DGHTPba4ZulvYSTI6EfwIEH2QN5rnTPUncVCtDQ4FyA8dlF/u4zTx9Rl68HCuG7sau5suwyNI58PAnjUm/k7UxaiDWFFiyReUq2QueLFG2ys0qermigELINTKc2TQkhmInWfPA2bSeqFNGChnI38P0kRDCm/n3WopS3MjAKa62X8Ye/22t4O4NQ6YC3AqEfCzc3/1z0qOG1ezN97e5zyEgw1qUIcafepVagq+zkNny95mMEioRwq3RrheXEZNtUvGB+2+PR9QPuQ5kZEWVy0E6RlCy1OB+sug1NojsWCInp6xPiFwrLYibz7l62PM4FM9FDExa3NLlpHmzncuhS8tsVU/W7ElEz9g57IvGhzEwd9vGTg1DS+NWFW3pFQRXoMaKbArSMKaDjf/sxS8i+hq0pZ0TYMRov2AxtVu4N3Ucl/QVkpAyYfQ+kK4V12z8n+3Qlh5bqxbY5EgZiQANUu/qhp0Z9e+763uBsgLlazp/4uiTE1oH9sre3zBB5JVh8MmM575cgK2WBGSzbZ8PHmzU2Hky9xDVaLKbHduG8UVgebSZRlSus57r/HNh7vep5rOCxlRtkKabDNsxjm6JzrDAbfPOKSMaoSDo1uFm6uc1H63aykAti9ozjw+XERohXsvw8Mtmm2976gA8NZYPCEcVj3WxAijiySah+KvgwWmlk048JG1Y+NQhzIlhN5tCjciwRk0WbCoUCOTUwwrW6Ao0u49B9kcowKHn8mp4be4cGFKBkFAlmsaq49UNjr2DWefKdYVZY0fNIoA5syOMEDZa+CGMwinYK93VV3Pi1/Vyd5+i5Rd6MC8gqrHUjJ10qIaJtyVBuLStHaStsKYElpx0OntGOe+tdfWUFE4hyYByBDDNEWWl+APnmRsmvEVsHoelCCyNoyeq6FKP0YpVAgqscJCZEp03gfrhfMBw3jWMfbhVlX66EQwVoLJRMgMvRoLUrX31j0AkrMaiDSk2xdIFmgkN6kGvq6pEcc6m6wLyGwFF9lye9thHoqtvwKdpcldiW02yknZwoU06qOxLUgW3PGqHMyTv8aCsg7VRpyk2dtwZbqbEkVMTccmQc51ukYELTQ3tu1D8JXxNhsXwPOyAapq/Os+39mUztnPJdfmZ3/aoUDvl4ijpEsJfbARiKMBZIGzA/6OAvgILRe/aDcXLWYXxbMCFRmYoAqMc1Q0Qfr2341F+1qO6ZqGPGehXJ2xt2kpAA2z8TfUURWz/m16lW7u7DD6eEty3igvPHFceY1MEss8CKdCtTE3mXMxEp1jsRW2QJlERDg6CN/Dt43FIeFI+0//vAsPfIofuLQI9mbfUFfhOkaVMf+affP+us0gdT3lXgOvQvc/uXFm8uxYr7YNbNMvvXysqK94Ex4uwjItva9a7OE4L0WUxYn/hv77l7s8GLOuegwwE2+Bw6oA5Nu7v8uoXpDzvxDn6bXRPLgLnN0uLK4b+nYZce9ceEvuksVwkCaC3OPESHwj7QsXdnJgOiHNZhCtYlnkG5sa7B3PSerD+k3WExQoC4+En47LJmxLYfhgGE3Pk1X60CW+mG+Hzlt/GLd20CD0uP5J6UW3hPFwBvCxfH+UIcWYpECLLTnQuaqlvzeBhHl051v4Xc3Dsn4CEkCVzwIg228/C8FkdjD4dmk0DO1hCbI9t1OJyIKM1/vMBbmxMCPQlBE6fz6EMdubH/+siVg/76aDBwo1GPx6XK9kSdw8rSc+WFxqby0x8lBVZgJ8fBRFhK7t+C6B0xVERnr+zOyaDzzQrngvEP9fUKWOSJy7Dbz017xTYL1FYcwo9SqLuUCUkxvwnZVqLCaGczHddLFDJXYuE34ffcVWH4PedSQquN2LN8IR5Z8upIbErZnvaW+Q5VT5hIBoIUjfcqdtT0dbrT62u9HSCuPxl/vWpnt2DMSIKWqWLo2sL9fFgCFrebsQSGT9MNv9pgWZwfVrLaJuHwK+rVD4Ma1yq2PGwN3BtAA4OOV7Bm4WQheWhEbindEJkbdjfGOoPFNEKf9hmigS89pNbZxqG6y1Jugmg6GtJJ1kR7h+i4/VzcSl5duB7AZIqQXBNVPfiao52OCGdPZydyAMePBVCe54Wvu7lTGzBhXNRYPOOJlX7U6IDLaUHrx+VOS5owChMVu/drhWIRQQZTolVZ4EIE2YosE2ghkcUuUu7kzRgtTNM56TiZLkkuFDTgBS9EQLVWQHdv8IbCOae+6w/5fB7SBSE91e2LZLipTvqL1lYwYhWLdjzElM0SLFEYil/1oWlxAN2kYabIEgzBsLc1qWpfK6v2lXNRvXbJxo4L3qAUBr3IlmoGcIDjbKl3OrqXF/NgM15HabUWDbcUDzTyUUMpXJdKXK6vKiNJ3w3EYRrVL6X9GnAjGXOoVCxYmmx0LWStmAlnFcVrm4SAgf3lCrhx1SDvXOIxHg4jpOIXhEU+A8FLNWgShyoPCgaHyotpbhfNB2RwXKIDsFbWPAe2YmHHCdhWViRhbBjfQUVi3ZBubJHOSmQ4aUIpHUKUcanOZPEk73RKSYrO95NtINeaBoQb4HZmObeWxXZzyTZzY0UQQok5NF6w/EwSsBGSDkbYUaE5ETRJFJhrjUYnyoLaWuEE5t8VETgje90xYbFRVTZ9LSvb199qOq9F5/smqECAQzas6Ka/oWjHAhjn4LPO8SZuBjikX7CZ1AD6EIFBpNDYMSO9muk8I1cOiFPAF7/IrWz/gWd73Vl8RBNhzNueNxyXykUL88WvlMK+3O0PlG27de72u4Tm4i40tbQ122Fe8F4z1neFP7/SU90Il6qLj/tV9184WI2Vbwc40wBsjS0CPqHEjhCGzjAuj32AvMtr4Sj2QlgFuZMYiKBUKq/CA78DYN0E34qrVbhN08lDdf+AFCDITR44jngq8rJwIl6QNcgmxQH0YPMqEscJ67/WyALLS8WPzONhAMaU4PAkcUa90vRK7t5ENNptnVNHFvhJOqPqP427fQX+dKnzTxyAu4V3B3DctGmC4KuS0aatUCwhJeJROLe9CsctNVJc7re9AgX1gYEOBZVUFg8XrdFrb8U5I/OTCjCOaZnI5oyTwnsRFZID5drbegQ9TsV5w++IwUL7HGRND16Z2cHJrH+YyTBpit4xjf5OatJ7VcKaI0YLh6pEErcCdaZWISKUbq95XRJB9jhn4Yekz24dy2T7GtBtDu2cd1jEWek9zbdXl8joYquBDp+Xeb0TkWHGBt+y6EDDoU0rt8IQDNtwoK9wd7h1IwdITRMaraWKKGlx41WvnX8cX9jRvQCxvArfAFwiI9OwEa86FzIcbE7o/rtGQBjiUDt4WAt1l9pDXNB+9RhK+dgEAqjaoM0Bi8vT4CXwQcgTu9erNpqL4m3gOf0xn+uNqGV2PYSuKCGcUbAT5vnpwOu2C4Yy4fLoAuI7ZlRCeDBXIzajcou5hd3S79T3WTAJvY8MKepWRcRceTDbfTMaC8RD+CWCCKTFxeqY4Zpkv270UrEmk+RYKdSIs3qFwppsJFFkht614tmRBc6aJI3a1wSZkiwEroNb9KOr6U8TdaIi6I16uV5t6JZAEZolFsMOcDRenLf5nkoB52/VK9pIb9YZuRWpdmtG/mFvS1oEhzeGSfv223aPrquvOXGnE7Mi+sH+YrlYONJbBn1IrOhstozWCSw/NeFvBLjGVURY4VnKHxSUJbUqg+HGv1Q6X/YbVWlqWan01CTGIQU9MNoJJ/EyK0ui5m9wQSUcdBB78ofG1uttcne0uP3XCLO+ODRuGuvTIzNPgEHOEuoOE/pJ8umC/83BVvdU365vaFNgbbkD8tADg85k0rAgJTRj+Sw+NtU9J1U0MP8F01gzugdCm90Txa4X64p2wuOMJYf9ZQQTxqxGcRijXYtWP5qoPn2c6YhOYGOFAT9Noe5SLhSMuRAi7gwXRAPucRuiEDSuCNo8gFzegO/ACbiCO2f0xAFBcBZHc81leEeuYkSx2O60IFgd14pa/MJr1M8vMcu9ETbU3qOS18pFcCFuhG0kgEK7w52vq++FgkGvx4G7qh6GUd5g0Of313Te2kY7qYAt52ItM6XMh1I0k1EJUdERyQfRefTqAW10WWMASG1rTEGQKFpoj6YkiTh2e2apL0UZqRdkzhgI2w3P0UO7Al1Fnj8P3o9hMiw+lXJI1AW283l0ZpYMpUok8iX1/M4auu7YLTn8Kh+q2l5JHfe9KawV5EwTZnzV4+0twpufgkXZ5dM+0kdNWu2Z0gI4CVPc+c1MhuGcaEYld0rFF36V4flFt+l13WAKDV/CxbLGv6PrDGwVq4uXLmeTG1uFPrBeENdgfsu/DVPTbK8JiuoKnlmapuof8PnW29H9zcRYAGnInYCheNzSqeefOXhKK/XVYpcHrR76zRV1qAEIgzJDwegpokRSSrnMsBVDRRSr5iXnuMm1J+HBsPho9GHUHwnHgsFI0N9CkRMl+K7UD/VXszZX53wS3AF3co+dGZjxj3aVzmpCSmHabU/zWVosNOPG671Yk+JQFbnLy7yRhgkpO01WHoZZAl92a4LH7QhFXGED5UYi71vGTBlruuWxZnJs1CkZ+3ynheMsoUTQtLKYj9wxeEN3/HoT11PHIlA+VD50XTcXA15yGMIEV9JwJcKbl6lj+ZIoZI3H0oyZytOdMBCKyJyT1gNuglusJZOUojwVqHK+UGE52jl6ZBzZLRuv82mxC2v1ytTClQmZLo6uFJumZiG3HVUzTThX9upoCbc63TxmvrVcJ53HD929kurz+1MTq+QeDyFJXVGh8vUul63N7cSHjvEajfpSP/48jCKzzIvMcVnTyKg3JemgjfkSWsL1i/n/9jzxnHNcpUi2exS5zMkIMoYhyOACSWh/L+vNklgnwzlbQLnoDiudKemcygVBkjCqY/A9lckt4L2z5AdaDQnI5PoMoE+w0g76ylP8q1cZnwvsOPDqM8KprKNEOWvkpL8t+ztXF0T29fE2cWHDlxK+YBprUarZfZKvc3uQCI2Kp92nA4/AzkUcwBd214cKxWJ+FaJXcgAFzPseWPhtKbZ7GfY0OHMfWIr7DNS3q8ESThPqbwTB2bBvxjSA/+EkC2qK2Jowc1AmmhucCXDN0rr9QltHs1568MmWt/9chmjDt2DH1waD3x2eSYYMzWBe/kmeKW7nJ9VLUOTQgo9Bh8hS0BH8MzLyK7MH53k8CPqZzBDqS0SIDd69BRCF5JDyYH/P6ACt2k6z5cI+TgAZKHlsOMaZ94Vayt2S7Pe8HIfX9g7B7reTnRQCPHfFUsnvZ1Baz0+tQ+xyrg2BL1thuLYWKiSHV0ePadV9Bi7+5VXk/buhcDLIv1NX3cAWujIy3/O9JPZMQLoEArx1Ds5dge4HcydmYAsQIm428JELnHoyE5h7choLxG0bsA93Bv9cjWx7H34Hjy/6F9wH986KpipaQu3Qhe3q7hYcTWKW2IZUmVqtLlteDNaG2uS2EvlWbYupQY0kYZoyr1bj8gQrxfEGbBKBQY5G6YeIcS45zNV8veYcsPmCRomy4qV1GaW+GEllAKTa6Z4g7u3VtFzEL4sMw7rcUQ5FXsvmHRVoZbCI5FBOc20hpUxgqi2Wc2EwinMTsUJBEKCuaiTmOnDtkIfuSrrvR73KOlsSs2o7wNNZbXdjDUoaVzWUoHJkvySdIzstYMyURIm3Rz1iM5IJDvM2gYO6UmiogIwRBYE5TKBoow2HA/s6IFr0oAs0c2wXrpJzeC4C5R8Lfezj9V7ASjxfAleygAIMOlvzThexyIu2uoTkmUEbrDTFwm1GY0IdPB2gfyczOR2mNzR4ihM2o+iW6DQcyg7bilYiPYpYf59qRFe6r007epC/ukLoIIJiQMPkeFg92IlsuvktC9Z+XF/wgrc81lqpwqHOmUNgOS8Wq4dp5YMxxjNtO6Hf//wY6H9i//kdX9sjBNoKkzBZuatCn5DdKC0/5LApUUokRaE4g1nIZnX7yl4IBZfXN6mDitTG7qbmQfghi+Ko4zACWJC7e3sbu4YBh9V6Wcrc0Ubf3ZkbNVINaWWZ0ogztnRpCUG/cbkvl/T73FC5v73p/t4T1E4UGaLMmQL5+SFjuAPu4B5Llhy1g+nH5hVV2qXvF3orxaVwecgsrrhhVVeQE4bRfC63kJFx65DFOQjbQTxCm3hsqqM1G2Fd9sjCXFolwaUxmAyPjVGc35HUxbj9L6HhKp8OgNkGR53CtfRvxthqV3/Aw0jSbZOLKMCgkTGtquXjEKSdrX6Nkni2v7t5E16rM25kOw1rRcMLiwmuB7CIdUKBHJD6IRQd4okripRb9Wy1BWDPFwu8AvMetP76HhnwnX9p3rNbQb0Bt/8f5HAlupKPRW6RWu3g6rA/aF9+0pa38ATy2ML2sFJVsYNMp5HdhkNSmJJXVw94CiSjnNwATZ9KCoVlivcttrGw5k8WUbrTau9IG6P4sY2DlVjuKv+l+oc8OO35P0HLQb83hyt6GmyBDGqerhMSgbN9uKiX799nNvn2YyVtwwghIl1a1jRzj/NaczwbRNj9KTRNNf2Ga20ixkpklbObdVU04Y6ESUoiVHEbc+kAluHrRm2tS6YwlQ3NM+HXylIoudoiDDNRedSLMnAjLkzs5dDjZVJxfGLfJ8TGoP9a0aWem/1uyqLtHxyBIdqUCGYpPTYfmwi6mAj0fQmMLeA8bls0rS0ypdNSf83P23jMd4WWGr7xDX9rLX+BAXmlN9gqA5BBmlH7NOflT/lztKnVakCfiw9yvbdQOMX5kXZ8ojc0vnhW/HB44JpAADQb4SHmAcLf/+JIOQl4Gp4++rTCZIVGMMh9MLS0VpRdOKFpGZ4wdlO1y2EvoGZHfdvq7myYrlJ/ECxCC+DI4kxJGs+QrmLMQhw6BxRmolqvu1ocXJ7lHOfI7mlwAqTL0NPyIsQhnk/DgrLb+an0AOmm6a5BksKlXhN7n7k+jR1SL4Tb+RPJmRwgRaxpDWKlbbzLtjATWhbWUZRbB6+/ah+6kIOs9p2o8hgrpRNc9r403ArOVt2rBzfCN3y+Bbnvt9KpSMQVgyyxXJbZzFaNK9VKutfKNpkGfKas7qdMTpK4KyKSPhUF/S/qW5eqbpLCFwWxZXtrGkycBvVO5dNH0oPqj/uMSdeGKglNUTMuJYN6pHi8zJCLRV37YKyJa7SrYrqEHAzJlNjTkj0TkuJ7mJrwSbNGmqgP80NaDODTn45RNr9yiSvh9qC/3j0NrwL9jmhEbo4pTf3zlIPF4+XwYNPGm+g0YI2Fm8lNjpnIdMdbkVODk00Madn4Ij98/NB8ARMoBayxdDN/mqQcx3IIRTJCiFHp44gVd/dbhz0mdrIbQMk/z0zk1U+/YcqsGb5HD47Tf27p06MJIIB9PUBJpyAomQ6HQcUywY/VBi9WA1QsGMpYDlB3yfZKHUABfTAEuzrBQGA2xmhAj5EItGOUA5dj5KE+KjuB7UWFiPWtqICZCMTZ9RsFwTVwrVsf7MddqeoBAqPCmjtuQNtdI6PsoEwYfVtujbVWcxgRRqD9G01Mq5MGOH6rYOXYYOxEEYXnp3B84EFwet+bKuBDFV1Uo54eCfGUR6ltgwUsG0qOxAUatWYQ4HAZSnMeTVCia6IY4ZZwGJjAt32Qdf7udGG2nKKSWBn9Y6ObAg54pdPMQgE4Fx6QxdY6qI/opwRtePYsvqvscTYKpmojl/wHf30RWXD3Me1LcPGEchk8DX1QrKRpGw2bh3XaOBSogi0ipuqqnDN1BLYWUJzY7CITgjuQa4fdencvfv00BeyvueRjswPibIBFnFogHtj7TsepdTBgRJkQjSsYTFjrknDYddcQZNc52HPQXt1xEHijVoQIHw4+xB4VuzaE0nJ02YDuZmFMuG0D5nzHf7WZyTe1Axh6g/FK2cLXGEB3A82jZDllIAKtn5btydIfVchRIMmLDSHi0GLsSzZDjWCJQwQrT/lwMeU3djDfrrhwWmrqDJ1eMmlC0l4PvjNpRS0UVBUkjMvZsOSlSbIoZzwCr9UkDpu8baS0jzape3Aj3NjIEUrN4lLDhHSJxYAxwbleDqnnvsrCZeNlyxSPQlszMhE6mIKdIamiGtbLgqzzD/qXsbIqZRiGzu7wqMxYMfR9qUt2YdJ+yOMJeZ28VjCtabreiQbGwGa1VuaOAKORRLddVZHAtE0n1PGwmSfCD6NtuzgOYIpm0nHAgtuPkQbrHUeqTTgJ5D5Ih7ByATYKu5f3D8SQF9nOkoA/gPHLdzAMI7ztXEcwXzP6naZCRJWH7NC07X6ZEIO0Ba+FlAAxUW0KboPfiXKOQ0laNI5EFNAhRZDRRp+myl4EoWqFshqwfEsl7dEyuUIJs+BtmQy3vYYkFmxof3+1UiOebySOxcouxNAFwDUCWspoiuD5wnTSU2jH1qmGGZZxcFs6d1ipxbzB8X6CdWDMgzCtBMWtL7UuhwGF+aZnOy2ao6C1aKAUIKIp+VglXi5S61wwruXzqlo4QjojE8/rx7r7TGdsADZDn/Daej2XPXoQkiwaEEh9JxyOtzg97PYDBAh2ygGq7A2HBZWyrvIcxgwEjs4LxqknHLhFiW40gmW3b64o+pRcDojG6fqcKZezfIGQe5OgDe5zKPOUh+XxoqnbpnGZ+g1mOuhiq8738s6gY1td+tokHsxI0LJdFoB0xTtLHpeFhJttcxA2agihY75ZK6pSAjeq6jFJcLTE1q1ajPv9w1iZ6C3hfonFcqHkCty4xFHipqRkzITsuIGl9BFAmJB4ZwgwApiVHkh9jraqcDo+J0EoTXWeLbrdQsk4mQkCOMzaUWsLnlkzEQDiJxIjKHQX5EEorKuZISDE5gyizzyyzT899SSQT7836jSQhYVOM3eR0/1MAPX4OOmwkjFGRwveO1B+r72HZHQrMWj6tfOdMpSo2tt1YIz5+UdADrvyFK5AFWTDH5Z6j58OYHvyaNGmAkXfew4eC8j/dwwEENNxYeEkBzbhXRuWTObnGIAHcESt8xahobIY6UWB5Lb43AUOJ26wzw3jXVsd93WnTYsbfbiuSJQHgHmOiWJhZnnbGFFJH5Y2inYjj0Uf+KBd+O32+wiA00pE9vlO+WOCckQBY088Wcehi1ZcI4o59YcerVjMcjCvqOMa+w7lchVfB7uNepeLDoVCybjN+3ThisTR9TrNSdJx+uEzKRyDSTT18BxZshvNTmt7uXhskSrS8JKLE3l8O2MicRKDQzmFjziya9IPOSKPxXkkji1e3y/53MwtLnyY3Jgui4LM18lu1FsfuCtxpSZomP1u6loxq1MfF/bejAClSeM9PekIh8mDc+zrhyM0jPVWCoW524/XuYo1d9fVflyMBd6yOyWCOsznrE/b/VB+clBX8/ljx3h2Mu7sTUVetm3HK5xWJVvwlK9SH6LcjCiX0w3balY4T9w5/U2Pl6LDsfqGsP4F8Txs6p60Xq7CkSHyk6GokOW0Ws3tE7TR81dG8+obITemHz4T7Aat6HOwP3vR4OveS1CREUESxTfxVt9fXrrws+d/EyjCSvHSpehNwK7QlfJxXajqIYz0ClfFSeV1C4694W3HYsLi16eLA1WsAkC15CTM73NtzK/vfvrZbS1jQJqC7nBctRrOHumEK+X7Y9uh1enKvVYO2TVltrcwudbpPIY86MFYsRDcrMylD/F1XKwwX1SDgx2GQZ65GwCyKrNlwGCHhuy2uconLRf0ua/PDbdjLaB0h8cI+BPO7y5KI+BJ/K0SKneZwXpyiyPgKP7CrR2Xls+trJxDef7+++cLBxvgIuEPi1Byoamwu1LA+ltjnMqZbi/ML0Nngnd/Mj8FzmCuqHLYaepe5YSbYCoB60B1Hr4SyILYQw8eUw7kh+GWFqWnAZ50c7wxC4ARYKQ1693nmyU/otyyH9B3QECCi3iEtDw69b546xR+LE5ZoQXwP+MDhVx/rKsQ8QN3uQ2DZi1i9xTSzyIj+Kk4BB1go1pgJfHt/xwWyuoz/ji0w31N4AmAK+DKwpx3bV4sWWKjxC9TNAVXEEXhU5sdLHAyGOcgq6LPt6aobot5rHIZcmQNhVAbHJxewaLo3NtZ5X52R6km3Sgb7USjwyQkHFNXuvnUH5DN9LIDk7Y6mvJrazZWlnaYfT73+FOvW02JF0gFhaFyObBNsVm8hCBkXiyMsav4KX+pcriZ1dSsbhiVNxQf8EmihTHnCC3NSJHHcu+kGJ7nM0VZXRUZHIxQwpS6XWIoqyudOc37q/3Urzsejca7NzpDbE779NNDBReBFnhRJ7unH38iCYqyZEHXv8g3Q9sGR71xnE9fJS5sZZX6OcRuvtkDbGvUf54PAIHcHZiV7IC+thUp3378DIBJxhZO9E7barp6r/TOAl8tKgIB9gXhDfhroZYc43prO4UaDUsQQskisjNS5H8hBz9++Kab3i2cwl+K07J8Fck5tRSe9obdryRut3vfhX34Dfz1n84XS3Fg7ncvEl3/+on/tHQkZSA38rtH8EsH2jutbqNnH+E/gAEmE9C+DM5x1MLBJXA8/7Fm0HIJFZSTrXA8/EPwQWSX7EIm7je/qi0HAUuI39yQwlfeXZPbmT/fbQYz+x/p0N7Z2b0eqCoJQeWEaw1IEWYMvmlVr/AXnQEa6OsGZaj5h4Mu4C1urAexz8wZf6da3aloaXNxI6iVq/gWp1NbMtEQRBCnt3PKTlWK+K/ufgh/L8QSglghEU5bQ6nnZwK+q0gXwC9kN3tAek9rzgdwg7u+v6TsFE4DiTxE49GNab9Z6OoGakO4VxXYTR68wsDyytOorCehMS8IzXUVFkAzg+kPwHoZj38D/72PbLMLviSBNNcGx2gP6WEgqfy00m8C/XgqlUAU/KFMO/Ksz31afuqp6WxPMILDxYHVQEmDZTwZzF0YG7RFGiD8B/r3wXCrdl12FNsoAvqe8gStZHcmlU0mQjSwSk49oaxqLJ05glCaua10vldawbVIBJqnd9ZbBrVKFyagBAzPpkRUAKGJXASZDBWAKY5Xei0fCbJpRcsHyeFFl3Kch7e/U2zxF2pdqmUfogaukegXFe1QabtYXbs0R07v4FoohD4xQKFRA11dA0m7JGwckWT3reuSJQhZNVGWiSZjkk8K+OGKHrcgB+z9X0bgDrALQwx06UhciYZDPIVLdxf7jhoiuIXyu92T8d6oCIiJGmXKd1mrASUrmGaNhOq4Ls8bSihgFPEWzFvIJacpSkEIxtmcpxVXAtM2R/nZrNeoxeQ+A2JMrxNm3Qy8DL/mujPxUKoue52UkvSkjh80LOVHSJ3HyxQ2M/znaLyNUHMZk4TzNpE4PaFyvjAGKBIZGkmBAq+HJ6L4pq6WB6dPVk48/b7bjMZeGnyYFFvPkjE9LcmuIm5/+KqSd9n4OOsieEnakzAaRrfRkrSATrAS1ukFC2bp+EIUrDuJF4AOsLreSCRokpX/71fXmHSIN2KXtmIdFh5wehPMAdznh7sJYUSgsZyJonU8rh3zJGt9aluIhiE1Gq+H8LsfVl4Qkat7r68lxUF5wG7L6zWx3Tc2cKDYJpuFGLbxN2Yd0Xn9sw+2ftchaywxnl5DFPgZX237Nm8AW1V31xP5VE4KG+Enjh/+FP6Mv8pawNlOSRiUvaclHoOmUdzMBzABmpf5CcvMpuwiruxDZL/KM7x36hOPxE6TRHSYWG3uUnhD2oSQjCAEnYn38y+3zTKNGwlYQXvXtnB8uN0CUs1pKexln2dOS4KYc221rjiMpkOf6qq+HHiRL0x01ROyKXV32iFi2jXqGMje60YmvHoS2B2KTiZfMn3r3tAdDqQkAbnE1oUzPT1pErMvzL9JCyG3Ovzxxj4ZiLM2cMgwpMasdtuXf57Eh3kaOJEVeZnZbRsd1mupzR06/7qk+yqs8g5icUaFj1rzKNHhpSfG1dc636+TMDno17dBZABYRBJGJYowtUwCrCcEgRkGKPUGXb29XWHqIRwPMQRKPSvhRvnVFvX6SfXErw237QfFitTudpLdPlyMCzR0GmpJ4POhrBXU0sFdWwWCxTBEMSJ2XIRI0lSXqXQZ3mp/0nl+6vLAE0tDGc6Vg2LK6IebhUKMUMVeITdyMHofAYoXELCBcdl0ImmWRS3P88zSpQpE7PSirBf1MCyzHtFHMl+vv1o+TSnGBBGNIYRXk7mJ07TlF3ufNgdIKLOoK449J2TpMrHl3TLG036/CycDm88wwMy650wsh8NeS6Ykk1Lr3PTcBxdVg0EAztYBLlRHWSy0W/P6aS/n7FlCU8LSAU3SvsswniBFPDXrOyB3bshquAdhOqvAY4IFTlBsj7UmOtZosiJVBK7XpiiVzCjVzeyglpdabqIH1ZQPJDhWhGrS9CDiOwIAo7pbbwzkJF9112qEEhKyjdPfO+eggCOXwmfg+++ItM9TO60n+GdyyeRNvlnn+dPjEPpSHxDNpfgAPngZ5yENREdPRoFjyiqWa1iRqmz6Vt1RbEIGklcoZMMia40K67oYzhaIsCoGVbYPigwxLl28VoXQg4lSNB2QhjHz2JSWYuFh6I/F/AnXT3ZJtQVpYdffz9XasuazAE7JCXWc1v9Zgh1oLHpwQmrvJABm1DBmcDNN29fwK7gXjtUc+/+AT2JQBofkADNEJ3FaLg2TeKFL4QQcfcWm3L9MjdPhQ7eptyQii6KUTfEGsUYa063l3W85zq0aJcdTVdW03+xw2kmrpayDCJWaHVcr2C5Wjuo9OyZkmYHlOIJafs8J5D5EkNDxDiOsmn+m+1vBMe0ayvL8yv4+IcTly/aEum9MOzkJ4S8dXD1hau+W2e2EAdbaxlf1Z4+6YO77KdBR/Cb0MFrCJQSZ1VMnhIIPO1x3nNDLOsyCM2bv6rSlWWE4lmNcbKttzVJ/F5l1O1SV3rwGlssQfFy5MUUteo4jtXQcXsMj8ISPDdm9Qgetduyj7ZISyOVQZ/yjbcNIukG2VuNSWKWuWgarW+A6qPVazvbz6jVbqVvf3HM4iqGlHEkch7PUl5mcKgjRqExZqb7URUhke8ZJmZO3+7BW4LXV4kmS+M3sb6qj3VAhYzg/ikWhUjCuPd5GOVU+Meysbxb6nc1xvUaroSJllszzTCxdcT+NNvI6SjIRkXgySEZnt8BN1/UARzH6tYEY5T+qIDDiM08NlmDihSit+YfPpgSsRjyYUdB/TDSxeAXqaFJJN9gt5BoIRxghkMaVx4FN00ddDhOxFwwHQaBwDPuc7ffXR90RwGyyvUW8aG4X2u0lL7wVCoZMYyAEplFxT9+uk7lN6h0H0p89zhcFA05sPLuG1iH7t1UPVHhHKFHd0D4RpRtKUOAh6CxR5dvuViCZqRyU5iDIg/C4Fo9DlUvfI4y25/P8pu4VQIvBOiWua3oJniSTi/rirvjwzs7G2adTpbJsuMCuRpXCCfI7bfpa7eo4sxjsR5BcqnOYJIHeuDSfoHx7qWuEoP2hXNKrzNG34uTFdvGpcmXL40vHsdlchzDEN5azxpH4UTVy1vS72cXwF1fC39BuLEXxWk9a9VvaK4dzQfWwMjSnv6lNuZg+JgJIm3uU1lqW63zpyvfdr+Lu3cCGiJ4tFtyV5Ii2v5CR00QM9ZmlbUok4BcSEHiJB58u2D9HAx334GP+Ng0zMDedQS9GO77lAo58qwiG3yRauYSxs8ilW2I3d0eAqbRQ9Su5sjB+6IrrLHpKkgpgwZzOpqDZeq8h8vUd4sqVhjW4EgPKdR3dBH/dPsaumoYreXkBIPGk88/GQC8uKunrw661FEEZyNVkNKBym7fBnMaY6T8dZpNtgM3PW0XOAgIncVAUikZ5xYeU71mIRTcus7egZyUYY0IB8ZbsvSZ9sbBy+zzKl7hf917qvoQtuPFLzk1RcgMFYbxVtxCLc+/fvnKBufNJIPt7I4AxgPltGiHt6PSnpongfwNozbZhRbUT9jKQ/U6wGiygJA1A8xujoOQtTxQeP4DY0ZwXWN4wQ8LP4TxUjH2NGecydxLN8+iJbDvmh8+d64NHT0eVtsHCq3kjXV2nItTHOnienzIYlkU7GGN+HnNxHU8/KtFCTCqR1OUshIrIFdglKmpMx7O/6jiTMd1QW5h+S8hdmA+heSbbL68r/pnt1ilbgm1di376NKpYpJa2MqCoJOzgSXjI3bof+o0nM2oPU6oR4oUqUVj6yT1P9Lvn1ivPuwJ65e/eesc94jtuvenKJm76QPz3nZgWHQyKNBplqC8GOx3i+8Nw/DRiR3Vr77gBR3IlXK2LrsZEZ/kR8+NTbFDDILwOpoL5HHeDRgx7ggKJm8H4ERwPumAcBLvKw7q8beoYiJtIHjfOMGjuNJtWaA59frpgNfcn4qnglbgoZ/VF1J31auiyAykT9LX7dlx3qA9KS2y5yz/ua1mefxLv8RLENE+qwXg0rqNO07cvCVXKMeuQCVB44ZkWHZ8yBcSLklJi+Q75a64u/XHw5APqYbCPmR06xkUuBQyUh/TIqhdN7shDpQxqFuDMf7+EgjQgE66xdxkP5UsNfqqxTuT2Tm3x7Gl8sMEdVA8Q+fE5ffHJnwWaQn2SENuuuMkpqmY/FG0OHMle8WCTPzigjAiCEPSdpE7zu4rHJbXuCRaaUJ4kZJG01/OI9E4FNMzYU8Ie2mbNczMB3B2rOxB5yNlznrjeAeEI0B5ySKm3Ot7C5+M5TRtNnjazbWcmZalO5MDzDxs2dDXi+qY0CTgU/ZSMDZsCZn1XFsO6xFs3nYfaA/RBn3i0Z/m15FVmmvGxrKqKU6FhmJaF4yI6YllOEMQmDM9jsoUz1DbZCaeBmb6O6YrgE2sVNXSrHhdOCqufpHhfY/b2En8uSXS/dF0cDETiXlAQbLmjyBKP0b25Y58BPXER0pAuVFchSgQRIjgumlkNc2gFH50TS7Tym6KrtrR/0Q3umrxYNuyQVBLcii7KGoZt6nIhsazxhKXD6biGAbwT4eHYV0CbddBA4w0OdMIfqYYnXKImtbe8TtdZnmiiJU1bLBMlzQiwhMikZPVIWZQ/PbB6MzawBSsVUeDMsvlrQvJQ3j5Ot007xIx/twxD6zswno3mcDgxQ/Og3dv/O4FeuHtrlJPcaUeEGjL89RpB5H4mcvOaiPr9Lf0OdaVAanuPiiplXvcIxGG1374YmWzT2jSIqnFk+Ed7bfOyRny5BZ8H81f/cOAnMa4OLMUgA1/F4AONGP7ACjDVfSDazM2FMhCIiWYSQ8hw3/cYKNEVdOUWgQDfPWP51/jWU7n63yyOvwHgl2ceb19c+K/5b6XntDzHrJkBdBgABP31sOacVpX+e6hFVeMK5ycJWa3j0UfOevSi1S7hdup3NPVX7FsH0CglSK0JIIxTHnT+khRD+mkXA+UUTs0ggKxGU6jUBo6oXy/Wj9hLA1W2emD0w/gaxkb9eq/Go2Swax9Rzj2AoGG4VaXBLdX0eFCRSMoaebatBIknppo1QDxMi+FUll6UFWfLg4gxSY1QjNRjDw61FYXyU4GKmpgUSG2A/JllCfXHhXPtpJ0IVp5GFfNyFVGSzXjU9NGxqhphc7lJheFQhngo/C61A2XNROvWh/DIYmTZosvSB3FOi11VjLJyfZWPtCoQW8oGQWmQBURJKIQL5DCFY+IUYXVTp3Qh842R04lEGg6RZMsVwvK8JQ2pmxRZif7uWWRJPUTQOBMu6/E3mJXMOQ3fYYzcYO8S58mXxAbN6dLlLbc5o1gOyCOePYHdWz+e6FNkGeu1lC4ntuQq+nTrVWHfHT72ZLlpB9ptKhoYrpS1mFeO/UwX36X0kZch1olgDU4pi/2hvdDmcbunW483LUFWCepZ4tGUTY34jNaMJvuQu0mWKJnHAI1+yXI5ierTdYeQHb7sUwWyYpD4fr4kziVxdmUlFqx8JkV/tbmWc9hjfwL2Yzcer+72+aQ1TyGfIvCwFDkB/zWucIGA86nDk4YKGZuWf9WGZIFDAyYAb5U4VQhRPIXhFKZwjrmhKMQZBFX2gSbb1nC4R70JApgffyWAEW+ljyBWEEq+MoD0TYkgvFGGCN2oEVt7AjKquwmSwDaqjBHZraTw9CkTJFoVCUODMkUgT5nBl6DMkXJXFhDkyhIhIawk8V2sIQBXKFMqk1aYKt22RKFsIXK5smDt+Sdk1hQnI3kbpE5thUxwWXqYHGKkPApjcxxvO0sVIsrHliIKOU+rVupHSjhytvkKKMZ4rMf8CGrsEpugwJ7Uxs6ZcmXhrDH6y1Xy5sBllnM9nDe3aVU1VxNcebCi+PQK+eo2Ti6b6kbbCokRFaaw3JKcWg725MhxN+RDJexDaZwAkWacNUZmD5WZAPOd9SpWLiU5c5RWV+TKc66OsMExXlumqGCyL1r9VLfElK1CI00vaw/nUyi4dbUsCtkvxRYHNSut22RKJLGP29hrAzM+0vQGZ3+w8wG2O58ojMmyS7bdFrFkJYe1t2RynXPBRTZs2ZG75LIrrrIP3l+dn57H2TXX5btpsT32cvGRK7ee7PCtKAp8vHjzse9NW0yh56L0xaU2ChKsTIh3QpU3on22sH7ifPMdVWrUJh+qRYWBaD3guV7qxKnXqEmDTebYJ94nCWZI1G2mJM1a/GKuZClSaXxw1KzMhSGnk4D1+sIEvR75VPqz82FGJCEQC7GRzhZj/WtXV756CtRXqIEiDRVrpERjpZpIaqqZ5lpoqZXWHDfpT98j00Zb7ZRrr0IHHcHjpLMuuqKLy5jUDhRbmNnsrE46aPimx013ePY7wB8LW5oMvlSGjTjokAGH/aHfSadQEfHQUy+9McfUpUOvHr+aFx+VpHujzQn0TKPdChyffTGIZMrEUpm28sOIr9NVCQmLiGrRqk27mLgOnbokJFkgwHx3PXLPfY9TM2oOlBhJT6QLxgd7xGI5JJaJJfN7RP1DyX7a45hLtkllKpaUw/My8Yxom9R6543Fx5aeR/TJYGTuofT7jWXmDA2m756xyWzm7Z7uxffcvdYV354Jfo32TNk735xd7e12DfYMzny9+sJ2zwuWCmEWBGHmB4lAVgBhLwv7FwuwQ31pC5YCHBRAAhDcHAgAIKk95Y6RilXieKJYDGuiUk/Ge3JEFVFlVOWq5XtB4qbcklP32SR+z/ecmD4hC1n70ILKKLdyL4G95M4h0yd/JxCIrhvh4IA7C/c7mOtTjcaqnyj01bCUh+1qnSV3zm4du3P2bdjJP3guwdzukfexk+PD8yP2N5i98J4Qq7Gsav6xIo9fyA4kPKlkMxLuolJPGV8Gmj/W05+6m0telBsa6ttNYpBjC1NDY3tHXHVbkKr8ETJ1oVS0gv9gq+1iSywHTbIO9GwYeTXv/u5eynYA) format('woff2');font-weight:100 900;font-style:normal;font-display:swap}
|
|
82
|
+
@font-face{font-family:'Geist Mono';src:url(data:font/woff2;base64,d09GMgABAAAAAFpYABQAAAAApxQAAFniAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGoItG5AUHIQqP0hWQVKEPj9NVkFSPAZgP1NUQVSBOACFEC9+EQgK8mTbBQuEEgAwgZVSATYCJAOIEAQgBYkGB4hDDAcbw5c1bJt2M7tbVRJAlsiNDOR2kIQS+Ssy4Y6HjQOzbMz90f//OUGOMRpUN1DL/hMJxhmyXe5uugdqtXuue0yY04k9dA91tfF6upgbQUQQQZanb3vGFmLGl4iwbIuxS0urM2/LFaiRudMd+hf6hP97YIpj85q195l1AfJ12fEFOPxF8hX9BN+iznrCjwwQETFJzIeJlJ9yYNok/3orWwS4TfyIxBWfJB7yZzpvv6Q7E0EVYmrjMgxUGaogtCbO/jy/zT/3PXjAI0XoBzzawCqMxlyzKseq/a7b+au3/nNdrnSZxjL5ilEAPfyBu7v3t4nORgPOEsiigAKiiIK0DdGcHishBW+gJXhEiX+IyX+S//zHnuRjTwimCTTooIy6T6xsY9bN23k3pxOzTkT/vqb+d87F7tOt7hYaw3aSR35+lI/sP1z7mS0u1lORWjO8WM5iR5ABeoQOkEMOkCPLJEutxnsXxzSxyeaOCGl7RnWlCRwhB8D85zT7dmAyHiAPcQZK6QLwaQ+nffdeTlV+dnq6LmHal1LCjmMrtiLbkoX0JbYkyyCdW5OxaeoXkFOhZY/mytndAN4m/J8Hyt0DsHCVcmZz0LLxgKha4Rls51Nn/z9SZhTSaDSKjIQAgb0CYxaHm/32+aXww+l/Uf6iujD8XPS/qXLem8Mmb8bezCYbjtx8+kFinLh2ZrVK1wVyYt0VlX0vWjF5ookTIMuyOytm9nsXZV/f5IdSm0UzCIcwJ5S/wTx7/L9iIW+Orh2RpX58Xt9XBYM6uSOuveMO2kIX6aThvLE729vbAbQTP4D/U1VXfp/A4o5Hv2cqVXoK3Mqw1SlTXsZMWcbDP4DHw+l0ECU5AiGatDsN9wYhhWJqm7LXMrVWt9qmZMuYYc6wjBn2/J+paTsfON5b0oFLxa88zuO8irXpFPFUdK5dNMMlIC4IklrydE8LKC2XJ78ljicPyLvnIRRJKqbwccEantKQSlBMlVW0TqFyru2qcVe7clHaXYhF2Ro++rFVefxZzBKRFLUWsx1ohC4amt9589gu9fP3y5IPiplldWziWEPQd++p/uZn8TLby4lIEJEQBreVv/vq/4+1/gPD2ZZOOU7jKCkpKuINb0pGtry/QkjMeZc4umgjrwyFIgr/bY6Pg3AmtKmCiBA0RLSs77AdBU5skT3C3ktgSEgwghA5ycwzyhxjgjnJbFFzNZHnJuJuNOqthdlsCzqaYKjsPxVIRBsLwok9quYmm0PwdIK74Ip3fLBIoAD5WdX8ybkb5bBy78Gj3mPkVl72vP/8ohfSDE6V9TaU1dPk7DmP99rHq2T9iF32ZZRHayaYxwUPBNH8A56Vqp6ouJJKq1DNKUo7DnfLS8+uWNx4cuUC7CKBQH/9phViDIIUAAcI2LLNVtsEm5SAYJ9RAbCbNBCgJ4ss2F7vhU4QXPRZPAkEGID6CgjsCNRBdvGITgcCoFRxkH10RhuD5QYhEUQQkt4Iaj0lBWoVw4MLoMWGJ6gm/fveZWYhLfs0wbDqU3oIODf+VL0cAL3ZMYUKXa6bK9fVL0AY7sj2+gDkQuwyKn5mwYZarl0HNW16m9mcWHlmPfXHPli92cBpMG4DbpSj/NkXJKhy5ZaqiQbYKvg9lyNldQIUE29XkdBHXm9f27Kt3i9T/+Qi21obQUmjlOKe++QoNkuiAG2ArY/c38/Wet3zHnCTP7iS6ifX+bAFit10D7p0mqXdcLahLGIgk8f3j0F9cwI80U04KyMGP9pODEnct+6kqxpJ+a0hhmySPMS8s7nqs9TZyM+uuFN8pkwhg9QgIOCF8pmKFyG9xFeb4OtsVQOlSMaUqZ3iM6ir0qGGCoJto6gS0sLr5uMsozYReWurLuclYUZYTqzmmqpESkuCRb4z1DrUoAX2kHzoSUyQ55yBhCktJTPrCVsGmYAipLLl8zTTh/WYcKQO5IQ2ApSoCNFIcbN1QuLtsVK6qKoxgcFpKhJegoVnyiYAIzgfLK2ttJWtsladlhCGlnISuu2dlSKxhfAMCdPBifAMWz44UcrWr6xmrQNZ2KZtr2pT8qDoUL9FUQR12VYKtNIDaBl9iGHduAmQYOIgLjKWlAQzECJOyHCA2oHCzJ6RRzQnn8hkCAc7kRMMgoiROLtUQAtszr0G2MPe+vm9p1AqWpgrh66upOxZggv32okrZxWtME1JBANJ9oKevB4WWe/bZcRFMLl0CDZ6tsCuW/9jlf+kaUud7ySHJBfFu86yaE3JTVKC9Qz4mvv3naMZQAT8oUId8wBw1AlDVXAKotTmPCEyY0iEEZqnDfzUA3hEuJHgmTDir+2cCDIxEC7z9KDdTCRIKGcSiCojB06sqGKiCKLIhI/k5vN0ZdKJUFOJhGSCWwWVI5BJIUQmEVjTF2yIOhormPqqIYEkixSR0aTY/A6qdKBC/MUGPavTVLwTGH3utRCx+0c2LX7PsLOYB8IDJh7iz6oEHAUL5lHhAvbe0Ha5JgbmBHDaI2CcP8tUCnFmT2YceM/KjmUA8t9X9gPGi8Z6RgG03VF+yVQmR2KwhM5AcMA2NljYrzmSwDMBvaKVgBYf+E+ZxSaRPQ0K9ypdIaWmarz/55ABIs4q1M7eT3qtmJKQA2phsfz+HBRB85+uIVFfSwsevZIEqSiQwArmWc7lAEPJZo0BCCWWkaaqMNvoB2Fk6EVAtYSFFNy80YUJvTk5wUjkBD0opMhb8Bgap+0qaIJL1U8GgyFOeNmlgB4RUKZPiova8LBFdY3aV4SG/r8kcfHwCQiJ+BHzJyElI6egpKJG0dDSoekZGJmYWVgFCBQkmE2IUGHCRYgUJVqMWHHiJdhQZovVZnc4YcTl9qCYFyd8/kAwRLaGI1Q0Fk8k6bZUe7qjs6u7p7evf2BwaHhkNJMdG1+fm5icmp6ZPX9ufsPCBYsbSVXNVrtj4afnl9e394/Pr++f3z+OF0RJVlRNN0zLdlzPD8IoTtIsL8qqbgbD0Xgync0BRJjs7O7tHxweNY6bJ602ftrpEmdkrz+g6CHDcrwgSrKiarphWraDXM8PDkaH40kCiDebF2VlbJAkFIQhHDEQExGIBQgASfYGhwbabvAv/mu48duKJeeNLo5xeNkShio0WoHyyAGdnsAo/SyDCcoN1SrAgf40OYXIpEQlwPHecRk417gLfCjHHJFZCBXoiWL8yz8NWru5+g7g3mWvgzKwSsB4c0HJJBiwAJ4Gu/RrTDGS/vKBHDI/HwmaTbxcTsNMMNNSf2cZhAuEJw/hshyGss7IaXcsZUD5KK42So83eUz9msfxTJ4wujppS/HJwmIBgJODHByHgckoc79sO6ExUMIkmG8XnwVF2KtTVVZd7RDOwDm48S6hVbSWNtAWOoq201mcl/Q8/ff6IwaWr8R44ew22JM607MT8AZnd/nTCpo6+Ug6oe9g7+9CteqB/w/0Wk2gCajqqzHY/v0fzwLgjzvEneJy+YfjzyOfvSm0Pxv09DYkCTjOcy2QeyB9uYlMvMHSZPJ4z9nrhiPqfHHbTRO5v8Ne2Om4ckeNSr4DOkV4C0KqGp8QnTSFSVSbghGIIw/V+z/nmPM+ORWAnXyqOg9pdD9koarG4IitHD3ey9nOeqLCBzvsU+OZWs9Vu8vHjzmu+Oh+mPDQe1z4EIPgszsOhoAN5rrqb3/5xwEEDAMbEwsHjz/OKjW+RgIGVkwVgQleqRgU3d7OZqI09DQzpFKtOnTBTNUXKaNcQ7mQUYxK8FpNR8KtZxprliAJ3TbjjbyAPNFFCPQ32yISHKQJZcGF0GfwLYuxCEgBZPDVA1rIUOVIVmW4Su5CRqpGE9kmo9UAfiFj1aFfyHj16BcyUSP6ucguI7S2QkbUlJ1lxjS1PwBI8DTanwKIC45BjlzjWazAAG5HeTx0n5GcM0UPl4bxyOKs3xKzcH0r7INgPkg4gDF1vOfakwsT4fYuFmBT7MfrU9ha4V5xnA7J6fMAYh7uwT1C6ajZRDEQXzqIxR2vqwl/p0CBVw8E4ArvO3tARdcRl0lGwR3jS5bi4T4TIMDQTmleEsvB2MKzPRjtlVBSF7DWIL1iOxpTPrA/KmGg8/SMUVocBmIlA6lIHIbGvlqYfMwO7N5HjwvRC8JW3hXzkcwK1o/S3K1V52Tnj+XSCxhb5OWpNBd3ORkOBiJ2X2AOa+N91nZ7v48xPCPn/LdaZ7V3ZTbrmqYI6H7C0nyNBeJ1Kp2U6cVjnBeTLZ/42G86SA+P1I96jsboYkifyS+I/5PA4sJuoCb1qnPKfESKhLAIklh69wxCjOEDBvSHeETmk13o6LFkVCSFlmd/vf2/01e688nv9mHQCYK97xsjTIDD+KY7IFaU0IKFe1cFsTnIaFh2P//exAjEzrkl8F6ClWp5iEjCFaNbgBELY4VEoCz6tNvEx1CnHVDDBUYakcq29QZTUUCdeoxi1KQRiOrvwfYpcV3bMT0qgitzSSsyXpSy8r/jHEkSOy2GCo+s+DIJwjvt1Kb597uvY0StDBWbe4sz483hx9gFX5lwpRv5U+ZO+eVtVr/Gsv4a8V+dyhuMopTVbgm2tcbC40mqzdR1RM31cV1BqhqwcEpo+8B5hkqOtCJZqkLygAF4Z8KKRhSZMuxvXMnIX1z0tvcEQo5B59zTDrMtN752UdthrL8B8Niq0gqQU/Q7we7qH2rxu4zSpadrBVPCixAalOgvtHr25WavJUYw6yrWHXruc0yzFU+5Ey2hvIPe6pbjyNjK6QiHtlW2U4vJmxfnOzQv7ixKyxZGnxeEVGB8izjnc1auUd6ovq9OCfM5tgYZVG0LQMtYUfKCm7l+vJEe+Rml1OVQ1Ib/U3dZau4WBA1gaNrNLvOhQMKt4o7QG66IWwYgEcOmj7FNJSuiAaVNqDyArojV1IlL1P6mhajJihCaaXfNV8qKiUzVFwo33liyi7h5z9WOsbIm1UOyblkk5/cwK51FD7CiVj21Pf2cS1IjXzhwjbpUjW5ynmji2Ujn4YwyGJTwz4wpNYq0cUi5+HOFsVx5q2Cm/d80M2028JU4ysvcftTL+CkvqCvyCL+ucgU9kT/AIHqM/fYui9DP4a92Hcqsb4tVxErdoqa86Uupne5uQQb1WdFO2IiEp/5zwl1MVlfsoSJZaoJZ41CbqOdViXO0JPOvHW5lLkwx1R3M/Slk7/kpdEqhg76792gNp146ZA3Pmr0Xjo+6aV2getgVWvLkNvk3hemajrFsjcu2fr6OiHIXOLqX+fm8nbJ+OnIBsSl9+SVLh9K6HbW+VozYb1Fgk0vyAeGOaj0nXADY16S2CnXCCvFgIZQrStJpV8/PKblJu7t87sat8iNzAZyMEExmLLz456kg9ZXMzJ+qfTEFsyHeXB9W/YL7fkjC3PXrFf/NnNanFO44j6q7d93Cn9zyL0dL1HmPTagZypUe4VidUp0NN1F/stFuB93NUxpyiqQTfaLcooI1MlAI8tK5Ti5+mEzabLYEuOTd3WRtl4SEeym4VPibW/7laeICXsRzLGPnRhOHvJvsWgE+t5BPulnYVlBIyILH2pmXz10ka4noi2N58rJ1xfTomnMltpcQj8l5zEBVitEVhylY79l7OrGiL+gPvMRd/Fxe8lteJ/XZ7dUS+bgxAXBqhol0fkuuZQYZJ6VOd26wP2Qob9BFbrsUuI5FvgyhBjOXFL/Hs3WUb6cFVlt+JCykUzBSAALdfCcfN4gBLXgDpmiH4c3kn2YS7vjdliN4vgxuN0crD/Ubtz43u8m4DRokZr1VDVTbTunmCllOD2hO4lhLTsCxokUwngxUq7+uGvyDcENG/m8rNY1/YS3RrOo83G9XfE+CxgIkqUOhc6yZsQ1n+b9iE+Lv2erR/nfDDcLWZPTAqdvYUOVRO+UdYicrd+2AjrMwpBNyQEcsO2hHGkUG0p0AafFZQ7r1BujcZA74IN+68TUAWPx59XTVwaaCpazpKUy4aO1tsU/Ww2S7i3seQa79ldpwbfmTegvWg2Kexn3MLRqiZOUjkFeC9S/+mvr3eL1VrNFWrSFuoa3XL1HHAdfF3oXDo9RV0CUfFY4TKbbdZgEeFaIHJOGQcQ/cfsbyVJ6sDKDO0ECx2cSvcZUbp+GGd+7/Uu0OOdKSXFRVrw4t3MV2VGKFGO37UaTIUJEcaRIiyJKorAK5dSEPFpf1TfdyrHfQrdEYVfhEmq+9aaDqhkNn0y4MURpfcI9wL6+y1W1p2W0mW6GJwltY23OR+SLbRj0r3cznQo8ffiv/PsBStiPGDYWixP5hIx5guTLJgBbsRqKde7qDUvwhbIYI/l5XbOlm23qbOx5hiXkIAUFdGIdBXs6PvHtzh9rcor3tTarpIeNKiCDz++74WdhSPEe+BKcE8mDoalQYLlTaOcnkrnNUJENq4j259wy7r+gEecfcXNXDXpuanRhpxT3mUL/LRa8bIzpI3T73Ri+NBoOQMTCBttPCNBw0JByxhp2eefH1KQ/n7nyLNiM3LO+d5rEvWV2k7A/P/Ob8uuxCd/f51HfY464J/dLMinbK0+IokyuuDXbcfh2VVFdYObkV74G6M0oWrsPyPcC49EHtxEj9FZf5R1bbwsIU4rDovLfruw7e1a6Rh+Wg9o1Lk32UOjxzSCue0vQq880Bij76J503nTsRPvH5KapoivVYcehTyl6XU4BeeXKqZICWSz0+MVRgT0iVvpbU4tD63ZG9Aa9Fb/I5iL3UNOEy6fRHkpgGivLoVNK6nJvAeoqOprfMrc20MSr84YDR5EfEG1Nt4sXxBTPZo9Eyis5nTW7bCuiV61M7w8QVuRxx+c7WVGskMJjbvmNHbudgwHpT30bR54I9g593O0Hvyj3h3bM1ZJiEgLTcNxl2XTM3YV+eoiNwr8nVX4YkGRVwMOIwBBDJxlSbdDGImLTeFmm6/sbwXwPkqutmvKzu5pcD60qf/rfvvT0ZEwbvvR2Q1O2N+6l7fDNglaMDFnM2j4gwz4viEaLMVPTN2OPpeTvAjo8mo8aGv3ziPb2ypXngjHAr6J1S/5zcRwFGaJuQPb0cb96DSfpg81dYsnqhHtCVOXLny72xFflqH5CVU5NR6/L0bJNjiQq3zU4bIhGD3ylebG8Xb/TDBgLYLgSLa9bglfKuwTOnTg0+1KVCdaqe6h3hcPV2VZGKMiJTO9e+EqSbooZlzRo4nHT7WjTOy10r/1HXn7ssfNnnt2lPe5OYqLV0ut0hx6aIzVVjZqmQ2wmHXNQsyB2lHmnt7nLBpLP1kc9RcjJkSR80dY9pybeOrsQqwZzpTWSsP3HL/XLfEOkvMu+6IZT9VUJX3hxfNN4DKfyYLGxBV6EWhmvNM31ED9MrEeXRxmmbiQBiswdgYjPQl/smbJ5RBpZk9CpxQghBLjaHz/vQGRReh1fRkMSrsxrpIQjHB8zWPqYzyaCFCCxUIwG7lvt0Ho2wYtX9FnmrxWiLZVQgL/cWxabeyoGsfHxx39zzsZuJrm0+X7AtsQ3mVjTwy2uXUqf3sfsgNaHAhMATdPqRoi6ulEGRAX0fb8ZV7NAB9JjrbGfv4fqDp+ihrsd6XMc5HjiP99bm7iVfw+43ruYBFNtTN5t6+/GX8CcKnCuGQnyFU+YmiVjF7fwsK2wyvHg6UVBGjKfozqFc37uszgoeQR4sMZybQlR0oaFGIa7HBHxErlxq6n8R1ywBVxX3jB/Ytj2+P9yCmUSTSfGfq3rIE8KG1Vc6C1hxFKcEV3IzdQ5fDF4dLSY5Hj7vVCLCKIkQESeBZ8eEp1dnYyEFrhKS/gp30XaVXrLuibYlrJvYzRWemgL+4mZPXdVjN/Nn/CuUZevO7z26ZrTPpZ+PRpHd60OEXP6htpR/ENZ4lIK43SpIuFXAVExkXJYNFGWZz7gJS1uzpfXGOOOUw6jFw4OplCGuXQ3uX7nVG5P7tKtAUXGGdqjM3fT9yq0i54qfMfP8nYLsOkFIfX6dBAmrXRD3KDWwfzDV5h8C/5Yu0IwsduY2WrcsRDqYlXi6cEshOc8/0fFjUd6ozTIRIi250Ro3JJQOghsVU5aZUbs3hJOpdIBM4kRt0Wj7ZxRoin2jLvMGijLPD7txz5DTMp+s2ZzvJKOeFkR9tj9RhU7S37ktDH8As9iCBHi1JcdL+G4kbmwiuC0fVRHnCZAsmQYKbZ6b44xTV225fpM5Jo1oWlyDsHnC5zfl+mHEmNDWUeR39rjEgJIodbemQWuBIOhuVqL+dDgcTIPeFSrw0KW/e2qlpiAkoS1RyhbQ40anP0BYAUm8dt3OX4imJ48Sd1Mgc4y4Xjxc/fXKtflOFo75SUCvUHfy6sdf/PBt9aibfx/I7AYVDbUcLWeB2IQTBF4laSA+JEDCCpw3dWi3OUupglwMuGhfSo7aaYr3d1IU9iZH+pdUEaYo9zPcl0FAuZ/09t+u8OMNUhupk9LGKGUjTH6rFOaJEI0NyIrxUQ2zqByLeC2VO+SjDL+Oo8FUqvXj9BcInCzumTh+wcbE0bDOaxVMxUQtdNTpTxxb3Lfxa3FEPB8jxQs4CoVq7KyELdO4vYbtOIc2m005HvpliZEvwfk8MQ5Cx64P5s2ctg1y2EK9AdUrAa4cTSEpWpe36fTqn594ug60t9vQo0RkgjgM8+Mu2cyaUjH+iRf+d4WyQNdMhjkgc7G//Xb7qvVPDBmGBhrSBYEYDXqXqZFu0fCduYyAecy5+syiPEeddFtme8qhOJ03I/jlc6oMGT8Owx8y+5X5/k0BaOLOHS/svJN4kbnvO6bbhZgy/lFx8D65duZFAnTFnh6VkdTEGeOowWFxcgXV7acu9pfGJDaZUoElpTCSUGr9C3FGW5FXYF7g1V15+PfWkqTEIlfpkTQfkL+5dF2dLXB3pi/sjsbQfLBFa6CIPr1lZNZJOkOUzdk87G80y0Nyu8ehU91nT8K88G9gLuDDiH+of4Qs/n7itbPEQwIKikPtw+koZQ/ovSY7RmJWikVuWSl3JlHw++N+gy6//xKkIoKFwXnqvjcwFu7loKQG1gx7Al82QP+o8B5UtPvsjrO9R1dVSPfh1zCCFccIW/2Jbm7D46zUiXuP7278OvXxbh2EqdAESlxVXv8EAX6daflPpsiZsmt5eQ31f2LRVVu7Gh1eHHUtlO7mcK2cvOKqBm5Td/Zds7VWWB1ec2xmDSsCZDPBtjRJnpoKBlIdITKV9julT0VHPR0YdD8UdR+Y6av+N7QKb8/6wHjSspybUMbpQHCM7sKJnNRYMhCBUI9oYzyJtAxpsNpJxqX/owD1Zffld4rOdQtAV/TODymAfMrZ6cyQofD+/QLzb9+HFcQCRViSUTywY34CH9DqUzY9P0i5cNGPMY/cWbubAsLyiflsfjzBYDgpg1PmY38RHnaZt15ppIzC19ICDB4j4nLrsklGYWime8DTp9RHLUiYJMtYscK+gaAb7pjQgK2XpILR91KPY87YJclv8zuBtNzTr7EmiSRjPmi1ecKUD+J7EZGq2S9hpJT75yhZE+N/w091Wf12YwbDbNlRG1CUk+Ne876+RP3FzslhJOSIS/W+l5KsezdaEiOIclueq/LqUVKgRQcsplE8aB/PWrEIGk2ECDLqUvtEMkyjEfmwZvBwNuntqTeCAzZTHwYzfJ3HbL81bKKBrBxdr4LbPYG2Xt+ayofzCxGjId4JORxdkD5uRHD/i5WFa+jegGePeVyFRtCk02UPEWSIJOyumMWTMrmNJi1isZhdFi1VgPS14n7+vf1fw35zIoUoOsas6FfHSZyMPRn5bjjYlrA19rqtRuuVt74WfdNo/Vt46A2OPfC/EjRgb4n/FJpmJGwuzM6/dPen7bk09sTu/4N9sGFg/9H/AZkZjY6k09QwhrWulRpBLXIFhrqVXhn/H3kwpyYAPj74zUeAjfzXASC2/NmAiK1gGWl+7se7rUHhiypVFrYAcv2UQN1Os8sGqfV3bfvg+qozZpCZg2eU9dUKibCzviEsqzHV1NfUVL9H60oqf26DLWbHXyqe8M86LddA2oEOoU9pj/5q/z7WrqdvPZI9AMRmE9ri/CkuauRvE9LF/HrB7Tb5h9XmDSUjzuxjp1h8RoffA5rNzlDZ22Kvt2fpjab5V2s0zPyT8D2cXLseGg7da2bIuwxDjdOe8lsTZh3/QtuO3wQqp85m/1Ul+O2G0G38GHxDN4n2Nmi2i8zHb9plguMNH9WyL2Yv2hcy9VfwdW7uHPh+SbdRP3t23HISVLxoYHXk90g+6/NWwugp6OAlX+S4VEXYwCuI5syOpp83vLjg3ydzbZRjYPLNxmiXy0b4fUwsyWDA9r5hEw53a/Vt1uabe06eePUh/pgnswnrl35WqdBHTPJb+07uuq+lT2ph6h1OhEu3YcBXMFTwSHJd9rSGekSjfZhqppKH55pHeOC27mjXjsuIy47j7+471H/o7u5vrFueUiDd8oS35/aLXM/Di/Y9/vaW1qO1YCRTuSpF5gR8nenuGcDM36HBEOj5J3gwCCo7psZQIHgP0ZMJuKn9nDIcD9l11RqBRg5wS73e0CL7CQz8nKHOclaICA5sHobitwGHZuJEh39Qpp3Wh/XTWtmg/35O8UQwrgwoUz5DDGcW4AWGuC8V94oHwRMzv23bGPcWFHiZxtgRJLJZOXgmk5Zyoj+86AZ7MqsAvZdrA+9obmDvlz1A5u27uNp3w9lLPLDyKuJ4KBXdFVzt84OBlWODgzwGkOhc1ZvOVv/2vrwpm87/iQBmZ+lf43qHS/+Ck54JolJM5LzOyxYmlTdHJsdNSDgbMR7uOl+49WcJ7+355Eb+NmWkIRVCxOOIp5mtEzAvIQehBZ7ikYJArbCljHWybGS8VThFOmBNVOXs4h/p7ZVfELGaGn46b8GhipsjeW4RdKJWpl4bVOhvBd+X9091tylrUCvszvzC0AVSsVYySmImFx1qWXqe7efTRpjFgrFHqVF9/md6aVtI66rOu7WOCvGoIMRiXB9/06IUTAkk94okx3eBMkgfVWtou11LJ9T6jyrJ2egc0Dnc3UoTicQYix4jrNR0rWtyKoLlrrKIOC6S///K/U1+mNadEtqstNjg64syFjCNqSnvIYkOwSsDlQkZrZAcP3hkXinTISlhk0E1zOeyCZao7tUD62RPO9SguTi+qR27djSDLW9qj8c3t3uXM6Peaze3x2xoj1+/K9XesMeHelC3qz0l1yhPO+TXNXfY7c3pbVuoGRMokaA7n5ZJNNoA7NQGNVJH9tG42puOAvkdpqJmgpB2m0wpR6dbuluXFKdSoqt5mzNt0fVYrblc2ux0dprDSqdF7mnuWbBXyZrqPi+c43BPKqeu1mr+WFv1a4MWbO+fenqug07OsNM8ne5OW9J7RfEfxlOiDkvHGTEB2cWyHGkzqlA0Y5hElh2SZqyI9l4AnF8mFDXxeE0iYSP46iPFq96xnwCeSZEpkL97RLAn8MdHAR8V2z9ynH7weEki47bMU5RlQ8Y1ItFly+ahRmOH2dzkeGhzmDZ5RL+OicVjv4rEv3ZId/x6cu70tm0zTVu3bZ3B3caZ4UwfcLZN3yFQkJwj5mrwcaqBx3vfaYmsxStpnQRX0xEfUq1rsHaXO2Be+Bhjh14egGhauDV/omhndDHRcdO5f0XHk4bl3MTpcMZdJhsZvQgv5C2OhzGSN1AZXm/tdhRW1qwnOlfKXU1GfaJDa/V1qLWhbE/BI59wuWjjF0L5u0fvIK5Nyke/vyQOAvuDDwbPHPkct5B3GKta/w9+H5SebclT0O1/JEsTu7PZ/q9e/d0q+oFM28iqq2M1smM3DH1LV13e9dngOzAyePfzogKME9t1a8mpNnb26WEo55yk+QgaYMTTBHfo6eDV4jNd48nA9q/55a8YRzGKa0/ruoWxPrPoue8PXVWpurZlzQDuhG+jiWv+ApnJneRwYixTWbIWkDp2PWX163wGp7cVtW3tEgpCpxIuPfsU+D1jjdDRWCRptbYwFp1PPMMdr9ePcF9S7OQNT/g/TqyTxaMBs7W8rLR0uBFlZbqjuWAOmHffBWVnPlxaKjRNCoLre7sT3TEeAv+HSKCLJhfT8CJ4J5p+Ysn0ymhqa7pHNNBT8ZiBjLb5PFIzfCvPeKvGLiU8QOYQu8WzzbtYM6w3ORjoqqwoKbWUs+YqW2poCZtb+s+AkkqroEViB8vj3hIv8ASs2tMs4U8S1uOLzYvzZerTIkMd+J/LEjRQvBP+1wn+sldqVKcq/e9RB7VnBmZo8vxBXRkG3fpNfu4HZc3ioLMrLHjzHAUNAtcOfzU/7xWErB2V9/1QAFMhoEuDmQ6t5TaS66llrpvc+krlNtHSDNzRjvYGNMvv6LxO+oU577GTwzTXY0Cu28B166t9aLnEBdIjY2d5Z3k/lS677LB6clijyM7GzsbORp9WO7z4UwV2jugc0Tmic8RhEygkfaOjYSSE2z0rc7hXtdd/l6NpVbY5m93Wnmzehg1zTLun5HA3bfU/Z3tu+P8YaG/YL4dnzBm7xwJ/W/2GbP+d/j+V5CvZcQPIDik1k+yw9oaZOTy13S1H/Q8bdih5MQsm7ziJ5PBsandvlmZWZGccq2EWEd0B6PLv4P8fr2Bt0EZt0mZt0Va28clk+/qY8FolbAgbw6awOWwJW8M2tq+m2VBHVIY/23lTEj1t5LqbudbbVGct6Avz5UNrOX+5Z67AeucSdvo0qVmAZ0EdOMW72FpSnWrnQ2F2FptWU/+qEJbR5wpaa5vM7DW4JrP10Zfyltm5zS3ryTZlm7Mt0O6LYI1P6O0Gd/L6uztZX+40dOkCcH8W34KlIOiP6jMLqSO7suJIUHbZ6pfdFrW+1Zx4Y9e9lWKpq9l6iIQ6F1bJgOrArSFGmZ4bDd2r3fhqYqzXc0gjoAYVQDr4UTx9hEjHM9bTjEeP6yEVkoByPGO9nkMbAaDMx+yxNcnSQ6OmVRUxWu6tGkcVRKUllarK7hPjib5EvO7iB6qixkNdQ4eNXzepDueY6PUWohVO3rOFVWOvSVpdz6hprqIQGfTEagZkbXGJUaOryObo+kExEeqjl9UWL7uGHSAHupRtEG+04l+G47C48YpL2AxxROBPcZpJuV5KH70siV7uJjuSaUFVbGNxAOJfgeOwuLGnSjZDHBH4U5zmfHkyilTx1FL0QCF2kdTXei6wwzE1LRbCnMpsaDAn4hZjWrGWR13Ppry9Sj5C0b13V1jYqJY9eelOfL12lJHurYoNpY/HquF2SXNlLW4pA71aNdvKVA90nOPV5D+Q3qTcwVWCgMBAmSobgDITuFZt/kipHHoaKn6e+PTM2hzkGBe61t/c4j6rospoQKMrbk4rOtDZaqtvqCUtd2X7e+XbP0Zx+vV9vAhufjh/F6PWCRj7mBuY19yb7pz7xv1BCAuMBfGC8YKFgmMsjFnGrGVymRJmC9PCfDxWrU6s7l19iiMpbCscLJws3MhZyzlCqoqCRXTRVNGGop3kT+QGcjt5kHySzxVGsbJ4C/cY9yy3g8cuqSJS9293Ee+92Ev386+oqmyv4Ijgrf4k8AqZ5WvKxeX+8mz5RcItwn3CyxZeMVBxb0WdX9Cag2t2s4A1yNpfiVUOz2D9Yp58XRJdla4KqQpVpaourTpa5Vubrp1ae986v3W6dVfM3v1d687LbsqeyN7KRdWW6l3VD+UvFBy2gd3OzrAX2HvYv7O3sw+zqxW+GlNzsuZ0zS36ftaosNo1tc21ulq4tqt2unZP7fba03VT6r6l/OtvbbA0XNVwXctrbGqUNZoafY2XNHqbck2P0VoOxmnnjHJ2cM7R9+lvepIr5PZzz+ce4l40hPOCvAxvI6+Ut5F3iHfL0MRX8jP8ef4t/FP8ZgFTsFbAp5PqBNgXPp+oftcJFeBtDjwgAxWgAAAc3P3KfgGYC09HT4qmXAFP7zpPmTP+x1f0C7CmbH1nwOb3ay/yv/nW/LVKC8xBFgV3HVDAFhYADpr+OAZswAQALYQE8rXhEqIBJhCHPw+fT5tdAh+o/XrM6lzSS/w7YA3zzS3ws/jY1tmh0Mm6zrkb9y6C7W3w9O5T3eNk7GkwL/++/7PGj4omH+j77tS+UN79HTgMf/vtJ1JAnpMHygikTIoUO9J4fpwIiXStGtSiw10jl83XV2VO/gMrTwloz2dcyHH4KQoZ99l3GKl0V2TWXbaoMrFhT7uCCApy8uTjv/6t5hL75v7DP2+a+YJ+k1WerzRpwFZK6G4c/hTLDUEvT2SPUNrtbRWUBLFOMkj7eD8g6dBnfEQf3CB8OScwq1PmVhsPiS0ONbjU82OE0oAGpkUoWeonfQILhDaR2CLu3z+qFpsHFNYZOvHx1kU/rMWE1l4OOIte4O2dLYTPS4DPHy6z97rOXBcfjMBCyGELAhsEAgAxpN3RlAXBG9K/YqrNPRJWwlrIda3PA20QTDwsvssm2CDRxfGTa+Zv5TvhmwfXQcRgTTRz9zaPLwaoZKllCQRjAgCG1LDDkpL/vBqd184d3bkIpGX2uzR0XnvL8wODMYqMr/83sIYMJNHQ+Imm4PlESadvwO/ly8Mgi8Um1mDi7fD4qRZ3eEKX/+uru77zoRvrqkAvbzmVwVPGctLe13ZX/JygeT4qe6Qsp+aI3t0nsSPGJLQDHbM81S4TS2MEqoKHbc0uVsPwmoKJV0cto99QFZJBBebVPe9v7i8W07coms406qO//P9yWWbwoC4cXceESncBWTI6onofI3xWArzzk1UHvziXrjlTsXXdg31JQQc6SiL0PV9Z3V4sUAtX0Ryh4jJeYoIeI9iXX9rviMT/3rzaamX40VPZtY/Bctu8MqNDJurrLFg4j1+k8uK3wLdI8E/VAZRrF+CCBxJ3DnmkAZNjAmRwFqtEG1RQXbFZbMldQS5zzba19jCaskjuByxnHN/rGyy01iKhy6OodlluHRpaTE834H2pFgTwrQd9QVZ1r0jffaLI9HvJYywiutDGybiJu7ZW3qpB7cjJ3y6K2ccfrC5en4sEoSMEz8yAdI3MEiwUGzabG0aCbzm/1GCsGK6t8YIOAlyJP55jDf7X79phkj64Wd+BiD//+RJ8JMO16aVh+uibzqb03lX8ZQ2+u91ZYXD0rf/9/8PVDzwHl0+ykw87gVaBIAd11YX0/2Udzf+FWpKECq/1IXu1N3cTvPw/N68S7f5Q59QPvAFX4X6t8z1kOg10diAD0hPsV/S+sYK2lGsIW7XQsuB/juBcGJNIoX2Sp60IlXa9xY3Ab42DbOnQyLUgHq0UIigHXCXB4fBu52/CeysQvfG7k1MjprgFa0tXJLrhT38aZkJ9dyv+fF6mEAwOt0BtRi7oDf0o9o293jtg+q29L0L84C99htWQoLEM3W2GXXqeYOzC1YX5j7usAD3LuRLQUTj48ukUAbQxTsMGAk6vtW6emxypd8hb6cakiVYK0SoSpRKlQP/gX/sCEfrdQ2ofDutDGvNnugvrmNg00nt5IRRoVYLDEdzA9H9b3u8NiY1XDjfrC1eXlr5lf0cgASj3GUrBRQ1R8n2uiui1Vuj/yEW3rl2xa0cpMJLMTkwm4OYCVTlJoES25rZ1/0Ze1mtL4vq0cZHK4oDOqZSdM6yHcAhXcg6TrNYDk4hLGMcKZwj/GMbN78QReC12nIYpaHvJ/ZN8El79m+FVmIwOoEeUe7U5xUuak2cS09V0n11MpQlQPNP3cW5QCIXXsyzx1QBAeDp+WrFliES9trgwO21fy9n1LP+XhV8AOJ5OQ+8zJnVeDpoCShbtEWIWzBHdErD+dDNRBmxbrWiZsFuqjOeiZ60y8/TbuMt+39KXb8NKka8lN3YMfCodtx7iAAVHfrkrsoERHRq+KXRLm2GM/urWf7/71+HvceHxP4wb8OslsVt5LQVKK/Er/qw36OqA+FTci46OYAQUGeZWqewE13EqNDHpK7Qjz3d1g4Ols6xa7edauSERhrIRJZQkrKA22nTUnbawpNwJs8OIuFajgb2kcEOt2RnTTKqzSeMhNy60n8xNm7C0b/VKJkD+7CTv989XDVQZ7XowsvV/vvzhfL70G4SC+HakiZUojFXL7ZByz16xa3lqtmZpSrTjKk+kqRypueVq71CTuSSJo6YrwxpIlQSgCcjUQD2FpwsTcPSA1xi8LlyAHgSvs1JQayAK4AsC0VUtyyAqFqg4bxWw1WqZ4CUADFJM9taugWCwx2BObTC2Ebd1JwqKWxWuZH/MPRwIwW/eZgeu585oeQAV3mXfmE+C3xKf/CNYxOqg7HKods+RMc9awktRcuNRzVxdHn1FLP8xVd7ksSxjXt/cn3u76fcpzNmBul1z748hEhIcupRoGVc/qEYLFQNFSdFvvLInzPXK8OFZeg60IAKfwvUNeH3uPtt682Leuw8Oa//p/1TnAafwnTl85IMQpJQVRXAdjJZzFdrw84+sS53gV+BQsFJPMLoIf7gISJFTrQ6GgOxt9hf2f5nvsK/YH7KftvwXjHTGQGVB5J3CbC3hOLXyTad7XrV5XD5tohxRWqAgzbBjUBm5FPe0+xdtQAqsmSMj7NoRVChY2TNc6JBJ9I3IxGZQUzA1imF+C/GVdEnChURw6my2xPGARbtvfYMKc0tJbpFBg2bpRhQ4yS9mTo9w3FvbPyZtTM1I36/5tDOEmyUoyDVED2t3mpZq3woCEvD3XczkfIoChIs1VNgkOg/8engQnSkAp7H0D7gGk2nSZLJPYH0uWAjL4l1RN4sbjkRjOGbsRTzcFYTh3MRYVvpKxI1WeLuynkalqpbSbLMW7pPVqTXLfnM3V0ewAYnlwGeeLfKr/Fs4JjnJkpnPDDaK7ldH2Ka8/ePWuRSQZAxHsCckC9IwijcR1vB7udzxPBdA1JlSS5bkSc3I5NHfu9iZMzGEekZP3Ay4GaS0vPDoLNz5TDkb6v3/sTDygIlRQ0t2Vmse33Evv6GpF3tGb6xysSGCxtMDeFDZCFpJ9aUG6nqtjBKVmSdrSMNT+pAOIBcK6sqndGvoLD8Y1FTTS6actsEpt5f2uP2CrfhmjFFMPDjeSZh41FXzHrP2yto6bgF/RDM5hAYOh+UrZXtzOB59Sa3VvW67u1EQBCfFw5PP+CZReX1fhRIT92dImxUDd05VIKStwA8zKGmn4XJ0ItHWgXyQDMlpHwckkoaAzrrKe0ltbnSEfYR2Xi6cqCpIOC40j/Oo9APtyAjEwMYpI818NO9N2UaEtpI0NLTRu6dWMbOLOXhRthq3ImdO80JJoQnB1Lo6aKC5VBxcfmSW+KrdyJQ8DBioDuSoL254QVgTA8/1GyQ5LNHLKPQt4c0V8lBX+/9vIoibaO0VPJ13ZRgtdqgJN3EuBspzeOQDK8TXeROFncn3aIYeEMTr6v1W54xhLz8/JeDCJCouBAYnelWQXo2KXGvDaJkEOkYjkDTeT5G7lFgCBn1gkDK5ulBMbirSWTHeVqY5tooQlc1GjW1ylGQjio1sQ7wEFvSXQHmqRQ5oDZLIxAzsHwuqm+Dreus4Ali9j8jB4eQmR7Of31hv8qahpFJmqKFYh8L3g3PwUv/CDb/88ybMzNrULst2j8ZbmCiVH04LW6RtjRgO0lh1KymvB/f/+w/3vIEzr8sPaZCe9q/nS7oCr4Ky4+Q/XVS6FVJm//P+aGtnE28BWpKIoe+/+Ft6lSRz8NwAI1GK+3NNVoJfmL4bPsLXXmuuDfz4li6//+6uOu3sM1pE3gpUVsWPupsO9NrigAOxe7tRMBZgrIqgoAIQaAEMdE7Tj8PLXsDy4WM0dH6izk4tYJTKcmTimInLGj2swYlWIC7ZM2NF4lwF1qawwuQAMTQZfPP8m/KJwStRSgeTnv1prMia4OMavCIbDGGDaZh+6SK9/R3pT9xUgZjG96BHIQs7YXhzVU6SZ1maeGK+BcE4anLOzG6UZjNd/ywenjbA+B57QmJB05Rw7cQEsICCyTyB0za0bOvntnytNxSOh2eUIHsEqkds7zcekUiSW8eNo14os328CVNkQYQjskvuy7zXONfTCFMYEK1GMoQPLZYtKZaOmLXKCNggrJR1UlVe9kbpUC64N0eitcsfAyCfyHbeY5UrdPdVjQb2s7vs10bD8VavW4Y27B/wUnN15GHKDRawSR7fd7oGvjg8QzHpm/pCCSImUiwFGTJfAUTdikH9OtLui1du1IBYN4e9cPgO2vhscfeEqO3OeiCAFj2z6KnRGdkiZuQ/oNCPLzJ6oQM6oJUaQqvLikKFaCh4SYz6CoGKZl3PoPpjbS3C+0Mxfax8RXj5H4je16MD59c951uDPzyZRwYXEihBSbmEXJW/93Bv+t2z0RYq+AqDCmNL7txdx1fLiw/a0X8EyH84/x0ivvjSIrKCAQyhjEsMHIS0yUIO2gJBfbjar6VmetRZgI+JDXkVccghVLYDpXKPPAl05bTRaOw17NdAvK0HhdAjbUC6Rb2wN062PA/5yaFU8GDx1JQHC3afN/L2lNShYg/EpCe2p5P9dWbiqekv+GpCifWM2nVeRGT9xhCtdst+t+iklMSU9mrBu8WF2NYpdsRT8rvjSQwjIuQhKp5Sq2p4cmo67avcS+ixYJAHGgRCFJ0jdE02ZAVFBztU7NkSf0WmbXr8pArzT3S/4Cb1j/Mh+t6rk/P1xnAjQ8H+hd9W4fCgbsl5j2hKDaprQUhd024deh7CGPXh0lJBVKudxoUIWgqmgI644nEJUYk2CNjlgliB/bMuv8VmdhrVOeRdid0VPv5A1F/SKip6ZsuUDz+7/fCWC27d2wEql8jlCBna40bDfXRg281s830i7C/khhJnBsQbvIyqV3VNS+n/97Sq09s6dapVQ3jWbvmewDthMTQFp2iXFPFYKb2/NkiUPRISOwA984wxV/m3B24kz0HNeoWAphyWDLQHnO5oNOp5wBvgv3BRUeKNg52R3A93Rkgfjfkd7mS3Iu5fDLxwCLRWU0BaQ9jmBWfBpZMDUyyl/7ZLhyJlqi4d4tSRJay1dHh3KYYqjpLWgN2KSZJs61TFWpDkqQOLB/XhhHYaOQLpRp+m9eiZbCSpGMk2P6BJcuo36dPa86AUJ/NnDWldIx88aR51xq+/AjLr8FSCdoUc2aC+sj3aruoco2yE83pza7tf18vj80b3kIpVtGCMPpEQUu7JstR5VEXQlQ65dMiot+ERdINuUUOSv9pgIBoay6ul8qxmacxgKS6qS5oKVqXtliKcaay8jSYbVQwNtUIaEvfYMAftNqED3NvQqnQA6rdh4y77leFWu+iUVUFQ0865Ya89yHg1P95WOFbnneFe/ThmynsxS5zR9boFfUei56peYlEkc7ak3FTKy13PhEobyKeWD252e4Wvz6Vh0Vlkzne7/f4y1Y9twnNLJedPLYmJf+v1MZ5pCQMf9vjDxjHmr+PKBGRqtTGHHDn7MdHHT3uEaCMb2iJBtfB25zWcHJ834lMqmWlV3CWs8iK3Peu0SywEkU4khj03lkH3HKIhxq4LkrkuXGVw20Dp1FQoOSyVCmYTM0R5hUA36nrKXYU9a8U4bTjDtXSj65HsDYF3IBc1yaoLsixKiioj7Th9Z6Tz9d24S48HQZYFuxRTKrq/mMciGArDyBrHpEuXW1Yl2oz36wfTPh2M1zZL2vb+hgkjynfQ5jRFKqiYmqVgdI4zLzUtRz4JPN1xRC8RVKu1OocG1ip4HMvx4lS5yUxaqob+tW3nxWJXoW3Mp6Vs0GMM+93yc1rLRLKPsFVVeRxRsRnTK8gYLu5Ct03cX+uOF5cuhTgs790peLXGlIIQux5yDVkV6A19wwkpuJ1GK0Xot/GBIIl+ue2U/lzUmJBZawDxKZ2e2n2qafAKGlDSnLOOhZvHX3IpyC/A7BqmOhmh90cT67znVF3qw6ZIzNB1wjWe73vIrOU4cOcQudwtDQzZu72w5xjC2M6/KkKaPPmlPNgGcofdxkdH7f+c3v9/MrH/osWSL7uU6CZCvfZkSDg+wYzb9KngRen0LNlbI5s1QkSnWr00at/c33UKAQvY4kTzSHYN0TQoG554q3mYB6ylkvWvpFvGLGjr/eQ6o8t+gKXypGl+Qj/r9DgWkp+oDPrLF/gem11IXX5mM5iP149IY/pBt10yxUIBfPBLjSK7CvrHxkMFH78xLpo/Art3thgKot8lZ0u/UmmNvRZcn83fWBgtj56BCVv+8aL4MYTfWOpvp99Nn46u7dbQHYDfZhh/H7awTX+5jKgLfyUI1KCm3aQZfl2efiuoz7m7+1PIMbjSs/J3emrubg7YtTXxrFd8WK4sQSWIvgtJdeFLiwZMFoEo9uaeF5TBN9X2bYnXjd294jCu2SSGjzHG7R9Hd1kf+b7ZFb/GrT8ZVGpWYO3r9f1wfreqIP0R0QldlaBmtA+Bz+xkIp031fRvuby4Br71MeqdP3268vfXSdNda59+h0ARDMdZTtJsHp8lMFkSp7WGwQ3/rFNMVdWCqcGClAxPiO6xekqqkGpoJCXd5V0aEEWyXi+ramUkqvJCS6mUq6COi8orzJrMthycfS5QhIK51siMikTaoOVIGDCHoe87BRN06yDLnu5wdFszvSwHFXkFdhpEJLRWsWxxG/k+Mh57ga1IY6DuSPYw17Hs1TEUIiA8xoxmRQjNJmYQUrSgXqxEsdrdKUTb9TYov2wE6PXWHgWxMykxgRru05PmNzPGxBB8/RqCMmxAsxFLcnDC3AYKeucL0M8+3nPdF/MwluvgERp/khhfrI/GmnRMmuZpqUFJMsIm4HmogYfd0kjjxLsOJvkm0oDYUZxKyoZB+PX1zv3y6hR5DDiNZv6Jk40gNJJ4jQMVPJA8d4qC8ypgPCIyzpzgjKyE2KgvPjRPL5EifHpw84gY05/z20389aIu/PW7pY2xNRADjtAB5R2CFKikl1R0ppmG7KxAx6iOMH4lm0zrYH/MSkZJyXYHWsbM02ObSjgTzVwTGAKmyuS42dwYSrBbs0f/PUPj/3ugFn9DeaLWDQ2PLCOcv/F7UZJijJotSnTDGvF4i4e8ScY2d22Zd43rxc1XphthKAxdxQxsGnSJ8GrhlIlm2nESGpZBkKjBn4p3EtzAVJDUjuPrR08YLeaCVS0FIUepSdmdJHeNBW08MeRoPZcpV1vBbDWLhf4MSwS+3j1APvxj0D+Yx1PRzQ/+HeR1d+yC96ufWOmH8DohGBKBH9Oibgf1IUJqH611jl3iajw/+8ywfirFPZdBx1hZ778G5NQtiHcWgzeGvw/OwpWHSPdeK+0r4NJNIPyK+Z9fvs5bwe7fm8P3wNoC1H85jUQprufRpTP/HKlrPl0kjg/OViTPu9IkukMnr3TVWVxTt0XazWOqAi3fCjK17al/bnLEyrHFP0VlmE1KljYI/c6r5h9uavXxClh7F76QXrTpJvJts0UCBOVpMdW174UvH8JpdXrYTHtG3PVfqLiKkWVcao7Vg2GsKIFZqbGxte9I00oSxLsYZKMVM5QVO57wtgqeWN4qufuSr1izkcckUu596J/i0p0Z8/75s//1tSSnrI94uN7S59E5yJYjT49aEFeSBmEdps4OHcf6tzubtn3+0Eslno0DjKg4alql9AHT/QPnEWrEJIXr+a7slRkl1dluVFUEBZYgVM5Rhfx8GsjbeZu4PZ/Z++kWuCxhnN/vD0aDIzbVWMgJ6aeeyNLOZDLV3nxtQpW+f6Pf2dv6l2SwVQ4mk31enm5vXl32H49ORsyDiWxSBcjF4uEZSv2fXUTXu1BmuJQkG6kZZ3N1oQ18Ag1g12Uf2PVTCmLyhDCgOK0kuaJXCpek1KjAO0UYW4Wbx94b+abl+6V/ZJJLs3sQ3x5tkTzOdTcXzAR8knBDKb/FfQNqNvEG9H6KZiB9gfeBHug6Rp61/1eE66RtlTVir8Q0oQy2oOmKYouZnyRRBJ1Dfkvdo7e4nFu7lRaDQPxyZyGtHX/iomZOb9ql6822O1mKDc8LW3AdOhHLvSVtrhyMVHCHGMOkMqI025qVfj/WFaeCWcs44vfOJHm/oHLMcMVrIOwsh4t054e/zDKI282t608WbjCAIe5nDv0p1+v5/75Y+xUWkywFZ8LkpQ4i9KbalARtMJLOSO3gwtX7kL3JCpGYf3G0ObG+dQZAYCnYGKA7dB9qvGw2nTJ3AdRPrXRYVRz5DSaIyJa9kl0e/LaPhJi4gPZp3mfn01osXK6IQvjQCY5Ti2UriAiUQ1kDmxmNXD4TZ8oVzTVC4mS8dsTQ1ma5BS2ubgpi4pgQzPuk8NoRKCY+JST44JEcgYv50+vIhk6lBX/gRia1Nx/5UfNBEANNBUFTOjGnmxpZrs8H7AgvEECEfKlStNmEZbfpYjpeaL39O0nYYelkg0IYiAN7jAVzCxl4M3RAMerQqDUIB3lHjSvAWVLlJp9AUEhr8JUf310uGM7vKqAPnLTfGUSEFGqrhjQSBTELbBYz8MFiokOzyv4TcBOztudDq+6tMupF5PJo4njeKM0labtFiDIfQlq7dgzvvK192R6VYdI+k6HzzGv7fCNrSjmnbC5WBVY6g9I/G57Qw5EuyK+EqBIT81FmM1YX+MwWjEKK+IOpvDEw4SYCTVFlQk3KC0l4zx8Xl9lYRiEK221Fxxi18jS0dZnTtcx9h5lNZ3aGSmntb+j35x70gp7U2HPIxnu0NqZAUUXjI2VBMKtGkSpuZSQyWideX9CcreDBFP2Z8NhjtJeSZjNYddwe10RdwcBbmY+Vqmvvavun6wGiOs+1J6eUW9b1+bEbVxcj0GnI0Ntm5Kcj2MjQVstaWLczUis/N54XsJnFBKY6KCDm/bGh1YhO8l2lrsgtcH2pM35HSejZu+UmWQ5u1wDnNqVvCaRAKtXPo5JP5QD8NI0NaDbVqmz8gmOs/NDg9QWBmDrjKWYQxR4zto9mV53S1ALmvB+3Pe9ZZoOQJbUKlyTqD9MgiV0rqOvVYwpdP5zL0FVxn5bGjMUDcUXocVe9Po2JL5LEwMTIJ6huCDQ+Weh2aiwmRqI7O6mhcFw6aMpYgpQqyMRoN36xZ42DYCDAczE0g5DUgjZfjMgiWQuv1O7x9AGKuNFBSPdnfdDCwzUPwUFR0QJaimBaWF7qbjQlofQCAm1mQrO9zC+YeZSAFjTfTi8IaK+n6ExZLUShI/DjLJtqkc1nEfO2HduQ48OLjGC21WIokpLv9zGxTupFf2QDRTHOaBHoBSSwAnaKSNGiBJ+iBh4RY/Ao+sARg5FLndyPpMHyot7Qql65RsMjGU3hWgfLSh5NTA9AuisVvMCVLaPQnewqaAKSoie1QiQgUjCCWooGNCBMgZDJiMnhViEgnvPeUDMwBEyyHLSZbyuFdQPbzzhMGgfJZW0vRYZOPy3OIcPOncrngDt6wK1enjeEoQu9mMO90Z+//zB9JD/K0OmtDuVxe9JC8NnhbE5lpdACf96Ob3MDEztWwNYdVPAUl0VsuyQudn0OpuvfwndsqJX90y3CzKGz0eFGf0Pe5xha9kptF/RIfZA8w48Ri+jWicLZJvjKcH5BZLfI8j3X1NH7cUzcaHWL6ULkqY+mouWL+Zi93Gfn8pOiDflXphnuEhztER5KZpggJtiuF1/qVAxyIUcbjmNizWS0aLMiJ+I9YonKkCUTbVsxDE0rfH0QsglGWh2PypJAsPz0vuJ1SBq7OWRb5dGGH8nUsHt8KO2MO/uv3tCtKLuARPCs1W1R423rac5kO9vopK5cnu2ia4DnYt9a53EtolrDL3PPuCqTEwuyHmWCgzRQZWKFA/kg1JLbOb1hhlVp38KM5gUvBGlV1AsxxD5EY8YvVXuGZ04lynxIT9Z6xDJHCy+rj7KqM9jd93nByuBg/14UmDPBJQHSnA7rjukg7vX7jKh5hevXr/OdxmBedrmzQa0wOMRN0Kwd+ul4MmP277vfm3BqS6ORcfg3PHbawWr+xd0nfhl8WNs/xBGYAML2aBVtK5bv7+JKjMfLx7jDGb6bC1jB2ix/cFkxNU46DyA44TG+6V2eh5TcEN/z64ABaJBTbZkNGG88a54Td/MGcM+5bkPZ+QXaLp/rS+IQEIAWpWdGuX1mnddLz7vrgFMgTMMPyqzinbnfYKJ97N5kcnT6ZHzVfBJdaq3kl/VHZB9v3Q2/2vPJankkCrjA+gUSCTpCELG0E3GXXN1knSf3qJ4AvacQS5+y0xjv95KS6LvLXJJxy9/GLfg++00/S2zcEcHmb8CG8M6L75kQ4d5aiw9hlRvW34b/wFHQpmFBPFSiR4qFzAk2nIwgKs4RfNX8y/BSn6YQ6X7n9db+a+X9rcpsZYyZwwFB6ff1rJWH2MrQ96aLbFwTHlbGBUCBx+Hql53DPcTDDvWVfrca6vYp+F15FlPoBt2H60k34nGeRuQ1Q7XdZIk3KAMk2qxaBuG5eIUqpFIo0thNErREmkz1xGrQVterAhOKlMr/xwcss7oYHYzoabL8UhsJSK+MxngnLgo8DjbSJq6PWWq/lKUgPG8FUI/8xbKP6Qbhjkyy5kF7uPUqx/O4kLXmqAafjjtIQoHPB7XPFzX4hNYh30sS5HVIoaGv1I2oFkom5TCIMRGuOvdZTbFAjkv7QeLyacqpuF4i/rfdosTieTsI8d5KxV7coSMxpIUvvaaswdPRcGvom9xyw2uicQ0u+wCGDuBMq3VJsPPiUtEDzgDn0kEDby1UhLGXP1/48vWr1eQE//EAj5igReXzt6fB/vm83G/8Y7dniBhaHCIc1q95CjTQhPhcpiiMj32mcrZKmH+sop/gevN3Nkw/m41GSxzrSQbXcQr6JuS4ODe0AAJg97Nz8XHcfzsAVoMYxBXjTjrVW1Zj4xH/lDDQidiCR7OrMgpsXa9lp2MbA07By3BpalVaV/Hxji6etp6fTwLyFUJ03UOfmWYPVji4NV+E/lbxx+Nx893hBUKEHWfrThdPcbD68s3pLYx7dxxI2Lnzxjh9bzj0X3aa05u99YZzeezjL2x7sI3A+AmH145Fp+kRCf6gsLXX+FWbebG9va7ECmgK3h/blXus2P+TC0IMNzGR/39QdCc7wl4fRFUdJckRKtWBmoKa6w8e90UZuxl12YgX9kK5tiu4mpkE1vs5WqkS6RB0wrTIaOQ5poIGnwzmizxPn6DS8Fivk0S0ZceOLJWo30nBdazy4myPlBt5yVCCAR3P1YZGmqmZyneLLXMeSwhOw7U0cutJ8JLtKaWkibCJPD5FQB6XdCoxtqLMGs9CVV20DRhkwbK83I5NNz2Fiy40z0KRKqogHaWJbnu8Qd03PCFYSUNO0VitGbnQapB9w2G2J1GS5thLpCh7kufrku3gE6HPUgEzZzdKDByNlDb3mC1WC5HVWl7zZG9l4voKzjyZurFspAMsIM1IlCLrhGcyJYy6ltgqNUHKNzmPCWET1VASr7txolBtbdAAo5quFEtZuOqQ5I5meupTizy/dmZbS+bw5dMRjlFNGz5Ug68XnSobNRyshwGGPc8h1/WunB19cvsSScfFnA06HTV9uSmX044eFKl51F2pY0dNivUtCYUQTYjQEi9s/SBgmZ7/XpTVl7ml6f9f/l1Kbv73bLB6CDfWSYNjMx/9MEyP/Xn+yUgbrbDWVdVd9/YrmTX+QIa5stdxpsXEw692SYu/KFei7V+sg/mGx3e8NKF6U7LbgR6Vdd1EsPHS4ZfUimp8oYRxwPk+21Dn8h/P/+f7v7F++3+n3nxzgdLkRMoahBQMfH18d5vBz/ALvcujrvk3B3b12WWD4KnQCXQ8tlcaBsemaaWpd7BzRk8IuJtnguE/tpmUU26ytthVZ9eJMqTUOKY5RPA6HZro61l1qd2oNDAI7RVds954oYF7Q8I2Wrfc+FkBznO03toOYYhmSvSaWhksJ1sEd2U6o6jvTJLV7GQft90hhN6g+eWTfxqGfqUx3+1dxNGyW52HV2JxUYn9c+d6vOVeJo9FcUXsxN3hyjpi3FM5bWOpPfFNYJ0dkCa2TjnQdOdlJh8vWeNJvWiC0eyozFBgCK2kcCLJuOsjgydLkeXMOAsRBT4YBXNS3JQuKbgSOjIUhAXpRoIrQbuvv9OfSM7GkZqnMtXoSYd72sQSlZ112YlPhA9Eewzap2zTV6DVFt+E1hlgQRP+p8/X/whP3GAQtadUAQ0IGGgkmOC9jCchlZSftv2LvZ/YdKC+/9fGkqRiEpUFLvQWOIeOGXSwwaAzLqnGFTDjUDJl4M2IQtshTnuIgDcYa5lBA1UWuiwtocHL9kB0KXKO7OyU7nn3knGGhXl3sjKieT7PiddsekLVSJI3U0rlNL8quG2ds+YjS3NHS9br51IPwirDWYY7sS1r0PU82QUyqMijtNPmEQW3ggxzXoTIeryerdou2yZljYbFLAjgAzP1cFB3Y5spOu51kMYv29IOi7uuGd4MKs+QXDccwzvSuCN51YIdOz6cxrlLKoWmjQPMoLfJucMDdx49LS8Z+yQVU4n4zCt+zXnMCoV7/htYmMLCX+TT0GTvSiNzwp9TxoyQ2r2ov7BW1PgI3MbgtrMMEktxFwYN6aZllijQ5g1w4gKUW/6a1LqIjXOTuoiQ0gt1KyrqCaRmRPusSCMPxmH09wP6eiixcLPRs40oWucDGgCvAMd11ejfGpSZQjJoRA9auWvfivIHXolnXTvkGgUpBikUd05Xe8nGhzvu01u949aLX51qI48KQm6Z4Twb1OvKLgdu1QyC29biA+ERfXPwVm8HVyHkQm40lGXHg3LBcQol1l+WkEhlx6wgdaLM1eieLptGJZpoZ4sh3+/gM9Vo0N64SoNW8yPSKxeTsU3Wfrd9v4mlGI/bUja2wcwCR5guBa6zg9J84ZUVNgLfxKzSkFI7hAaPUk6ghIDLQ3wqfcJHBK/iQz9NfXWoKGhydHW43ALKQkd5Mjvy4uChGSTL8x5rlCIlqx6+PCoXgSuWuCfLbelxVXg/B/HlMsa5IM//ywbIRsNQyXyoXug8ZR1melALfKodMoE3HS86jp7T+kW8VuhylDJaRXbkOn2HHN+f3ri8Jd+/bd2286WVYRls933o167eIafc7JBAV4EErMRcpUq4PElC1XRPblKapUuVGzVu4NFeKoiyyhL/IoQPjyeOXPRpov7P3avEdpIdO18GHiS3smSXpCWSt/GQSLOHpsQZaR7JMc7lCd8ONH7vhXCTokNj6mkUj+iha1pCqgctigL5UCRP0pZ1go8OEtnuiqzFK1Vnc7BkE/PoQ3ozPRA3Skf1BLd8GsfrLClaBdbA9t8+KKvJm2F+ladR/MNPzlPNi828J+WByF47nQcUOEMnBB8TBR0Rk1gUpzu5gWgqayzj7UGHL8iG+diBkgejp8MVNcbgC9LJuH2MpGF4ANZe0VLask6sVlgXY/fESvR7R0cbY88N2/CY7qiyQQtN8Gaif3i0LoL+Hs/Hj1FNemB/9be1EcPgzcoqtOQ84qA9MyFujY65xsekn8yzA+6tTPgtr/4Ex+NRCJirgaxSuTjATTn15uwt887j3+wn1+cEwYLc8BcHuE5n//aDYP/MGmeb63V6xkStIrpmwATWZXM3F6x7aXKIdGtJ9u2q0Hl/uoUxDkkbdYS4Ri4yjsjO63Kd90ECEhszhM/TueWqxswhByxQh67MPA/cJ97lG/T4umTjEiFcly7QGQul4LpjdP6ycPctWN06mlkHUzmcPVxaXmZzWxSVoH1ak/v7oju9tmjWuoKdOKPbYLVklnqZ9rMZ1o33nGm53TGuFYVz8+qnt5lFnJKLJ/nu0veXhWxwOcLZ+/pX1fHI6lRLVrn2dK5WxaX75y88pU2E0qbitl68TW8Lj3uXelL1vrgfOuaI2L4SLInsUa8gQ5UjHy681bxkPekP8CY3fC2U8G5/GIprCC19cou23xiyTZTOgbWDhOJtwD4h02S3VlQjLHCl1UnDoOgwgEOiu0YaFwuFnhn577Axlp65no+ez2BgTxeYM1IyVsN22BoMpo6MZM0ysuIJAoMCFkpVlTTowe3SJsfUbW1cGnFo+g4hh08JVtd/gvy1c+sSv/luhd1NiR/cASrJCC2y9Jp8SyxLCDTViFZqyN4eKY+ykyj0rhDJQxKCtFAAIDWDjUJzAVf6Q3p3WRnFTlhQoFJ2QmoKbIzzSo/Vy3l6i34fzLSDf+qvup/1mNwLNo0Yku7Z/R8/REwFqcue+uus0BOwFGEkLixNFhdIp2KURDpiUokeVCuXRzXrKFqkEuzDNkUfgiecaDNTZEs9KaOIrnxqJ4WTE1s0H6Y5WQwnWpDDSCYs4/CWZM7DxZIjcpvyKMqaQnfqUY/Gns3nxcqjS3VCX0zOcCS8ysZjxDJQM6mgFcwSBVpNrsQYNPmjnw9wmzjIJ9oCdM6A9/eW1rffePUMKhCYfbsEIUKHrt+L4rbf/+JhChjg2smtgn7abtalhttM/3AB4P+1g7ERLexmXRrcWBe+ewrljw4u6J3zDMxKffM0fX/h0SUXj/CP3vg1vHk8vcMuWrhe2b4e3M23uHDHBuogxQ3mM3+7G9RaxcT4StTDtUWcX/dbidfnO7nKf2KInqr8lqoKlff4nPt+w9U9j6zb2UmhXM/kavyEW8ununko9Ny2fb81IMwhrDLVMHQ5z+sKiojcMN7W0wrVXQiNriFSlP+k85pBYQWUxza1sLEoCq6YR4oH8OPhB04LAYTGXBS8stZdbAuHy5jbT48UqVLAeWdaFWbgImx0eenmXHj6ufEDs5OJ2NTmUttkPgv6It1DPMQ/HyYPTLXmKtZ15mNLKcHAOw9d9kzP4u4W8jAmdpVQzm2TyFQ4QFtmVlkdbMsNgjHrrI/E98SCd/RC01P2yS7YNoM74cEFGEkQZzc4Qug5Kdy7W7t5g9u/lGtfBxuzjUJ8ctUFt4ImwWg6aYlhHLONqlRxOtQjNTbTsr8aH7lrCaKkOCEnwl1gBTOqlfOS063cdc+rOoDHM3QFQGtrVwH3/TOus4RoiKbys6HtPmXCJVp4AFn6sWU4VJAWUWQLhglKVYBWHYeKBR9zThHLFZt2F2paGK0n+NPmRkazxIdx7aqSXpb0yiPJwFrDcGo4ooJDB1UELMatgkBk8V4u3Yvq7qcwJbzQr3iZZNzWnSfqovipNbBKdCH+qtejd/o/RVJ9SkFE4yfiCfWs2v7m+31N4ej1JlQpxOKXPHQmsOOI0r6SoCu3kdv/CAHy5vfSvKobKkz6Y+P4e4B/jyuPA/Dfovidrd8yJVjpMQA6MICArx6fl+PMRHWqbTpZ7AVa540h/gioXVMELRE0VY1o92qwvkRCElu1QEbRpGhQg2jQmdzrQlIjkCRAzephQQu1BC858Bhtw2tJVGdNW5CYQrNmikQCPUvzSwCiCn11C2FtBsIqaCLBBZmD3MC1XMlmruZS37h/r0t1SFyyfg+2zQB8xw/67jFEQYQ8fjgejyZm6XVVziegrPb6h30f/MZXInWKZf5SlUiKqKeM/XwduSWZlz9iCDBc5BRZGYltdpJGqb1rMELQZCH8YMiopgw8n4isX/QYy7mFZ/ybhvc5jJuuxLLBDpdfYoOXqthmBm8g/K5u1GKmsXiQhWx4ZUyKSLnF8R4o86mFSMo4CyMogyxcfDFZDKYCFpMqby2CIRdPEIhN6WwIoHcWW/6wM9uSQGWIJUWSfEtGQAyWHDIKS4FWm1LWhlCqsghLDc9Xi4J209IgcsbSYvKHpYPtB4uGUmLpEXNZBixSLCOkaMuElkyYYZDCcn000OaOMCajSxZuUucdNaQHpo/9EIggYMzqHKOAQFfTSJOsCzxm246cLoh9O5veaaKN5063RkS4lEk8cX56LRCAXjElMZqoFf5OMGAwcXJG2sh1iU2yNaAMQmf3tbg+Pa6pTjtugpmKsl2LPo/sN6vJo0MVsmYf5dXnDBQR4Ws4YOpIBTkxhZwc6gUcmtlTG6hpNKe/CA5XN9QsYtZ1dfGzFI2z5ELS1cLa+XIBBBwQR5AWnICu3IdjDEg5FsMqe3gIm4pNjrWPcmtJhGKAUbH9lG5KPed7qDIdCEbpjWp4oxVRK6VxpdR5zp0dVQU5sfgI0w2Y7KlgVOl2c49bDgqJ9BL7kkSfJz3tGe4HB2vUFFnFbgj7QYANGJCHMBxX1PqWArL7q14z6HUSiPg6hW5D7Ibth2VlnMhiLImvkhx/Eb2eCPL0b5g0bSb5fCcQhlfIQzQr7Hzmx4PNWVpwe8R3iBC5K8oL6B96kROy/hv3taM+ErEjv2paCAuxEQeRiIt4iI8ESIhE+inWX4lSZcpVqFTFKR7rbYhaSo1addLqNWgEjwk/QhpaOzFsZrDJBSvwEcRSiRxwUAYuniGGSZHmokt0kHCHcjLIfxETyYhjpeXWWG2tJTFrYaj3ljqNFSvL/ELgG7f30PR0fjTcFukJgJ1AgwzWZoihhhluhJFGGW2MscYZbwKlMq3zWJ0nqj0N3uxXuDvXN91XPJUdUiNa7bg6pPxjPaNU2MccDplz2pa4Jyf6hFm1eizbV/VPzgxk0QaaGL1CmpbbDAxNr02aGJplEC8nMV9dTpqUHco6d6DnGJRToVCuMDtCc4RCMXOF2RXKERo+rQsCIQNjKgaktHMVMp6RBrtrfajVix/WXeDBy8tH3rtX1XXSletWzkyNFwb0x8UmVUdpK+pArmt60M90r4Q9U5ONK8NUlpvowsmh0d5juziDY2MjrC8crizpHZt083VE6jeGSV7bez/74naknK/ed1z3WFuv4WYAAA==) format('woff2');font-weight:100 900;font-style:normal;font-display:swap}
|
|
83
|
+
html{font-size:15px}
|
|
84
|
+
body{background:#000;color:#fff;letter-spacing:-.005em}
|
|
85
|
+
::selection{background:#fff;color:#000}
|
|
86
|
+
:focus-visible{outline:2px solid #fff;outline-offset:2px}
|
|
87
|
+
[class*="border-white/[0.04]"]{border-color:#ffffff1f!important}
|
|
88
|
+
[class*="border-white/[0.07]"]{border-color:#ffffff33!important}
|
|
89
|
+
.nav-wrap{background:transparent!important;border:0!important;padding:0!important;gap:2px!important}
|
|
90
|
+
.nav-pill{background:transparent!important;border:0!important;border-radius:0!important;color:#a3a3a3!important;font-family:'Geist Mono',ui-monospace,monospace!important;font-size:11px!important;letter-spacing:.08em!important;text-transform:uppercase;padding:8px 10px!important}
|
|
91
|
+
.nav-pill:hover{color:#fff!important}
|
|
92
|
+
.nav-pill.active{color:#fff!important;background:transparent!important;box-shadow:inset 0 -1px 0 #fff}
|
|
93
|
+
.provider-icon.gitlab,.provider-icon.gitee,.provider-icon.github{background:#0a0a0a!important;border:1px solid #ffffff26;color:#fff}
|
|
94
|
+
.radio::after{border-color:#000}
|
|
95
|
+
th.ops-h,td.ops{background:#000}
|
|
96
|
+
th.ops-h{background:#0a0a0a}
|
|
97
|
+
tr:hover td.ops{background:#111}
|
|
98
|
+
#view-repos h1,#view-settings h1{font-size:32px;letter-spacing:-.04em;line-height:1.05}
|
|
99
|
+
.ov-wrap{padding-top:4px}
|
|
100
|
+
.repo-tags{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:6px}
|
|
101
|
+
.repo-tags span{font-family:'Geist Mono',ui-monospace,monospace;font-size:10px;line-height:1.6;color:#a3a3a3;border:1px solid #ffffff26;padding:1px 5px}
|
|
102
|
+
.space-edit{border:0;background:none;color:#737373;font-size:11px;cursor:pointer;padding:0;text-decoration:underline;text-underline-offset:3px}
|
|
103
|
+
.space-edit:hover{color:#fff}
|
|
104
|
+
.space-tag{color:#a3a3a3}
|
|
105
|
+
#spaceDialogList input{accent-color:#fff}
|
|
106
|
+
#toast:empty{display:none}
|
|
107
|
+
</style>
|
|
108
|
+
|
|
109
|
+
<style>
|
|
110
|
+
.mast{display:flex;justify-content:space-between;align-items:center;padding:0 0 9px;border-bottom:1px solid #ffffff1f;color:#737373;font-family:'Geist Mono',ui-monospace,monospace;font-size:10px;letter-spacing:.13em;text-transform:uppercase}
|
|
111
|
+
.mast b{color:#d4d4d4;font-weight:500}
|
|
112
|
+
.alert-band{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:18px;align-items:center;margin-top:18px;border:1px solid #ffffff26;border-left:3px solid #ff8a72;background:#ffffff08;padding:18px 20px 18px 12px}
|
|
113
|
+
.alert-band[data-state="ok"]{border-left-color:#7dd3a0;background:#ffffff04}
|
|
114
|
+
.alert-band[data-state="warn"]{border-left-color:#e5c07b;background:#ffffff06}
|
|
115
|
+
.alert-label{display:block;color:#8a8a8a;font-family:'Geist Mono',ui-monospace,monospace;font-size:10px;letter-spacing:.12em}
|
|
116
|
+
.alert-label b{color:#fff;margin-left:4px;font-weight:600}
|
|
117
|
+
.alert-band h1{margin:8px 0 6px;font-size:clamp(24px,2.6vw,34px);line-height:1.05;letter-spacing:-.035em;font-weight:650}
|
|
118
|
+
.alert-band p{margin:0;max-width:760px;color:#a3a3a3;font-size:13px;line-height:1.5}
|
|
119
|
+
.alert-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}
|
|
120
|
+
.btn-pill{display:inline-flex;align-items:center;justify-content:center;min-height:36px;padding:0 16px;border-radius:999px;border:1px solid #ffffff33;background:transparent;color:#fff;font-size:13px;cursor:pointer;white-space:nowrap}
|
|
121
|
+
.btn-pill:hover{border-color:#fff}
|
|
122
|
+
.btn-pill.primary{background:#fff;border-color:#fff;color:#000;font-weight:600}
|
|
123
|
+
.btn-pill.primary:hover{background:#e5e5e5}
|
|
124
|
+
.ov-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin-top:14px;border-top:1px solid #ffffff1f;border-bottom:1px solid #ffffff1f}
|
|
125
|
+
.ov-stat{padding:12px 16px;border-right:1px solid #ffffff17}
|
|
126
|
+
.ov-stat:last-child{border-right:0}
|
|
127
|
+
.ov-stat small{display:block;color:#737373;font-family:'Geist Mono',ui-monospace,monospace;font-size:10px;letter-spacing:.1em}
|
|
128
|
+
.ov-stat b{display:block;margin-top:6px;color:#fff;font-size:18px;font-weight:600;letter-spacing:-.01em}
|
|
129
|
+
.ov-stat b em{color:#737373;font-style:normal;margin:0 5px}
|
|
130
|
+
.ov-stat p{margin:6px 0 0;color:#a3a3a3;font-size:12px;line-height:1.45}
|
|
131
|
+
.ov-unit{font-size:13px;font-weight:400;color:#a3a3a3}
|
|
132
|
+
.ov-panel{border:1px solid #ffffff26;margin-top:12px}
|
|
133
|
+
.ov-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:13px 16px;border-bottom:1px solid #ffffff26}
|
|
134
|
+
.ov-head h2{margin:0;font-size:15px;font-weight:600;letter-spacing:-.01em}
|
|
135
|
+
.ov-head p{margin:4px 0 0;color:#a3a3a3;font-size:12px}
|
|
136
|
+
.ov-head button{background:none;border:0;color:#d4d4d4;font-size:12px;text-decoration:underline;text-underline-offset:3px;cursor:pointer}
|
|
137
|
+
.sig-head,.sig-row{display:grid;grid-template-columns:minmax(0,1fr) 96px 150px 96px 96px;gap:10px;align-items:center;padding:8px 16px}
|
|
138
|
+
.sig-head{color:#737373;font-family:'Geist Mono',ui-monospace,monospace;font-size:10px;letter-spacing:.1em;border-bottom:1px solid #ffffff17}
|
|
139
|
+
.sig-row{padding:12px 16px;border-bottom:1px solid #ffffff12;font-size:13px}
|
|
140
|
+
.sig-row:last-child{border-bottom:0}
|
|
141
|
+
.sig-row:hover{background:#ffffff05}
|
|
142
|
+
.sig-repo{min-width:0}
|
|
143
|
+
.sig-repo b{display:block;font-size:14px;font-weight:550;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
144
|
+
.sig-repo small{display:block;margin-top:3px;color:#a3a3a3;font-family:'Geist Mono',ui-monospace,monospace;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
145
|
+
.sig-status{display:inline-flex;align-items:center;gap:6px;justify-self:start;font-size:12px;color:#a3a3a3}
|
|
146
|
+
.sig-status::before{content:"";display:none;width:5px;height:5px;border-radius:50%;background:currentColor}
|
|
147
|
+
.sig-status.busy{color:#e5c07b}.sig-status.bad{color:#ff8a72}
|
|
148
|
+
.sig-status.busy::before,.sig-status.bad::before{display:inline-block}
|
|
149
|
+
.sig-path,.sig-time{font-size:12px;color:#a3a3a3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
150
|
+
.sig-path{font-family:'Geist Mono',ui-monospace,monospace;font-size:11px}
|
|
151
|
+
.sig-action{justify-self:start;padding:2px 0;border:0;background:none;color:#d4d4d4;font-size:12px;text-decoration:underline;text-underline-offset:4px;cursor:pointer}
|
|
152
|
+
.sig-action:hover{color:#fff}
|
|
153
|
+
.sig-action:disabled{opacity:.45;text-decoration:none;cursor:default}
|
|
154
|
+
.sig-empty{padding:28px 16px;color:#a3a3a3;font-size:13px}
|
|
155
|
+
@media(max-width:900px){.alert-band{grid-template-columns:1fr}.alert-actions{justify-content:flex-start}.ov-stats{grid-template-columns:1fr 1fr}.ov-stat:nth-child(2){border-right:0}.sig-head,.sig-row{grid-template-columns:minmax(0,1fr) 92px 92px}.sig-head span:nth-child(3),.sig-head span:nth-child(4),.sig-path,.sig-time{display:none}}
|
|
156
|
+
@media(max-width:700px){.alert-band{padding:14px}.alert-band h1{font-size:24px}.ov-stat{padding:10px 12px}.sig-head{display:none}.sig-row{grid-template-columns:minmax(0,1fr) auto;row-gap:6px}.sig-row .sig-action{grid-column:1 / -1}}
|
|
157
|
+
</style>
|
|
158
|
+
</head>
|
|
159
|
+
<body class="bg-page text-text-primary font-sans overflow-x-hidden min-h-screen">
|
|
160
|
+
<div class="app min-h-screen flex flex-col">
|
|
161
|
+
|
|
162
|
+
<!-- ============ TOP NAV ============ -->
|
|
163
|
+
<nav class="sticky top-0 z-50 bg-surface border-b border-white/[0.04]">
|
|
164
|
+
<div class="max-w-1240 mx-auto px-6 h-auto md:h-[52px] py-2 md:py-0 flex flex-wrap items-center gap-x-3 md:gap-x-6 gap-y-2">
|
|
165
|
+
<div class="flex items-center gap-2 font-semibold text-[14px] tracking-[-0.01em]">
|
|
166
|
+
<div class="w-[26px] h-[26px] bg-accent rounded-sm grid place-items-center shadow-brand">
|
|
167
|
+
<svg viewBox="0 0 24 24" class="w-[17px] h-[17px] text-black" aria-hidden="true">
|
|
168
|
+
<path d="M16.4 5.8 L8.8 5.1 L4.4 12 L8.8 18.9 L16.4 18.2" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
169
|
+
<g fill="currentColor">
|
|
170
|
+
<circle cx="16.4" cy="5.8" r="1.9"/><circle cx="8.8" cy="5.1" r="1.9"/><circle cx="4.4" cy="12" r="1.9"/><circle cx="8.8" cy="18.9" r="1.9"/><circle cx="16.4" cy="18.2" r="1.9"/>
|
|
171
|
+
</g>
|
|
172
|
+
</svg>
|
|
173
|
+
</div>
|
|
174
|
+
CodeGraph
|
|
175
|
+
</div>
|
|
176
|
+
<div class="nav-wrap flex order-3 w-full md:order-none md:w-auto gap-0.5 overflow-x-auto">
|
|
177
|
+
<button class="nav-pill [&.active]:text-text-primary [&.active]:bg-surface text-text-tertiary hover:text-text-secondary rounded-sm px-4 py-1.5 font-medium text-[13px] bg-transparent border-0 cursor-pointer transition-colors" data-view="overview">Overview</button>
|
|
178
|
+
<button class="nav-pill [&.active]:text-text-primary [&.active]:bg-surface text-text-tertiary hover:text-text-secondary rounded-sm px-4 py-1.5 font-medium text-[13px] bg-transparent border-0 cursor-pointer transition-colors" data-view="repos">Repositories</button>
|
|
179
|
+
<button class="nav-pill [&.active]:text-text-primary [&.active]:bg-surface text-text-tertiary hover:text-text-secondary rounded-sm px-4 py-1.5 font-medium text-[13px] bg-transparent border-0 cursor-pointer transition-colors" data-view="settings">Settings</button>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="flex-1"></div>
|
|
182
|
+
<div class="space-select relative">
|
|
183
|
+
<button id="spaceTrigger" onclick="toggleSpaceMenu()" title="Filter or create spaces"
|
|
184
|
+
class="flex items-center gap-2 bg-surface-elevated border border-white/[0.04] text-text-secondary font-medium text-[13px] px-3 py-1.5 rounded-sm cursor-pointer hover:border-white/[0.07] hover:text-text-primary transition-colors">
|
|
185
|
+
<span id="spaceTriggerLabel">All</span>
|
|
186
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
|
|
187
|
+
</button>
|
|
188
|
+
<div id="spaceMenu" style="display:none"
|
|
189
|
+
class="absolute top-[calc(100%+4px)] right-0 min-w-[180px] bg-surface-elevated border border-white/[0.07] rounded-md shadow-popover p-2 z-[100] max-h-[320px] overflow-y-auto"></div>
|
|
190
|
+
</div>
|
|
191
|
+
<div id="connStatus" class="hidden sm:inline-flex items-center gap-1.5 px-3 py-1 rounded-sm border border-white/[0.04] bg-surface-elevated text-text-secondary text-[11px] font-medium tracking-[0.02em]">
|
|
192
|
+
<span class="dot w-1.5 h-1.5 rounded-full bg-text-quaternary"></span>
|
|
193
|
+
<span id="connText">No provider</span>
|
|
194
|
+
</div>
|
|
195
|
+
<button onclick="loadRepos()" title="Refresh"
|
|
196
|
+
class="w-7 h-7 rounded-sm border border-white/[0.04] bg-surface-elevated text-text-tertiary cursor-pointer grid place-items-center hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover active:scale-[0.96] transition-colors">
|
|
197
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M3 22v-6h6"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/></svg>
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
</nav>
|
|
201
|
+
|
|
202
|
+
<!-- ============ MAIN ============ -->
|
|
203
|
+
<main class="flex-1 max-w-1240 mx-auto w-full px-6 pt-8 pb-12 max-md:px-4 max-md:pt-6 max-md:pb-9">
|
|
204
|
+
|
|
205
|
+
<!-- OVERVIEW -->
|
|
206
|
+
<div id="view-overview" class="view active hidden [&.active]:block [&.active]:animate-fadeIn ov-wrap">
|
|
207
|
+
<div class="mast"><span>SYS. 01 <b>INDEX HEALTH</b></span><span>LOCAL / LIVE DATA</span></div>
|
|
208
|
+
|
|
209
|
+
<section class="alert-band" id="healthAlert" data-state="ok" aria-label="Index status summary">
|
|
210
|
+
<div class="alert-copy">
|
|
211
|
+
<span class="alert-label"><span id="alertLabel">ALL CLEAR</span> <b id="alertCount"></b></span>
|
|
212
|
+
<h1 id="alertTitle">Loading…</h1>
|
|
213
|
+
<p id="alertDetail">Reading repository state…</p>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="alert-actions" id="alertActions"></div>
|
|
216
|
+
</section>
|
|
217
|
+
|
|
218
|
+
<section class="ov-stats" aria-label="Index and queue health">
|
|
219
|
+
<div class="ov-stat"><small>INDEXED</small><b><span id="statIndexed">0</span><em>/</em><span id="statTotal">0</span></b><p id="statIndexedDetail">—</p></div>
|
|
220
|
+
<div class="ov-stat"><small>QUEUE</small><b><span id="statProcessing">0</span> <span class="ov-unit">running</span></b><p id="statQueueDetail">—</p></div>
|
|
221
|
+
<div class="ov-stat"><small>NEEDS ACTION</small><b><span id="statFailed">0</span></b><p id="statAttentionDetail">—</p></div>
|
|
222
|
+
<div class="ov-stat"><small>CLONED</small><b><span id="statCloned">0</span></b><p id="statClonedDetail">—</p></div>
|
|
223
|
+
</section>
|
|
224
|
+
|
|
225
|
+
<section class="ov-panel" aria-label="Repository health">
|
|
226
|
+
<div class="ov-head">
|
|
227
|
+
<div><h2>Repositories</h2><p>Sorted by health · <span id="ovRepoCount">0</span> sources</p></div>
|
|
228
|
+
<button onclick="switchView('repos')">View all ↗</button>
|
|
229
|
+
</div>
|
|
230
|
+
<div class="sig-head"><span>REPOSITORY</span><span>STATUS</span><span>PATH</span><span>ADDED</span><span>ACTION</span></div>
|
|
231
|
+
<div id="ovRows"></div>
|
|
232
|
+
</section>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<!-- REPOSITORIES -->
|
|
236
|
+
<div id="view-repos" class="view hidden [&.active]:block [&.active]:animate-fadeIn">
|
|
237
|
+
<div class="mb-8 flex items-end justify-between gap-6">
|
|
238
|
+
<div>
|
|
239
|
+
<h1 class="text-[22px] font-semibold tracking-[-0.025em] leading-[1.3]">Repositories</h1>
|
|
240
|
+
<p class="text-[13px] text-text-tertiary mt-1">Manage your code repositories</p>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div id="addRepoPanel" class="bg-surface border border-white/[0.04] rounded-md p-6 mb-3">
|
|
245
|
+
<div class="flex items-center justify-between mb-5">
|
|
246
|
+
<div class="text-[14px] font-semibold tracking-[-0.01em]">Add Repository</div>
|
|
247
|
+
</div>
|
|
248
|
+
<div class="flex gap-2 mb-5">
|
|
249
|
+
<input id="searchBox" onkeydown="if(event.key==='Enter')searchProjects()" placeholder="Search repositories..."
|
|
250
|
+
class="form-input flex-1 h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
251
|
+
<button onclick="searchProjects()"
|
|
252
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Search</button>
|
|
253
|
+
<button id="btnAdd" onclick="addSelected()" disabled
|
|
254
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 disabled:opacity-35 disabled:cursor-not-allowed transition-colors">Add Selected</button>
|
|
255
|
+
<button onclick="openLocalModal()"
|
|
256
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">+ Add Local</button>
|
|
257
|
+
</div>
|
|
258
|
+
<div id="searchArea" class="empty-state hidden flex-col items-center justify-center py-12 px-5 text-text-tertiary text-center">
|
|
259
|
+
<p class="text-[13px] max-w-[320px] leading-[1.6]">Enter a project name and click Search, or add a local repository</p>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<div class="bg-surface border border-white/[0.04] rounded-md p-6 mb-3">
|
|
264
|
+
<div class="flex items-center justify-between mb-5">
|
|
265
|
+
<div>
|
|
266
|
+
<div class="text-[14px] font-semibold tracking-[-0.01em]">Repository List</div>
|
|
267
|
+
<div id="repoCount" class="text-[13px] text-text-tertiary mt-0.5"></div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="overflow-x-auto border border-white/[0.04] rounded-md bg-surface">
|
|
271
|
+
<table class="table w-full border-collapse table-fixed">
|
|
272
|
+
<thead>
|
|
273
|
+
<tr>
|
|
274
|
+
<th class="text-[11px] font-semibold text-text-tertiary text-left px-5 py-3 border-b border-white/[0.04] bg-surface-elevated tracking-[0.06em] uppercase">Repository</th>
|
|
275
|
+
<th class="text-[11px] font-semibold text-text-tertiary text-left px-5 py-3 border-b border-white/[0.04] bg-surface-elevated tracking-[0.06em] uppercase">Path</th>
|
|
276
|
+
<th class="text-[11px] font-semibold text-text-tertiary text-left px-5 py-3 border-b border-white/[0.04] bg-surface-elevated tracking-[0.06em] uppercase">Status</th>
|
|
277
|
+
<th class="ops-h text-[11px] font-semibold text-text-tertiary text-left px-5 py-3 border-b border-white/[0.04] bg-surface-elevated tracking-[0.06em] uppercase">Actions</th>
|
|
278
|
+
</tr>
|
|
279
|
+
</thead>
|
|
280
|
+
<tbody id="repoTbody"></tbody>
|
|
281
|
+
</table>
|
|
282
|
+
</div>
|
|
283
|
+
<div id="repoEmpty" class="empty-state hidden flex-col items-center justify-center py-12 px-5 text-text-tertiary text-center">
|
|
284
|
+
<p class="text-[13px] max-w-[320px] leading-[1.6]">No repositories yet. Add a provider in Settings first.</p>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<!-- SETTINGS -->
|
|
290
|
+
<div id="view-settings" class="view hidden [&.active]:block [&.active]:animate-fadeIn">
|
|
291
|
+
<div class="mb-8 flex items-end justify-between gap-6">
|
|
292
|
+
<div>
|
|
293
|
+
<h1 class="text-[22px] font-semibold tracking-[-0.025em] leading-[1.3]">Settings</h1>
|
|
294
|
+
<p class="text-[13px] text-text-tertiary mt-1">Configure repository providers</p>
|
|
295
|
+
</div>
|
|
296
|
+
<button onclick="openModal()"
|
|
297
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">+ Add Provider</button>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<div class="grid grid-cols-1 xl:grid-cols-[minmax(0,1.3fr)_minmax(320px,.7fr)] gap-3">
|
|
301
|
+
<div class="bg-surface border border-white/[0.04] rounded-md p-6">
|
|
302
|
+
<div class="flex items-center justify-between mb-5">
|
|
303
|
+
<div class="text-[14px] font-semibold tracking-[-0.01em]">Providers</div>
|
|
304
|
+
</div>
|
|
305
|
+
<div id="providerList" class="flex flex-col gap-2"></div>
|
|
306
|
+
<div id="providerEmpty" class="empty-state hidden flex-col items-center justify-center py-12 px-5 text-text-tertiary text-center">
|
|
307
|
+
<p class="text-[13px] max-w-[320px] leading-[1.6]">No providers configured. Click "Add Provider" to get started.</p>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<div class="bg-surface border border-white/[0.04] rounded-md p-6">
|
|
312
|
+
<div class="flex items-center justify-between mb-5">
|
|
313
|
+
<div class="text-[14px] font-semibold tracking-[-0.01em]">Global Settings</div>
|
|
314
|
+
</div>
|
|
315
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
316
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Clone Base Directory <span class="text-error">*</span></label>
|
|
317
|
+
<input id="baseDir" placeholder="/srv/code-index"
|
|
318
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
319
|
+
<div class="text-[11px] text-text-tertiary">Where to store cloned repositories</div>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
322
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Auto Sync Frequency (hours)</label>
|
|
323
|
+
<input id="autoSyncHours" type="number" min="0" step="1" placeholder="6"
|
|
324
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
325
|
+
<div class="text-[11px] text-text-tertiary">Auto git pull + reindex indexed repos every N hours (0 = off)</div>
|
|
326
|
+
</div>
|
|
327
|
+
<button onclick="saveGlobalSettings()"
|
|
328
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Save</button>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<div class="flex items-center justify-between gap-4 border-b border-white/[0.04] pb-2 mt-6 mb-3"><b class="text-[13px] font-semibold tracking-[-0.01em]">MCP access</b><span class="font-mono text-[10px] uppercase tracking-[0.08em] text-text-tertiary">Streamable HTTP / JSON-RPC 2.0</span></div>
|
|
333
|
+
<div class="bg-surface border border-white/[0.04] rounded-md p-6">
|
|
334
|
+
<div class="flex items-start justify-between gap-4 flex-wrap">
|
|
335
|
+
<div class="min-w-0 flex-1">
|
|
336
|
+
<div class="text-[11px] text-text-tertiary uppercase tracking-[0.08em] mb-2">Endpoint</div>
|
|
337
|
+
<code id="mcpEndpoint" class="block text-[13px] font-mono text-text-secondary break-all"></code>
|
|
338
|
+
</div>
|
|
339
|
+
<button onclick="copyMcpUrl()"
|
|
340
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors whitespace-nowrap">Copy URL</button>
|
|
341
|
+
</div>
|
|
342
|
+
<div class="mt-5">
|
|
343
|
+
<div class="flex items-center justify-between mb-2">
|
|
344
|
+
<div class="text-[11px] text-text-tertiary uppercase tracking-[0.08em]">Client config</div>
|
|
345
|
+
<button onclick="copyMcpConfig()"
|
|
346
|
+
class="inline-flex items-center gap-2 h-7 px-3 rounded-sm font-medium text-[12px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors whitespace-nowrap">Copy config</button>
|
|
347
|
+
</div>
|
|
348
|
+
<pre id="mcpSnippet" class="bg-surface-elevated border border-white/[0.04] p-4 text-[12px] leading-[1.6] text-text-secondary overflow-x-auto font-mono"></pre>
|
|
349
|
+
</div>
|
|
350
|
+
<p id="mcpAuthNote" class="text-[12px] text-text-tertiary mt-3"></p>
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<div class="flex items-center justify-between gap-4 border-b border-white/[0.04] pb-2 mt-6 mb-3"><b class="text-[13px] font-semibold tracking-[-0.01em]">Access tokens</b><button onclick="openTokenCreate()" class="inline-flex items-center gap-2 h-7 px-3 rounded-sm font-medium text-[12px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors whitespace-nowrap">+ New token</button></div>
|
|
354
|
+
<div class="bg-surface border border-white/[0.04] rounded-md p-6">
|
|
355
|
+
<div id="tokenList" class="flex flex-col gap-2"></div>
|
|
356
|
+
<div id="tokenEmpty" class="hidden text-[12px] text-text-tertiary">No additional tokens. Create one to give a teammate scoped access.</div>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
</main>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
<!-- ============ MODALS ============ -->
|
|
364
|
+
<!-- Add/Edit Provider -->
|
|
365
|
+
<div id="modalOverlay" onclick="if(event.target===this)closeModal()"
|
|
366
|
+
class="fixed inset-0 z-[100] hidden [&.show]:flex items-center justify-center animate-fadeIn">
|
|
367
|
+
<div class="bg-surface border border-white/[0.07] rounded-lg p-8 w-full max-w-[460px] shadow-modal">
|
|
368
|
+
<div id="modalTitle" class="text-[16px] font-semibold mb-6 tracking-[-0.01em]">Add Provider</div>
|
|
369
|
+
<input type="hidden" id="editProviderId">
|
|
370
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
371
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Provider Type <span class="text-error">*</span></label>
|
|
372
|
+
<select id="modalProvider" onchange="onModalProviderChange()"
|
|
373
|
+
class="form-select h-9 px-3 pr-8 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] cursor-pointer transition-colors">
|
|
374
|
+
<option value="gitlab">GitLab</option>
|
|
375
|
+
<option value="gitee">Gitee</option>
|
|
376
|
+
<option value="github">GitHub</option>
|
|
377
|
+
</select>
|
|
378
|
+
</div>
|
|
379
|
+
<div id="modalUrlGroup" class="flex flex-col gap-2 mb-5">
|
|
380
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Instance URL <span class="text-error">*</span></label>
|
|
381
|
+
<input id="modalUrl" placeholder="https://gitlab.company.com"
|
|
382
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
383
|
+
<div class="text-[11px] text-text-tertiary">Self-hosted instance URL (not needed for GitHub)</div>
|
|
384
|
+
</div>
|
|
385
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
386
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Access Token <span class="text-error">*</span></label>
|
|
387
|
+
<input id="modalToken" type="password" placeholder="Your access token" autocomplete="off"
|
|
388
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
389
|
+
<div class="text-[11px] text-text-tertiary">Personal access token with api scope</div>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
392
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Display Name</label>
|
|
393
|
+
<input id="modalName" placeholder="e.g. Company GitLab"
|
|
394
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
395
|
+
<div class="text-[11px] text-text-tertiary">Optional friendly name</div>
|
|
396
|
+
</div>
|
|
397
|
+
<div id="modalCheck" class="text-[13px] text-text-tertiary mt-2 mb-2"></div>
|
|
398
|
+
<div class="flex gap-2 justify-end mt-6">
|
|
399
|
+
<button onclick="testModalConnection()"
|
|
400
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Test Connection</button>
|
|
401
|
+
<button onclick="closeModal()"
|
|
402
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Cancel</button>
|
|
403
|
+
<button onclick="saveProvider()"
|
|
404
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Save</button>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
|
|
409
|
+
<!-- Add Local Repository -->
|
|
410
|
+
<div id="localModalOverlay" onclick="if(event.target===this)closeLocalModal()"
|
|
411
|
+
class="fixed inset-0 z-[100] hidden [&.show]:flex items-center justify-center animate-fadeIn">
|
|
412
|
+
<div class="bg-surface border border-white/[0.07] rounded-lg p-8 w-full max-w-[560px] shadow-modal">
|
|
413
|
+
<div class="text-[16px] font-semibold mb-6 tracking-[-0.01em]">Add Local Repository</div>
|
|
414
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
415
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Repository Path <span class="text-error">*</span></label>
|
|
416
|
+
<div class="flex gap-2">
|
|
417
|
+
<input id="localPath" placeholder="/path/to/your/repository"
|
|
418
|
+
class="form-input flex-1 h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
419
|
+
<button onclick="browseDirectory()"
|
|
420
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Browse</button>
|
|
421
|
+
</div>
|
|
422
|
+
<div class="text-[11px] text-text-tertiary">Absolute path to an existing git repository on this server</div>
|
|
423
|
+
</div>
|
|
424
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
425
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Display Name</label>
|
|
426
|
+
<input id="localName" placeholder="e.g. my-project"
|
|
427
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
428
|
+
<div class="text-[11px] text-text-tertiary">Optional friendly name (defaults to directory name)</div>
|
|
429
|
+
</div>
|
|
430
|
+
<div id="browserPanel" class="hidden mt-3 border border-white/[0.04] rounded-md overflow-hidden">
|
|
431
|
+
<div class="px-3 py-2 bg-surface-elevated border-b border-white/[0.04] flex items-center gap-2">
|
|
432
|
+
<button onclick="browseParent()" title="Up"
|
|
433
|
+
class="inline-flex items-center h-7 px-2 rounded-sm font-medium text-[13px] text-text-tertiary bg-transparent border-0 cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors">↑</button>
|
|
434
|
+
<span id="browserPath" class="text-[12px] text-text-secondary font-mono flex-1 overflow-hidden text-ellipsis whitespace-nowrap">/</span>
|
|
435
|
+
<button onclick="closeBrowser()" title="Close"
|
|
436
|
+
class="inline-flex items-center h-7 px-2 rounded-sm font-medium text-[13px] text-text-tertiary bg-transparent border-0 cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors">✕</button>
|
|
437
|
+
</div>
|
|
438
|
+
<div id="browserList" class="max-h-[240px] overflow-y-auto"></div>
|
|
439
|
+
</div>
|
|
440
|
+
<div id="localModalCheck" class="text-[13px] text-text-tertiary mt-2 mb-2"></div>
|
|
441
|
+
<div class="flex gap-2 justify-end mt-6">
|
|
442
|
+
<button onclick="closeLocalModal()"
|
|
443
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Cancel</button>
|
|
444
|
+
<button onclick="addLocalRepo()"
|
|
445
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Add Repository</button>
|
|
446
|
+
</div>
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
<!-- Token Modal -->
|
|
451
|
+
<div id="tokenOverlay" class="fixed inset-0 z-[100] hidden [&.show]:flex items-center justify-center animate-fadeIn">
|
|
452
|
+
<div class="bg-surface border border-white/[0.07] rounded-lg p-8 w-full max-w-[460px] shadow-modal">
|
|
453
|
+
<div class="text-[16px] font-semibold mb-6 tracking-[-0.01em]">Access Token</div>
|
|
454
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
455
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Access Token</label>
|
|
456
|
+
<input id="tokenInput" type="password" placeholder="ADMIN_TOKEN / MCP_TOKEN" onkeydown="if(event.key==='Enter')saveTokenInput()"
|
|
457
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
458
|
+
<div id="tokenHint" class="text-[11px] text-text-tertiary">This server has authentication enabled. Enter the <b>ADMIN_TOKEN</b> (stored only in this browser's localStorage). The MCP token is only valid for <code>/mcp</code>.</div>
|
|
459
|
+
</div>
|
|
460
|
+
<div class="flex gap-2 justify-end mt-6">
|
|
461
|
+
<button onclick="clearSavedToken()"
|
|
462
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Clear Local Token</button>
|
|
463
|
+
<button onclick="saveTokenInput()"
|
|
464
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Save & Retry</button>
|
|
465
|
+
</div>
|
|
466
|
+
</div>
|
|
467
|
+
</div>
|
|
468
|
+
|
|
469
|
+
<!-- Worktree Branch Modal -->
|
|
470
|
+
<div id="wtOverlay" onclick="if(event.target===this)closeWt()"
|
|
471
|
+
class="fixed inset-0 z-[100] hidden [&.show]:flex items-center justify-center animate-fadeIn">
|
|
472
|
+
<div class="bg-surface border border-white/[0.07] rounded-lg p-8 w-full max-w-[460px] shadow-modal">
|
|
473
|
+
<div class="text-[16px] font-semibold mb-6 tracking-[-0.01em]">Add Branch Worktree</div>
|
|
474
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
475
|
+
<label class="text-[12px] font-medium text-text-secondary tracking-[0.02em]">Branch Name</label>
|
|
476
|
+
<input id="wtBranch" placeholder="e.g. dev_ibu" onkeydown="if(event.key==='Enter')submitWt()"
|
|
477
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent focus:shadow-[0_0_0_3px_rgba(94,106,210,0.12)] placeholder:text-text-quaternary transition-colors">
|
|
478
|
+
<div class="text-[11px] text-text-tertiary">Creates an isolated worktree + code index for this branch. The branch must already exist on the remote.</div>
|
|
479
|
+
</div>
|
|
480
|
+
<div class="flex gap-2 justify-end mt-6">
|
|
481
|
+
<button onclick="closeWt()"
|
|
482
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Cancel</button>
|
|
483
|
+
<button onclick="submitWt()"
|
|
484
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Create & Index</button>
|
|
485
|
+
</div>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<!-- Create space -->
|
|
490
|
+
<div id="newSpaceOverlay" onclick="if(event.target===this)closeNewSpace()"
|
|
491
|
+
class="fixed inset-0 z-[100] hidden [&.show]:flex items-center justify-center animate-fadeIn">
|
|
492
|
+
<div class="bg-surface border border-white/[0.07] rounded-lg p-8 w-full max-w-[420px] shadow-modal">
|
|
493
|
+
<div class="text-[16px] font-semibold mb-2 tracking-[-0.01em]">New space</div>
|
|
494
|
+
<p class="text-[12px] text-text-tertiary mb-5">Group repositories by team, product, or environment.</p>
|
|
495
|
+
<input id="newSpaceName" placeholder="e.g. Platform, Mobile, Aftersales" onkeydown="if(event.key==='Enter')createSpaceFromModal()"
|
|
496
|
+
class="form-input h-9 w-full px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent placeholder:text-text-quaternary transition-colors">
|
|
497
|
+
<div id="newSpaceState" class="text-[11px] text-text-tertiary mt-2"></div>
|
|
498
|
+
<div class="flex gap-2 justify-end mt-6">
|
|
499
|
+
<button onclick="closeNewSpace()"
|
|
500
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Cancel</button>
|
|
501
|
+
<button onclick="createSpaceFromModal()"
|
|
502
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Create space</button>
|
|
503
|
+
</div>
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
|
|
507
|
+
<!-- Space bindings -->
|
|
508
|
+
<div id="spaceOverlay" onclick="if(event.target===this)closeSpaceDialog()"
|
|
509
|
+
class="fixed inset-0 z-[100] hidden [&.show]:flex items-center justify-center animate-fadeIn">
|
|
510
|
+
<div class="bg-surface border border-white/[0.07] rounded-lg p-8 w-full max-w-[440px] max-h-[80vh] overflow-y-auto shadow-modal">
|
|
511
|
+
<div class="text-[16px] font-semibold mb-2 tracking-[-0.01em]">Spaces</div>
|
|
512
|
+
<p id="spaceDialogRepo" class="text-[12px] text-text-tertiary mb-5"></p>
|
|
513
|
+
<div id="spaceDialogList" class="flex flex-col gap-2 mb-5"></div>
|
|
514
|
+
<div id="spaceDialogEmpty" class="hidden text-[12px] text-text-tertiary mb-5">No spaces yet. Create one below or from the space menu.</div>
|
|
515
|
+
<div class="flex gap-2">
|
|
516
|
+
<input id="newSpaceNameInline" placeholder="New space for this repo" onkeydown="if(event.key==='Enter')createSpaceInline()"
|
|
517
|
+
class="form-input h-9 flex-1 min-w-0 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent placeholder:text-text-quaternary transition-colors">
|
|
518
|
+
<button onclick="createSpaceInline()"
|
|
519
|
+
class="inline-flex items-center gap-2 h-9 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors whitespace-nowrap">Create & assign</button>
|
|
520
|
+
</div>
|
|
521
|
+
<div class="flex gap-2 justify-end mt-6">
|
|
522
|
+
<button onclick="closeSpaceDialog()"
|
|
523
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Cancel</button>
|
|
524
|
+
<button onclick="saveSpaceBindings()"
|
|
525
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Save</button>
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
</div>
|
|
529
|
+
|
|
530
|
+
<!-- Create access token -->
|
|
531
|
+
<div id="tokenCreateOverlay" onclick="if(event.target===this)closeTokenCreate()"
|
|
532
|
+
class="fixed inset-0 z-[100] hidden [&.show]:flex items-center justify-center animate-fadeIn">
|
|
533
|
+
<div class="bg-surface border border-white/[0.07] rounded-lg p-8 w-full max-w-[520px] max-h-[85vh] overflow-y-auto shadow-modal">
|
|
534
|
+
<div id="tokenCreateForm">
|
|
535
|
+
<div class="text-[16px] font-semibold mb-2 tracking-[-0.01em]">New access token</div>
|
|
536
|
+
<p class="text-[12px] text-text-tertiary mb-5">Shown once. Pick the roles and spaces this token can access.</p>
|
|
537
|
+
<div class="flex flex-col gap-2 mb-5">
|
|
538
|
+
<label class="text-[12px] font-medium text-text-secondary">Name</label>
|
|
539
|
+
<input id="tokenName" placeholder="e.g. Cursor · Platform" onkeydown="if(event.key==='Enter')createToken()"
|
|
540
|
+
class="form-input h-9 px-3 bg-surface-elevated border border-white/[0.04] rounded-sm text-text-primary text-[14px] outline-none focus:border-accent placeholder:text-text-quaternary transition-colors">
|
|
541
|
+
</div>
|
|
542
|
+
<div class="mb-5">
|
|
543
|
+
<div class="text-[12px] font-medium text-text-secondary mb-2">Roles</div>
|
|
544
|
+
<label class="flex items-center gap-3 px-3 py-2 border border-white/[0.04] rounded-sm text-[13px] cursor-pointer hover:bg-surface-hover transition-colors"><input id="tokenRoleWeb" type="checkbox" checked class="w-4 h-4 accent-white"><span>Web UI</span></label>
|
|
545
|
+
<label class="flex items-center gap-3 px-3 py-2 mt-2 border border-white/[0.04] rounded-sm text-[13px] cursor-pointer hover:bg-surface-hover transition-colors"><input id="tokenRoleMcp" type="checkbox" class="w-4 h-4 accent-white"><span>MCP</span></label>
|
|
546
|
+
</div>
|
|
547
|
+
<div class="mb-5">
|
|
548
|
+
<div class="flex items-center justify-between mb-2"><div class="text-[12px] font-medium text-text-secondary">Spaces</div><span class="text-[11px] text-text-tertiary">Unchecked = all spaces</span></div>
|
|
549
|
+
<div id="tokenSpaces" class="flex flex-col gap-2"></div>
|
|
550
|
+
</div>
|
|
551
|
+
<div id="tokenCreateState" class="text-[11px] text-text-tertiary mb-2"></div>
|
|
552
|
+
<div class="flex gap-2 justify-end mt-6">
|
|
553
|
+
<button onclick="closeTokenCreate()"
|
|
554
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Cancel</button>
|
|
555
|
+
<button onclick="createToken()"
|
|
556
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Create token</button>
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
<div id="tokenReveal" class="hidden">
|
|
560
|
+
<div class="text-[16px] font-semibold mb-2 tracking-[-0.01em]">Token created</div>
|
|
561
|
+
<p class="text-[12px] text-text-tertiary mb-4">Copy it now — it will not be shown again.</p>
|
|
562
|
+
<pre id="tokenValue" class="bg-surface-elevated border border-white/[0.04] p-4 text-[12px] leading-[1.6] text-text-secondary overflow-x-auto font-mono"></pre>
|
|
563
|
+
<div class="flex gap-2 justify-end mt-5">
|
|
564
|
+
<button onclick="copyTokenValue()"
|
|
565
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-text-secondary bg-surface-elevated border border-white/[0.04] hover:text-text-primary hover:border-white/[0.07] hover:bg-surface-hover cursor-pointer transition-colors">Copy token</button>
|
|
566
|
+
<button onclick="closeTokenCreate()"
|
|
567
|
+
class="inline-flex items-center gap-2 h-8 px-4 rounded-sm font-medium text-[13px] text-black bg-accent hover:bg-accent-hover cursor-pointer border-0 transition-colors">Done</button>
|
|
568
|
+
</div>
|
|
569
|
+
</div>
|
|
570
|
+
</div>
|
|
571
|
+
</div>
|
|
572
|
+
|
|
573
|
+
<!-- Toast + Message Popover -->
|
|
574
|
+
<div id="toast" class="toast fixed top-5 right-5 z-[200] px-5 py-3 rounded-md font-medium text-[13px] tracking-[0.01em] bg-surface border border-white/[0.07] shadow-toast"></div>
|
|
575
|
+
<div id="msgPopover" class="hidden fixed z-[100] bg-surface border border-white/[0.07] rounded-md p-4 text-[13px] text-text-secondary max-w-[480px] max-h-[300px] overflow-y-auto whitespace-pre-wrap break-words shadow-popover"></div>
|
|
576
|
+
|
|
577
|
+
<script>
|
|
578
|
+
/* ============ SAME JS — ALL UNCHANGED (Tailwind handles the classes via [&.xxx] arbitrary variants) ============ */
|
|
579
|
+
const $=id=>document.getElementById(id);
|
|
580
|
+
let S={},providers=[],repos=[],spaces=[{id:'all',name:'All',builtin:true}],currentSpace='all',selectedSet=new Set(),results=[],ME=null;
|
|
581
|
+
const esc=s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));
|
|
582
|
+
function toast(msg,type=''){const t=$('toast');t.textContent=msg;t.className='toast fixed top-5 right-5 z-[200] px-5 py-3 rounded-md font-medium text-[13px] tracking-[0.01em] bg-surface border border-white/[0.07] shadow-toast show'+(type?' '+type:'');clearTimeout(t._t);t._t=setTimeout(()=>t.className='toast fixed top-5 right-5 z-[200] px-5 py-3 rounded-md font-medium text-[13px] tracking-[0.01em] bg-surface border border-white/[0.07] shadow-toast',3000);}
|
|
583
|
+
let TOKEN=localStorage.getItem('cgm_token')||'';
|
|
584
|
+
const authHeaders=()=>TOKEN?{'Authorization':'Bearer '+TOKEN}:{};
|
|
585
|
+
async function api(p,o={}){const r=await fetch(p.replace(/^\//,''),{...o,headers:{'content-type':'application/json',...authHeaders(),...(o.headers||{})}});if(r.status===401){let j={};try{j=await r.json()}catch{};promptToken(j.error==='web_role_required'?'This token is MCP-only. Enter the ADMIN_TOKEN or a web-access token for the UI.':'Access denied (401). Enter the ADMIN_TOKEN or a web-access token.');throw new Error('unauthorized');}if(r.status===403){let j={};try{j=await r.json()}catch{};toast(j.error==='admin_required'?'Admin token required':j.error==='out_of_scope'?'Outside your allowed spaces':'Not allowed','error');throw new Error('forbidden');}return r.json();}
|
|
586
|
+
function promptToken(msg){if(msg)$('tokenHint').textContent=msg;$('tokenOverlay').classList.add('show');setTimeout(()=>$('tokenInput').focus(),0);}
|
|
587
|
+
async function saveTokenInput(){const v=$('tokenInput').value.trim();if(!v){toast('Please enter a token','error');return;}TOKEN=v;localStorage.setItem('cgm_token',v);$('tokenOverlay').classList.remove('show');try{await loadMe();await loadSettings();await loadRepos();connectSse();if(isAdmin())await loadTokens();toast('Token accepted','success');}catch{}}
|
|
588
|
+
function clearSavedToken(){TOKEN='';localStorage.removeItem('cgm_token');$('tokenInput').value='';toast('Local token cleared');}
|
|
589
|
+
async function loadMe(){ME=await api('/api/auth/me');applyPermissions();return ME;}
|
|
590
|
+
const isAdmin=()=>!!(ME&&ME.admin);
|
|
591
|
+
const canUseMcp=()=>!!(ME&&(ME.admin||ME.roles.includes('mcp')));
|
|
592
|
+
function applyPermissions(){
|
|
593
|
+
const admin=isAdmin();
|
|
594
|
+
const navSettings=document.querySelector('.nav-pill[data-view="settings"]');
|
|
595
|
+
if(navSettings)navSettings.style.display=admin?'':'none';
|
|
596
|
+
const addPanel=$('addRepoPanel');
|
|
597
|
+
if(addPanel)addPanel.style.display=admin?'':'none';
|
|
598
|
+
if(!admin&&document.getElementById('view-settings')?.classList.contains('active'))switchView('overview');
|
|
599
|
+
if(repos.length||spaces.length){try{renderSpaces();renderRepos();renderStats();}catch{}}
|
|
600
|
+
}
|
|
601
|
+
window.addEventListener('unhandledrejection',e=>{if(e&&e.reason&&e.reason.message==='unauthorized')e.preventDefault();});
|
|
602
|
+
const STATUS={registered:['','Registered'],cloning:['cloning','Cloning'],cloned:['cloned','Cloned'],indexing:['indexing','Indexing'],indexed:['indexed','Indexed'],removing:['removing','Removing'],failed:['failed','Failed'],pull_failed:['failed','Pull Failed']};
|
|
603
|
+
const PROVIDER_LABELS={gitlab:'GitLab',gitee:'Gitee',github:'GitHub'};
|
|
604
|
+
const PROVIDER_PLACEHOLDERS={gitlab:{url:'https://gitlab.company.com',token:'glpat-xxxx'},gitee:{url:'https://gitee.com',token:'Your Gitee access token'},github:{url:'',token:'ghp_xxxx or github_pat_xxxx'}};
|
|
605
|
+
|
|
606
|
+
/* view toggle — .active 由 Tailwind [&.active]:block 控制 */
|
|
607
|
+
document.querySelectorAll('.nav-pill').forEach(b=>b.addEventListener('click',()=>{
|
|
608
|
+
document.querySelectorAll('.nav-pill').forEach(x=>x.classList.toggle('active',x===b));
|
|
609
|
+
document.querySelectorAll('.view').forEach(v=>v.classList.toggle('active',v.id==='view-'+b.dataset.view));
|
|
610
|
+
}));
|
|
611
|
+
|
|
612
|
+
/* Modal show/hide — .show 类控制 display:flex/none */
|
|
613
|
+
function openModal(editId){
|
|
614
|
+
const isEdit=!!editId;
|
|
615
|
+
$('modalTitle').textContent=isEdit?'Edit Provider':'Add Provider';
|
|
616
|
+
$('editProviderId').value=editId||'';
|
|
617
|
+
if(isEdit){const p=providers.find(x=>x.id===editId);if(!p)return;
|
|
618
|
+
$('modalProvider').value=p.type;$('modalUrl').value=p.url||'';$('modalToken').value='';$('modalName').value=p.name||'';}
|
|
619
|
+
else{$('modalProvider').value='gitlab';$('modalUrl').value='';$('modalToken').value='';$('modalName').value='';}
|
|
620
|
+
onModalProviderChange();$('modalCheck').textContent='';
|
|
621
|
+
$('modalOverlay').classList.add('show');
|
|
622
|
+
}
|
|
623
|
+
function closeModal(){$('modalOverlay').classList.remove('show');}
|
|
624
|
+
function onModalProviderChange(){const p=$('modalProvider').value;$('modalUrlGroup').classList.toggle('hidden',p==='github');$('modalUrl').placeholder=PROVIDER_PLACEHOLDERS[p].url;$('modalToken').placeholder=PROVIDER_PLACEHOLDERS[p].token;if(!$('modalName').value)$('modalName').placeholder=PROVIDER_LABELS[p];}
|
|
625
|
+
async function testModalConnection(){const type=$('modalProvider').value,url=$('modalUrl').value.trim(),token=$('modalToken').value.trim();if(type!=='github'&&!url){toast('Please enter URL','error');return;}if(!token){toast('Please enter token','error');return;}
|
|
626
|
+
$('modalCheck').textContent='Testing...';
|
|
627
|
+
const r=await api('/api/providers/check',{method:'POST',body:JSON.stringify({type,url,token})});
|
|
628
|
+
if(r.ok){$('modalCheck').innerHTML=`<span style="color:#7DD3A0">✓ Connected as ${esc(r.user)}${r.version?' · v'+esc(r.version):''}</span>`;toast('Connection successful','success');}
|
|
629
|
+
else{$('modalCheck').innerHTML=`<span style="color:#FF8A72">✗ ${esc(r.msg||'Failed')}</span>`;toast('Connection failed','error');}}
|
|
630
|
+
async function saveProvider(){const id=$('editProviderId').value,type=$('modalProvider').value,url=$('modalUrl').value.trim(),token=$('modalToken').value.trim(),name=$('modalName').value.trim()||PROVIDER_LABELS[type];if(type!=='github'&&!url){toast('Please enter URL','error');return;}if(!token&&!id){toast('Please enter token','error');return;}
|
|
631
|
+
const body={type,name,url};if(token)body.token=token;
|
|
632
|
+
if(id){await api('/api/providers/'+id,{method:'PUT',body:JSON.stringify(body)});toast('Provider updated','success');}
|
|
633
|
+
else{await api('/api/providers',{method:'POST',body:JSON.stringify(body)});toast('Provider added','success');}
|
|
634
|
+
closeModal();await loadSettings();}
|
|
635
|
+
|
|
636
|
+
async function saveGlobalSettings(){const baseDir=$('baseDir').value.trim();if(!baseDir){toast('Please enter clone base directory','error');return;}const body={cloneBaseDir:baseDir};const ah=$('autoSyncHours').value.trim();if(ah!=='') body.autoSyncHours=parseFloat(ah);await api('/api/settings',{method:'PUT',body:JSON.stringify(body)});toast('Settings saved','success');}
|
|
637
|
+
|
|
638
|
+
async function loadSettings(){S=await api('/api/settings');providers=await api('/api/providers');$('baseDir').value=S.cloneBaseDir||'';$('autoSyncHours').value=(S.autoSyncHours==null?'' : S.autoSyncHours);renderProviders();renderConn();}
|
|
639
|
+
function renderProviders(){const list=$('providerList');list.innerHTML='';$('providerEmpty').style.display=providers.length?'none':'flex';
|
|
640
|
+
providers.forEach(p=>{const isActive=p.id===S.activeProviderId;
|
|
641
|
+
const div=document.createElement('div');
|
|
642
|
+
div.className='provider-item flex items-center gap-5 py-4 px-5 bg-surface-elevated border border-white/[0.04] rounded-md transition-colors hover:border-white/[0.07] [&.active]:border-accent [&.active]:bg-accent-dim';
|
|
643
|
+
if(isActive) div.classList.add('active');
|
|
644
|
+
div.innerHTML=`<div class="provider-icon ${p.type} w-9 h-9 rounded-sm grid place-items-center text-[16px] font-bold tracking-[-0.02em] text-white">${p.type[0].toUpperCase()}</div>
|
|
645
|
+
<div class="flex-1 min-w-0"><div class="provider-name text-[14px] font-semibold text-text-primary tracking-[-0.01em]">${esc(p.name)}</div><div class="provider-url text-[12px] text-text-tertiary font-mono mt-0.5 overflow-hidden text-ellipsis whitespace-nowrap">${esc(p.url||'api.github.com')}</div></div>
|
|
646
|
+
<span class="provider-badge [&.active]:bg-success-dim [&.active]:text-success [&.inactive]:bg-surface-hover [&.inactive]:text-text-tertiary px-2 py-0.5 rounded-xs text-[10px] font-semibold tracking-[0.06em] uppercase ${isActive?'active':'inactive'}" title="${isActive?'Default provider used when searching repositories':'Click Activate to make this the default search provider'}">${isActive?'Active':'Inactive'}</span>
|
|
647
|
+
<div class="flex gap-2">
|
|
648
|
+
${isActive?'':`<button class="btn-ghost [&.active]:bg-surface" onclick="activateProvider('${p.id}')">Activate</button>`}
|
|
649
|
+
<button class="btn-ghost" onclick="openModal('${p.id}')">Edit</button>
|
|
650
|
+
<button class="btn-danger" onclick="deleteProvider('${p.id}')">Delete</button>
|
|
651
|
+
</div>`;
|
|
652
|
+
list.appendChild(div);});}
|
|
653
|
+
async function activateProvider(id){await api('/api/settings',{method:'PUT',body:JSON.stringify({activeProviderId:id})});toast('Provider activated','success');await loadSettings();}
|
|
654
|
+
async function deleteProvider(id){const p=providers.find(x=>x.id===id);if(!confirm('Delete "'+p.name+'"?'))return;await api('/api/providers/'+id,{method:'DELETE'});toast('Provider deleted','success');await loadSettings();}
|
|
655
|
+
|
|
656
|
+
/* Connection status */
|
|
657
|
+
function renderConn(){const badge=$('connStatus'),text=$('connText');const active=providers.find(p=>p.id===S.activeProviderId);
|
|
658
|
+
if(!active){badge.className='inline-flex items-center gap-1.5 px-3 py-1 rounded-sm border border-white/[0.04] bg-surface-elevated text-text-secondary text-[11px] font-medium tracking-[0.02em]';badge.querySelector('.dot').style.background='#737373';text.textContent='No provider';return;}
|
|
659
|
+
badge.className='inline-flex items-center gap-1.5 px-3 py-1 rounded-sm border border-white/[0.04] text-success bg-success-dim text-[11px] font-medium tracking-[0.02em] border-success/25';badge.querySelector('.dot').style.background='#7DD3A0';badge.querySelector('.dot').style.boxShadow='0 0 6px rgba(125,211,160,0.4)';text.textContent=active.name;}
|
|
660
|
+
|
|
661
|
+
/* Search */
|
|
662
|
+
async function searchProjects(){const q=$('searchBox').value.trim();if(!q)return;if(!providers.length){toast('Please add a provider in Settings first','error');return;}
|
|
663
|
+
const area=$('searchArea');area.classList.remove('hidden');area.className='flex flex-col items-center justify-center py-12 px-5 text-text-tertiary text-center';area.innerHTML='<p class="text-[13px] max-w-[320px] leading-[1.6]">Searching all providers...</p>';
|
|
664
|
+
try{const r=await api('/api/providers/projects?'+new URLSearchParams({q}));
|
|
665
|
+
if(r.error){toast('Search failed: '+r.error,'error');area.innerHTML='<p class="text-[13px] max-w-[320px] leading-[1.6]">Search failed</p>';return;}
|
|
666
|
+
const spaceRepos=currentSpace==='all'?repos:repos.filter(x=>(x.spaceIds||[]).includes(currentSpace));
|
|
667
|
+
const inSpace=new Set(spaceRepos.map(x=>x.gitUrl).filter(Boolean));
|
|
668
|
+
const inSpaceNames=new Set(spaceRepos.map(x=>x.name));
|
|
669
|
+
const r2=r.filter(p=>!inSpace.has(p.gitUrl)&&!inSpaceNames.has(p.name));
|
|
670
|
+
results=r2;selectedSet.clear();updateAddBtn();
|
|
671
|
+
if(!r2.length){area.innerHTML='<p class="text-[13px] max-w-[320px] leading-[1.6]">All matching repos are already in this space, or nothing new was found</p>';return;}
|
|
672
|
+
area.className='border border-white/[0.04] rounded-md overflow-hidden bg-surface-elevated';
|
|
673
|
+
area.innerHTML=r2.map((p,i)=>`<div class="search-item [&.selected]:bg-accent-dim flex items-center gap-3 px-4 py-3 cursor-pointer border-b border-white/[0.04] last:border-b-0 hover:bg-surface-hover transition-colors" onclick="togglePick(${i})">
|
|
674
|
+
<div class="radio relative w-4 h-4 border rounded-xs border-text-quaternary flex-shrink-0 transition-[border,background] [&.checked]:border-accent [&.checked]:bg-accent ${selectedSet.has(i)?'checked':''}"></div>
|
|
675
|
+
<div class="flex-1 min-w-0"><div class="text-[13px] font-medium text-text-primary">${esc(p.name)}</div><div class="text-[12px] text-text-tertiary font-mono">${esc(p.gitUrl)}</div></div>
|
|
676
|
+
<div class="text-[10px] font-semibold tracking-[0.04em] uppercase text-text-secondary bg-surface-hover rounded-xs px-2 py-0.5 ml-auto">${esc(PROVIDER_LABELS[p.providerType]||p.providerType)}</div>
|
|
677
|
+
</div>`).join('');}catch(e){if(e.message!=='unauthorized')toast('Search failed: '+e.message,'error'),area.innerHTML='<p class="text-[13px] max-w-[320px] leading-[1.6]">Search failed</p>';}}
|
|
678
|
+
function togglePick(i){if(selectedSet.has(i))selectedSet.delete(i);else selectedSet.add(i);const el=document.querySelectorAll('.search-item')[i];if(el){el.classList.toggle('selected',selectedSet.has(i));el.querySelector('.radio').classList.toggle('checked',selectedSet.has(i));}updateAddBtn();}
|
|
679
|
+
function updateAddBtn(){const n=selectedSet.size;const btn=$('btnAdd');btn.disabled=n===0;btn.textContent=n?`Add Selected (${n})`:'Add Selected';}
|
|
680
|
+
async function addSelected(){const items=[...selectedSet].map(i=>results[i]);if(!items.length)return;const btn=$('btnAdd');btn.disabled=true;
|
|
681
|
+
for(const p of items){const r=await api('/api/repos',{method:'POST',body:JSON.stringify({name:p.name,gitUrl:p.gitUrl,branch:p.defaultBranch||'',providerId:p.providerId,providerType:p.providerType,spaceIds:currentSpace==='all'?[]:[currentSpace]})});if(r.id)doOp(r.id,'clone');}
|
|
682
|
+
const area=$('searchArea');area.className='flex flex-col items-center justify-center py-12 px-5 text-text-tertiary text-center';area.innerHTML=`<p class="text-[13px] max-w-[320px] leading-[1.6]">Added ${items.length} project(s), cloning & analyzing...</p>`;setTimeout(()=>{$('searchArea').classList.add('hidden');$('searchBox').value='';},4000);}
|
|
683
|
+
|
|
684
|
+
/* Space menu — .show / .active 由 Tailwind [&.active]:accent 响应 */
|
|
685
|
+
async function loadSpaces(){spaces=await api('/api/spaces');renderSpaces();}
|
|
686
|
+
function switchView(view){if(view==='settings'&&!isAdmin())view='overview';document.querySelectorAll('.nav-pill').forEach(x=>x.classList.toggle('active',x.dataset.view===view));document.querySelectorAll('.view').forEach(v=>v.classList.toggle('active',v.id==='view-'+view));if(view==='settings'&&isAdmin())loadTokens().catch(()=>{});}
|
|
687
|
+
function renderSpaces(){const menu=$('spaceMenu');if(!menu)return;const sorted=[...spaces].sort((a,b)=>(a.order??0)-(b.order??0));const cur=spaces.find(s=>s.id===currentSpace)||{};
|
|
688
|
+
$('spaceTriggerLabel').textContent=cur.builtin?'All spaces':(cur.name||'All spaces');
|
|
689
|
+
menu.innerHTML='<div class="px-3 pt-1 pb-2 text-[10px] text-text-quaternary tracking-[0.08em] uppercase">Filter by space</div>'+sorted.map(s=>`<div class="[&.active]:text-accent flex items-center gap-3 px-3 py-2 rounded-sm cursor-pointer text-[13px] text-text-secondary hover:bg-surface-hover hover:text-text-primary transition-colors ${currentSpace===s.id?'active':''}" onclick="onSpaceSelect('${s.id}')">
|
|
690
|
+
<span class="name flex-1 overflow-hidden text-ellipsis whitespace-nowrap">${s.builtin?'All':esc(s.name)}</span>
|
|
691
|
+
${s.builtin?'':(isAdmin()?`<span class="del opacity-0 text-text-quaternary text-[14px] leading-none px-1 rounded-xs transition-all hover:opacity-100 hover:text-error hover:bg-error-dim" onclick="event.stopPropagation();delSpace('${s.id}')">×</span>`:'')}
|
|
692
|
+
</div>`).join('')
|
|
693
|
+
+ (isAdmin()?`<div class="flex items-center gap-2 px-3 py-3 border-t border-white/[0.04] mt-0.5 text-accent font-medium text-[13px] cursor-pointer rounded-sm hover:bg-accent-dim transition-colors" onclick="openNewSpaceDialog()">
|
|
694
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>New Space</div>`:'');}
|
|
695
|
+
function toggleSpaceMenu(){const m=$('spaceMenu');m.style.display=m.style.display==='none'?'block':'none';}
|
|
696
|
+
document.addEventListener('click',e=>{const w=e.target.closest('.space-select');if(!w)$('spaceMenu').style.display='none';});
|
|
697
|
+
async function onSpaceSelect(id){currentSpace=id;renderSpaces();$('spaceMenu').style.display='none';switchView('repos');await loadRepos();}
|
|
698
|
+
function openNewSpaceDialog(){$('spaceMenu').style.display='none';$('newSpaceName').value='';$('newSpaceState').textContent='';$('newSpaceOverlay').classList.add('show');setTimeout(()=>$('newSpaceName').focus(),0);}
|
|
699
|
+
function closeNewSpace(){$('newSpaceOverlay').classList.remove('show');}
|
|
700
|
+
async function createSpaceFromModal(){const name=$('newSpaceName').value.trim();if(!name){$('newSpaceState').textContent='Enter a space name.';return;}try{const r=await api('/api/spaces',{method:'POST',body:JSON.stringify({name})});if(!r.id){$('newSpaceState').textContent=r.error||'Failed to create space';return;}closeNewSpace();toast('Space created — use + Spaces in the repository table to assign repos','success');await loadSpaces();}catch(e){if(e.message!=='unauthorized')$('newSpaceState').textContent='Failed to create space: '+e.message;}}
|
|
701
|
+
async function createSpaceInline(){const r=repos.find(x=>x.id===spaceDialogRepoId);if(!r)return;const input=$('newSpaceNameInline');const name=input.value.trim();if(!name){input.focus();return;}try{const sp=await api('/api/spaces',{method:'POST',body:JSON.stringify({name})});if(!sp.id){toast(sp.error||'Failed to create space','error');return;}await api(`api/spaces/${sp.id}/repos`,{method:'POST',body:JSON.stringify({repoId:r.id})});input.value='';await loadRepos();openSpaceDialog(spaceDialogRepoId);toast('Space created and assigned','success');}catch(e){if(e.message!=='unauthorized')toast('Failed to create space: '+e.message,'error');}}
|
|
702
|
+
async function delSpace(id){const sp=spaces.find(s=>s.id===id);if(!sp||sp.builtin)return;if(!confirm(`Delete space "${sp.name}"?\nRepos bound to it will be unbound (the repos themselves are kept).`))return;const r=await api(`/api/spaces/${id}`,{method:'DELETE'});if(r.ok){toast('Space deleted','success');if(currentSpace===id)currentSpace='all';await loadSpaces();renderRepos();renderStats();}else toast(r.error||'Failed to delete space','error');}
|
|
703
|
+
|
|
704
|
+
/* Repos — status badge 用 [&.failed]:text-error 等 Tailwind variant */
|
|
705
|
+
let spaceDialogRepoId='';
|
|
706
|
+
function openSpaceDialog(repoId){const r=repos.find(x=>x.id===repoId);if(!r)return;spaceDialogRepoId=repoId;const list=spaces.filter(s=>!s.builtin);$('spaceDialogRepo').textContent=r.name;$('spaceDialogEmpty').classList.toggle('hidden',list.length>0);$('spaceDialogList').innerHTML=list.map(s=>`<label class="flex items-center gap-3 px-3 py-2 border border-white/[0.04] rounded-sm text-[13px] cursor-pointer hover:bg-surface-hover transition-colors"><input type="checkbox" value="${s.id}"${(r.spaceIds||[]).includes(s.id)?' checked':''} class="w-4 h-4 accent-white"><span>${esc(s.name)}</span></label>`).join('');$('spaceOverlay').classList.add('show');}
|
|
707
|
+
function closeSpaceDialog(){$('spaceOverlay').classList.remove('show');}
|
|
708
|
+
async function saveSpaceBindings(){const r=repos.find(x=>x.id===spaceDialogRepoId);if(!r)return;const checked=new Set([...$('spaceDialogList').querySelectorAll('input:checked')].map(i=>i.value));const current=new Set(r.spaceIds||[]);const add=[...checked].filter(id=>!current.has(id));const del=[...current].filter(id=>!checked.has(id));try{for(const id of add)await api(`api/spaces/${id}/repos`,{method:'POST',body:JSON.stringify({repoId:r.id})});for(const id of del)await api(`api/spaces/${id}/repos/${r.id}`,{method:'DELETE'});closeSpaceDialog();toast('Spaces updated','success');await loadRepos();}catch(e){if(e.message!=='unauthorized')toast('Failed to update spaces: '+e.message,'error');}}
|
|
709
|
+
async function loadRepos(){repos=await api('/api/repos');spaces=await api('/api/spaces');renderSpaces();renderRepos();renderStats();renderConn();}
|
|
710
|
+
function statusCls(cls){
|
|
711
|
+
/* 把 status 的 class 映射成 Tailwind class 字符串 */
|
|
712
|
+
const map={
|
|
713
|
+
'': 'text-text-tertiary bg-surface-hover',
|
|
714
|
+
'cloning':'text-warning bg-warning-dim','indexing':'text-warning bg-warning-dim','removing':'text-warning bg-warning-dim',
|
|
715
|
+
'cloned':'text-success bg-success-dim','indexed':'text-success bg-success-dim',
|
|
716
|
+
'failed':'text-error bg-error-dim','pull_failed':'text-error bg-error-dim',
|
|
717
|
+
};
|
|
718
|
+
return map[cls]||map[''];
|
|
719
|
+
}
|
|
720
|
+
function statusDot(cls){
|
|
721
|
+
const map={
|
|
722
|
+
'':'bg-text-quaternary',
|
|
723
|
+
'cloning':'bg-warning animate-pulse','indexing':'bg-warning animate-pulse','removing':'bg-warning animate-pulse',
|
|
724
|
+
'cloned':'bg-success','indexed':'bg-success',
|
|
725
|
+
'failed':'bg-error','pull_failed':'bg-error',
|
|
726
|
+
};
|
|
727
|
+
return map[cls]||map[''];
|
|
728
|
+
}
|
|
729
|
+
function timeAgo(ts){if(!ts)return '—';const d=Date.now()-(typeof ts==='number'?ts:Date.parse(ts));if(!isFinite(d))return '—';const m=Math.floor(d/60000);if(m<1)return 'just now';if(m<60)return m+'m ago';const h=Math.floor(m/60);if(h<24)return h+'h ago';return Math.floor(h/24)+'d ago';}
|
|
730
|
+
function shortText(s,n){s=String(s||'').replace(/\s+/g,' ').trim();return s.length>n?s.slice(0,n-1)+'…':s;}
|
|
731
|
+
function repoList(){return currentSpace==='all'?repos:repos.filter(r=>(r.spaceIds||[]).includes(currentSpace));}
|
|
732
|
+
function repoLabel(r){return STATUS[r.status]?STATUS[r.status][1]:r.status;}
|
|
733
|
+
function repoBusy(r){return ['cloning','indexing','removing'].includes(r.status);}
|
|
734
|
+
function repoBad(r){return ['failed','pull_failed'].includes(r.status);}
|
|
735
|
+
function spaceNames(r){const ids=r.spaceIds||[];return spaces.filter(s=>!s.builtin&&ids.includes(s.id)).map(s=>s.name);}
|
|
736
|
+
function spaceInline(r){return spaceNames(r).map(n=>`<span class="space-tag">#${esc(n)}</span>`).join(' · ');}
|
|
737
|
+
function renderStats(){
|
|
738
|
+
const list=repoList();
|
|
739
|
+
const total=list.length,indexed=list.filter(r=>r.status==='indexed').length,processing=list.filter(repoBusy).length,failed=list.filter(repoBad).length,cloned=list.filter(r=>r.status==='cloned').length;
|
|
740
|
+
$('statTotal').textContent=total;$('statIndexed').textContent=indexed;$('statProcessing').textContent=processing;$('statFailed').textContent=failed;$('statCloned').textContent=cloned;
|
|
741
|
+
$('statIndexedDetail').textContent=total?`${Math.round(indexed/total*100)}% of repositories${cloned?` · ${cloned} cloned`:''}`:'No repositories';
|
|
742
|
+
const busy=list.find(repoBusy);
|
|
743
|
+
$('statQueueDetail').textContent=busy?`${busy.name} · ${repoLabel(busy).toLowerCase()}`:'Queue is idle';
|
|
744
|
+
const lead=list.find(repoBad);
|
|
745
|
+
$('statAttentionDetail').textContent=lead?`${lead.name} · ${shortText(lead.message||repoLabel(lead),38)}`:'Nothing to fix';
|
|
746
|
+
$('statClonedDetail').textContent=cloned?`${cloned} ready to analyze`:'Nothing waiting';
|
|
747
|
+
renderOverviewAlert(list,{total,indexed,processing,failed,cloned});
|
|
748
|
+
renderOverviewRows(list);
|
|
749
|
+
}
|
|
750
|
+
function renderOverviewAlert(list,c){
|
|
751
|
+
const band=$('healthAlert'),lead=list.find(repoBad),busy=list.find(repoBusy),admin=isAdmin();
|
|
752
|
+
const set=(state,label,count,title,detail,actions)=>{band.dataset.state=state;$('alertLabel').textContent=label;$('alertCount').textContent=count==null?'':String(count);$('alertTitle').textContent=title;$('alertDetail').textContent=detail;$('alertActions').innerHTML=actions;};
|
|
753
|
+
if(lead){
|
|
754
|
+
const auth=/401|403|auth|token|credential|permission/i.test(lead.message||'');
|
|
755
|
+
const op=lead.status==='pull_failed'?'sync':(lead.localPath?'analyze':'clone');
|
|
756
|
+
const reason=shortText(lead.message||repoLabel(lead),150);
|
|
757
|
+
set('bad','NEEDS ACTION',c.failed,`${lead.name} ${repoLabel(lead).toLowerCase()}`,`${reason}${auth?(admin?' · check the source credentials, then retry':' · ask an administrator to fix source credentials'):''}`,auth&&admin
|
|
758
|
+
?`<button class="btn-pill primary" onclick="switchView('settings')">Check credentials</button><button class="btn-pill" onclick="doOp('${lead.id}','${op}')">Retry</button>`
|
|
759
|
+
:`<button class="btn-pill primary" onclick="doOp('${lead.id}','${op}')">Retry</button><button class="btn-pill" onclick="switchView('repos')">Open repository</button>`);
|
|
760
|
+
}else if(busy){
|
|
761
|
+
set('warn','PROCESSING',c.processing,`${busy.name} is ${repoLabel(busy).toLowerCase()}`,shortText(busy.message||'Index is catching up.',150),`<button class="btn-pill primary" onclick="switchView('repos')">View progress</button>`);
|
|
762
|
+
}else if(!c.total){
|
|
763
|
+
set('ok','NOT CONNECTED','',admin?'No repositories yet':'No repositories in your spaces',admin?'Add a provider, then connect a repository to build an index.':'Ask an administrator to add or bind repositories for your spaces.',admin?`<button class="btn-pill primary" onclick="switchView('settings')">Add provider</button>`:`<button class="btn-pill primary" onclick="switchView('repos')">Open repositories</button>`);
|
|
764
|
+
}else if(c.indexed<c.total){
|
|
765
|
+
set('warn','UNINDEXED',c.total-c.indexed,`${c.total-c.indexed} repositories are not indexed`,'Clone or analyze them before relying on search.',`<button class="btn-pill primary" onclick="switchView('repos')">Review repositories</button>`);
|
|
766
|
+
}else{
|
|
767
|
+
set('ok','ALL CLEAR','','Everything is indexed',`${c.indexed} repositories ready for MCP search.`,`<button class="btn-pill primary" onclick="switchView('repos')">${admin?'+ Add repository':'Open repositories'}</button>`);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function renderOverviewRows(list){
|
|
771
|
+
const rank=r=>repoBad(r)?0:repoBusy(r)?1:r.status==='cloned'?2:3;
|
|
772
|
+
const sorted=[...list].sort((a,b)=>rank(a)-rank(b));
|
|
773
|
+
$('ovRepoCount').textContent=String(list.length);
|
|
774
|
+
if(!sorted.length){$('ovRows').innerHTML='<div class="sig-empty">No repositories yet. Connect one from the Repositories page to build an index.</div>';return;}
|
|
775
|
+
$('ovRows').innerHTML=sorted.map(r=>{
|
|
776
|
+
const tone=repoBad(r)?'bad':repoBusy(r)?'busy':'ok';
|
|
777
|
+
const op=r.status==='pull_failed'?'sync':(r.localPath?'analyze':'clone');
|
|
778
|
+
const action=repoBad(r)?`<button class="sig-action" onclick="doOp('${r.id}','${op}')">Retry</button>`
|
|
779
|
+
:repoBusy(r)?'<button class="sig-action" disabled>Working</button>'
|
|
780
|
+
:r.status==='cloned'?`<button class="sig-action" onclick="doOp('${r.id}','analyze')">Analyze</button>`
|
|
781
|
+
:r.status==='registered'?`<button class="sig-action" onclick="doOp('${r.id}','clone')">Clone</button>`
|
|
782
|
+
:`<button class="sig-action" onclick="doOp('${r.id}','sync')">Sync</button>`;
|
|
783
|
+
return `<div class="sig-row"><div class="sig-repo"><b>${esc(r.name)}</b><small>${esc(PROVIDER_LABELS[r.providerType]||r.providerType||'local')} · ${esc(r.branch||'—')}${spaceNames(r).length?' · '+spaceInline(r):''}</small></div><span class="sig-status ${tone}">${esc(repoLabel(r))}</span><span class="sig-path" title="${esc(r.localPath||'')}">${esc(shortPath(r.localPath))}</span><span class="sig-time">${timeAgo(r.addedAt)}</span>${action}</div>`;
|
|
784
|
+
}).join('');
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const shortPath=p=>{if(!p)return 'Not cloned';const base=(S&&S.cloneBaseDir)||'';if(base&&(p===base||p.startsWith(base)))return '…'+p.slice(base.length).replace(/^[\\/]+/,'');return p;};
|
|
788
|
+
function renderRepos(){const tb=$('repoTbody');tb.innerHTML='';const list=currentSpace==='all'?repos:repos.filter(r=>(r.spaceIds||[]).includes(currentSpace));$('repoEmpty').style.display=repos.length?'none':'flex';$('repoCount').textContent=list.length?list.length+' repositories':'';
|
|
789
|
+
list.forEach(r=>{const [cls]=STATUS[r.status]||['',''];const msg=r.message||'';const shortMsg=msg.length>60?msg.slice(0,60)+'...':msg;
|
|
790
|
+
const multiSpace=currentSpace!=='all'&&(r.spaceIds||[]).length>1;
|
|
791
|
+
const removeBtn=isAdmin()&&multiSpace
|
|
792
|
+
? `<button class="btn-ghost [&.active]:bg-surface inline-flex items-center gap-2 h-[26px] px-2 text-[11px] text-text-tertiary bg-transparent border-0 rounded-sm cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors whitespace-nowrap flex-none" title="Unbind from this space (repo is kept)" onclick="doUnbind('${r.id}')">Unbind</button>`
|
|
793
|
+
: `<button class="btn-danger inline-flex items-center gap-2 h-[26px] px-2 text-[11px] rounded-sm cursor-pointer border border-error/20 text-error hover:bg-error-dim hover:border-error/30 transition-colors whitespace-nowrap flex-none" onclick="doRemove('${r.id}')">Remove</button>`;
|
|
794
|
+
const tr=document.createElement('tr');tr.innerHTML=
|
|
795
|
+
`<td class="px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
796
|
+
<div class="repo-name font-medium tracking-[-0.005em] overflow-hidden text-ellipsis whitespace-nowrap">${esc(r.name)}</div>
|
|
797
|
+
<div class="repo-url text-[12px] text-text-tertiary font-mono mt-0.5 overflow-hidden text-ellipsis whitespace-nowrap">${esc(r.gitUrl||'')}</div>
|
|
798
|
+
${msg?`<div class="repo-msg text-[12px] text-text-tertiary mt-0.5 max-w-[280px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer relative" onclick="showMsg(this,${JSON.stringify(esc(msg)).replace(/"/g,'"')})" title="Click to expand">${esc(shortMsg)}</div>`:''}
|
|
799
|
+
<div class="repo-tags">${spaceNames(r).map(n=>`<span>#${esc(n)}</span>`).join('')}${isAdmin()?`<button class="space-edit" onclick="openSpaceDialog('${r.id}')">+ Spaces</button>`:''}</div>
|
|
800
|
+
</td>
|
|
801
|
+
<td class="px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
802
|
+
<div class="repo-path text-[12px] text-text-secondary font-mono overflow-hidden text-ellipsis whitespace-nowrap" title="${esc(r.localPath||'')}">${esc(shortPath(r.localPath))}</div>
|
|
803
|
+
</td>
|
|
804
|
+
<td class="px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
805
|
+
<span class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-xs border border-transparent font-medium text-[11px] tracking-[0.02em] whitespace-nowrap ${statusCls(cls)}">
|
|
806
|
+
<span class="w-1.5 h-1.5 rounded-full ${statusDot(cls)}"></span>${esc(STATUS[r.status]?STATUS[r.status][1]:r.status)}
|
|
807
|
+
</span>
|
|
808
|
+
</td>
|
|
809
|
+
<td class="ops px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
810
|
+
<div class="flex gap-0.5 items-center">
|
|
811
|
+
${r.status==='indexed'||r.status==='indexing'
|
|
812
|
+
? `<button class="inline-flex items-center gap-2 h-[26px] px-2 text-[11px] text-text-tertiary bg-transparent border-0 rounded-sm cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors whitespace-nowrap flex-none" onclick="doOp('${r.id}','sync',this)">Sync</button>`
|
|
813
|
+
: `<button class="inline-flex items-center gap-2 h-[26px] px-2 text-[11px] text-text-tertiary bg-transparent border-0 rounded-sm cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors whitespace-nowrap flex-none" onclick="doOp('${r.id}','clone',this)">Clone</button>`}
|
|
814
|
+
<button class="inline-flex items-center gap-2 h-[26px] px-2 text-[11px] text-text-tertiary bg-transparent border-0 rounded-sm cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors whitespace-nowrap flex-none" onclick="doWt('${r.id}')">Add Branch</button>
|
|
815
|
+
<button class="inline-flex items-center gap-2 h-[26px] px-2 text-[11px] text-text-tertiary bg-transparent border-0 rounded-sm cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors whitespace-nowrap flex-none" onclick="doOp('${r.id}','analyze',this)">Analyze</button>
|
|
816
|
+
<button class="inline-flex items-center gap-2 h-[26px] px-2 text-[11px] text-text-tertiary bg-transparent border-0 rounded-sm cursor-pointer hover:text-text-primary hover:bg-surface-hover transition-colors whitespace-nowrap flex-none" onclick="doOp('${r.id}','status',this)">Refresh</button>
|
|
817
|
+
${removeBtn}
|
|
818
|
+
</div>
|
|
819
|
+
</td>`;
|
|
820
|
+
tb.appendChild(tr);
|
|
821
|
+
(r.worktrees||[]).forEach(w=>{const [wc]=STATUS[w.status]||['',''];const wtr=document.createElement('tr');wtr.className='wt-row';wtr.innerHTML=
|
|
822
|
+
`<td class="px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
823
|
+
<div class="wt-branch text-accent font-semibold pl-[18px] font-mono text-[12px]">⎇ ${esc(w.branch)}</div>
|
|
824
|
+
${w.message?`<div class="repo-msg text-[12px] text-text-tertiary mt-0.5">${esc(w.message.length>60?w.message.slice(0,60)+'...':w.message)}</div>`:''}
|
|
825
|
+
</td>
|
|
826
|
+
<td class="px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
827
|
+
<div class="repo-path text-[11px] text-text-quaternary font-mono overflow-hidden text-ellipsis whitespace-nowrap" title="${esc(w.localPath||'')}">${esc(shortPath(w.localPath))}</div>
|
|
828
|
+
</td>
|
|
829
|
+
<td class="px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
830
|
+
<span class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-xs border border-transparent font-medium text-[11px] tracking-[0.02em] ${statusCls(wc)}">
|
|
831
|
+
<span class="w-1.5 h-1.5 rounded-full ${statusDot(wc)}"></span>${esc(STATUS[w.status]?STATUS[w.status][1]:w.status)}
|
|
832
|
+
</span>
|
|
833
|
+
</td>
|
|
834
|
+
<td class="ops px-5 py-3 border-b border-white/[0.04] align-middle">
|
|
835
|
+
<button class="btn-danger inline-flex items-center gap-2 h-[26px] px-2 text-[11px] rounded-sm cursor-pointer border border-error/20 text-error hover:bg-error-dim hover:border-error/30 transition-colors whitespace-nowrap flex-none" onclick="doWtRemove('${r.id}','${esc(w.branch)}')">Remove</button>
|
|
836
|
+
</td>`;tb.appendChild(wtr);});});}
|
|
837
|
+
|
|
838
|
+
/* Worktree */
|
|
839
|
+
function doWt(id){$('wtBranch').value='';$('wtOverlay').classList.add('show');$('wtOverlay').dataset.id=id;setTimeout(()=>$('wtBranch').focus(),0);}
|
|
840
|
+
function closeWt(){$('wtOverlay').classList.remove('show');}
|
|
841
|
+
async function submitWt(){const br=$('wtBranch').value.trim();if(!br)return;const id=$('wtOverlay').dataset.id;closeWt();const r=repos.find(x=>x.id===id);if(r){r.message='Creating branch worktree...';renderRepos();}try{await api('api/repos/'+id+'/worktree',{method:'POST',body:JSON.stringify({branch:br})});toast('Branch "'+br+'" indexing, retry search later','success');}catch(e){if(e.message!=='unauthorized')toast('Failed to create worktree: '+e.message,'error');}loadRepos();}
|
|
842
|
+
async function doWtRemove(id,branch){if(!confirm('Remove branch "'+branch+'" worktree and its index?'))return;const r=repos.find(x=>x.id===id);if(r){const w=(r.worktrees||[]).find(x=>x.branch===branch);if(w){w.status='removing';w.message='Removing...';renderRepos();}}try{await api('api/repos/'+id+'/worktree/'+encodeURIComponent(branch),{method:'DELETE'});toast('Branch "'+branch+'" removed','success');}catch(e){if(e.message!=='unauthorized')toast('Failed to remove worktree: '+e.message,'error');}loadRepos();}
|
|
843
|
+
async function doOp(id,op,btn){if(btn)btn.disabled=true;const r=repos.find(x=>x.id===id);if(r){if(op==='clone')r.status='cloning';else if(op==='analyze'||op==='sync')r.status='indexing';r.message=op==='status'?'Refreshing status...':'Processing...';renderRepos();renderStats();}try{await api('api/repos/'+id+'/'+op,{method:'POST'});}catch(e){if(e.message!=='unauthorized')toast(op+' failed: '+e.message,'error');if(btn)btn.disabled=false;}loadRepos();}
|
|
844
|
+
async function doUnbind(id){const r=repos.find(x=>x.id===id);if(!r)return;if(!confirm('Unbind "'+r.name+'" from this space?\nThe repo, its clone and index are all kept.'))return;try{await api(`api/spaces/${currentSpace}/repos/${id}`,{method:'DELETE'});toast('Unbound from space','success');}catch(e){if(e.message!=='unauthorized')toast('Unbind failed: '+e.message,'error');}loadRepos();}
|
|
845
|
+
async function doRemove(id){const r=repos.find(x=>x.id===id);if(!r)return;if(!confirm('Remove "'+r.name+'" and delete its directory/index?'))return;r.status='removing';r.message='Removing...';renderRepos();renderStats();try{await api('api/repos/'+id+'/remove',{method:'POST'});toast('Removed','success');}catch(e){if(e.message!=='unauthorized')toast('Remove failed: '+e.message,'error');}loadRepos();}
|
|
846
|
+
|
|
847
|
+
function mcpEndpointUrl(){return new URL('mcp',location.href).href;}
|
|
848
|
+
function isLoopbackHost(){return ['127.0.0.1','localhost','::1'].includes(location.hostname);}
|
|
849
|
+
function mcpConfig(){const url=mcpEndpointUrl();const server={url};if(!isLoopbackHost())server.headers={Authorization:'Bearer <MCP_TOKEN>'};return JSON.stringify({mcpServers:{cgm:server}},null,2);}
|
|
850
|
+
function fillMcp(){const url=mcpEndpointUrl();if(!$('mcpEndpoint'))return;$('mcpEndpoint').textContent=url;$('mcpSnippet').textContent=mcpConfig();$('mcpAuthNote').textContent=isLoopbackHost()?'Access from this machine does not require a token. Remote clients must send Authorization: Bearer <MCP_TOKEN> (falls back to ADMIN_TOKEN).':'This host is not loopback. Add your MCP_TOKEN as a Bearer header before connecting.';}
|
|
851
|
+
async function copyText(text){try{await navigator.clipboard.writeText(text);return true}catch(e){const ta=document.createElement('textarea');ta.value=text;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.select();let ok=false;try{ok=document.execCommand('copy')}catch(e2){}ta.remove();return ok}}
|
|
852
|
+
function selectText(el){if(!el)return;const r=document.createRange();r.selectNodeContents(el);const s=getSelection();s.removeAllRanges();s.addRange(r);}
|
|
853
|
+
async function copyMcpUrl(){const ok=await copyText(mcpEndpointUrl());if(!ok)selectText($('mcpEndpoint'));toast(ok?'MCP endpoint copied':'Copy failed — text selected, press Ctrl+C',ok?'success':'error');}
|
|
854
|
+
async function copyMcpConfig(){const ok=await copyText(mcpConfig());if(!ok)selectText($('mcpSnippet'));toast(ok?'MCP client config copied':'Copy failed — text selected, press Ctrl+C',ok?'success':'error');}
|
|
855
|
+
async function loadTokens(){if(!isAdmin())return;try{renderTokens(await api('/api/tokens'));}catch(e){}}
|
|
856
|
+
function renderTokens(list){const el=$('tokenList');if(!el)return;const active=list.filter(t=>!t.revokedAt);$('tokenEmpty').classList.toggle('hidden',active.length>0);el.innerHTML=active.map(t=>{const roles=t.roles.map(r=>r==='web'?'Web UI':'MCP').join(' + ');const scope=t.spaceIds.length?t.spaceIds.map(id=>spaces.find(s=>s.id===id)?.name||id).join(', '):'All spaces';const created=new Date(t.createdAt).toLocaleDateString();const last=t.lastUsedAt?timeAgo(t.lastUsedAt):'never used';return `<div class="flex items-center gap-4 px-4 py-3 border border-white/[0.04] rounded-sm"><div class="min-w-0 flex-1"><div class="text-[13px] font-medium text-text-primary">${esc(t.name)}</div><div class="mt-1 text-[11px] text-text-tertiary font-mono overflow-hidden text-ellipsis whitespace-nowrap">${esc(roles)} · ${esc(scope)} · created ${esc(created)} · ${esc(last)}</div></div><button class="inline-flex items-center h-[26px] px-2 text-[11px] rounded-sm cursor-pointer border border-error/20 text-error hover:bg-error-dim hover:border-error/30 transition-colors whitespace-nowrap flex-none" onclick="revokeToken('${t.id}')">Revoke</button></div>`;}).join('')}
|
|
857
|
+
function openTokenCreate(){if(!isAdmin())return;$('tokenName').value='';$('tokenRoleWeb').checked=true;$('tokenRoleMcp').checked=false;$('tokenCreateState').textContent='';$('tokenCreateForm').classList.remove('hidden');$('tokenReveal').classList.add('hidden');const list=spaces.filter(s=>!s.builtin);$('tokenSpaces').innerHTML=list.length?list.map(s=>`<label class="flex items-center gap-3 px-3 py-2 border border-white/[0.04] rounded-sm text-[13px] cursor-pointer hover:bg-surface-hover transition-colors"><input type="checkbox" value="${s.id}" class="w-4 h-4 accent-white"><span>${esc(s.name)}</span></label>`).join(''):'<div class="text-[12px] text-text-tertiary">No custom spaces yet. The token will access all spaces.</div>';$('tokenCreateOverlay').classList.add('show');setTimeout(()=>$('tokenName').focus(),0)}
|
|
858
|
+
function closeTokenCreate(){$('tokenCreateOverlay').classList.remove('show')}
|
|
859
|
+
async function createToken(){const name=$('tokenName').value.trim(),roles=[];if($('tokenRoleWeb').checked)roles.push('web');if($('tokenRoleMcp').checked)roles.push('mcp');const spaceIds=[...$('tokenSpaces').querySelectorAll('input:checked')].map(i=>i.value);if(!name){$('tokenCreateState').textContent='Enter a name.';return}if(!roles.length){$('tokenCreateState').textContent='Pick at least one role.';return}try{const r=await api('/api/tokens',{method:'POST',body:JSON.stringify({name,roles,spaceIds})});if(!r.token){$('tokenCreateState').textContent=r.error||'Failed to create token';return}$('tokenValue').textContent=r.token;$('tokenCreateForm').classList.add('hidden');$('tokenReveal').classList.remove('hidden');await loadTokens()}catch(e){}}
|
|
860
|
+
async function copyTokenValue(){const ok=await copyText($('tokenValue').textContent);toast(ok?'Token copied':'Copy failed — select the token','success')}
|
|
861
|
+
async function revokeToken(id){if(!confirm('Revoke this token? Clients using it lose access immediately.'))return;try{await api('/api/tokens/'+id,{method:'DELETE'});toast('Token revoked','success');await loadTokens()}catch(e){}}
|
|
862
|
+
fillMcp();
|
|
863
|
+
let connLive=false,sse=null;
|
|
864
|
+
function connectSse(){if(sse||!canUseMcp())return;sse=new EventSource(TOKEN?'mcp?token='+encodeURIComponent(TOKEN):'mcp');sse.addEventListener('repos-changed',()=>loadRepos());sse.onopen=()=>{connLive=true;};sse.onerror=()=>{connLive=false;};}
|
|
865
|
+
async function boot(){try{await loadMe();await loadSettings();await loadRepos();if(isAdmin())await loadTokens();connectSse();}catch(e){}}
|
|
866
|
+
boot();
|
|
867
|
+
setInterval(()=>{if(ME)loadRepos();},30000);
|
|
868
|
+
|
|
869
|
+
/* Message popover */
|
|
870
|
+
function showMsg(el,msg){const popover=$('msgPopover');if(popover.classList.contains('show')&&popover._target===el){popover.classList.remove('show');return;}popover.textContent=msg;popover._target=el;const rect=el.getBoundingClientRect();popover.style.left=Math.min(rect.left,window.innerWidth-500)+'px';popover.style.top=(rect.bottom+8)+'px';popover.classList.add('show');}
|
|
871
|
+
document.addEventListener('click',e=>{const popover=$('msgPopover');if(!popover.contains(e.target)&&!e.target.classList.contains('repo-msg')){popover.classList.remove('show');}});
|
|
872
|
+
|
|
873
|
+
/* Local repo */
|
|
874
|
+
function openLocalModal(){$('localPath').value='';$('localName').value='';$('localModalCheck').textContent='';$('browserPanel').classList.add('hidden');$('localModalOverlay').classList.add('show');}
|
|
875
|
+
function closeLocalModal(){$('localModalOverlay').classList.remove('show');}
|
|
876
|
+
async function addLocalRepo(){const localPath=$('localPath').value.trim();const localName=$('localName').value.trim();if(!localPath){toast('Please enter repository path','error');return;}$('localModalCheck').textContent='Adding...';const r=await api('/api/repos/local',{method:'POST',body:JSON.stringify({path:localPath,name:localName||undefined})});if(r.error){$('localModalCheck').innerHTML=`<span style="color:#FF8A72">✗ ${esc(r.error)}</span>`;toast('Failed: '+r.error,'error');return;}$('localModalCheck').innerHTML=`<span style="color:#7DD3A0">✓ Added successfully</span>`;toast('Local repository added: '+r.name,'success');closeLocalModal();loadRepos();}
|
|
877
|
+
|
|
878
|
+
/* Dir browser */
|
|
879
|
+
let currentBrowsePath='/';
|
|
880
|
+
async function browseDirectory(){const panel=$('browserPanel');if(panel.classList.contains('hidden')){panel.classList.remove('hidden');const currentPath=$('localPath').value.trim();await loadDirectory(currentPath||'/');}else{panel.classList.add('hidden');}}
|
|
881
|
+
function closeBrowser(){$('browserPanel').classList.add('hidden');}
|
|
882
|
+
async function loadDirectory(dirPath){currentBrowsePath=dirPath;$('browserPath').textContent=dirPath;$('browserList').innerHTML='<div style="padding:16px;text-align:center;color:#737373">Loading...</div>';try{const r=await api('/api/browse?'+new URLSearchParams({path:dirPath}));if(r.error){$('browserList').innerHTML=`<div style="padding:16px;text-align:center;color:#FF8A72">${esc(r.error)}</div>`;return;}if(!r.entries.length){$('browserList').innerHTML='<div style="padding:16px;text-align:center;color:#737373">No subdirectories</div>';return;}
|
|
883
|
+
$('browserList').innerHTML=r.entries.map(e=>`<div style="padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:8px;border-bottom:1px solid rgba(255,255,255,0.04);transition:background 160ms" onmouseover="this.style.background='#1E232C'" onmouseout="this.style.background=''" onclick="selectDirectory('${esc(e.path)}',${e.isGitRepo})">
|
|
884
|
+
<span style="font-size:14px">${e.isGitRepo ? '📁' : '📂'}</span>
|
|
885
|
+
<span style="flex:1;font-size:13px;color:#F2F3F5">${esc(e.name)}</span>
|
|
886
|
+
${e.isGitRepo ? '<span style="font-size:11px;color:#7DD3A0;background:rgba(74,222,128,0.12);padding:2px 6px;border-radius:4px">git</span>' : ''}
|
|
887
|
+
</div>`).join('');}catch(err){$('browserList').innerHTML=`<div style="padding:16px;text-align:center;color:#FF8A72">Error: ${esc(err.message||'Failed to load')}</div>`;}}
|
|
888
|
+
function selectDirectory(dirPath,isGitRepo){$('localPath').value=dirPath;if(isGitRepo){$('localName').value=dirPath.split('/').pop()||'';$('localModalCheck').innerHTML='<span style="color:#7DD3A0">✓ Git repository detected</span>';}else{$('localModalCheck').innerHTML='<span style="color:#FBBF24">⚠ Not a git repository (will fail to add)</span>';}closeBrowser();}
|
|
889
|
+
function browseParent(){const parts=currentBrowsePath.split('/').filter(Boolean);if(parts.length>0){parts.pop();loadDirectory('/'+parts.join('/'));}}
|
|
890
|
+
</script>
|
|
891
|
+
</body>
|
|
892
|
+
</html>
|