@litemetrics/ui 0.1.1

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/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # @litemetrics/ui
2
+
3
+ Pre-built, themeable React dashboard components for [Litemetrics](https://github.com/metehankurucu/litemetrics) analytics.
4
+
5
+ ![Chart](../../examples/chart.png)
6
+ ![Cards](../../examples/cards-2.png)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ bun add @litemetrics/ui @litemetrics/client @litemetrics/core
12
+ # peer dependencies
13
+ bun add react react-dom recharts @tanstack/react-query
14
+ # optional — for WorldMap widget
15
+ bun add react-simple-maps
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```tsx
21
+ import { LitemetricsProvider, AnalyticsDashboard } from '@litemetrics/ui';
22
+
23
+ function App() {
24
+ return (
25
+ <LitemetricsProvider
26
+ baseUrl="https://your-server.com"
27
+ siteId="your-site-id"
28
+ secretKey="sk_..."
29
+ >
30
+ <AnalyticsDashboard />
31
+ </LitemetricsProvider>
32
+ );
33
+ }
34
+ ```
35
+
36
+ ## Theming
37
+
38
+ All colors use CSS custom properties (`--lm-*`). The provider injects default light/dark theme variables automatically.
39
+
40
+ ### Custom Accent Color
41
+
42
+ ```tsx
43
+ <LitemetricsProvider
44
+ baseUrl="..."
45
+ siteId="..."
46
+ theme={{ accent: '59 130 246' }} // blue-500
47
+ >
48
+ ```
49
+
50
+ ### Full Custom Theme
51
+
52
+ ```tsx
53
+ import type { LitemetricsTheme } from '@litemetrics/ui';
54
+
55
+ const myTheme: Partial<LitemetricsTheme> = {
56
+ accent: '59 130 246',
57
+ accentLight: '219 234 254',
58
+ accentText: '29 78 216',
59
+ bg: '255 255 255',
60
+ text: '15 23 42',
61
+ };
62
+
63
+ <LitemetricsProvider theme={myTheme} darkTheme={{ accent: '96 165 250' }}>
64
+ ```
65
+
66
+ ### Dark Mode
67
+
68
+ Add `dark` class to `<html>` or any parent element:
69
+
70
+ ```js
71
+ document.documentElement.classList.toggle('dark');
72
+ ```
73
+
74
+ The components automatically switch to dark theme colors. You can also use `.litemetrics-light` and `.litemetrics-dark` classes for scoped theming.
75
+
76
+ ## CSS Variable Reference
77
+
78
+ All values are RGB triplets (e.g. `99 102 241`). Use in your CSS as `rgb(var(--lm-accent))`.
79
+
80
+ | Variable | Light Default | Description |
81
+ |----------|--------------|-------------|
82
+ | `--lm-bg` | `255 255 255` | Primary background |
83
+ | `--lm-bg-secondary` | `250 250 250` | Secondary background |
84
+ | `--lm-bg-tertiary` | `244 244 245` | Tertiary background |
85
+ | `--lm-border` | `228 228 231` | Border color |
86
+ | `--lm-border-hover` | `212 212 216` | Border hover color |
87
+ | `--lm-text` | `24 24 27` | Primary text |
88
+ | `--lm-text-secondary` | `113 113 122` | Secondary text |
89
+ | `--lm-text-tertiary` | `161 161 170` | Tertiary text |
90
+ | `--lm-text-muted` | `212 212 216` | Muted text |
91
+ | `--lm-accent` | `99 102 241` | Accent color (indigo) |
92
+ | `--lm-accent-light` | `238 242 255` | Accent background |
93
+ | `--lm-accent-text` | `67 56 202` | Accent text |
94
+ | `--lm-accent-hover` | `129 140 248` | Accent hover |
95
+ | `--lm-positive` | `5 150 105` | Positive change |
96
+ | `--lm-negative` | `239 68 68` | Negative change |
97
+ | `--lm-chart-stroke` | `99 102 241` | Chart line color |
98
+ | `--lm-chart-fill` | `99 102 241` | Chart fill color |
99
+ | `--lm-chart-grid` | `244 244 245` | Chart grid lines |
100
+ | `--lm-chart-axis` | `161 161 170` | Chart axis text |
101
+ | `--lm-tooltip-bg` | `24 24 27` | Tooltip background |
102
+ | `--lm-tooltip-text` | `255 255 255` | Tooltip text |
103
+ | `--lm-tooltip-muted` | `161 161 170` | Tooltip secondary text |
104
+ | `--lm-bar` | `238 242 255` | Top list bar fill |
105
+ | `--lm-bar-hover` | `224 231 255` | Top list bar hover |
106
+ | `--lm-pie-1..8` | Various | Pie chart colors |
107
+ | `--lm-map-empty` | `244 244 245` | Map empty country |
108
+ | `--lm-map-stroke` | `228 228 231` | Map border |
109
+ | `--lm-map-hover` | `129 140 248` | Map hover color |
110
+
111
+ ## Components
112
+
113
+ ### Provider
114
+
115
+ | Prop | Type | Default | Description |
116
+ |------|------|---------|-------------|
117
+ | `baseUrl` | `string` | required | Litemetrics server URL |
118
+ | `siteId` | `string` | required | Site ID |
119
+ | `secretKey` | `string` | — | Secret key for auth |
120
+ | `defaultPeriod` | `Period` | `'7d'` | Initial time period |
121
+ | `queryClient` | `QueryClient` | — | External React Query client |
122
+ | `staleTime` | `number` | `30000` | Query stale time (ms) |
123
+ | `theme` | `Partial<LitemetricsTheme>` | — | Light theme overrides |
124
+ | `darkTheme` | `Partial<LitemetricsTheme>` | — | Dark theme overrides |
125
+
126
+ ### Display Components
127
+
128
+ - **`StatCard`** — Single metric card with comparison percentage
129
+ - **`TopList`** — Ranked list with icons, bar fills, and tooltips
130
+ - **`PieChartCard`** — Donut chart with legend
131
+ - **`PeriodSelector`** — Time period toggle (1H, 24H, 7D, 30D, 90D, Custom)
132
+ - **`DateRangePicker`** — Date range inputs
133
+ - **`ExportButton`** — CSV / JSON / Markdown export
134
+
135
+ ### Smart Widgets
136
+
137
+ Pre-wired to fetch data via the provider context:
138
+
139
+ - **`StatCards`** — Grid of 4 overview cards
140
+ - **`TimeSeriesChart`** — Area chart with metric selector
141
+ - **`TopPages`**, **`TopReferrers`**, **`TopCountries`**, **`TopEvents`**, **`TopBrowsers`**, **`TopDevices`**
142
+ - **`BrowsersChart`**, **`DevicesChart`** — Pie chart variants
143
+ - **`WorldMap`** — Interactive choropleth map (requires `react-simple-maps`)
144
+ - **`AnalyticsDashboard`** — Complete dashboard combining all widgets
145
+
146
+ ## Hooks
147
+
148
+ | Hook | Description |
149
+ |------|-------------|
150
+ | `useLitemetricsUI()` | Access provider context (client, period, etc.) |
151
+ | `useStats(metric, opts?)` | Query stats for a specific metric |
152
+ | `useTimeSeries(metric, opts?)` | Query time series data |
153
+ | `useOverview(opts?)` | Query overview with period comparison |
154
+ | `useThemeColors()` | Get theme colors for SVG/Recharts (auto-updates on dark mode toggle) |
155
+
156
+ ## Peer Dependencies
157
+
158
+ | Package | Version |
159
+ |---------|---------|
160
+ | `react` | >=18 |
161
+ | `react-dom` | >=18 |
162
+ | `recharts` | >=2 |
163
+ | `@tanstack/react-query` | >=5 |
164
+ | `tailwindcss` | >=3 (optional) |
165
+ | `react-simple-maps` | >=3 (optional, for WorldMap) |
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,46 @@
1
+ import {
2
+ numericToISO2
3
+ } from "./chunk-CBT6H723.js";
4
+
5
+ // src/widgets/WorldMapInner.tsx
6
+ import { ComposableMap, ZoomableGroup, Geographies, Geography } from "react-simple-maps";
7
+ import { jsx } from "react/jsx-runtime";
8
+ function WorldMapInner({ geoUrl, countryData, getColor, setTooltip, strokeColor = "rgb(228, 228, 231)", hoverColor = "rgb(129, 140, 248)" }) {
9
+ return /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
10
+ ComposableMap,
11
+ {
12
+ projectionConfig: { scale: 147, center: [0, 20] },
13
+ style: { width: "100%", height: "auto" },
14
+ height: 380,
15
+ children: /* @__PURE__ */ jsx(ZoomableGroup, { children: /* @__PURE__ */ jsx(Geographies, { geography: geoUrl, children: ({ geographies }) => geographies.map((geo) => {
16
+ const rawIso = geo.properties.ISO_A2;
17
+ const iso = rawIso && rawIso !== "-99" ? rawIso : numericToISO2[geo.id] || geo.id;
18
+ const value = countryData[iso] || 0;
19
+ return /* @__PURE__ */ jsx(
20
+ Geography,
21
+ {
22
+ geography: geo,
23
+ fill: getColor(iso),
24
+ stroke: strokeColor,
25
+ strokeWidth: 0.5,
26
+ style: {
27
+ default: { outline: "none" },
28
+ hover: { outline: "none", fill: hoverColor, cursor: "pointer" },
29
+ pressed: { outline: "none" }
30
+ },
31
+ onMouseEnter: (e) => {
32
+ const name = geo.properties.name || iso;
33
+ setTooltip({ name, value, x: e.clientX, y: e.clientY });
34
+ },
35
+ onMouseLeave: () => setTooltip(null)
36
+ },
37
+ geo.rsmKey
38
+ );
39
+ }) }) })
40
+ }
41
+ ) });
42
+ }
43
+ export {
44
+ WorldMapInner as default
45
+ };
46
+ //# sourceMappingURL=WorldMapInner-AJ3WDZR2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/widgets/WorldMapInner.tsx"],"sourcesContent":["import { ComposableMap, ZoomableGroup, Geographies, Geography } from 'react-simple-maps';\nimport { numericToISO2 } from '../utils/worldmap-data';\n\ninterface WorldMapInnerProps {\n geoUrl: string;\n countryData: Record<string, number>;\n getColor: (iso: string) => string;\n setTooltip: (t: { name: string; value: number; x: number; y: number } | null) => void;\n strokeColor?: string;\n hoverColor?: string;\n}\n\nexport default function WorldMapInner({ geoUrl, countryData, getColor, setTooltip, strokeColor = 'rgb(228, 228, 231)', hoverColor = 'rgb(129, 140, 248)' }: WorldMapInnerProps) {\n return (\n <div className=\"relative\">\n <ComposableMap\n projectionConfig={{ scale: 147, center: [0, 20] }}\n style={{ width: '100%', height: 'auto' }}\n height={380}\n >\n <ZoomableGroup>\n <Geographies geography={geoUrl}>\n {({ geographies }) =>\n geographies.map((geo) => {\n const rawIso = geo.properties.ISO_A2;\n const iso = (rawIso && rawIso !== '-99') ? rawIso : (numericToISO2[geo.id] || geo.id);\n const value = countryData[iso] || 0;\n return (\n <Geography\n key={geo.rsmKey}\n geography={geo}\n fill={getColor(iso)}\n stroke={strokeColor}\n strokeWidth={0.5}\n style={{\n default: { outline: 'none' },\n hover: { outline: 'none', fill: hoverColor, cursor: 'pointer' },\n pressed: { outline: 'none' },\n }}\n onMouseEnter={(e) => {\n const name = geo.properties.name || iso;\n setTooltip({ name, value, x: e.clientX, y: e.clientY });\n }}\n onMouseLeave={() => setTooltip(null)}\n />\n );\n })\n }\n </Geographies>\n </ZoomableGroup>\n </ComposableMap>\n </div>\n );\n}\n"],"mappings":";;;;;AAAA,SAAS,eAAe,eAAe,aAAa,iBAAiB;AA4BnD;AAhBH,SAAR,cAA+B,EAAE,QAAQ,aAAa,UAAU,YAAY,cAAc,sBAAsB,aAAa,qBAAqB,GAAuB;AAC9K,SACE,oBAAC,SAAI,WAAU,YACb;AAAA,IAAC;AAAA;AAAA,MACC,kBAAkB,EAAE,OAAO,KAAK,QAAQ,CAAC,GAAG,EAAE,EAAE;AAAA,MAChD,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAAA,MACvC,QAAQ;AAAA,MAER,8BAAC,iBACC,8BAAC,eAAY,WAAW,QACrB,WAAC,EAAE,YAAY,MACd,YAAY,IAAI,CAAC,QAAQ;AACvB,cAAM,SAAS,IAAI,WAAW;AAC9B,cAAM,MAAO,UAAU,WAAW,QAAS,SAAU,cAAc,IAAI,EAAE,KAAK,IAAI;AAClF,cAAM,QAAQ,YAAY,GAAG,KAAK;AAClC,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW;AAAA,YACX,MAAM,SAAS,GAAG;AAAA,YAClB,QAAQ;AAAA,YACR,aAAa;AAAA,YACb,OAAO;AAAA,cACL,SAAS,EAAE,SAAS,OAAO;AAAA,cAC3B,OAAO,EAAE,SAAS,QAAQ,MAAM,YAAY,QAAQ,UAAU;AAAA,cAC9D,SAAS,EAAE,SAAS,OAAO;AAAA,YAC7B;AAAA,YACA,cAAc,CAAC,MAAM;AACnB,oBAAM,OAAO,IAAI,WAAW,QAAQ;AACpC,yBAAW,EAAE,MAAM,OAAO,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,CAAC;AAAA,YACxD;AAAA,YACA,cAAc,MAAM,WAAW,IAAI;AAAA;AAAA,UAd9B,IAAI;AAAA,QAeX;AAAA,MAEJ,CAAC,GAEL,GACF;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
@@ -0,0 +1,192 @@
1
+ // src/utils/worldmap-data.ts
2
+ var numericToISO2 = {
3
+ "004": "AF",
4
+ "008": "AL",
5
+ "012": "DZ",
6
+ "016": "AS",
7
+ "020": "AD",
8
+ "024": "AO",
9
+ "028": "AG",
10
+ "031": "AZ",
11
+ "032": "AR",
12
+ "036": "AU",
13
+ "040": "AT",
14
+ "044": "BS",
15
+ "048": "BH",
16
+ "050": "BD",
17
+ "051": "AM",
18
+ "056": "BE",
19
+ "060": "BM",
20
+ "064": "BT",
21
+ "068": "BO",
22
+ "070": "BA",
23
+ "072": "BW",
24
+ "076": "BR",
25
+ "084": "BZ",
26
+ "090": "SB",
27
+ "096": "BN",
28
+ "100": "BG",
29
+ "104": "MM",
30
+ "108": "BI",
31
+ "112": "BY",
32
+ "116": "KH",
33
+ "120": "CM",
34
+ "124": "CA",
35
+ "140": "CF",
36
+ "144": "LK",
37
+ "148": "TD",
38
+ "152": "CL",
39
+ "156": "CN",
40
+ "158": "TW",
41
+ "170": "CO",
42
+ "174": "KM",
43
+ "178": "CG",
44
+ "180": "CD",
45
+ "188": "CR",
46
+ "191": "HR",
47
+ "192": "CU",
48
+ "196": "CY",
49
+ "203": "CZ",
50
+ "204": "BJ",
51
+ "208": "DK",
52
+ "214": "DO",
53
+ "218": "EC",
54
+ "222": "SV",
55
+ "226": "GQ",
56
+ "231": "ET",
57
+ "232": "ER",
58
+ "233": "EE",
59
+ "242": "FJ",
60
+ "246": "FI",
61
+ "250": "FR",
62
+ "262": "DJ",
63
+ "266": "GA",
64
+ "268": "GE",
65
+ "270": "GM",
66
+ "275": "PS",
67
+ "276": "DE",
68
+ "288": "GH",
69
+ "300": "GR",
70
+ "308": "GD",
71
+ "320": "GT",
72
+ "324": "GN",
73
+ "328": "GY",
74
+ "332": "HT",
75
+ "340": "HN",
76
+ "348": "HU",
77
+ "352": "IS",
78
+ "356": "IN",
79
+ "360": "ID",
80
+ "364": "IR",
81
+ "368": "IQ",
82
+ "372": "IE",
83
+ "376": "IL",
84
+ "380": "IT",
85
+ "384": "CI",
86
+ "388": "JM",
87
+ "392": "JP",
88
+ "398": "KZ",
89
+ "400": "JO",
90
+ "404": "KE",
91
+ "408": "KP",
92
+ "410": "KR",
93
+ "414": "KW",
94
+ "417": "KG",
95
+ "418": "LA",
96
+ "422": "LB",
97
+ "426": "LS",
98
+ "428": "LV",
99
+ "430": "LR",
100
+ "434": "LY",
101
+ "440": "LT",
102
+ "442": "LU",
103
+ "450": "MG",
104
+ "454": "MW",
105
+ "458": "MY",
106
+ "462": "MV",
107
+ "466": "ML",
108
+ "470": "MT",
109
+ "478": "MR",
110
+ "480": "MU",
111
+ "484": "MX",
112
+ "496": "MN",
113
+ "498": "MD",
114
+ "499": "ME",
115
+ "504": "MA",
116
+ "508": "MZ",
117
+ "512": "OM",
118
+ "516": "NA",
119
+ "524": "NP",
120
+ "528": "NL",
121
+ "540": "NC",
122
+ "548": "VU",
123
+ "554": "NZ",
124
+ "558": "NI",
125
+ "562": "NE",
126
+ "566": "NG",
127
+ "578": "NO",
128
+ "586": "PK",
129
+ "591": "PA",
130
+ "598": "PG",
131
+ "600": "PY",
132
+ "604": "PE",
133
+ "608": "PH",
134
+ "616": "PL",
135
+ "620": "PT",
136
+ "624": "GW",
137
+ "626": "TL",
138
+ "630": "PR",
139
+ "634": "QA",
140
+ "642": "RO",
141
+ "643": "RU",
142
+ "646": "RW",
143
+ "662": "LC",
144
+ "670": "VC",
145
+ "678": "ST",
146
+ "682": "SA",
147
+ "686": "SN",
148
+ "688": "RS",
149
+ "694": "SL",
150
+ "702": "SG",
151
+ "703": "SK",
152
+ "704": "VN",
153
+ "705": "SI",
154
+ "706": "SO",
155
+ "710": "ZA",
156
+ "716": "ZW",
157
+ "724": "ES",
158
+ "728": "SS",
159
+ "729": "SD",
160
+ "732": "EH",
161
+ "740": "SR",
162
+ "748": "SZ",
163
+ "752": "SE",
164
+ "756": "CH",
165
+ "760": "SY",
166
+ "762": "TJ",
167
+ "764": "TH",
168
+ "768": "TG",
169
+ "780": "TT",
170
+ "784": "AE",
171
+ "788": "TN",
172
+ "792": "TR",
173
+ "795": "TM",
174
+ "800": "UG",
175
+ "804": "UA",
176
+ "807": "MK",
177
+ "818": "EG",
178
+ "826": "GB",
179
+ "834": "TZ",
180
+ "840": "US",
181
+ "854": "BF",
182
+ "858": "UY",
183
+ "860": "UZ",
184
+ "862": "VE",
185
+ "887": "YE",
186
+ "894": "ZM"
187
+ };
188
+
189
+ export {
190
+ numericToISO2
191
+ };
192
+ //# sourceMappingURL=chunk-CBT6H723.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/worldmap-data.ts"],"sourcesContent":["/** Countries where Natural Earth sets ISO_A2 = \"-99\". Map numeric ISO 3166-1 → ISO-2 alpha. */\nexport const numericToISO2: Record<string, string> = {\n '004': 'AF', '008': 'AL', '012': 'DZ', '016': 'AS', '020': 'AD',\n '024': 'AO', '028': 'AG', '031': 'AZ', '032': 'AR', '036': 'AU',\n '040': 'AT', '044': 'BS', '048': 'BH', '050': 'BD', '051': 'AM',\n '056': 'BE', '060': 'BM', '064': 'BT', '068': 'BO', '070': 'BA',\n '072': 'BW', '076': 'BR', '084': 'BZ', '090': 'SB', '096': 'BN',\n '100': 'BG', '104': 'MM', '108': 'BI', '112': 'BY', '116': 'KH',\n '120': 'CM', '124': 'CA', '140': 'CF', '144': 'LK', '148': 'TD',\n '152': 'CL', '156': 'CN', '158': 'TW', '170': 'CO', '174': 'KM',\n '178': 'CG', '180': 'CD', '188': 'CR', '191': 'HR', '192': 'CU',\n '196': 'CY', '203': 'CZ', '204': 'BJ', '208': 'DK', '214': 'DO',\n '218': 'EC', '222': 'SV', '226': 'GQ', '231': 'ET', '232': 'ER',\n '233': 'EE', '242': 'FJ', '246': 'FI', '250': 'FR', '262': 'DJ',\n '266': 'GA', '268': 'GE', '270': 'GM', '275': 'PS', '276': 'DE',\n '288': 'GH', '300': 'GR', '308': 'GD', '320': 'GT', '324': 'GN',\n '328': 'GY', '332': 'HT', '340': 'HN', '348': 'HU', '352': 'IS',\n '356': 'IN', '360': 'ID', '364': 'IR', '368': 'IQ', '372': 'IE',\n '376': 'IL', '380': 'IT', '384': 'CI', '388': 'JM', '392': 'JP',\n '398': 'KZ', '400': 'JO', '404': 'KE', '408': 'KP', '410': 'KR',\n '414': 'KW', '417': 'KG', '418': 'LA', '422': 'LB', '426': 'LS',\n '428': 'LV', '430': 'LR', '434': 'LY', '440': 'LT', '442': 'LU',\n '450': 'MG', '454': 'MW', '458': 'MY', '462': 'MV', '466': 'ML',\n '470': 'MT', '478': 'MR', '480': 'MU', '484': 'MX', '496': 'MN',\n '498': 'MD', '499': 'ME', '504': 'MA', '508': 'MZ', '512': 'OM',\n '516': 'NA', '524': 'NP', '528': 'NL', '540': 'NC', '548': 'VU',\n '554': 'NZ', '558': 'NI', '562': 'NE', '566': 'NG', '578': 'NO',\n '586': 'PK', '591': 'PA', '598': 'PG', '600': 'PY', '604': 'PE',\n '608': 'PH', '616': 'PL', '620': 'PT', '624': 'GW', '626': 'TL',\n '630': 'PR', '634': 'QA', '642': 'RO', '643': 'RU', '646': 'RW',\n '662': 'LC', '670': 'VC', '678': 'ST', '682': 'SA', '686': 'SN',\n '688': 'RS', '694': 'SL', '702': 'SG', '703': 'SK', '704': 'VN',\n '705': 'SI', '706': 'SO', '710': 'ZA', '716': 'ZW', '724': 'ES',\n '728': 'SS', '729': 'SD', '732': 'EH', '740': 'SR', '748': 'SZ',\n '752': 'SE', '756': 'CH', '760': 'SY', '762': 'TJ', '764': 'TH',\n '768': 'TG', '780': 'TT', '784': 'AE', '788': 'TN', '792': 'TR',\n '795': 'TM', '800': 'UG', '804': 'UA', '807': 'MK', '818': 'EG',\n '826': 'GB', '834': 'TZ', '840': 'US', '854': 'BF', '858': 'UY',\n '860': 'UZ', '862': 'VE', '887': 'YE', '894': 'ZM',\n};\n"],"mappings":";AACO,IAAM,gBAAwC;AAAA,EACnD,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAC3D,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAAA,EAAM,OAAO;AAChD;","names":[]}