From 5b4f07668945a082c4c82ba08178cf18e9717012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sat, 21 Mar 2026 19:25:00 +0300 Subject: [PATCH 01/15] refactor: chart components seperated --- src/components/content/AreaChart.astro | 311 ++++++++++++++++ src/components/content/BarChart.astro | 217 +++++++++++ src/components/content/Chart.astro | 400 -------------------- src/components/content/LineChart.astro | 375 +++++++++++++++++++ src/components/content/PieChart.astro | 300 +++++++++++++++ src/components/content/ScatterChart.astro | 435 ++++++++++++++++++++++ src/utils/chartUtils.ts | 76 ++++ 7 files changed, 1714 insertions(+), 400 deletions(-) create mode 100644 src/components/content/AreaChart.astro create mode 100644 src/components/content/BarChart.astro delete mode 100644 src/components/content/Chart.astro create mode 100644 src/components/content/LineChart.astro create mode 100644 src/components/content/PieChart.astro create mode 100644 src/components/content/ScatterChart.astro create mode 100644 src/utils/chartUtils.ts diff --git a/src/components/content/AreaChart.astro b/src/components/content/AreaChart.astro new file mode 100644 index 0000000..dc295ac --- /dev/null +++ b/src/components/content/AreaChart.astro @@ -0,0 +1,311 @@ +--- +import type { DataPoint } from '../../utils/chartUtils' + +interface Props { + data?: DataPoint[] + id?: string + height?: number + colors?: string[] + animated?: boolean + showGrid?: boolean + strokeWidth?: number + fillOpacity?: number +} + +const { + data = [], + id = '', + height = 300, + colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], + animated = true, + showGrid = true, + strokeWidth = 2.5, + fillOpacity = 0.2, +} = Astro.props + +const chartId = `area-chart-${id || Math.random().toString(36).slice(2, 9)}` +const chartData = JSON.stringify(data) +const chartColors = JSON.stringify(colors) +--- + +
+
+
+ + diff --git a/src/components/content/BarChart.astro b/src/components/content/BarChart.astro new file mode 100644 index 0000000..6654fa1 --- /dev/null +++ b/src/components/content/BarChart.astro @@ -0,0 +1,217 @@ +--- +import type { DataPoint } from '../../utils/chartUtils' + +interface Props { + data?: DataPoint[] + id?: string + height?: number + colors?: string[] + animated?: boolean +} + +const { + data = [], + id = '', + height = 300, + colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], + animated = true, +} = Astro.props + +const uniqueId = `bar-chart-${id || Math.random().toString(36).slice(2, 9)}` +const chartData = JSON.stringify(data) +const chartColors = JSON.stringify(colors) +const chartAnimated = animated +--- + +
+
+
+ + diff --git a/src/components/content/Chart.astro b/src/components/content/Chart.astro deleted file mode 100644 index a019141..0000000 --- a/src/components/content/Chart.astro +++ /dev/null @@ -1,400 +0,0 @@ ---- -interface DataPoint { - label: string - value: number - color?: string -} - -interface Props { - type?: 'line' | 'area' | 'bar' | 'pie' | 'scatter' - data?: DataPoint[] - id?: string - height?: number - colors?: string[] - showTrendline?: boolean - trendlineColor?: string -} - -const { - type = 'bar', - data = [], - id = '', - height = 300, - showTrendline = false, - trendlineColor = '#ef4444', - colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], -} = Astro.props - -const uniqueId = `chart-${id || Math.random().toString(36).slice(2, 9)}` -const chartData = JSON.stringify(data) -const chartColors = JSON.stringify(colors) -const chartHeight = height -const chartShowTrendline = showTrendline -const chartTrendlineColor = trendlineColor -const chartType = type ---- - -
-
-
- - diff --git a/src/components/content/LineChart.astro b/src/components/content/LineChart.astro new file mode 100644 index 0000000..eac5370 --- /dev/null +++ b/src/components/content/LineChart.astro @@ -0,0 +1,375 @@ +--- +import type { DataPoint } from '../../utils/chartUtils' + +interface Props { + data?: DataPoint[] + id?: string + height?: number + colors?: string[] + animated?: boolean + showDots?: boolean + showGrid?: boolean + strokeWidth?: number +} + +const { + data = [], + id = '', + height = 300, + colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], + animated = true, + showDots = true, + showGrid = true, + strokeWidth = 2.5, +} = Astro.props + +const chartId = `line-chart-${id || Math.random().toString(36).slice(2, 9)}` +const chartData = JSON.stringify(data) +const chartColors = JSON.stringify(colors) +--- + +
+
+
+ + + +
+
+ + diff --git a/src/components/content/PieChart.astro b/src/components/content/PieChart.astro new file mode 100644 index 0000000..9444a32 --- /dev/null +++ b/src/components/content/PieChart.astro @@ -0,0 +1,300 @@ +--- +import type { DataPoint } from '../../utils/chartUtils' + +interface Props { + data?: DataPoint[] + id?: string + height?: number + colors?: string[] + animated?: boolean + donut?: boolean + donutRatio?: number + showLabels?: boolean + showPercentages?: boolean +} + +const { + data = [], + id = '', + height = 300, + colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], + animated = true, + donut = false, + donutRatio = 0.55, + showLabels = true, + showPercentages = true, +} = Astro.props + +const uniqueId = `pie-chart-${id || Math.random().toString(36).slice(2, 9)}` +const chartData = JSON.stringify(data) +const chartColors = JSON.stringify(colors) +--- + +
+
+
+ + diff --git a/src/components/content/ScatterChart.astro b/src/components/content/ScatterChart.astro new file mode 100644 index 0000000..e920e09 --- /dev/null +++ b/src/components/content/ScatterChart.astro @@ -0,0 +1,435 @@ +--- +import type { DataPoint } from '../../utils/chartUtils' + +interface Props { + data?: DataPoint[] + id?: string + height?: number + colors?: string[] + animated?: boolean + showTrendline?: boolean + trendlineColor?: string + showLabels?: boolean + pointSize?: number +} + +const { + data = [], + id = '', + height = 300, + colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], + animated = true, + showTrendline = false, + trendlineColor = '#ef4444', + showLabels = true, + pointSize = 6, +} = Astro.props + +const chartId = `scatter-chart-${id || Math.random().toString(36).slice(2, 9)}` +const chartData = JSON.stringify(data) +const chartColors = JSON.stringify(colors) +--- + +
+
+
+ + + +
+
+ + diff --git a/src/utils/chartUtils.ts b/src/utils/chartUtils.ts new file mode 100644 index 0000000..7595b31 --- /dev/null +++ b/src/utils/chartUtils.ts @@ -0,0 +1,76 @@ +export interface DataPoint { + label: string + value: number + color?: string +} + +export interface ChartThemeColors { + textColor: string + gridColor: string + axisColor: string +} + +export function getThemeColors(): ChartThemeColors { + const isDark = document.documentElement.classList.contains('dark') + + return { + textColor: isDark ? '#e5e7eb' : '#1e293b', + gridColor: isDark ? '#3f3f46' : '#cbd5e1', + axisColor: isDark ? '#52525b' : '#94a3b8', + } +} + +export function getDefaultColors(): string[] { + return ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'] +} + +export function getColor(index: number, colors?: string[], color?: string): string { + if (color) return color + return (colors || getDefaultColors())[index % (colors?.length || 8)] +} + +export function createTooltip(container: HTMLElement): HTMLElement { + const tooltip = document.createElement('div') + tooltip.className = 'chart-tooltip' + tooltip.style.cssText = ` + position: absolute; + pointer-events: none; + background: oklch(0.15 0 0); + color: oklch(0.95 0 0 0); + padding: 8px 12px; + border-radius: 6px; + font-size: 13px; + font-weight: 500; + opacity: 0; + transition: opacity 0.15s ease; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + z-index: 1000; + white-space: nowrap; + ` + container.style.position = 'relative' + container.appendChild(tooltip) + return tooltip +} + +export function showTooltip(tooltip: HTMLElement, x: number, y: number, content: string, containerRect: DOMRect) { + tooltip.innerHTML = content + tooltip.style.opacity = '1' + + const tooltipRect = tooltip.getBoundingClientRect() + let left = x + 12 + let top = y - 12 + + if (left + tooltipRect.width > containerRect.width) { + left = x - tooltipRect.width - 12 + } + if (top < 0) { + top = y + 12 + } + + tooltip.style.left = `${left}px` + tooltip.style.top = `${top}px` +} + +export function hideTooltip(tooltip: HTMLElement) { + tooltip.style.opacity = '0' +} From 9c437612fee74fe1ac286ecdde4bb92387fa5bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sat, 21 Mar 2026 22:39:21 +0300 Subject: [PATCH 02/15] feat: csv and json viewer components implemented --- src/components/content/CsvViewer.astro | 462 ++++++++++++++++++++++++ src/components/content/JsonViewer.astro | 432 ++++++++++++++++++++++ 2 files changed, 894 insertions(+) create mode 100644 src/components/content/CsvViewer.astro create mode 100644 src/components/content/JsonViewer.astro diff --git a/src/components/content/CsvViewer.astro b/src/components/content/CsvViewer.astro new file mode 100644 index 0000000..8d9011a --- /dev/null +++ b/src/components/content/CsvViewer.astro @@ -0,0 +1,462 @@ +--- +interface Props { + data?: Record[] + csvData?: string + id?: string + pageSize?: number + searchable?: boolean + sortable?: boolean + title?: string + delimiter?: string +} + +const { + data, + csvData, + id = '', + pageSize = 50, + searchable = true, + sortable = true, + title = 'CSV Data', + delimiter = ',', +} = Astro.props + +const uniqueId = `csv-viewer-${id || Math.random().toString(36).slice(2, 9)}` +const viewerData = csvData ? JSON.stringify(csvData) : JSON.stringify(data || []) +const viewerPageSize = pageSize +const viewerSearchable = searchable +const viewerSortable = sortable +const viewerTitle = title +const viewerDelimiter = delimiter +--- + +
+
+
+

{title}

+
+ { + searchable && ( +
+ + +
+ ) + } + +
+
+ +
+ + + + + +
+
+ +
+
+ 0 rows +
+
+ + 1 / 1 + +
+
+
+
+ + diff --git a/src/components/content/JsonViewer.astro b/src/components/content/JsonViewer.astro new file mode 100644 index 0000000..c948ade --- /dev/null +++ b/src/components/content/JsonViewer.astro @@ -0,0 +1,432 @@ +--- +interface Props { + data?: any + id?: string + pageSize?: number + searchable?: boolean + title?: string + expanded?: boolean +} + +const { data, id = '', pageSize = 50, searchable = true, title = 'JSON Data', expanded = false } = Astro.props + +const uniqueId = `json-viewer-${id || Math.random().toString(36).slice(2, 9)}` +const viewerData = JSON.stringify(data) +const viewerPageSize = pageSize +const viewerSearchable = searchable +const viewerTitle = title +const viewerExpanded = expanded +--- + +
+
+
+

{title}

+
+ { + searchable && ( +
+ + +
+ ) + } +
+ + + +
+
+
+ +
+
+
+ +
+
+ 0 nodes +
+
+ + 1 / 1 + +
+
+
+
+ + From 627ae1f2df034ff168c3f3d0a6a2991a5f960a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 12:06:14 +0300 Subject: [PATCH 03/15] refactor: chart type errors fixed and new features added --- package.json | 1 + pnpm-lock.yaml | 218 ++++++++++++++++++++++ src/components/content/AreaChart.astro | 94 +++++++--- src/components/content/BarChart.astro | 93 ++++++--- src/components/content/LineChart.astro | 124 ++++++++---- src/components/content/PieChart.astro | 84 ++++++--- src/components/content/ScatterChart.astro | 132 +++++++++---- src/utils/chartUtils.ts | 5 +- 8 files changed, 601 insertions(+), 150 deletions(-) diff --git a/package.json b/package.json index d34af83..0d4c32f 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "devDependencies": { "@astrojs/check": "^0.9.8", "@eslint/js": "^10.0.1", + "@types/d3": "^7.4.3", "@unocss/astro": "^66.6.6", "@unocss/preset-typography": "^66.6.7", "@unocss/reset": "^66.6.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdbb451..d0d4d69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,6 +54,9 @@ importers: '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@10.0.3(jiti@2.6.1)) + '@types/d3': + specifier: ^7.4.3 + version: 7.4.3 '@unocss/astro': specifier: ^66.6.6 version: 66.6.6(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) @@ -1050,6 +1053,99 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1062,6 +1158,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -4294,6 +4393,123 @@ snapshots: tslib: 2.8.1 optional: true + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -4306,6 +4522,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 diff --git a/src/components/content/AreaChart.astro b/src/components/content/AreaChart.astro index dc295ac..009dade 100644 --- a/src/components/content/AreaChart.astro +++ b/src/components/content/AreaChart.astro @@ -1,6 +1,12 @@ --- import type { DataPoint } from '../../utils/chartUtils' +interface AxisConfig { + min?: number + max?: number + tickInterval?: number +} + interface Props { data?: DataPoint[] id?: string @@ -10,6 +16,10 @@ interface Props { showGrid?: boolean strokeWidth?: number fillOpacity?: number + pointSize?: number + xAxis?: AxisConfig + yAxis?: AxisConfig + title?: string } const { @@ -21,6 +31,10 @@ const { showGrid = true, strokeWidth = 2.5, fillOpacity = 0.2, + pointSize = 5, + xAxis = {}, + yAxis = {}, + title = '', } = Astro.props const chartId = `area-chart-${id || Math.random().toString(36).slice(2, 9)}` @@ -37,6 +51,10 @@ const chartColors = JSON.stringify(colors) data-chart-show-grid={showGrid} data-chart-stroke-width={strokeWidth} data-chart-fill-opacity={fillOpacity} + data-chart-point-size={pointSize} + data-chart-x-axis={JSON.stringify(xAxis)} + data-chart-y-axis={JSON.stringify(yAxis)} + data-chart-title={title} data-chart-height={height} >
@@ -53,12 +71,18 @@ const chartColors = JSON.stringify(colors) if (!container) return const rawData = (wrapper as HTMLElement).dataset.chartData - const chartData: Array<{ label: string; value: number; color?: string }> = rawData ? JSON.parse(rawData) : [] + const chartData: Array<{ x: number; y: number; label?: string; color?: string }> = rawData + ? JSON.parse(rawData) + : [] const colors: string[] = JSON.parse((wrapper as HTMLElement).dataset.chartColors || '[]') const animated = (wrapper as HTMLElement).dataset.chartAnimated !== 'false' const showGrid = (wrapper as HTMLElement).dataset.chartShowGrid !== 'false' const strokeWidth = parseFloat((wrapper as HTMLElement).dataset.chartStrokeWidth || '2.5') const fillOpacity = parseFloat((wrapper as HTMLElement).dataset.chartFillOpacity || '0.2') + const pointSize = parseFloat((wrapper as HTMLElement).dataset.chartPointSize || '5') + const xAxisConfig = JSON.parse((wrapper as HTMLElement).dataset.chartXAxis || '{}') + const yAxisConfig = JSON.parse((wrapper as HTMLElement).dataset.chartYAxis || '{}') + const chartTitle = (wrapper as HTMLElement).dataset.chartTitle || '' const chartHeight = parseInt((wrapper as HTMLElement).dataset.chartHeight || '300') const chartId = parseInt((wrapper as HTMLElement).dataset.chartId || 'uid') @@ -87,7 +111,12 @@ const chartColors = JSON.stringify(colors) gridColor: string, ) { const gridG = g.append('g').attr('class', 'grid') - gridG.call(d3.axisLeft(y).tickSize(-w).tickFormat('')) + gridG.call( + d3 + .axisLeft(y) + .tickSize(-w) + .tickFormat(() => ''), + ) gridG.selectAll('line').style('stroke', gridColor).style('stroke-dasharray', '3,3').style('stroke-width', 1) gridG.select('.domain').remove() return gridG @@ -100,6 +129,19 @@ const chartColors = JSON.stringify(colors) const innerWidth = width - margin.left - margin.right const innerHeight = h - margin.top - margin.bottom + if (chartTitle) { + const titleEl = document.createElement('div') + titleEl.className = 'chart-title' + titleEl.textContent = chartTitle + titleEl.style.cssText = ` + font-size: 16px; + font-weight: 600; + text-align: center; + color: ${getThemeColors().textColor}; + ` + container.appendChild(titleEl) + } + const svg = d3 .select(container) .append('svg') @@ -141,14 +183,13 @@ const chartColors = JSON.stringify(colors) const x = d3 .scalePoint() - .domain(chartData.map((d) => d.label)) + .domain(chartData.map((d) => d.label || String(d.x))) .range([0, innerWidth]) - const y = d3 - .scaleLinear() - .domain([0, d3.max(chartData, (d) => d.value)! * 1.1]) - .nice() - .range([innerHeight, 0]) + const yMin = yAxisConfig.min ?? 0 + const yMax = yAxisConfig.max ?? d3.max(chartData, (d) => d.y)! * 1.1 + + const y = d3.scaleLinear().domain([yMin, yMax]).nice().range([innerHeight, 0]) if (showGrid) { renderGrid(g, y, innerWidth, gridColor) @@ -163,16 +204,16 @@ const chartColors = JSON.stringify(colors) applyAxisStyles(yAxisG, textColor) const area = d3 - .area<{ label: string; value: number }>() - .x((d) => x(d.label)!) + .area<{ x: number; y: number; label?: string }>() + .x((d) => x(d.label || String(d.x))!) .y0(innerHeight) - .y1((d) => y(d.value)) + .y1((d) => y(d.y)) .curve(d3.curveMonotoneX) const line = d3 - .line<{ label: string; value: number }>() - .x((d) => x(d.label)!) - .y((d) => y(d.value)) + .line<{ x: number; y: number; label?: string }>() + .x((d) => x(d.label || String(d.x))!) + .y((d) => y(d.y)) .curve(d3.curveMonotoneX) const areaPath = g @@ -211,9 +252,9 @@ const chartColors = JSON.stringify(colors) .enter() .append('circle') .attr('class', 'dot') - .attr('cx', (d) => x(d.label)!) - .attr('cy', (d) => y(d.value)) - .attr('r', 5) + .attr('cx', (d) => x(d.label || String(d.x))!) + .attr('cy', (d) => y(d.y)) + .attr('r', pointSize) .attr('fill', colors[0]) .attr('stroke', '#fff') .attr('stroke-width', 2) @@ -223,20 +264,25 @@ const chartColors = JSON.stringify(colors) d3.select(this) .transition() .duration(150) - .attr('r', 8) + .attr('r', pointSize + 3) .style('filter', `drop-shadow(0 0 6px ${colors[0]})`) + const labelText = d.label ? `${d.label}
` : '' showTooltip( tooltip, event.offsetX, event.offsetY, - `${d.label}
Value: ${d.value.toLocaleString()}`, + `${labelText}y: ${d.y.toLocaleString()}`, container.getBoundingClientRect(), ) }) dots.on('mouseleave', function () { - d3.select(this).transition().duration(150).attr('r', 5).style('filter', `url(#area-glow-${chartId})`) + d3.select(this) + .transition() + .duration(150) + .attr('r', pointSize) + .style('filter', `url(#area-glow-${chartId})`) hideTooltip(tooltip) }) @@ -283,16 +329,20 @@ const chartColors = JSON.stringify(colors) const d = chartData[idx] if (!d) return - hoverLine.attr('x1', x(d.label)!).attr('x2', x(d.label)!).style('opacity', 0.6) + hoverLine + .attr('x1', x(d.label || String(d.x))!) + .attr('x2', x(d.label || String(d.x))!) + .style('opacity', 0.6) const hoverD = [d] hoverArea.datum(hoverD).attr('d', area).style('opacity', 1) + const labelText = d.label ? `${d.label}
` : '' showTooltip( tooltip, event.offsetX, event.offsetY, - `${d.label}
Value: ${d.value.toLocaleString()}`, + `${labelText}y: ${d.y.toLocaleString()}`, container.getBoundingClientRect(), ) }) diff --git a/src/components/content/BarChart.astro b/src/components/content/BarChart.astro index 6654fa1..9c7cadb 100644 --- a/src/components/content/BarChart.astro +++ b/src/components/content/BarChart.astro @@ -1,12 +1,22 @@ --- import type { DataPoint } from '../../utils/chartUtils' +interface AxisConfig { + min?: number + max?: number + tickInterval?: number +} + interface Props { data?: DataPoint[] id?: string height?: number colors?: string[] animated?: boolean + showGrid?: boolean + xAxis?: AxisConfig + yAxis?: AxisConfig + title?: string } const { @@ -15,23 +25,30 @@ const { height = 300, colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], animated = true, + showGrid = true, + xAxis = {}, + yAxis = {}, + title = '', } = Astro.props -const uniqueId = `bar-chart-${id || Math.random().toString(36).slice(2, 9)}` +const chartId = `bar-chart-${id || Math.random().toString(36).slice(2, 9)}` const chartData = JSON.stringify(data) const chartColors = JSON.stringify(colors) -const chartAnimated = animated ---
-
+
From 1f5b9e531ed5a8b88c25245e258f8af180bfd6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 15:11:46 +0300 Subject: [PATCH 05/15] feature: 3d scatter plot implemented --- package.json | 4 +- pnpm-lock.yaml | 59 +++ src/components/content/ScatterChart3D.astro | 536 ++++++++++++++++++++ src/utils/chartUtils.ts | 6 +- 4 files changed, 602 insertions(+), 3 deletions(-) create mode 100644 src/components/content/ScatterChart3D.astro diff --git a/package.json b/package.json index 0d4c32f..3f2c590 100644 --- a/package.json +++ b/package.json @@ -32,12 +32,14 @@ "rehype-katex": "^7.0.1", "remark-math": "^6.0.0", "satori": "^0.25.0", - "slugify": "^1.6.8" + "slugify": "^1.6.8", + "three": "^0.183.2" }, "devDependencies": { "@astrojs/check": "^0.9.8", "@eslint/js": "^10.0.1", "@types/d3": "^7.4.3", + "@types/three": "^0.183.1", "@unocss/astro": "^66.6.6", "@unocss/preset-typography": "^66.6.7", "@unocss/reset": "^66.6.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d0d4d69..29368d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: slugify: specifier: ^1.6.8 version: 1.6.8 + three: + specifier: ^0.183.2 + version: 0.183.2 devDependencies: '@astrojs/check': specifier: ^0.9.8 @@ -57,6 +60,9 @@ importers: '@types/d3': specifier: ^7.4.3 version: 7.4.3 + '@types/three': + specifier: ^0.183.1 + version: 0.183.1 '@unocss/astro': specifier: ^66.6.6 version: 66.6.6(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) @@ -192,6 +198,9 @@ packages: '@clack/prompts@1.1.0': resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==} + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + '@emmetio/abbreviation@2.3.3': resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==} @@ -1050,6 +1059,9 @@ packages: engines: {node: '>= 8.0.0'} hasBin: true + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -1191,12 +1203,21 @@ packages: '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + + '@types/three@0.183.1': + resolution: {integrity: sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} @@ -1331,6 +1352,9 @@ packages: '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + '@webgpu/types@0.1.69': + resolution: {integrity: sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2025,6 +2049,9 @@ packages: fflate@0.7.4: resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -2488,6 +2515,9 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + meshoptimizer@1.0.1: + resolution: {integrity: sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==} + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -3116,6 +3146,9 @@ packages: engines: {node: '>=10'} hasBin: true + three@0.183.2: + resolution: {integrity: sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==} + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -3760,6 +3793,8 @@ snapshots: '@clack/core': 1.1.0 sisteransi: 1.0.5 + '@dimforge/rapier3d-compat@0.12.0': {} + '@emmetio/abbreviation@2.3.3': dependencies: '@emmetio/scanner': 1.0.4 @@ -4388,6 +4423,8 @@ snapshots: fflate: 0.7.4 string.prototype.codepointat: 0.2.1 + '@tweenjs/tween.js@23.1.3': {} + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -4556,10 +4593,24 @@ snapshots: dependencies: '@types/node': 25.5.0 + '@types/stats.js@0.17.4': {} + + '@types/three@0.183.1': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + '@webgpu/types': 0.1.69 + fflate: 0.8.2 + meshoptimizer: 1.0.1 + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} + '@types/webxr@0.5.24': {} + '@types/yauzl@2.10.3': dependencies: '@types/node': 25.5.0 @@ -4780,6 +4831,8 @@ snapshots: '@vscode/l10n@0.0.18': {} + '@webgpu/types@0.1.69': {} + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -5614,6 +5667,8 @@ snapshots: fflate@0.7.4: {} + fflate@0.8.2: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -6257,6 +6312,8 @@ snapshots: merge2@1.4.1: {} + meshoptimizer@1.0.1: {} + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -7223,6 +7280,8 @@ snapshots: source-map-support: 0.5.21 optional: true + three@0.183.2: {} + tiny-inflate@1.0.3: {} tinyclip@0.1.12: {} diff --git a/src/components/content/ScatterChart3D.astro b/src/components/content/ScatterChart3D.astro new file mode 100644 index 0000000..5a98f9d --- /dev/null +++ b/src/components/content/ScatterChart3D.astro @@ -0,0 +1,536 @@ +--- +import type { DataPoint } from '../../utils/chartUtils' + +interface AxisConfig { + min?: number + max?: number + tickInterval?: number + label?: string +} + +interface Props { + data?: DataPoint[] + id?: string + height?: number + colors?: string[] + animated?: boolean + pointSize?: number + pointOpacity?: number + showGrid?: boolean + xAxis?: AxisConfig + yAxis?: AxisConfig + zAxis?: AxisConfig + title?: string +} + +const { + data = [], + id = '', + height = 400, + colors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'], + animated = true, + pointSize = 8, + pointOpacity = 1, + showGrid = true, + xAxis = {}, + yAxis = {}, + zAxis = {}, + title = '', +} = Astro.props + +const chartId = `scatter3d-chart-${id || Math.random().toString(36).slice(2, 9)}` +const chartData = JSON.stringify(data) +const chartColors = JSON.stringify(colors) +--- + +
+
+
+ + + +
+
+ + diff --git a/src/utils/chartUtils.ts b/src/utils/chartUtils.ts index 7ba03fb..47cf5cd 100644 --- a/src/utils/chartUtils.ts +++ b/src/utils/chartUtils.ts @@ -1,6 +1,7 @@ export interface DataPoint { x: number y: number + z?: number label?: string color?: string } @@ -37,7 +38,7 @@ export function createTooltip(container: HTMLElement): HTMLElement { position: absolute; pointer-events: none; background: oklch(0.15 0 0); - color: oklch(0.95 0 0 0); + color: oklch(0.95 0 0); padding: 8px 12px; border-radius: 6px; font-size: 13px; @@ -45,8 +46,9 @@ export function createTooltip(container: HTMLElement): HTMLElement { opacity: 0; transition: opacity 0.15s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.3); - z-index: 1000; + z-index: 9999; white-space: nowrap; + pointer-events: none; ` container.style.position = 'relative' container.appendChild(tooltip) From f3808b012128ecd756883760106ed5cc2a5f4d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 16:25:56 +0300 Subject: [PATCH 06/15] refactor: .mdx file support added for update-data script --- scripts/update-data.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-data.mjs b/scripts/update-data.mjs index 64c4b30..8f4d56a 100755 --- a/scripts/update-data.mjs +++ b/scripts/update-data.mjs @@ -29,7 +29,7 @@ const writeJSON = async (file, data) => await fs.writeFile(file, JSON.stringify( let mdFiles = [] try { - mdFiles = (await fs.readdir(contentDir)).filter((f) => f.endsWith('.md')) + mdFiles = (await fs.readdir(contentDir)).filter((f) => f.endsWith('.md') || f.endsWith('.mdx')) } catch { console.log(`No content directory for language '${lang}': ${contentDir}`) console.log('Skipping article generation.') From 19ff745bd7fa8ddab6399118fd83f7c58e8c3669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 16:31:12 +0300 Subject: [PATCH 07/15] refactor: remove slugify in article page slug finder --- src/pages/categories/[slug].astro | 5 +---- src/pages/en/articles/[slug].astro | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/pages/categories/[slug].astro b/src/pages/categories/[slug].astro index ba69e67..fbdb542 100644 --- a/src/pages/categories/[slug].astro +++ b/src/pages/categories/[slug].astro @@ -31,10 +31,7 @@ const langPrefix = catLang === 'en' ? '/en' : '' const allArticles = await getCollection('articles') const catArticles = allArticles.filter( - (a) => - a.data.lang === catLang && - a.data.category.toLowerCase().replace(/\s+/g, '-') === category.slug && - a.data.status === 'Published', + (a) => a.data.lang === catLang && a.data.category === category.slug && a.data.status === 'Published', ) --- diff --git a/src/pages/en/articles/[slug].astro b/src/pages/en/articles/[slug].astro index 06f540c..2afe9b4 100644 --- a/src/pages/en/articles/[slug].astro +++ b/src/pages/en/articles/[slug].astro @@ -26,7 +26,7 @@ if (!article || article.data.status !== 'Published') { const { Content, headings } = await render(article) -const categorySlug = article.data.category.toLowerCase().replace(/\s+/g, '-') +const categorySlug = article.data.category const articleUrl = `${SITE.origin}/en/articles/${article.data.slug}` const ogImageUrl = `/og/${article.data.slug}.png` From 903dfacf5d8d86ab45107a67cae13e211cd19341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 16:31:51 +0300 Subject: [PATCH 08/15] article: supervised-learning-nedir --- .../articles/tr/supervised-learning-nedir.mdx | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/content/articles/tr/supervised-learning-nedir.mdx diff --git a/src/content/articles/tr/supervised-learning-nedir.mdx b/src/content/articles/tr/supervised-learning-nedir.mdx new file mode 100644 index 0000000..2615faf --- /dev/null +++ b/src/content/articles/tr/supervised-learning-nedir.mdx @@ -0,0 +1,118 @@ +--- +lang: 'tr' +slug: 'supervised-learning-nedir' +title: 'Supervised Learning Nedir?' +excerpt: 'Supervised Learning, etiketlenmiş veri setiyle input ve output verisi arasındaki ilişkiyi anlayan makine öğrenmesi tekniğidir' +category: 'machine-learning' +tags: ['supervised-learning', 'teori'] +author: '@omerfdmrl' +date: '2026-03-21' +views: 0 +status: 'Published' +--- + +import CsvViewer from '../../../components/content/CsvViewer.astro' + +Supervised Learning (Denetimli Öğrenme), etiketlenmiş veri setiyle input (girdi) ve output (çıktı) verisi arasındaki ilişkiyi anlayan makine öğrenmesi tekniğidir. +Input verisi ve o veriye karşılık gelen output daha önceden hazırlanır ve daha önce görmediği verilerin sonucunu tahmin etmeye çalışır. + +Eğitim sırasında modelin algoritması, input ve output verileri arasındaki potansiyel korelasyonları keşfetmek için veri kümelerini işler. +Ardından modelin başarısını, başarılı şekilde eğitilip eğitilmediğini bulmak için farklı bir test veri setiyle Cross-Validation (çapraz doğrulama) uygulanır. + +Örnek bir veri seti aşağıdaki gibi görünür. + + + +Yukarıdaki tabloda, çalışma saati (input) ile sınav puanı (output) arasındaki ilişkiyi görebiliriz. Modelimiz bu eğitim verisini inceleyerek aradaki matematiksel bağıntıyı öğrenir. Daha sonra, test verisindeki (örneğin 7 saat çalışma) bir durumu modele sorduğumuzda, daha önce öğrendiklerinden yola çıkarak bize tahmini bir sınav puanı (örneğin 82'ye yakın bir değer) üretir. + +## Çıktı Türlerine Göre Denetimli Öğrenme Çeşitleri + +Denetimli öğrenmede, tahmin etmeye çalıştığımız output'a göre kullanıcağımız yöntem de değişir. Temelde iki ana problem türü vardır: + +### Regresyon (Regression) + +Tahmin etmeye çalıştığımız çıktı nicel (quantitative) yani sürekli bir sayısal değerse, bu işleme regresyon denir. + +**Örneğin:** Yukarıdaki tabloda çalışma saatine bakarak sınav puanını (82.5, 90.1 gibi) tahmin etmek bir regresyon problemidir. +Veya atmosferik ölçümlere bakarak yarınki ozon seviyesini tahmin etmek de bir nicel bir ölçümdür. + +### Sınıflandırma (Classification) + +Eğer tahmin etmeye çalıştığımız çıktı nitel (qualitative) yani belirli sınıf veya kategorilerden biriyse, buna sınıflandırma denir. +Sınıflar arasında matematiksel olarak büyüklük-küçüklük ilişkisi yoktur. + +**Örneğin:** Meşhur _MNIST_ veriseti, el yazısı ile yazılmış rakam görseline bakıp bunun hangi rakam (0,1...8,9) olduğunu bulmak. + +Örnek bir veri göstermek gerekirse: + + + +Yukarıdaki tabloda, bir çiçeğin fiziksel ölçümleri olan Çanak ve Taç yaprak uzunlukları bizim girdilerimizdir (X). Bu girdilere bakarak tahmin etmeye çalıştığımız Tür (Setosa, Versicolor, Virginica) ise bizim kategorik çıktımızdır (G). +Model, bu ölçümler ile çiçek türleri arasındaki örüntüyü öğrenerek, doğada yeni bulduğumuz bir çiçeğin yapraklarını ölçtüğümüzde onun hangi türe ait olduğunu sınıflandırabilir. + +### Topluluk Öğrenmesi (Ensemble Learning) + +Regresyon veya sınıflandırma problemlerini çözerken her zaman tek bir modele bel bağlamak zorunda değiliz. İşte bu noktada Ensemble Learning (Topluluk Öğrenmesi) devreye girer. Bu yöntem, aynı görev için birden fazla modelin eğitildiği ve sonuçlarının bir araya getirildiği bir "üst düzey (meta)" yaklaşımdır. Havuzdaki tüm modellerin tahminleri birleştirilerek (örneğin ortalaması alınarak veya oylama yapılarak) problemi çözmek için en güçlü genel sonuç elde edilir. + +Bu büyük topluluk yapısının içinde çalışan her bir bireysel algoritmaya zayıf öğreniciler (weak learners) veya temel modeller (base models) adı verilir. + +Peki neden birden fazla zayıf öğreniciye ihtiyaç duyarız? + +Bazı zayıf öğrenicilerin yanlılığı (bias) yüksek olabilir (veriyi fazla basite indirgerler). + +Bazılarının ise varyansı (variance) yüksek olabilir (eğitim verisine aşırı ezber yapıp, yeni verilerde çuvallarlar). + +Teorik olarak Ensemble Learning, her bir temel modelin en iyi yönlerini bir araya getirerek bu ünlü yanlılık-varyans açmazını (bias-variance tradeoff) hafifletir. Tıpkı zor bir hastalığın teşhisi için tek bir doktorun fikrine güvenmek yerine, farklı uzmanlıklara sahip bir doktorlar kurulundan (konsilyum) ortak bir karar çıkarmak gibidir. Biri yanılsa bile, çoğunluğun kararı genellikle gerçeğe en yakın olanı verir. + +## Notasyonlar + +Literatürde veya farklı kaynaklarda araştırma yaparken, bu input ve output değerlerinin her zaman belirli matematiksel sembollerle ifade edildiğini göreceksin: + +- X (Girdi / Input): Modele verdiğimiz özellikleri temsil eder. Gözlemlediğimiz değerler genellikle küçük `x` ile, matrisler (tüm veri seti) ise büyük ve kalın harflerle ifade edilir. +- Y (Nicel Çıktı) / G (Nitel Çıktı): Hedeflediğimiz gerçek sonuçlardır. Kategorik (Grup) çıktılar için genellikle `G` harfi de kullanılır. +- $\hat{Y}$ (Tahmin / Prediction): Modelimizin ürettiği tahmini değeri ifade eder. Asıl amacımız, elimizdeki eğitim verisini (Training Data) kullanarak $\hat{Y}$ değerini gerçek $Y$ değerine en yakın hale getirecek kuralları bulmaktır. + +## Kategorik Verileri Bilgisayara Aktarmak + +Peki çıktı değerimiz sayı değil de "Başarılı/Başarısız" veya "Kedi/Köpek" gibi metinsel (nitel) verilerse bilgisayar bunu nasıl anlar? + +Modeller matematiksel denklemlerle çalıştığı için bu kategorileri sayılara çevirmemiz (kodlamamız) gerekir: + +- İkili Sınıflar: En kolay yöntemdir. "Hayatta kaldı" için `1`, "Öldü" için `0` (veya `-1` ve `1`) gibi binary (ikili) değerler atanır. Model tahminde bulunurken `.5` eşik değerini baz alabilir ($\hat{Y} > 0.5$ ise 1. sınıftır gibi). +- Çoklu Sınıflar (Dummy Variables): İkiden fazla kategori varsa (Örn: Kırmızı, Mavi, Yeşil), Dummy Variables adı verilen bir yöntem kullanılır. Her renk için ayrı bir sütun açılır ve o anki veri hangi renkse o sütun `1` (açık), diğerleri `1` (kapalı) olacak şekilde vektörler oluşturulur. + + + +Dikkat ederseniz, "Araç A" kırmızı olduğu için sadece Renk_Kirmizi sütunu 1 değerini aldı. Artık modelimiz metinlerle değil, tamamen 1 ve 0'lardan oluşan matematiksel matrislerle çalışmaya hazır! From 7fdbcd91af93420cdbd765b3cbb160283f49444a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 16:32:14 +0300 Subject: [PATCH 09/15] article: linear-regression-nedir --- .../articles/tr/linear-regression-nedir.mdx | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 src/content/articles/tr/linear-regression-nedir.mdx diff --git a/src/content/articles/tr/linear-regression-nedir.mdx b/src/content/articles/tr/linear-regression-nedir.mdx new file mode 100644 index 0000000..df24d76 --- /dev/null +++ b/src/content/articles/tr/linear-regression-nedir.mdx @@ -0,0 +1,217 @@ +--- +lang: 'tr' +slug: 'linear-regression-nedir' +title: 'Linear Regression Nedir?' +excerpt: 'Linear regression etiketlenmiş veri setlerinden doğrusal ilişkiyi modellemek için kullanılan supervised makine öğrenmesi algoritmasıdır' +category: 'machine-learning' +tags: ['linear-regression', 'supervised-learning', 'teori'] +author: '@omerfdmrl' +date: '2026-03-22' +views: 0 +status: 'Published' +--- + +import ScatterChart3D from '../../../components/content/ScatterChart3D.astro' +import ScatterChart from '../../../components/content/ScatterChart.astro' +import CsvViewer from '../../../components/content/CsvViewer.astro' +import JsonViewer from '../../../components/content/JsonViewer.astro' +import Grid from '../../../components/content/Grid.astro' + +Linear regression etiketlenmiş veri setlerinden doğrusal ilişkiyi modellemek için kullanılan supervised makine öğrenmesi algoritmasıdır. +Oluşturulan bu model, farklı bir veri seti üzerinde tahmin yapabilmemizi sağlar. +Kullanabilmek için input ve output arasında linear bir ilişki olmalıdır, yani input noktası değiştikçe çıktının sabit bir oranla değişmesi gerekir. +Bu sayede düz bir çizgi ile veriyi ayırabiliriz. + + + + + + + + +Buradaki ilk grafikteki veri setimizi linear olduğu için ayırabiliriz, fakat ikinci örnekteki $y = x^2 + 4$ parabolünü ayıramayız. + +## Değişkenler ve Terminoloji + +İstatistik literatüründe input değerleri yordayıcı (predictor) veya daha çok kullanacağımız bağımsız değişken (independent variable) olarak adlandırılır. +Çıktılar ise bağımlı değişken (dependent variable) olarak kullanılır. + +**Örneğin:** Öğrencilerin kaç saat ders çalıştığını ve aldığı sınav puanlarını tutan bir veri setimiz olsun. + + + +Verilerden yola çıkarak çalışma saati arttıkça notun da yükseldiğini görebiliriz. +Yani bu örnekte sınav notu tahmini çalışma saatine göre yapılmaktadır. + +- **Bağımsız Değişken:** Çalışma saati çünkü kontrol edip gözlemleyebiliyoruz. +- **Bağımlı Değişken:** Sınav sonucu çünkü kaaç saat ders çalıştığına bağlı. + +Bağımsız değişkeni (input) bağımlı değişkeni (output) tahmin etmek için kullanırız. +Eğer bu örnğimizde olduğu gibi bir adet bağımlı ve bir adet bağımsız değişken varsa buna **basit (simple) linear regresyon** denir. + + + + + + + + +Buradaki ilk örneğimizde olduğu gibi $y = a + bx$ basit bir regresyon örneğidir. Çünkü bağımsız değişkenimiz sadece `x`'dir. +İkinci örnekte ise $y ≈ 5 + 4x + 2z$ formülünde `x` ve `z` bağımsız değişkendir yani toplam 2 adet bağımsız değişken vardır. +Bu yüzden 1. örnekteki basit, ikinci örnekteki **çoklu (multiple) linear regression**'dur. + +Eğerki birden fazla bağımsız değişken (input) varsa ve birden fazla bağımlı (output) değişken tahmin etmeye çalışıyorsak **Çok Değişkenli (Multivariate) linear regression** denir. +Yani diyelimki elimizde aşağıdaki gibi bir veri seti var: + + + +Burada çalışma saati ve çözdükleri deneme sayısıyla; matematik ve fizik sınav sonuçlarını tahmin etmeye çalışıyoruz. + +- $$y_1 = \beta_{01} + \beta_{11}x_1 + \beta_{21}x_2 + \epsilon_1$$ +- $$y_2 = \beta_{02} + \beta_{12}x_1 + \beta_{22}x_2 + \epsilon_2$$ + +olarak iki farklı denklem ortaya çıkar ve buna da multivariate linear regression denir. + +## Çalışma Prensibi + +Linear Regresyon'da ilişkiler daha öncede örneklerini gösterdiğimiz gibi bilinmeyen parametreleri, bilinen verilerden **linear predictor functions** kullanarak modellenir. +Yani model, tüm değişkenlerin olasılık dağılımından ziyade, elimizdeki girdilere bakarak "Bu şartlar altında beklenen çıktı nedir?" sorunusuna cevap verir. + +Linear regresyon, tarihsel olarak istatistikte titizlikle incelenen ve pratik uygulamalarda yaygın olarak kullanılan ilk regresyon analizi türüdür. +Bunun temel sebebi, bilinmeyen parametrelerine doğrusal olarak bağlı olan modellerin matematiksel olarak çözülmesinin ve uyarlanmasının (fitting), doğrusal olmayan modellere göre çok daha kolay olmasıdır. + +## Makine Öğrenimindeki Yeri + +Linear regresyon sadece klasik bir istatistiksel yöntem değil, aynı zamanda temel bir **makine öğrenimi (machine learning)** algoritmasıdır. Daha spesifik olarak, **gözetimli öğrenme (supervised learning)** kategorisine girer. + +Öğrenci notu örneklerimizde olduğu gibi, modele hem girdileri (çalışma saati) hem de doğru cevapları yani etiketleri (sınav puanı) veririz. Algoritma bu etiketlenmiş veri setinden öğrenir ve veri noktalarını, yeni ve daha önce görülmemiş veriler üzerinde tahmin yapmak için kullanılabilecek en optimize doğrusal fonksiyona eşler. + +## Temel Kullanım Alanları + +Linear regresyonun pratik uygulamaları genellikle iki ana kategoriye ayrılır: + +- **Tahmin ve Öngörü (Predicting / Forecasting):** Amacımız hatayı azaltarak gelecekteki veya elimizde olmayan bir değeri tahmin etmekse linear regresyon harika bir araçtır. Mevcut verilerle öngörücü bir model eğitilir. Daha sonra, elimizde sadece girdi değerleri (örneğin çalışma saati) olan yeni bir durum için çıktının ne olacağı (sınav notu) tahmin edilir. +- **İlişkileri Açıklamak ve Varyansı Anlamak:** Bazen amaç sadece tahmin yapmak değil, değişkenler arasındaki ilişkinin gücünü sayısal olarak ölçmektir. Örneğin; bağımsız değişkenlerdeki değişimin, bağımlı değişkende ne kadarlık bir değişime yol açtığını bulmak, bazı değişkenlerin sonuçla hiçbir ilgisi olmadığını kanıtlamak veya hangi değişkenlerin birbiriyle gereksiz/tekrar eden bilgiler içerdiğini tespit etmek için kullanılır. From a351ad73354ab3f484a6cca7c051a2ca71e480e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 16:35:07 +0300 Subject: [PATCH 10/15] refactor: remove child gh comment process --- scripts/update-data.mjs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/update-data.mjs b/scripts/update-data.mjs index 8f4d56a..c7b2dcf 100755 --- a/scripts/update-data.mjs +++ b/scripts/update-data.mjs @@ -110,11 +110,6 @@ const writeJSON = async (file, data) => await fs.writeFile(file, JSON.stringify( for (const c of addedCategories) lines.push(`- \`${c}\``) lines.push('') } - if (lines.length) { - const comment = lines.join('\n') - const { execFileSync } = await import('child_process') - execFileSync('gh', ['pr', 'comment', String(prNumber), '--body', comment]) - } } } } From a55c01fb79b115c5d22effed8ac9977d2475ea36 Mon Sep 17 00:00:00 2001 From: omerfdmrl <62325045+omerfdmrl@users.noreply.github.com> Date: Sun, 22 Mar 2026 13:35:38 +0000 Subject: [PATCH 11/15] chore: auto-generate data and og images --- public/og/tr/linear-regression-nedir.png | Bin 0 -> 37352 bytes public/og/tr/supervised-learning-nedir.png | Bin 0 -> 38192 bytes src/data/tr/articles.json | 13 ++++++++++- src/data/tr/categories.json | 11 +++++++++- src/data/tr/tags.json | 24 ++++++++++++++++++++- 5 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 public/og/tr/linear-regression-nedir.png create mode 100644 public/og/tr/supervised-learning-nedir.png diff --git a/public/og/tr/linear-regression-nedir.png b/public/og/tr/linear-regression-nedir.png new file mode 100644 index 0000000000000000000000000000000000000000..c0625956500dd5a78938cbac65190c64a8e5b5a1 GIT binary patch literal 37352 zcmeFYXH-*dyDl0F`U(OT6h#3Ml@8LSD@c>xdlN%10trYUfCcas0Tl>RW2m7cEwlj2 zD@Y9iLJJU)5(oqoNDx9f6aBum)*0jc*z3nRXYYMZ{=k?cne%z>a^2T`-Ov2n*if65 znTr_&0gp{pXQWx;IYg z3VJ3V*U7a;KYO5ihAIDrAt)a$as7+B|8;{`g4}t3n{ph#v0rrLS@zB+k?C2dj_m74nz>|T`bwmGY$+c08{=H<6|Mt#UXiFOZ{`Hgp_OJgpFyDWF z3zkZEzC}5u?QM~85Xx{@OI&*)GQx`|a`#kq*DaECoM_cc?qk1$X+ZLfvxP^TScG%G z(wcqskIyU_IJJXG(V7AEU>{{w#!7XbkAKT{feko?;In zGZIHjZwZ}FSESMAMw%0}z-pTV?Y%cscL^hpb^kGA>xz ze`15$>xmIY!AC{#yn!WiolJ-Cr(~Pmq;5z3 zGBetHt}vfWBBH{NofP+9ZSsG{U7Y{ew=L?2*h@*+LfXN5M=Wd{Ws8}V8rfacZo2%( zkS75{_QiopLe{2r*+p5s9oCvR5=?M~!s=pT8!Oc~$xiN&W_YT9!#qNkUHNee3l!Sx;brtd-N@8wS#kzP=f$ ze4yqOI)9;h>|xw^NS|30bZ5!^{dpthu9hCm?s`ueW6eLzF3S(RLW+#y_7ZZ^UY`uY zrSqu3#!$8))1iexxMZ!I`D213fOjWE+Dt2N?9kO{ErW_2jzwfbZ<;jKzc^^Zvv3@= z{75O;e;k}D$*%e<>i43HPFi+ue2>GqpLoiywp+Qqza@{+_Ec#BV_hwNAcig_e@X2J2N4{f=&TZ zvY3;Ima@ld*hsfvqAw;d6V{g|-I*COjqLr{$>V;}=FwRt3pw9vSca$4g$elnwtMi3 zdY4qUY3OS_pF@+;q?BgQ-mlrjB)jXetXJGRhhZxpJNs^Bgv;Bkxg+6Th4Q)$>gN0L z@;sg+4qI2;@>47yz;IX~T^B~|{G~CIfpJrG1pL%*Q&2R30H!ln4l}%>FIeiRbC}%D z4PbUDwnrjDMr_+8x~8TeF%#Ra!Y^J-X-sF;OztTV5TnE(~2R!}z>O z=FI`)ne zZ`Y_|I}*6P0fVzE6-0L!WbfL#)o-pgN%vK{v{Q{G#WbGguK2g@_+*jBV{4m0(Y|+xEy?L8f3Iw;`6;MGfqK@t@4aOEX`=?wZMFelF7#rzrd?x;QD?N-^+;YNWz?pN?wJAU<8B$g6UUX9 zU!6E@-ErbB-QX#7#D@6#a!?j`x*qE7>B~|wp}+PvaBBV+otmlg>=*G;JG7yomW9Fk z<~FhW8xiYW0&>Pzl70RFsi4-OJO8SLZLMZ)4hL`qH}BDjUG>qNe#9oU4fts31J)*8EDh$b&cE!=O;v9Or#iIs(T^V5CdwAb zvtq?IQ=<~-r~{_fJ>gkGky+tIRXeZA`T#fJHf0-M0lQ=<>on`P<=Fj|sI>Y-DOqCe zvb863#_;n8?7E|6P~q0@4^?0$SuX#?Pmg#^DsIWI42dOEJ|=6kyCLO#y{g2KpVbIB z_Q?8-k7j8~tC!r{(JjH&xG7svA=mT;{6!EC6+4`R3YhOd1^>9>F1!~&6Lal)EehNBologlPSD|MdW zfh}$Wh>g5Vxq~4^G>u0vsMG=bt*H>F{3Djrm@|sZ-qT@gDpxvA*WM#cRz=Xq3+42i zI#2T`kF|K~5qF2pjMCP^$xVRemn&LZ8rHj8YlSccc3wsKeb?h4RcjLyfjwfq$bCpQ zfa=>Y`pT4Tp(<_67PgcRL^tqglDK~|x7RXIa3uYkCEMBh@}cNviixaqo0Ke3qWF2L zBMg5CWo#JM^$}}Vu`81Xb-&S%t9GUBLT^3yr?W5ix0BM@pztE-C*wUGdFfuYY>t<{xC z)8={a?1tO_x?j*t8{u_1@DE(JZjGc8bi3B^SAm*iaoO1*L$0s1dA(cr(sgDnM>vfz zhnig)Xb)jfH`Lk`H+rS}es&)8vw?|_p|$il@kVrCu*Vfm%&)(vjj|@__g0uA`W47m z?fTEJG}R4@Jis^>V7C`C+-eCe>7w$~FPuoDqwO_KYb~8V7zbatB2Z?z-Nq_W!5-o% zQ~;VkLS+Ux(jFOEXDNmFRU2*gy(}rMO6NX|)B!Y2?o!$drXV+*oO^a)DPWP8YZ0W< z7CJ2W>w6M!Sp8lklp|)PayfgUTTepxw20hPpdBO9)sHKv_Wxv&DhMe;*Mt7~ANStZ zOJ;ps%cbsa`Was3yUWpZ%=~Gd#;Wl^Mi$jr??=J*5Zj&gUpFSXBFR)bclsfd5I!o( zS-w~&W8yD@9nRIp)CXjo3iN^4valqDDDRxXcc-+NH-6wsLl;&eEXUJqg?6_wkBI%7$&(OYMh4%7jHAWFi@gdDG4||@K z_8jc#YB8`2?8*-eOFb<8f0@ME)9C~ zD8q4(x1(tmBpLW)HdVSqAoOj!XFeqLvF1{WM56r^5p&qIwf6y-N#|Ivv(6TWDaS06 zp6~okwccyYW7paDf&&LL%EfaaB`u_PFcWeH~Qm9RPY=uv(S7%{SIZybHh$|<={YicK zzDjd%G8?rQpL^<{Joy2;;$HCDp##`C)_gQR!zj zz8`Wic|vobT%^U1NFo$96koe1*4!o%R~V z!bW>(As=V0oWiC?=Do1{JJo2z?&H?B^u}OC!gI91TLiVj37YG5xqW0E#vcB%Fq z>s_33!#w=uNE7a<_6?UJk1JDE;m55Bd~IHX!$nEN<6Jb;D15cMQT5mBaW&$4>|6Wa zp0mFT;A@OFK7v5Z>c7AJ38+~Auw58Z=Cpr|QlOwoV6{hoql9&Eay^xpB zib{EOU~D5DSNtEw+A42NR?FR{xWsxnKnZHS0GRs6E_Acxl(+0~RpV3{6R5hHsm6SX z<#B7*&V)UZ2Fcq2f{$KckVw63wfQielO8#=m-G@p>ESx>JJe`S$J?Zxk)7bb0I%HT z=g<*sGhO_N6|0lv%-m+AKXxynhJ2jSGrE$lY<8X^_+;E=lvhB-V^hh*SaxV1V9e19 zwhP}}Ln(a`P zd*tTZhVGaE<~R;p&%L09UcW3Y?9}iie&>sCD@A?C)6RT^C)hFGKM`g!j=}U;VwHWK zir+n{paacgP!ei;qw-zBC0N!EI%Vis4LH#~wU zMPdR+oklQci><5>)7aEr`&5r6r)`q%ZE8V=pA-POh1|uowqsjq^9Z|sz$5dXr06+= z)aeMo0U(-*b?$&V-!oW;@_O5^k!fXSMVZxvkMdl!{+}A$OqZD@I!Fo_`M1ku`H!`p zzGiVBZhC-?x*9FcY{g+%?>Xr5#b`_EwoM1r%E66P+shweyfNJyV&@M&Pt!8Bp6y0g zKH^t0-0-l;@!jc)IC%s2IPj@qI^`eIh^TEUOvK@F$C@3+37T_Pi$?pgw{>f62s&3c zO87erIlbTf(_*-XYdL(#r>4uTjG=2T!CU|novwy9Dwjua)K9r2rc}+Opqssn_UCsI zpf&B_ENK!P0}Z?Cs|_RKF9C&ZK3V`=e_KN(p2OVJT4uitGwlWE&Ee6=)lM{A2wH7D zh^YSlN(5osu}yY5bGmZfk1tck^WAob{7ALN{9N>1F$_b@Kh{?8pRn2ZrC>CoL!!Dj zu#C8~)q2q9Q$KS8fc?EyfQI{>hiuCjI7UGMmYl9a|Mii(IA!a@FX224?_rq3IkbQ< z2jvhyA#D7dz1J>rLNcM<0dCMM{|MH6WtQiN5G54)S6vmuMN2aqLY6=97UVU2=;)h8 zi!z&;i<|u5Tg4t_Q}a2G5w+5y@gza+ymJgrujBK#<4)1W=I+wsI`l@WEYv6s1;m9( zdZSd+mf`d-$oSB*88+`ZFGdujvJ`riDxtR6owHs-&iX8p($4-#onukx+eVm=&?ECi zQEpf9xujC+UDu1YFwdiptx1!s&0 zbaE$cb2C|sl+~C@8TN?gfR?}VM*^^4R{8mww998~FnVOTXsCCzrLK`%nEdHv^|8_| zQY7hwpT^37Js?k%E=^;@GS*A_TfB_eq@a{#EtS#miG_GME|=_3w;XsX6(HAou;*YV zYk5+5+%`Tj7^6{}4Ws;=bD?_LBD)tOc4Q1?rg<+OV4pSY@1@qR9R{_`_YApGo8WKP zHnv*rJrHICKigG&zXgQ`#*G2d+Sa_^eIsc0eM!>N2+{e4!u)&0V+|C6vRs;R{0#H0 z`znzfUiRCgalIvSknx||V)y<=WObGe_04*~%kAGejy%;KkDBcdSa6j1_P8eyJgm$d zU$scbNC##U^Pmi3I=JDi3ji%JmAia%6i#el6 zMbcI1FuvgHw@)JJVXF;9Hp0%2cJ{sx_e91~fsPRhj8F>+?B+Y0kSYIhR0G1+OLt=~ zDRDH}iCAFO8qxE<+@Wb~9&5c3hTlN%(5e2NqbKZC#b#5is*q2iJ!0dyHq)rWYZ5&! zvD^z~Ye9A~2sp~+V~JjU4h0Kz2g_tTf2{BR+Ce#{^^lYd{1tsM;@bKFU2ULQZfX9; z9pk9^&!Pt8*ejH>>f*iJT?5y0-2$s?GtbU(Hu}`f*XiO!nXZNoZrrg&I*CW${66i@ zbk)zM{B^qaJ%UhT5 zdLRSLqILZmWyW^F%Uw&%j3>cTaAQ9P38dc05Mg-Bm30@kiZr`1~ z;jJ`q2qd!SomKWWhuogC%2m2_y3@1vO2@8g%%pfs0veWw+~i{oe%RXL^Bhl_*(OL0 zYXw>md~b5;7HM}uzK>w#G^Gf)P2&LwV#T9lrA;fwVksq&2jG<*Y6d99gqC8FAnCdf z1{5+ZkQeC@>vL8Hg@#!xO}5PFjzk<=G7o&yWCObfC$U`!+n!&!EhceDvVwXo?R>`z zb{qHn+oI+tULW$D+iQZ!KaQQHy~9(38o%hVI~@!y6^?xr8xm@MJQJh-W@{QQzx4O4 z;J^eV&ev9hb8CNBI>PC^tw-~-o6(Bm_5pvaTxK-MD2-|t>q#-`8Ib?!xu;6IXgYr3 z^CNutew|wv3)%bx&2O-32z-%H1QTwc5;lda!`9~Ft;W;KIH|6*7pCJDmL`{I?Z|!Q zQ%RE$Pg@M-3zKzDqxt1c=Wr%ygoH4%z-+B71H$;`nm8sIwDd%WSHOn&v;&Z;bf4;D zIeGZU-Yyur9G*VM) z@wGX`ncgeRH+S#ezt>^3rFQMqja|^aKW09f2}baG`0bC@Y8{DFe^(YfrjUk<7PlQq z+$X8(u5nG37dR@4uySW_ZU)?oSum^rz7SBO+G@|N`wte!d}p%C{&H=EZ_n!@v!tcz z_o`y2YHId3n^l3ZrHsrE$q7CRc>20UWo?GhAuu~cESN`YC&4~?5@ix2)LQ-d%&{J= z9!@HdNWE`)cK2xZB2(uU3-rwD^S8};$~0=kgp>RVMVTvWrixkAb_30`;p~dn8)}U^ z|9<3qlIu`$Y6j_{VxF{|3u14^~N;$ZmaJ8 zIJN;OqL^tnoBJxLM`>UL=0KuA5x&~su8+%Bp*Gn9ir~v}pK9JQR9%5#U&O*lvXEIX zrKAE(xV+) z+FKelM^3n5NSZVS*PY=!&XmuJv{Jnx>GsXtie{pMHnmc3FxQ|n^WRqX9J)pP2If;5 z9-)mo`Ln6*KHeB}O(f{&wz+oQOs&xn+l=`5<)ZYV*mcT_!?~<>#5NeM0Ejxh?}dVUvyU(L?;4U6Xu%g4O5z{?|_4NFRhl@Q1DqN{K{9f0hyv_0V?9 zP$^yx=phT?ob<(9_O$&y1`%beIiDu8^yS8r^`fl|I#IZ!)C&-aeU)z6wMM|HioAZ+ zyd0q7lcS$ma8p}{LBAlz#Ko+!`{rg`0lo&cG@%z(?xd2m*=<)BQD`;2=8C$jhq=>8 z)Qa;6+k6GjxEn>NDKaC{znWY_5s%3_N{okZ^!0t=bjobD`cMn3Uf^k*2W(35eZkMA zN{z`o@&|_`O$%u7b{to+TaJfn^&?wsjcA!tPXjsIJwbKn5+El3`hi*?9kjHwfrr*> z_W_xTXdl!EEnA)lCYJS$yCYL}`{Oq}cYn;3j8P*;TYBWYh7k+R8~wE!U(91#;0z+w zw<%lHwZq=KF(3pAN-? zQXcDEt$3*r54>Dev*i~&18*ocV@l(Ztu*NmM8JT`X*KBT2aNDPM)x_&E-)X z(13u8c*3?EKLZ1;@dP1lzKPeD&5}YjU|WuZ;$bFA!`GKBKf{V90s9vy?EgAX{jKx? zouVx(&%|K=h;(UHwnKD6BA`Opum`9C)1yAXVu<6WtAuliUL78;mc^X7TQkv%pJsu! zN%TU_Tm!X^*qflVI@YLP)OgZzTI@wSKy7?Qn^@DeE3#F>h-Vgig7wG9$Vj*L&q7_W zDqwWcDTPh{><{?PpL zXekv#ng$d?koV#MDj5B~`|(UPCsUX+d~exF7GS+efW~-R3&%)Tg*O69ZXCCnKDJzS zXOUmpW8i@(UyKn!(l7SPEPIR?k?3G zTu$=x(@!+2^dqsbif>-@m2tOkZTI9ByVy|bj$yv0Rr@kxB%aPPu-8B8EMkuuL!5eJ zoFnB*${W2*9pi_}vS5azK--|T3S_qiyUN#<)`R^nne@?Uo1Lu1LdRU~HJ|;xwWgIz zzoXaqBCz!JqoUfV=x1lth9AoqK8=kaiDG#6IHL7o74w|o%R*C1h}&aOKtg(m4v(5%|iw1*6el@&%K!1%J=Gr4F>F_IK|-COzh6VSZC6ABFB zDwocES*;l|ly7BB=>`ir&UTV~Ev;Rm<@nw_LxB-CBjM{^?sV?ozp*|83g{@@Ebz10=UP7DkgwRNH?EM7Eg>yXICA`Q0<5p7?g0!mn={Ni%n9 zmFuT?vdh2_wckU|Hcs{iltf3M~L_ulU?|G$j#|3{r8 zJOCYsQV;CHcRPj_hQ_6-#&Hr*n@Q2e;qGAc7wrmz$^-u%Bv#N&1{vXTVVYg_J+J{=tz&tl`EH z%W6x-C$IJ*<1WkZ4>oVjr39lj6A8h$_5A7DO2glylXi#)n0ceEzgY`zWnmJyy-Kz) z5x%gnPl9rax}o zIem=(J;MTlJOF5B+Py}8PWG2HtC*T%t6jvmj+tqMu^Qm#wbe z(V%w8-@c530f^hiq`-^&mB{+eQc?n`s@$MrG&A^N52@?76@=LW1CLXT)~n8L?iOuX zNbKv46_@d!j7Z+|ErUeKIv180jLwhfjqFj7t*(V%>xRF&zMJ<_?UN|a^z;j>>ZXV& z{V8yFrG27mWq^_cShIMq^&@JCN$$qZ-=RY%jMgS*5X7TK-rN!rnZvmKRz$`suyezO zE0sgC1@m&BYpMR;wEdAIx}f}P6uqpl8$A|JWVii^k77*v+ps)3e!St-cWdu&`o7O6 zTlLldQ1 z?zDwEfs#^Hd584RHHnVg{;_|`K)QC6#SzXq!5;xLW<5WufkPj<9&<*s-VJc$q01ZH zq22-{(Z!=>2zOhmf24FOOXWYUvL-4eRoQ7b#W3vqdC<_Ej?eR3u(o}-RYiCx*dS;~ zNPk1m{@staH>Xk;+Y%1k_CH;7c)ybhQ!zdhu%-|7M9`jRcsV8o zCb^B763RO#-#U9#l<85Uudzx!Vrs80PXKX+yu8~=JgM0lq9}oVP&HlpO{AjK6I}S` z4420VAbtn$B{anwWAh-44|#=W4=h_Y#iGQ=2A@wAIyN^|JhFHzw=vBWbH9`Q*o9lc7I*{jb{lZ4rBO^eRlv3xwDE4s=($ZbQ5&{VczBdhLn= zpxt&Y@P#F)&CezEhOJ@UBU!Hfb8uP(Ao+CZ%?}D|lR>{u9{~*=ni7h)~#caPQanc#g~Rkd%bxxSp_j$Y((Fr?X^+u!b4N8M* zJiD3dhu7Q!Cg*i6Cyjl2%Cyp46O{lYH1j9Ycej|tYIy6$0`&lWWhAUxc>B4@VrB_w zHt&3-du+=cb?AD9q-$VBP|cd+pu5g!km86Om!#CGigiIqlj|haEffCJUl`?}g%`-+|Bpp>V4>dI+gWM%DH{_&4VKM`E;r=dC~N3&Yz@Bbo5_+lMgN_lFvEU=0_@RcIWIB zSY>bTT^^*yTfDGJ*uS(ns}d~m&ry)5F@WeYt;b3og7;7gi{CU>jO`k44>?_IcyljT zoi$dt=tvG{)I~}b%CRmqGVj~v*o~N@XRzI-QTf+O&Z2_Ek67C6glJTVN=C*MvsGN> z4pTgF1Y~V|cx*W0q;<|2JDG&L zW&=eP1C#{atEm$PJg8NQ^1U=Han-GRPUyH5`u^c_WPhc@V@_9I8*G(TY+WXzNAfX&eA9wG&&oJeFeMv#8 zVT1*LY-(!~E`JQv_ACl$BO5{ZXq~sAz;Sab-|%BWFa4!zK8;vbX*<7Ki=sF`6OH>4 zpqEcSi(o1FpnLb!Rnis6;%IefPNc*pMR1sZrMRTxqsBHz<=(?qxb(sMT*s}@#DpKd ztyDR=-S3HMpr}J7O5H2(ytB)_3to4Ihu#mm&yUR--QHttVb<1{o;l$BjzwJp;-|!s znVsYMaBg?d>a*+S0?ZY_10bSHDcUL(oBqxw>>s805Ip<==O|sYuq2QtneL$t&0Aqm4n@Rfcdjp9 zM4 z?7>JcbK-1u*UocCDy>y{uiLY;#*M(Y!KoJOpi$%Yd!1I;cp>hP9eV!t2}3Lw3TB)J zeO>IhX@+~bkvJ4E3n^88zez7nA8EzEJlj>4w0b9Cc_c`GU)D{(Dlt4=8{97q?oOWE zE1F-feyh#+3fH>I?QV+{L3*UC_&&B~CoIQYiLWF-b?KrPOXdeX^SDAtx=SntF9(^( zrZ_BH?Rh#eh_e)Xd+tSdiyEqTX+yzf$%l^u4bP;s>-x9$8wi`J)F@L6l{GsxFZ*rp zyhiVDni2DHUy_CBDZ7~u6UUd|1;h{jL}>TmCQew%VJ9OjF55Ri63n=9EW)jhp*2Y8 ziaTDlg4_^64d|yezz8k$t%mL*oZf44r*YOIi5Xkv0oUAE;Ev3xB82%7HXFgyz8S#s zu|eJ*VKQY4r%s7!lPCAKh@F6hIPek37#&XS4e2&!hqlEW!wOMU=w9IRQLB5pc+KYA zxap046@jgsN}k4nslXnSuIG9^gFCf-UJzM5Mo0yA%P?Xj7cZs!b9jh;usx}Y-@o(Y z!*gnMAl>S*_Y_Q41oC?EXv=g*a(4vo7h}a(HN~LmW=Wp7BRvH z0ABUp5UZ;}rtsb(nj%#BUCW!wSA)_yvPK7qAEZ*soS3+^xdZO6SF3pPE~aQhb6lko zDSk#*9wg%($&;+U10}x3dx-o%tI!D1u!s>~|DDXm4Plfb-rcrBbixh0&OvZjAzS*6 z@*1h}Vy$Up60 zwiQ2j{Q+kyr{gYp?r0fL<6i{nOI#=@<)JZ+C@Aa2YY0HFOhEt%Ghb5R{|&Ulu@jqQ!m|ci6}*rz2y{5hQXoNFDvrY8e+SL z1JZ)Lv9DCEa;lb?0R98id(;3^LQQ9M+)!_{gNNhZ7wgDSi(D&Ck9dzci74AtE*q~i zGJ2e?G&w}={=gRx6^f9?;xqHk!GKPr@tE;BY7jR4f}++d@%=dST6@`79Kz-wD~6qm zF)rmaiZvnh!&%@So~Y?~O&SWyne`lUbusA4Y@ch=`bGTD6zvV3pp~Ebu#RL@;F872 z?ykL8mMx~+_(Jqi=BihV?+1Q|SApxwHx0I0jlPn?)x5>#g}QrWhR8vHf$3WsS;%8CgDmgNGVG^|w>^~*7)Z*}3WTV!3)Bx^@&TRbKg8*jrWQv1UFAbY6gH^y1$ zecGRTowJi^MIO;Yp9C-Rtx~tsKputrxi$}GKTOR+E4RYa3x8h8&^JxGFo}D}` zrp=iHUR1Z@Y(~X>f~AOQRL7&Q@5l+u9(~0jyLq3aZ$7LOSc9W^SuS1@YWyVu9KMdD z)b|GV*td=9#aBzk>yvASa6XCy4ZBqcqUJ>P+nbs_A?y7X6IMI+lntAkDFzj9=sWhs z(>=;a9a;&jJROEEF7)D|N(0fw&8f;{#avlj05ZI1uY(Wm^VN`!6g*4qfvGmp22=+` z5SThCXhaSaQ8I}Ss=3vU548}3^RRW6uBwR*gULwd*OtHfAO2-%o z{Yo+To+;sXruYs-7!Ppm9DAN1%N-P2e1}{|-ZnWnQRHr-}Z4!4?A4bTK?)8$46V0dR(A0B1n4TrHw}zNxqP(25fZ z1E1sJQhYmmanhSoxW#YFt zagi05PRn*!i@MQke5`4)!SGyhY)*`$%72Cq&aA*Cw`w^>J6_u1D<}SE$%&Y5!JQ5< zxMR0pA>oidz^x%tby$RYH&f_!M5U*#D{7rF6eHGc);Cy*+Kz0F-{w3 zI7E8D0rdAYb{qaBp(7(vyp{Q)a&Mv*~dO*>oT0_ws1))nSn}C$tUi zhAvw@zeoHK_d|IiCa0oS+-os^={hYT03+ZOy82n!q}_}a-%Ksl{+-6S97W-cQCC8c z<2KG9gfChd=RMlIYLkxF2Sht z{zfw?oawEw(fnhU!pbdUFB1C{ko| z9NL*gf7mUAd_y*Yol>k}ME%WXbm*+X#7;#A4GhTt=u|B&5@d@as zvZ(xnsC{4YX{bwatB04;t3`F7>ZP}$dAj8`Yq&LC94*v4Uy2WJi0=>8MArJ7k7}Ba zl8BF7m68oGycdDDiQaOx@u7T&$XXSmVAvfrpCiY_gT?xgSCocc1)bqalPL~3Vo2+? zXp4a(Z=;%j{ zFN3TwLdMbiWoAd-jgY?9dABsrsw2L@9!5UI3WL1UB10(oIHxqVotLT_!rIdjM?fEP4{K5yOJ>4V zXHQ4$tHsY0pt~vayy_+d;6|LELvqpEOz21CO$fdOq|5s9jKiH`e1YPirk^Eybd6hL ztQ4zCw3Q8?eb`^z{9ZnkX9{r?LnwMSXC+AeerJZG4PY@g^BUwj7r&=)BA0DwjW5x^ zDW1FdLb?%$F-P>{goIErJ*Y*VM^pFJpq$4adL>ts_Fh*rhy6_$BHrW}nHLj*oOT50 zSerU6kC+4zDWv;eraUzt4tqe(%lX!Um(0aQ`Bjv<^P*?Utg>@vubT&t2W9HSA9B zVAdVcjctkaRvQ$q(rk157@+9f1sI+b=N-+bn$@qwZ)^T3S9Z8)n>5&fQwGh7Tq7J7 z`cnKCWG1!nZAZ9`S0#GJG;i_AU$UDM)hjjt118WXRT!57IM8UxENxa{+0t&)cC89! zn@&gcTR~c~1~Hc^Y`SS9x-l+7;ZAb@=j(Up?e!jtzs!`<%>Lw=`}1ZBLz|(4h{ zjk3u5@Xq={nxo~Sw2&=EHA7fC1>!?V5m>7iJfAmu{<_YN+*cVwQvyR2rD(plWD!Hr z0O;$yNAgIa)!Why*y3){tS07Maj&6Ab~9XDF>x{coj}QJ&6+--bdW8Oc20|=prXR7 z-~=$Y5)5pz4cOZp3GOcoTXo#^x6JL%+}hMWt`d10%MJor4~&7lyKqz^^AGb!$r8bW zBr8kS1Ew+)&V%iWXwIfaw9-~)H6YgX^=1^rErfND{A1Uto)YJ(#W&UG?|lEEL5-ex zq-36rNGqIwMg!h%QE04O9c^vJW6n_S42Y?7ahgJum4BN5jP_7rh#;WI@H_n0i8`# z7&tbxd{$A-jf!#WMha|R)*nSl)U3VgHd_cvt|gTWoI9RSqcILiz{^@llQ}^kMP@*! z>%*~JJ>?*J(P2zh+8>b<=Z^n^;DUO}1MhPceJd2!(>6RsU?lfkWk} z-C4LBhgVT*GS@my(5zLH+F0VnbHH{nhD>*r)&_%lw|8$#LL$0|WH2S)%ZA)&yj{I;%F`_k1y8j? znid#t(8H=XPkw4%evPoBYS;2e1N>w0P|8!2c$}BU(&V<#H|W8CQ^qOmFY0+Fuduxp za+lNH4e{$&ENWJb9p(tXch}drTh%(@y0T&#zaxdLEf)G=%zoF&q+Pv6g%<92qeIIw zY>gorp)p;!V>yr&!f;Wk31@mh&I${bve!yGlOxJh+qhTNORmSyUY9|t&#w8tBQ^0G z<&q1v$UTFOondc{;SZ)2p(~D1ni@;k8Be0c$#XlC^VAI~nrUrmkV9A|uLiO;2OL9p zaD+cdQ|u$I^$pNs`$IxE*W9oe|6`zmw}52f5#jLd4k~Uc=f7_3D^Z~Fhvnn^llZ#S zW~d%oe4SE>eUb3i%|oe11)S0VMSI!AXAGzoZw2v7%2iDSk;wR;A7-z|`E@GNf1Q8F z^Txv1qZppw;rz0nWkO9eei0`0Rq;@H4V?4btoMMlyczgNTJNo|V_h2v*JP-aVH38l zqjr*WpopcjWXd(jvh{RJ1}|9~9x+I69BAoJ7r0@5pxJY}FT)BcrIsLjs%(^71oTHO z6NI4nr~yxBUvQ;qas~rgdlNE~9)iD+UZL`cdS2F4qWRamJk+Ehl*&#!*JX=2UmVx0 z=p%u0y|1D$h`Sr0_4?}N(^n|tKv`%j2Fv=XGtkqx_lk zt$y#N6L5-$lRFUI#Yuo$7Wztz(s(=5UyKlWmXH6t=km}AbM*PI8!3(pa>y%;UKnC> zBUFi7(?-4bE;R8lKQ7SE&h$y;DK74K>=E`VA$FiS6guVz&?vfrrNe_XTugRqE_osO ztZnvZ4HYu_!Fd7Ip#{r$vA?RZ{t~Hh#A*Xi) zq1pr%5*=uyePQj3v9YZfLFWIC!r?Ie7k=LFnm5c+$hCCE={R!}WU<=R1MAI`Y*Xq@ zUl6&>qbV4i25k^%KjD9C?66-ts9!)vN7j4H?OsykydWlBv$jq#Q-=zp_NHGBz=*C{ zAkaUrfA?0bp18PpH?ASTfBAT=2aC_P?3T-th#Dwi3iQHGouEms1LsQPVxPB1ECmHr zDj*VKsW&07(LC$o`!|Pwyeajs>dragY;w#NCvAGH5*tmK1BiXbZ+$L>v$!w^=2sD9 zjGig1T&mJ*$B=5}qiqx;1@%oGPIJqZ-oEiQXCtm(I)0!*qw{%$*Oagxmcv=->jRcU za8aij(XX zun>bp49gWDMJ}BK;Jx;N2k8Q#XZOy?{euyquf0i_vOj0XfDwWC<4ia4mGm@uz1;X@ zV}{;4r!nIkeFfm*5akyOS)9-r7HURLTqHjFiOJ#(ugansJ1um?D@vU( zs#2FCrtQ%L5MpfqJyeVnmK++|~kKXaAwV&O@H-V?Ys)ZD{}x8m!yYjkw;8M+5t+T0Xt^Nvl&G->M003Ak_Cn>rp;q%=i= zq@+o_sY%WmXb~yKZ3mPd0Y%mQo^(h&R_BcScLu&q8`U`#t-X;`sJ)nxwBDV4aYv+{ z<3GS3kOd4q4jzoVQWrjAI8Ph}G=&Oxdkb~e>q5j5eF(AG&q>VG97lz+4f+NaWS-Y|BBHU z^ykSA?u(?q;N>y~hwK5hk^79FNrx)58UNJONI{*&oB@>S2t}s<`q+ro52+M_sO^Z(ig=7B(`M{nM=$dFw z1neY-O7FxeSuKihatTTTMce82NlZ|Y9qb;cs_DJ1NxQnUtAm%cnwvhw&Ovt^l=HId z84MnghNsg*{C*qzcM zkI@?XC;{&^8McgO9ei*!r4g-K>sZ#Rqrw1AiO3u_bsLPM0vxwF{#120E+JIw)zP8c zFVyo6PnOdxV2{hV8gK@g?{|Am&4c^QgJoWkQ56VJE)D5^>C=T#bk($mvWNcaPus9dv2%aqj+;I|UMC+tqF*>q1{yv6*aA0VlD`oJqDGdhACj{ym-M|grwNO9p+vP~VVxrl7{ z5|KMUkC&4bE}RV9Fv9b|{Yse_f;d=V|+EFAoz+C*<7$tj+UkC4gW`b?;X{2*7kd& zj5>l?K}Qg<(*%?j=_=Bs*U(Xs4nayNfq-QI%TNUAy_ba02|bDhB|w0XAcP_!B?(1= zP(nL9%*=h?&vWkezR$bPI`3KQtcQPQ%`)YeYwvySy|429xQyuc?(>p{e-ZdIJa1Ke z?wli{UwGLlCiXnXTR*f*kn4~pM~rFa`BX^x6)TOD8{NG>OGi25Nb$n?#hLi@r!S%# zVx?NT7M~~>tshoei@o@VXk36ts$z;GF3NGb@w`x6TP5t?i((lbGX=M8;T!EgT)Dc9Ue&|SkIXu^XjX9z5gd7JQ7(!vR%}7Q z<4u!Oe!(Sz~7`l~^g$@efW`z;M-Yi`t%R#(qDu3b#xyFN;)1@aXW`M-3su~n z_QiW>GfO2Dk3!uPzKhi6?2mq0b`H(DBMKZp{m}Am&*3&L z%tH{dg>)KfvDw@yOUAd^`QY3E*9RF2avkX>kd7-sgLie?)1xg{gLp_@rxlzSDR|;& zdq(-pZ-?fZa+#*Kopgt?PK5#*V=voLY2cm{TE4iLkM6UYIe*Pd#nox_ikXaYhg~2f zcaW1*)qhspEtcCh(A+4}j1_@~rxxgHmCOCCce8~{`!z`nD%QFdg9KqF*>O&&;H9?mi>kmeW0>1(;;Yxo_;aMp)6&h zZ9srgos|W37 zWhsVcPxUE*LsVxTvOAcF32=+>o@=>{T}^8Qlc~M7VU}e3WVIT?Erx9jA|^nN0YCt7Ps@=m%dioH*gLq5%3p%dYCMW3S z?w0Quy)UDwDs%E6s6cq}FV2eNx5cS=aN9F>yr7=i@Po(pJ5??cQFoJzq_OuH?&hh| zfn3hfQhJ4DjwdVWbKD9zLK3(ov}A*t9MgC0FJLA{$@$a2$=+#?-Wv_7K#cBv%FQ-u zFTi>7m%J+2Pp)jw>M_mxa%6#)qd=Cw$ru(6uru`fj2I~QjaxD6-RA0T?XOEZ*j9~2`;ew%d7$(t;RbzZ3W9~xm_^0l-GTlqmQ;eyf(dKIKpj< z`h7}#_lWjQ#RE%d2!~yf8R&Z?sRYzC(piXV2SuikDaM zW134w;|!N%9vD3Yu0^OtWhCQh4H;(&fl-L zVa3%9YO=`v-p5Ge_1@UY`KJisPrUcA)>yfOkrl<+Mz~$+^b)1h+vql<=%^TXor0Bt zbXL45!F_2W@CX=e|BN9agp;XDn2WtkH32?LUP^Iw!WFrWSP!YnaBrpM5{fX&n~xx- zJE?Zl2_QXR8x_Qm2bam-R^t?S^peO8vcKr<{b0-bosE+}PJ%WZj^&MZbvq;0EXo)7 zI>-8I#85<|b$Onb0+E^O`D-2$L?GYJP>*VJU!+>^bjT%7hZ|ndx#!VopN5IZ1h
    O->+s!{%~;`LY3VqJIPGgPd$ZL`{yT2(kXP_1Q_ekaEN_SVjY-s{jomu8P!OKU;5>+E|)&oi>SkjzqtswuxmVJ;TWBJAdMtz6R z2z*DTDBbV=k01&6!LRghZ`7VJV#h}UP{?FS*6Tx#9lg8f#)*~fpr2|3>eyqM!7G+i zKubqAf`3G`IC7E3x5<4sd=@8&J;LhMSi28e4IW*dF@KxZKAIR34-M{v_vP%uPiu&#a#j4Z_VT|wCw9l=f1z`tHXOnDM^nuIwo+Em?Q;3=dfESf z$L!WJbD8h%ROj}m=8bL&P*byQ^q19Ho%6CouB&ex-ZkHDD72><>Qy~7K(0~y*sMTR zU%GSF!ZNhyg;T?0v7vJgnIW?|eLQqtBGGaY_7ST z;2}&JvkB=IjQ1Q1q4Rfr9Y-bOB~=KzSqg*tMmjj?U3jm2SjEC0>nqRnI-#wtiSir$ z6LU@1UBYH7S0%eT14doNL1EZdX%-)*rB63xN<{JU7#1>=-+%3=`ZpD?s8DjxHKZe> zw)5M|RnW$J4G0UE6KX1ZoRL$V0i7pelw8JGi z1s(>QQ*m?2UEb~Ch%%F5Ix7TF??25{z5JLI z6Sj@InB6$_LPv_7H(V`vehWqZadUitvC}M78j*&0KIh7gmaleBGl@}ci!O;qWl2#& zzEt8Q;kqHOgnyVi;CczXizRjrTt@zbOU~L4gW6sl_!97emLhkOi55e*&gS~=cGup0 zyW8DxSzaRfbM95A1$%y{NGqA4Vt@J-K64i)RqjpdRlttM&)%dD$`xJ$(7I3v8Vf z)fD&T>WC19(feFRw1Wk#=f5_f3_hVp07QzuMrtGE;t>ZkmLg9`1uJfC4nz*_NIu96 zTz=cBQH$V3y5}u?dFXCNEYN`}e*98F7ipTcc1Q59)0f|IFvx>Jt68Ua4SmQ;&FC{xm6?nM4S|UC!xGN<(Iv%A*xAHa`3EAHEau?=j?;T!j4C=}-^#k}JZyXQSv+|Nn@hP} zt7?i>!NPDiOT4Xcd-0x{JHnPlbx;Os9SAxg%eJNlS~^M{K8$rM!I0Z4PW&fwf4UjmEvq@0c5)$SKYxo^cOtd2L$`Y6=(1AlqVt z2dom6u8+eMVSkJi?vb6SJ>!|TW9iK(a?A zLZW-w>fCqfiNxu;fiYFg@PK`A!TNSFf1IP1`TDXVHUvAi=`B60l;dzQMy#C#wTbJG z4?BeN5DiPEsS`^NqCY39=}YRR@^iFr8WfU%c>QlIfD6$lO_iz%dPaE(q_o2X4k`)VmOCRWNeY~)KL zmp2uP=+YCuj^;7<0$@IPVrxKqmf~fw03WSKv7z>Ee`qb8gZRV zA{>f1TF=|gp;Ty)mVOA;yVS5nP+qp#gg5Sw;8nG}sZOqerSr;Kwep593~3}p@R-lE z@V2PzyT#n<3>eeqy^ZnUnB8RX^uCJ=yUVwXFn^txv%e%U2VRSpCK-QZ*Omagn26EgZG~fdS={b(fsmhp~WMc+>vuJa0E) zNoTq>ymYbLlkYtYS<1O(2QNo{BNsU;V!Y?QoCV4VV}zjg-&O4h8pOKiv|Af(S>!qP zu!iWp`;|9zD6xmxYxt3K3$-IdDqYqDggPaUs)BVE+3NQht`Q|z(L)tam%|&FCj4R^ zzU|LYs-%*_6RVco3AT9nhWYptUIr{w9Gb-w6%ZAojNW;CFo5FV=|f*(ZfH)D{eU8) ztnXCFYcv$!>nCbIQ$739l~SV*jf7EZl{^i!%;lNO{CC-N%x1kPqx0ykh9!aE!DlU( z9zZS4P9|7qq3}B)Fk?yP8Pu6l=&I#$==PQER%nXp$(^O7(EXWWFAKe=)u8?f`0{sX zWO}W=DQq>xw;lCBkDeuFx?@#@;>>{XWGlcz^@`?9Pup15N+`e6pD;6+zkUMO%xpmk z&ILeEi9V$%(5rJ0%LE9*JbMb7QY+!TBqkGfchlBimlNv5Kd@qacHJE6nOCPnDVZRs z{)frJD~9-(z#B}x>O|>n65`^O#gHCpL+bnMncaofsn%Y+CPYK;C{J;UY~v=p^zk?( zw2v!ex#rtPk>46S6W@BhcVg6g6E_@>mO-N~%ifS};l&2Yl+iSz97s{tbu(!zrb3~o zd1hP@vL9tya30X4Zx97ialD^{-mqNpwGakRSe=7avCUA#vRu=CJ4D*Zww9zOT^}MW zs~>f1hwtQwzc#|Y0nU8;#65k3lA=lXg%{6>DqIe|OSg$K52MHs$`31&$?Lbty z?1w|Bo{7sf3IYL@49QZ?o!|T!3xzz8FvMUf1o^ahuLv&M#&XlMN2PqVdVHXTX4GOl zhMdkIJHD8L+Lmr)tA^R7fbxZ(Nuy(63wX168}y_ve`t08y2hm|E77lIqNXwSOET%J zXdDc^MmK$WRkHYKm{o$o@bxY_#N$Ik*O5!yzqw{s?>r3AbI`YOxb8lp0?DvHtQm#Q z+MzwfV@OWCo4wp#^7UAYc6S)5^^5Pf*+Hd@QBfk&kP z_o^@Bz@|f+IBr&9Wct%i$dr-%ZERhEDwdfcSmR8}FlcYyS?3miH%-$AD*_J9`ieUX}ji}I{i+VimCowJ6fTvXnuHcYf)Aa$Cm{6 z;7(wP2_)Nttu?mwOF+~m+)pZRhJ@}Xw_oHre7PS{$)}MU>9>AWX8-7gu7|tvyZ!Ex z>gRQ&U60-&WI1sLDz_8hcBP~eHG0ogG_UP)81o9|?Q5Q2pKr9d8y1WemE0_|yB?aS zJGo5%yo!1kQ=8MKS$8v}013r=J{`O3PhU-1c(5hmKEj)1CTY3!5*55t|8Sy@=+w*X zICDZ}WKVFzOALGu4BM6k~)F7>L^<; zlf`SrfeLZUFV4xy!c=}EQ;E_B%Xg<_AZ21%52Ac+1~&s0?!$#4Qik3Ly;?}zQL>29ld#E>jfp%lKe!0f7M#X;g+PPA%feLv4 zr&Ejw?Q#R`+&cDLgPkGuhLjg29YIFTX%CdtEBvHuS4Gp&uB1268@W^@Ehp~=KFgk% zpG3tEjW+w}Fas>{1m0=xEeSpPah!)fGXRr$Yj`==agB037g zxa(BwASBER8`>*)ewi&u`Dv$7xxz?Ck;6rI+wD!`n(``|K7-iltOozOS59+@-m+Ib zY`a-X$B0GHc(fi4_HfHn8yN3$O2}v}*R$ohtCn06;%fLlUn7C^htR$s58vKstjwj# zwD6iXUUFJ03((>{)$voqRaaNuGz!nEYx6y(i| zj*_vGQz?jhugA#?l4GBl8HrhT*pKUsnFpZ5d46l6ElS5AO`Y!B{yspCa^6ytd~H0j z2y!fO0!wChM?A#>pAB3uV8=@nWM&@zcH}rnxNoDqTM-@N)G)df8Dp6>Z%b)P zUSmzJBb6t=6|Umv6HAun#p?cGPCRN88ks6W&iN7>8BGdgXYCe!=OLfQ$&+RHu7&!^ z2XiRDkp?^G`Rbf7h|QP8+D)fv)>!?^LWWnC3kXQm^kE}Q-sVNbyBj``ebRr{XyO9@ zuf@)Z0^jHm^FDgN42@BMtJ=W7UapCg?e=zD3M-mMFlGi(wVIX}hJD~V24v5hn55KT zSS+XRrq6ZE<~PV2(?~E6ZJu`RdvFeh2wT@DFI17sye%gQsrEMf>l-~RbAGV5@4Yro z&N4fN`0C3vzt&r(g$Rw0c12QBLHF`d5^RvV{$UF<`}YE-&iS;5qu8|#=!L!h^*08+ zaZ9XUi&VNiMC zP8GRm-A^nY(L0l@OOQ;}z;~zF<8%l~SUXRHM*R<@=eskrsEFiUFwvt*K7)Lj1{-Hg z6RZjeo+c^(ZhbRtM1J%?9U^8K@N05lLCmcr74EicV8-jwRf+2-peMZv^(L0X&c>!E z`H9;I;)caAbJLLq&gy0g`8o#l#H7O&ezUXp2FgZ*bKu&&t#<}8tTh@#ho#=zhay4; z=n($94b0a1O-dBS`!EESrVOCezFhe8>N~6#jGguEC2LFFG(kZXSW?L*Dd7HOfHSt;`X8+)|CsueFWXCFuc{baK6) z@*VQ#H(I-@#I7f;B2UwLR4;FOH`FLnP^qV zZW&B-oB};1&T|f7DH8c6SIuS0x*Ir^gEyO?vG{TGOXPsXan;{mq4y^h zLXRAk6+I(s_gsOP&QZ;N(dX5dzG4|C!hznkzQ6FC$BJ?lD z&#iK`{>vR-@vaEh|Nc)cqx+5B@|_d({0_||n~xL(7hMmcO!Zg)hsh4y zu*01!*?m>fm|p zb{H|bQMBHH(U~Mrprv6u*N=L-H-`GzZNAu=ElkRi#ci-hn8*MN7Zm?(U!HT|mT|V< z{_B>8m9EWcTXxJbD~)LhTfR{ZWk|a~+tjQ{3|5t1#Bk^9Z@LxrMe_VUW7@q7U9)&Z zS2?3lq|?x#Jz3R#DgMc!@R`sG3R+{OMFrJ3Cyi7bZ_ZYT4T5L75w`uM&hH1(WO3#k21XT|+*p4`a_K%$(oTG`_S)Tv9XNYDq7c&%ycMr7`jUEdKS6XZh#L ztX+P7`0pvhTGap5g;~9a{OupQB`5!1fOvCXW7Q5{!!b$aow+Y1+{QkP1G0*2jPgn{ zOPRKlh4X4>eL3iwKLlD0hY<9#F2ET`{^fR7*mi@ZOHTOgm%tF96QDgf8aTmK-FHV! z9jdbXhDTSx8{`49qjRP=kHtR&6s8oQaM$~%j3e87Rygs3#vfM)Y<%@1slEB5_kdD@ za^>!uS@}}4$AB?InPG9yZjx^v;C~=d|8?(Q$&Iu6Km>9KsDN}G6iUAvsc>2Ol>75b ziCS&w^DK2c0P{+>^r-yMDOTQmUuFIK2^P5_`G7#+o8gD=xcpgML*ONMDQ1xZh~jr+ z5zaA>zs|*=%3B*xLtyDZ)*lEw4qU12H~w4o2FW6Q(fUgY-3KuK#akgvYSEOAJu?pQ zij<2=8$G>&KE3{v`t$fJ)qe!hN{RsR3^W6I){@Mf;`QVz)joEn%8Csp#;7nq4Ff9B z)O^#zU!=Gjhk!0&|0n_HgIx(nXvt3G$LmW05yS=1JC1Z1e|JO`FgQePh~m%qdjX6q zeV8}Jf2j+S#vd^Jmo5LqklFoW(Gt9>kGSh zz*4+cG(}xFU~xi7Uk1rst^}IRxNSr=UZUyCxn0W~We2+4wE>S9Q)ynQfFShSDRXL( zKrSY-1bTB1S#bNu0bju08Ul!m$zLoQ6O8s)Yc2y3;lCkffTeO;*6sCQ@?6)>0zLKM zeHI*v@8`4fR};uNKkg`(J9VeKhM8mlE3bZr96EXW`oruX@R@&F4WK?iuP{)9NcreiPEW4LT256T2&YptPMVSb6Ds9>r;O6N*Z1% zp`5S}9N*`Q7sn>^0h&<>D|N%hW~j=tKJ-z+B&MQ`qwMYTC)`mcYsQnznr5&#N6vV@ zD>yHTzYwEzKhh1k`Lo@LT9Bll`I+(UrDUpvZR3~9!5R#p!JR!A00g1c6rCi3&s-*~ zbmZQc?bDI0Rl6tc)OnSK83O93@YUHtA^;azJH1jrS?SP0Z04ZW9H|NcYTmK{Iig2S z!+O;m8rcqde@rtO00YLNcZY(_5y)>+M*_xb|HP8H!@Nu+3xWR1AT0yHaHeMRV&v5O-D!^x2vn$%481PyyPxJ+mx3JlHtb7~rFh|x zX;-zBv-;WZ>@j@h1(wQG1RtzI&9@<_|EcbtY{c_HhtX&e1AGtD?6AGv*OR%E)RP~9 z#Iq1%_uJWCu*67}frR=-$3jM4h1!^ZvA1Q*%T!raAo&`*^i**3%`k9PE4Dg@1x#J9 zJcLSyPlw3V`x6GB$X+k~`n84(BgS_Zeng3W?J(eq$p^_LsOp_gwV(7Y)XzvAmats# zxjosdGj0ZyV`q~;mZ>fVJ8c~st5y4G6G&d@sws(|Gpu8eR(hM*fC`$NJk?cjD$}w3 zx9Uv}u@u|Y&4vc|Veu&p#jDPN=KKC<7}_$^Ysu4J%H9^~x%58oNNSiZGMvBqt}(xi zRhZe(^rH)U1iO|l_K5{42d}jveiPtLZ4teLQwd3S6%JJm)M&-cq?M6oEInqmatD&q zegW9wQ8e)9+8j2e`I+W)OcTuAA~##@p7wNi#$1!f#K$NdSiA4ivs*a{=2j)9Ea+Jn z%O&N6(B8gg974;4>(Oo&P`>-QXLOWH6?<^R_Q}{03Vm=y@kgnuojBzrR*!5rR5Up0yRF4ZGA?3hkQMtG2oUwwY@O({H!bbGz&!&s;`- zPP7Xx3|KfseSf!6NG>Cs1e4( zcD6bV+l_AISuceA2}dJm-+bDc7o*}9StWw@zV`=C-!mS@V|oO0g}DV7U9LdI772iT z$1Z3jyJH#(olfUc`+Y^r1?mQ!_OFZmD2RLz zX5zkKy8@g)*VUJ+vt}o**J?hh{k*D~QafO+DnfiBVE3Gs-OEIEoTNm{sYRAMZNs~G zv}1WVQfZUX)jd8Vet(`YN?F`JQT8j!M?)qPp!#h`jeh(D8^LAe@V=Z#$p|*Fnm@$r zzO>o+e7@var$!OpUTW!oDx#e6B&)hx0T0+2Mk}_~G@gQK(}ETtSgsv7MV<)WVhqF^ zip&9yz@3gX^>Ol#@pk|+Cz^)v6gh;qig8h`&-CM6p;}rEL8Bf%ep?2yb8f93GzT>c z>uEuouZFCZ;npcINnhgHV-wPzM1*b0D#@3i7dPz zLQgAD5iup0WIwFXZ8@FBckyOM-IA?hd7G5cLUk8*RAkUx^})e19sjMLgf3P&0^q#~ zm+0sz{-g?Z?bvQWHmu{XK=C~bSru?p($HV!@Jp zaw0js`|(x(cjVd`(J~SWZx|{! zRsqF$MNe{Gm536N`NCzRMt_b{Ge6Sg9KAbN!{-7SLB*$mAd6;#|9CoQ|0#I173^o3Rt_GriE1aUtAKt|bvVlpAraA%6h)ac=&SW*6u zC7XaRC3h-4=moN<$3c+q?sC<+7hLzZv7OT%UvesPI~#9PvPbW8Kx1=_Z@Ap{XI6bhR#8akk)#xpJ`B)fQxHWzxOa`|#=QS65Ekgk=17ZYmJH}0l zYuh-)F2j#40%mKfbMhM+6oj9jddgMEu$x6P(Zh$F|wy4BckA1^|Dzg&2Kp zDpJ+;;NxxY!4Fe|JK+MxgvJ`K(ky9x`NG;^j;;PCmv7Prq}OlD7QVc*+RNL!8M@`^ z%6{?mM7T62v$|uu+}*=)I>D$$mxQ7isMn3b4WxS_PA#ShdP;z)r50@nB>!#*vo@3SrUx(zCp_Y~p45G*n>_6ts{A6Yn-sH>ax4G9 ze2a7n!HAz~q5L7j19N7`X`j1-{>b&NawI0x5~DKG`jc)v+_;7Cov_)ZqWjr^<%<}Q zt3PSw0-zVy_x(n*tl1^ixq}kR&r9Vp65m%S)&joqkdOxxXATDbeK@P2Hu*SjJ`iW>jrvf}wH#xviQ0z~no#W4bPJ`CW zHFgQl2=yaA4;Pd_{4lINt+vh5Dx38$LdeCKiJeV7W|w7I(Y!*NV*G zd3msv(J$_KmRR7mm?q=SIAx5s=?z#_E>2)!SXv4=HG&Kb(nEE5nyNs%rq5bDwk>_Kte-?A3i;;jBr*o zr9T%EFc2`#QlIQ?<@}sB50GA5-|^55Bdan|hX3 znKMCpG*VvV2eH@MtDQf&YHz+M>=h3}?2m6d;Y~SUCe_qGq3dsBGBob2-tgTRIl^W;_;MvG zk7dd>9o`({_jL(vjPR4n^4-~56~b;M zSbhZ1623zfHE6W;uW+4_fz}ncqN%>!uOO6~WaZ`xFv-fPPF)DXVUlq@cDWB3H>$fx z2(wEI@y#({WM}ISJz55XrftF1Gs&(a)G&nS7FKae8`6Vo{5a(F($n9K zONN_c?cUSTjkj8Mh9-%L(_PXjr#iAym(I~$W2daC1JC}dIk{S7 zGML6y!968x*fDY75oR{DI6>3#1U3{>4y)~NMy!P(?4PA|d&WP}BIWHB8;O=5+3!4b zfP7*uIY)E`MQr}AAGvI1Y}qNyvd|c*K?^XM167ZE?Mr-Zxw%lSO@kP zZG4KlXpo`Mb%;;y%IO_|-Z;&$S#nJSc0@K|hCaf}2%e8*N953y{T2XJ5x+}aF)Ra% zzB|I%S4~cn4iZ}iSs$N&+<5m52faGbw0eBS7TL$;$M7Fqc#{|RMgtow(&>Xc5&lc) z)womruM_Mt>nlW%ipgT`3TqD?f8a$h0k8I+>Ovjp&hlQowgT}jVCU2%CfP#Ca`n_c z#=(2h(~H)Z)^N?8!x zM#QFnF89f@LVXz_3A>Mg#)RySiyZ4 zY*I zBn}z~28PD^g;0+%l=cX8b7{~#wP@&f73%(0PMfMHPo?_szT7s4_*>nM2i8}-PauW! z>64dYZ{JJLbj`o2wNxUMBKYii+ac|8TqfMnQ_ZgBfjD%jiE5+uUDAFy5Ry?m3vim4 zE1~=w!*z|;`>oj}73a+S1+ox6RDYL%;KGi}uq~J^tf{~H4mPmVIc1F44LTXM@kUy+ zw`AHG#=iIyp4IlM(TV3US0uhHPxC>3HXUK9AU6PUoJT z0CX3yIi36CR>m&)WmJ*lj!aqTi_8Ogs~l=)_DOTR`<^zoS=hj&p{$Dx6jyDO+381t z@mtVjAK=GbXeqt)$@Ybk}$5gG9fkjJ>iWL&+D}kpTUlTiA=e@2;K@%oDFbBUB4fk*yYM?F(6kBlz#q>gz6HQeV&gk%`F|x zmH_=2u(Ti}etDK&Eerto8*yrwt-?FOsu6*3T1_#3M^R%S!wgBh7qEdLvk+HxBmRmY z!U-+U{49-=DK~&uyg#n<1$hfRYL7$N~0xV>D;=5(JXHEP@M52iB%gILnph%~sF zGIRPK)YjkV_~mtC$!yeq3(IlSlgg5J+KZyuH7VJ4x|k3ICgF(~RKjTReVP4|OVbwO z#J)cei3uATw;&lfJs7Vhr6ALq#y=%#K$#tgiI;fkvH<}l!TH;_DG%{nJG!m>M#KNn zuXk`(^t+h$t8YBmyWBG=0V8Kx_JgC60+3})J#7ti#v@`pXS|5z@ zm>38r+$5A#sXnm_a*U%6R?i^>5i{qceZGJ6FD?u4`0X`Dv&+>?(@~JotAHJ@U3Uo&j!UnVR7Lq&2)BVy z_Im)1Mbu-3rwp%^1%pV^RqU`7xs@}RN!>}_!#))HG!4nc zq(89b0SMVcPT7ZeFWNn3@t=ozE&cR>y;kDx$vPW##7OCN#snxkKLJBj1W`9q^3ZHV@zKV==F?5m?y= z_V&0MkA4FoPZ87saalCzxa%D>=t-34hbkc-wv9gh*gr- zVq6Vb`|%)a%BZJcou!+(9NglEp8yw?=ltxQiio<`Fz=9W;|HX0A9nk_?%(>)*tI_P zUiION1SW6^B%){S}TKaumab3BOsi5=;TznE*q0r9l6-vPo-j-^7a z=ILr_rLj7%OvAiGZzUD5auzrVfcAa>v{ob&HkOG75ZroK?{04%+xH6qDBhW}FtjtE z@HbrTcaix50Zmp&9^tYEeqHMCr*aR(5I`w`>9e@$Itol$alYPxE>+MfU@;AhCg0zR z#}2s-Vvo9odZyJ?&dRw6v^2u$u;r+ijxOuKLpm)=<7Wq}S$F$6LnI)dW+j6NEWz+Co@AZ8GOl0*iV%daSirSKBf13z$s_RqPPY>5El;(NP4L4l!NL3EjuPPHY zkY3D$7C1lS;*%Rb{NW~i9ziMzo$gGD-~4mTSj^%+u&gqEImtg*Qk3ZyEk9+U$Xy;O z82Sla|Luh**rC;s8!LLZ{$vtX+&%xsEe^30vW~X}soC051U+SEbJ|sr`#`v{xIF2W zwgG7B0L06x1&XmjHGno!f@WE@%WP~FH>&oSA4%QT3;g={z#Zi)%H|v|tuh694ve^6 zcPC#_d~ifR?S4rJM+x@>$8|Ec)iE<N8R#61`DR%`se6!N`X|Eh)WQlLB^dezA$wEr8o;`<{J6U=<*xpLfDRb zqQ(8^U;TEUEPLX@o6<+HbNyw%^LH&%iCwYhfReur*ajGl$4896v;+qbOH73RwLPv1 z#6i-4hf$OceBKLYdB(=w<#jzrJgv9TS^)4oHCdM=crs|BXZ>}7&yR( z!2y5&viC3l=A!*qD<=MFCyz&@jKAOA`;Stl{{IUGV_Q`^E(zjk)N+Tp0mtF%Roq!4f2?VepJ|Lj<9zqQ@NR^JF zAcP)jLQ&}n0U-n=p`3-j@4LSrXPmLW{bQeT3cuD^thMI6`*mOU%s)m3+H9;BSV15V zo6bFr2O!YV3J~as|M6qM9m6|8wjfZ#wvNVK(`OmvDdwh&W}mj#8PCMsI}>L#B+T+o z>qA&r_JcEb?uwu|jYNng_lyPLocZ@o=f9LOj(ic;c2)e{CGqD2QFqX&ya(|{XY!gB zRks!7T^IX$Ql}=;Gk8NFD!MKKg&DpsE&&Un{XG(+b!%b$CxOL*zf&Sd#Qt7sM1xNJ zy)wSqcI401o#EraP7nW1$^Jj=u_^}h(En^X>c15CQ+` zF7saZ#b@6c3QP(OwyZ9y?96dee$N`Fh}Zs(v3mHA;L44^&cw&aV9p7x>fP?ySwy;? zg9JaJOt>Obzn-({X}J(ARIjipey4sVUyg#DC_~(H)oDAlgsykh=y*{BqrORkPNa_BbMYa0Z zBzw5K@5+bH6v>beC4tnDv0fo$aK_}xeN+U$T-`K0o$v1lvJs~o%=~5$+bL!ic3;_H zwpj78vEUykAQkF6Et~nWy(zb#FN+WMx1FkfM&2M?{*4;3(!MQHUGzq(ganu&2 zO9)xbk}~Y;vHq32PJIMgKX1M;s4Y;ZzC+tybL9`5yR2kNYCYJ)uvHvOi&+YqA#}gJ zsPLJchcX?sHqj95rLy}=o%jBF0f8PmFqVb@;_GGNTee$=5XE_5pHrcD z_t2Y#S+pRf1fTD1iM@NVzWN2g@)i9XWz|EGE%Y_6Gi3?D~*RvKk zIaF>BvX6MMR17ng%m%K1U6g*=79kKa9=zRX^DnFY`CNg1dfZXn-U5ev$*k;riq*0` zLrvrA#7f35l- z*)JUfj8kI~MqTd|5ZZ1b4j08ETr3joUh_-4^xT6x$>LI7vz!f-*8}{8Ay2|&TbYwW z${x=kLS42def#q=;63T`{TZSA$et&id_GB*4_TDV(r~6?)S>^y337! zC%M#&nCrs(n}2;iv9Lv0yID8opI#P(ZTM?9>gBd#bn_80YKygVqFLN2O|G6L+3=ga zwpj}STN8^ZoCdcCRj|nc9Ab7u;}}RNF1K^23qymgPENF>>0{vsd)vKfp5@L9eVMM1 zRG*^!7B~Cgbta|ZQOS^XUCMSV#5v-#Jpxu~^|VWnY$Pt;=-QvPP*yixdG|rcn?#D>SS*@M$TxYfJ;a z9_5b?hu`W=l@^k}8-?baAZ6Lzwm7f;oSB& zo8i<-_1=I{D_3U5(!-2088FlAk5=INx#Y4;fYvo5Mt}B58_FxKRH&yrXx|+SW4JL6 z`lcdMu}>qV@_K)qX74Q=xcB-bPuk?}K9FzabtR{^l9{92`1febDiF$YO;ek=1}$SDV=T(5(2|6hV%Kf?)h)ve@4}j?M%&5L7s_b$5Pj430rN& zYIpn4#)NeGT4$u=8(hG68gJcfLs-9sJxzHlP5C_g<3*>o5|RGuGylAu@IienmklIU7r;Q-?w5Z36jgwJ11 zQ|g?!S&ZQuMbstT?@lRj_iMe?i)~hY^v#HVkQAu8w`ro|bGv!X#BPpUH&g34^LuVb zkyPqrZt2KGFV0kyZNY0LhqLoY_57={1Rk#t^>7KxzoU-2KUQu>mTr9N^&OR-)w(?$ zl!}Ux_Fu8AqVy>dP&=!w2m1xbECXBBmV`@F!9lfbPOtIV9wAo1qMW=1Q=Z2-_`Q%d z`HHI4VG|chZz~kZQ#Gi?n_5y%vq^vI*)*SIGcEQ(Lx^5l2CbXw*j+YUB%rL*%3b@X z%b-M)*fK!Z)9fSg@6VZ6cC{u@wLqzMqIB>|lLvyWUw4=An=u|MH8Ke`=55*>iYljj^sN8hXd%d%;gWwZQZj2ojUv|yLpA=8&L77s${EKT}X|3$J^GR zjJ;6G$H0Tdf3$l`17<#89??I=ZdV=1FJQQ{pa(N8w&^mBc(TF8Nj&Fp|1BKWkK0T&T zonvPF!wJBbjG-+u5|5=Dbv zDo%|Y%1?IpY1t{T@+^}SqUZ+!i9WvVCOIo;RaS{?O33PKJwO!l$a$gp(hQyHzh`5i zylgU>*9jb@eVIyiRzfuAFS4VSSat?LCvL;9d1LiHzXB z?f)%hMj#*$(1`HKiFrCbr9`6filO=t9U0s8bjJlwM${w6w$-_t=$+gB>F68>%~OqMU1Icn7%7qf!0cE~ha&Ly_$;$O+B}wjsy9!k|N8u|d*+TY+aa{Q!_Ut#b|Js6 zQUrKwizYpTcS>qk?4*-tzP7Y%!Fj9uEU?sMq$c#i^|qlV`HI;hJ`M0cGCrX@9=y@H zp@QI;f+@eKwKU%Hme0Yi>11+)<=0wGtl|JEAf~>nRPNC;&mBUEoeZSxK-TMa;`Fg# zk963IVn8#MORLOrpfy)ciYpn+%8`f_mE}Mqs@7(6Ru6I-Y^~n$FUuAlM8u%ziVPR~`s^#v>WB9-r zd3RQ4+O~j0gmpJxj)Q0kbKL67nh6)1an!kf@OCNNTlhH2+5)o2;oHW*j7RNkyg+-F z*N?8$&FuT5#Yb$CH=U<=J$q7a9R%REt+^q=?{`)M>MGTtMSMdHX>JNR)MD&OvjDcj zKS(EiX7bzL*nV|{g~^f?+)~xczVNv!1j4!MRY&2M3to0tk9YM@<71L+3_j4-;keK& z#9Hh!p=wI1ys1sOsO&|3wC@W=R|X?Px;D6mgnNv`azLbL9$Zfk4=&!BgaC69KUQdc zdi(LQCPSwurpYV9>#@s$nb5ItcUJr^cG<6(2{*UiZwmCa=`{9{&s=re`ANjo4m>N4>c+Pll#CH_ z?M$~a$xR~?9E9)pP5tZM=D>wuw62ha9ap4*npBx@xN&*NW$Tz)7N~2^G$I6Xal}cP z!Q2FBYKGr}NC32!CSh7b&V;$y(*#?);)R5^T|H#lLo0XRBo;|do5IIP($VqRF2{RWN@x(%$^RIj#Fsb$K2Xo zP-!@6p%BI$CvV@d4}=AXm|LNR3RgQyeH-}b=kL*JWmd&ae5RAP95Hh?LXj0f@^4!` zLwv4`+35awdxVF(4;_#QXHa0Z<0==O~R1?zR`7WUQwElXn0fn(y^Y zP!!WAbnx`9ZrHYZV~^EZ3&C1In|g1IFB-tk!jj4&?cqE`_q^=;?KxvJK(ZQLyiB|$ z1K(VFq`3H;ng3=?Nk>lEvDbdS&uA0W`7wKT#UxhVlW<83rD^^V^~h{5VIKdGDrR zFO#hI1#M^Jl$@vkyx@4!fA;Zj$MlbZ^-{&#DS7ShH=Alsz>qOox89V~aVgcZe{ zj$TZYNcW|}oc;SVo5pa?hLVQlIQEdqCR@Z1?zS}~WFS#<-lAVA+(I-$;6=FbfJxip z_V|NuXnrevhwV2ZE9xDmb2cG?M@mIqdfb%T;+AoR=|CNBXRmoB%9>#xIDZybW+P9( zuMjZ#Bw_+KW*5KA$7ESjR%^YHwqT{fFgmDxfM=h#aZbGJ^7$1V(1JUzARQCz3{^7i zd&vP{&9&z3EVjwl)?A z7LE5B!>bo8#{B}}7cBOu4=KfLcK&;gx<)yHi%+nAfC95_BB+ zK(jou(Q3J34ZGx?47!~AM2KD0#mCe%x~U`thZxf{ z){V-GPrjU<-cYG>cC+HXaIW#=s~g2xB_~B2_YtJBy558Rwbm7-!<2IYHSn23V}Is3 zbNblz){LXftQEE?`Rlun)th0X)u9Qk0Bnn2KYQwj84Iu}-avnx8ez_~=lHFIdl=Kc?ygz98xR^A@u*+f<04i>pFR#PjKiLLj4 z%=LD8+Hv1M&WuCmHP9xBGK}I3RiSdz&zyMugFTQFh_A0wV)^r;@$R!#X6YDHQl4tQ@mh41|-+rcT}k79!#A5{Akb*@bI~CU(uIvdH3mMsVKZDIMEg%+tR&~r4)!M@iHT5 zk}Sa}Q~yLWF0H-qLaK(JugTk|C6)OdM1Cms)KvCvX27)05dcAfbmas3bKGTO+RsQj3WGZZRFt?EHSBqOjB!E%!=FWVTnc@oFTv`KqJTTl(o(B8k zr&P>F+gx^q_}H8~^yCw-)mT(d8NXYPXXLnlghlnvz%y1!+m9%aYj`4a8UVQagdL3U zRZdAX3gPMpG%2(n2L#DwyIkzZu&!$jsy#kSMdI6lAFIXWU*Ep`KYTk0)6kP4C(g45 zK$`eFx>!R)60XwBqD~A+%zpQkNs2~oidYjsHf5EkFXZy;6TcQTxL;^)t&qHI+UVBo zW(VH9qx(Hi_bv=_!|u#dhpx%T{AE*qadlJfM4e=lb4-@Sb@D~?QQd!dOL)WT$q_EX7C0OF9jZbshlM5dJ zHc^Pd+p@9zl8 zYNJn`y^ECw81*RptaQ#nIs+lU}?h z#uIs#;87ypn`9HxDb~C>B=?Bqfo*sJZ8hux`bsEakxA(g>4#|v2mfL`ZG1aFyL0Vy z?mB=2ALr^OCt*9ep1t|Z!Ng9Sn2nGKal3lK@OkWs>$V?%8eRolzFh0Y1szGqZuYgP zq_PH=_2i;?Xg!&|3(78o17@8KWz~?av`*JTWYRrIOP(Uz@}><|nTv4if|< z^~ptwjZ_Jb{#5A#!DckqXpyU3+A2>0J#^l#Pr)M@9LEpEFI+3(cRTr)EBo*X`w;NS z&^;1l;oH?%nON|aW$$fP_QE2Uwz{$y(p5$V8R<{GR^DrC3WWgz=G^s~UuQf_Qd#O7sOYiOvFX$e|6UZ^$YsG$lq1P8Zqik9sKAPf}JHHv{i8hauL zrOZrW#7{N9e^SQ4hstHreg69MDb+_^IZtG~-I}(BvalBY*T5SKW!szIf>h73R{Q;v z+vrvOhM23p0z1NB?hMca8LF(x2$}_qJp)O5)Ea0EkEqI!p**UaT9#CwlDmMObtFbn@uocf}MB zLyGuZ)?-*0AV{{aJ%7!g^UDdyB07eAWAtwQ?S1+h?_L@*$R2acsUro*=)LT{@{XTh zU29`C-tl8`T;jJ>k6^zQFtg`_6Jv!v+FF^Y&1gSCDo-D5-C211`H4odgLm~{fj>F} zTDy}~|NVt3(_)Dnak@Fe%A+WW?i|e<(8xCRT8JbQ3wL%`jLy|Q*No9V*k@d6{px(U zYtS%p$az0MdiDA$YmJ|Pq+R5a1n<|J=Rnyj1?gT+Tz!%dVwXIz`~jec2(ID`uRBuZ zplOGrr>8Gt-j#w#fC%z`xewL~we7YGQrGC$SECl0sr%D$J;$8!NOKIkIoIhG~H2)R#L4Cg>NzC^%i_nDY;r@V- zuD+mLJ+ExDUD4eTa8^+T7&ugV{|o`B^)#R?gIV$>a|OjxQzvDDy9ATYP+{*+~e%NV<|>AMA{5ssYsK`Ww#Z8y5CE z@)Xqr--@moUnh{?SG5C1U2L*(t?UW-dlierP{@IIJY5lwloC`9nXQ|dQ|TJb0vw6aTUVJ-B)rZS|`ZGyF8m)|mH zER=>rra%rF^2q?+p{nE7KKLf51QEmB*Y8$7E}L zg<~@IRqick(&{=I=i*e;ug2W&R>8_Y`&GQq!RvDm9xGD)lxnp00TTk_Ys~oCt4InS znGB}rcPa196YF1;)C0ubb4KQgY$2srtbyd3uC5K7OlTutT~g8MDDm2qO z()xuooj_`u1bGx92`QG2M4dKw- zXt`VQ*TJq4KeLVQbD4`=%((f(Ui$I(JMr^?_sZ_k${Zs8tkaeg2?SlxS~K<}zw$Uc zOpi5cMef~E&07N)sTS&}p3)=vzOU92|Crv(*F96^)wF(nVZwEmW#-SzcPM}!s(a;T zq`B>%J?A4n^YgjRq`pVTxFNp+2}eZdnVH6?(Ul2buV=SqZcF@mXKDO+MVbR~cr5M` z`sxK)k09xg-93L{erI6jG)x@x34?AY@qYa?n+((B2ByHJ4Mb77qYY#1FfnP@J{iyQ zop}e#>g;(YOCn6LA@4B|Bhr~l0U!GRB~J{fM8|)^S;)78r_lA7AypSV!b9wU~){^!k0az`!yT!7wOVEBXTK%gA0Fs48E!qBH3{`%M@kN-TD z##TW)^Y7c%|GE7?`S$;J9>YJxifjK{ki@SM;NJh&UjDz=IQj#y zuWTy^Ho==6xP|!osuiOsNk1#RSp6i?AN@4~qhE30gB4iA#V(`-N@oGIGRzvTS)~zs zwx_cSB7cxcO+(~|2ia{T4c7OkxvUE|5>U1CoAtE0-p(hp`DYUa{)BV3eUx~k7=yW4 z37_J~e(Tqs^iX#f)xbnPk3pw0yPgf3y+QVxXFullw+^5?vk0%3MO}*+A7I?Q*L!fQ zxpiH2flN1$_L&I+RukN{-YMKp|EeTuM}gzpkSY#I6?c>Ku*<&Gt{Ww@K*#kf6H$18~$3h;t zfc@SFI<4H=nzCL7dA50vrIg=*rijr;RDHZVztv8Do4HOI@k_I}RlwOID!Yj{l=%P3 zbXKm-It!>*BkY+j2R>d)ekitoLcFG{Kj9kvKh9US;cOtu+y<+z2M}!Z_CBTVI*x6e zY>j)$^8*g7)!Z6rbmor88=Bf8e^>BXd;J{(d#YoC^shqBcq}ud(a*Q4l |pg2|j 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
    2$jU(G!d(%TL z`_k3QkMR6{q>17LrpZVjA8Gg%A)S=(pL4vOs^TuCG{%$Ark2bN=ZjBA)!24r%J(yt z646ei(Ki#p(>Gtf9PgthPj++t9(i1YjP>jCuVPfGsG9ccXRS_Yhvm+!tJ6!GT@bHd zPPu5&JuzzMLRMrt?VA&e`i`oK&Et^186{R{wR2)f!Fn6j;f8B39-tAV+a5xAI+^}>QqL*=KH8lVsLyLD-x_W^P7U!jbG zE3FgO`$yhybM)@tcaxiV@(ZXJU&03FN9Coj>PFhZ?EP$g-OST{Wg0quK4Hz2qHiaj0=`fqgd)g{G8vroc|@etI8kqYBhIm5zgJ zyu|FB<;HI=(33%Hsh{#tGlz;pn2gslKeK^4^Y-tp?+$2y(VeSf--Q^xo(<%HfI`$6 z)a(geDKIw{9{b?$YrTctS*_zT)L)4l_Dr(hfcdTIwqTgn;6M8JwRO@NoS_aJij+Ib zW&|+G9sl`p*OC4EuIgw3OGg0-+3@XSpu^U)flxU6aXBls!NvHFKg}uZa?&mpy}=9Q zJldImsjZMNk!^35BJK1;DNWVZVyoJJRA#BXp&aOl)$uBM-gM5)v%mNn;!?94m<`%< z#ixP7sl#*7=LW6^T=oCiEZ6@?NAUmXr-5k+=GXo48u+92|MgnH|BIW^PcIGsBO(0# zKg*n+;lru_i|*OL63#n3Sz%LSeMQ+D@A45roER3Lu5AgT&he0CeypO3LZIDyKfMW= zTHU-Rr|j5W5^pbyK6F1Jfu+pd;%MyrDEJ> zY+_sRu+!y)O-3?%-gc^9@9u4r&9d6*;bQ{LQT1P6toJ;1GhP0*n$mor+ffrQ540FhbR|Zs?(yszrs-ClLg5=Rq0hIBX5&fs?oa=gJWB z3iCkNYqV4Hsz>F3N@mxj-@>o-(wmT(kLvX8Qp?p>LIOekKhzvgqd-0~lglwuT6&34 z3$O6a=I<=EDNlN75h$lG+ZpHL z6w(?HZ?5&7jc+CCsO_1bG_nnz%S+xL^DYauv1{(JmHBPSey>rqm`j0r65oF0=K6fE zTtV8Mm#DlT8~V$l^%L#8%|r6vgR)zJ{7TG<$ESjT7&*=H+Zh=b-p)PJIqR5ls>EgT zrmH>dCW`i9;m3G-Yc`0wSZ!X-);U~!^6nKX%>MxxC|lI6wssz`P@n0m*grDKsaDl@ zTC)1uf~2iA{(90ONdhKtX9*du(4xcR+Uh zL?!gP!*K*}Yf3zFTs@K^X9#6S{GJzax?bR*5m(;vneg62Jnci3O!;>Bhss2QJ z0YdyzYRw8Lnx@gSg6`}9Xpg;sqH!NeI^0l+pqoabHIV~#^9MqxlOonE zfhOSk;tVC<4q;E(UM!|&)llo5=t3`%23;_R`kBSE7rV?*!_6tGwhCn)WeP21a|^gw zC}h2Ejz;a7xTuLn)2ZIcQf0f1N(J3{2id6R^GrK3)L2d{jAb-`oq*!8HT*ib(51Z%xdEgx zUG2)3sT87|HyJcP-ByzrC495)P@S1yAT6QK!dAj;0~2T~g|;t(u3OM3>8UN&-V6^J zvTPRoe4JRj9Lz4ID!zri#8WgZWkPGF_*iy;u`s%_&IzkjK!ZQaOZS%&)f-X5j;@3; zYF<^x+VbL@e3Q&d3|BbWgJwvWKMCyem2->U8P!#IuDGhI$UXx!+-^O9Tw1w^xv?`U zmr>gH3^~zWW?gHq=pId>Z0nDz&iFZ5#NFB)p9O+&t2Ju12FxAlD6z{uS4bWw+{=*dNjI* z9$9Q3SzK|Hfix>Wx3JxfH%uFHQ`OrfIXUBfi{bvnTrq2i{~e*bDLOEGjnchkH|W|( zrBL-(wDLeDfi*K{6p`Kd0`6U1)Y*~_5e(=uwO&M3H;p{9PM;^&#rw6S2UV$rK^EX# zj3ul>3hAQz(XyjqHnki(G$poQ5vx+eg8WAc1yE#qaV@F9ZOO8{Q1!P}8M;VbnA}-~ z@=nakk?9+Dqm?%FqL@=P_3-!A853qRkMKNNPNN|Nta)8#JW`=)z67O(Zio*dl!kqM z5WHL?pI6u3?P&}GD|9$SHocQ)B8lKpoSI0S=pVLSH$|yeb`BJ*?&JHV z?AA0qxE?dIuoApCq`fR5*w9&ASvWa?F5g-Y(rs{6vrrr=_p(0;V~8`buw=8-VS9Sh z2m2DVX6pJhU;znT-fgA+udD6MylVUE$>xWht(TE8pk7*<2?FChG_Q*XFOI7&vDY5hsZQU8tHy1l_AGB? zS8l<-)}SejT$Hp2et02QT{tXcw49-2h~`nS#rD1sfZ=BZYIe2YU%cDOcL{Z0^Pzp# zmB72;9S#XWi&%6e((LCN)P#}D@ThQF-eXZzl6PJ7>!@3zZj?c=nr z!FpIbW&X9=Ya>5tp%up~9^Gm8tLY8WK!_VcHX_?gXU6JQ3*UeXtQFl)4X_}C8 zTbEwr3@iHFi#XVtcAj)CgV z;h#rxJX)CVjPYpsRuG6TGQiOv&n8%ZG2P}7AuNQq7}U{{Z1eGPJG(M*or2gZiKb~1 zM9sC&T{)vBC~S|rrwy%_GZe-4jL=A6q@5~%IH3a(vrVfUc{5KOd4g}D(o#8c-=9^; zb+sh!YRnBGyWMffiQb-RDf?j}!NU@-ZCuaXp(DY>6zd=heZ{dujfN0PlhpsD#OljE zFT4+zC$mjej69BIc^X#vHL=KP@x10tZgB7wFB_O@2eJoPcpBI7!Z2W1S=_+MY-~0z z<3rjdr^D)Brvin{hzo8?Sr$@f_Hy;-DEN-el5=sGtas5H54J>sW?V@`*t_M#)xPNt zq?E@vQliARGM{I3gAwq>PA@K1t4j=Hu!RW9DfKKE$V!RwzvzyzL6Zi^kABx$|F&e3IAOUqg4$k}exE?FZ}(~fHF*__c6GCv+a?Z+4aTo83ZpE<-b5&LW};Kl}`H{&?HLUg!hK zAeU~j`{h9n`Bw>uIl?V=>FrDZcX>oac3wqiK|FVw>!N)iFewoSfg*0t%PN$MWjHp? z*HzM1iH;)g&#LJSu*M5tHHi}&y_oeKGX+xw3hcxtKx`irumFM>dF%(9#m?`7%(|5_XG z2p_eaAsw>c|LEgpQFQD9i7;eLe0qUYNpsL#A|=>zy${(Vf|N4oI@#iBxD6RK6!Z!i zP}#vga3y%0-E=AohMf*eJJs>qbQzBjN(9{pY$dg=@xbeMbnr7pl6iq1tt!S*qASDh z@rbzvz6e#k%;$cPr$RBsuJ4p94Y!bB`M8s*HZLv?g1cw_6|06T{>gm}8;AZtDe8}~ zCe3FB5u1fM4eOK@zu$R#b=y&E#w{exi1^|ai2W}p7Il(9T7ue@L`|9LoNr083q3c< zYHAl(Orfs?DgEvl!22xZ>7*)5n zsE)QpK5vQlAE^^YBj2=P;TAqaht41 z@>l9g#P!s=T6!VddxSh}LCZ2#$&Vh@tY9;iD7ROPb7`9APGCY%Zi4B`l;4*e>>(;P zu6g#HIMz7B*ORPTBf8HFuOrr;a~v9y(Np+Ng<1@D4P7Bo^I9tD>n|yFFMgV_fR3at zj+a#HSe}~<@z;fpI9)4GN@?(Zc2s}I_e%NB*NpHYcCZlZx#W4CMb>@aWv)dh32U@d zf*Nxr&Z0Fot&o%AAbP4{t=%cD>1WTtB|Uy5sWrHpT4k^_I)b1%j9?M#hES=Mrpmt` z;X$e|KGNf$ZrsvGV~9=Wa^2<*5o5PruaqECn>*c=5;o#tI1ub+mAK{PH@I5V{#-r1 zv{fc<{>y0IL+cjHB|CaEQmMYOPAOjs>QAAORP`=2#a?K#n_PHQW4l`yE(Ix1wYLbL zpEs=%Zy!GQ!+<^KN>Da<+InBAs@S){Roeg2hN}+Kk*e6$7mOFHET$`mI@DMPS#>Qq z1q0vBW|TTg;F|LFoOUu^1)^PImRf#1xAiS6E@?)^A*5aZcy^gdX-wU|^CdhFQd`c7 zx`w3H5l!^>2G1UrSvfNIj~SgDZ7m9vIC*QFPkySK<4*hR)Ra{1*)(696ED|Pc*Z?A ztlea0?`}{K*%gQXULTw4&B(o}1;@q#oUt zg54YU3cQBWr_`$XMJACe;|1=CML}EsB3~y&npSM@Tr3y=y5dt%S%)$)I}OJ%d~r)R zC)aj@NHUfg=EP@`OcN`$4%T35ZEKLLk%jBsXRagm6mxja@%y zl+H@vB4tC5C(3z^c*KfXZ$kpt3f4#`GwbDFng;nt+@hYrIe9Q1!A>i9N8aRWpS}&$ z`|k*Z^10V%SAxg5Ck7|vK#a%Aj($vmVL8}%36Rd*#Y0j{O{Gpt)d=MqOqbC9mrHPz z=&R0ZG1x*Aea%NXKuW?XT1~&EwsfwZDBHU(|NaKc)fX@YA6$~{zxh;G{B>{ORk6~d z&&;n5;3NYj8rX$1jd;Ooo+tXPi<2=JjU1-KB$uZycUaNM7itY6Rhf-G?)VwHVpmO*lcG6hE zY;-q|qL@{A+F+HtOsu-4h2N3PmtuGAjjB+!KJy;Pl_$>Sk6w6C-ei;fBf9beqOLz; zD^P#D_(^#zP)P49%efa|r}{p8P*dnstf38}{)u6Jr6F(_+&qJI>Drbv&dhx(Z`zbb z!M+nn+owc|Sx1L4n({-n5GK{*7Wj)oX0e7w{^;b=AYrq9-%~dqB^FF^S^luSc=0rn z&(q8l$lBEm>ej`}g$ezO`LipF%Kr)gE_er(2S(v9YC;wezRQc}H|^!04eBB-#x?Qb zHEz|m*ucl}FolBjxnfnK{Hf84p~|`=0cmpOpf}co&5>VX;qCX$^I4D7P$McROKOkU ze{dvgQ@EY#*qs`mCE=##Q~u$zdab0grI*&t zzPv<=6*XGUh#Cz_Nx54Ga`(GoE4xf+>mvyhV3=6b-=H>5m8ZpVlR(+QM+Z!x2LkI;BvCljTg^?e!3z(uLeMEE&wZyM*!PBNQ=jeSnPPLF*YE8+ zYU~G*LZDA)tdO&V7mnNQf1(-~Cq~|+{74$BX8cmoFIcFM*nmE|DfT0c{CPd{?=0ni zC1tMX$jzpIP<9f3limJ>cxPSe0{#2P@Bhix{#Rb&|EK7C|IcRJ0n6{7o1^)^&CNM& zjdc6}mke+O|FnkL}AtngDf{Z0Y&t!*a>@EeZ}AU)l8iBOeoA_M|-I z=Q}Jxh&&6JX>vI_aWxwJZ9pU1$->s({4WUIaRu;!K$iPK{l%+-lHB)I6Cmt>B(Mh{wP`93T_!f)>=MW5 zpL9M&94<*xWNgj!Fj00)M!`BDy`^4Ay!lT5^_7811kNCUNd;hL%$YbqARsyPMXC*h zzxr*hy?WFR4^(n`Ifl1^T&hQ+wU^>wSKF@&G+Y5!B?ZXem+-r*WrVCfqP^p3c8H!g z?JIJ&t*IM#PQ7r=<~bljyj20jC_tsr;i#y=dnsc8a&b)XHUN&r56!i4s>udUqiO-- zBY62Ia`7jTZgUqsnTN^}Ffo4}veq#l*g{VO)ed{{kgYG#0L2#Rfj#u*9FjJd6t8y< z2k-|L7*Ok+0R27S^#hT2S~GmOA#gR9B{8bHG;k_Nh$u1i)JSg~pc73KLgY^6B?M@&w$j zE5{wobGvBV;&Q3?1$%-Q`?Jb!B_=X8vAg^89^(v^CB_bVd~Vf}YE4n{FQ$FvX>kRJ zj*BaJPfW~TLcEuMZ!Nwq7=J&{nF*PScPL~sG?Q(O19*{uRdbFQGpr9(ozM1ohpzMe zKsD~{)7xzI6-)&PKmgBE08*@h9(`|u*5{M`dKKHx1TVkoSXaSjBu>E~TI(Yffctj_ z$!(CcIou2=YD|4(n0rw=pLe5=G-tv!v-0B!d-!Qa&G?-*_V_MU|Ddl&r5YW)8p!fY znw}7|j00XI9YB`?21{8LEh!!R?X~dr0S`9&lzNq(RRCqY)IM>i=was?&ok;BsYdVtw!jpEg27#nOp~ylWGJ1#m$Y*?5j*L6-MJ0Q>ax>vObr; zIL~Qwc^ubB9kF(PbRn?<{j$LA)Sw%`BW*3I#q;A+-gt2ZZ-T1;c4xgXCsvxr=FUCT z&!_XH7I<X-74djf6E>P6_+DZ*zye1XD63Z!J%^gbn`GjLrNNNc)mcI1r~` zp{s!6w_UB#lsMc_SR8r6;nDrqtEkoBdElxreOC8Ht6i|MBpQNLRFDE_R~peaKF4V>OMW=S9^B2@DKo|uCs$-bA5hk|@~wAlT0fDX!x;%fZByQ$WxaBqPhaMX z*{T8c+-yvkcRn=r&*(~i1m#gpw{4FYAYV#0EPB!(Ulv1GrqCFwY6^jI4Ly^P4Ga1W zRIJjMIJxz8cvG9#e3u8>CH*etn%|5ZYyx)Lz&%16k51lDUUbD_VBRu5I|S!$gS{%W zY#hE0C_Gn9u25l%pWJ%zr8xSpwxRKOB z98^lMcQP91{ymTAPe|=-D$v^}hnkqI_cNGoMv;x=C3Ke5yVgW@_>==m0oly<#paMC z5cbSepy_G@^yNA2rV4;(zKr4fZrKE&gvw$tBi>ahUXxQyCnzofUhY!1GyPI&c) zK%Hc&{G_l5E?dCv(M0CJhDRbi;0!#9!@g_sE;&K>*(tUZ(p(LuKZJ1ir`BEy&=1LW zK5`Q)xJ4>Kr#}$P{yYOV0s|ylo(88qARD*}FugBOYSzOKY4~&RG3-chUkX6q_5lFzaC!*_O*Pz6P7*p* z0ya=30F{)IC@ayfYyG^DidIPVu1=#(d+a$2gyn9SKy5AZySgT?gnk}c-lx}8K13(g zyQE9Q?5E_7xFfAemvgVeN0hyVk|4#Nl6OthECi~GY3-+ys+#9Z_hMq=GYzRBqlP6F z`a^WjC32u1i4-lK<K-?D2x;E`Z!)#n-rt zg(0i&*{$PO^=kN=8#l1>mzDuHQl?FEQ$RU3akX$6wM|v1opv1U3arKW+m%%>i{e0E zg`O?j*oPxiRxR#+hoe}|?l$B?WMAi@%cg5RA%F;rk4{W7m-p!!RWiocttncM`ED*( z>z{mGWH5O*G(2yOC|6xV+Uo3*I=^m|N;7k*6{wmT3cyUElLFA{=2yZK)CI=;0s8G z+jFhbfJ-Uv2S~Q~tPmz;cX>NfjqprG6Yx9~Z7om;H4(@xpd{!@@=)5D^g_KPzWkzN zR-BBj?9ovxkFxWHo!NHAq_l+mt40uPWg~!a!V_~UMO1KWj|)Ss@<-rSn=1qvdrqHw z_wGlg1x~j|DS0O!?PD~dTvQ`f*~*8M^{z#qy&|&D)gnBv;_8>-r$p2E1vc5i(8lR& zy3exQxE&G1K{-}2*^qIqg|W-?_8bMzMEO4!?X1uD$nm867o+m~Z>#Y+P$~rK=GF)G8f$yXbX`+R86jBhT@SLK+{?aAiYRq1+&a1T%%Q5-F5FVKxZM zZ|>5BMm`ivZLu*P1HGz2bwL|M{B$$UxBgA%`EnH^kr^x?RGv^5K@|G|^vz7{!E!#| zUYqT3ZlaFpMNvWV>$6st&h_DaOUszP%Q^xfZe*6rc$_R0Q7WMR-@izB|D2Vl>79 zlsSdsrN<}Z{B+!{U_t;sSOV*tz4%Qu8k#i%VwaREtxq#?YR*T%-X2oica4SLQ4+iO zC%sgaZ|M*!7jmuk-c!SkVf<{VX`G%RDQ%atCMyn7)Y2NIs*^Ex^5hQnT_V@cy^^tg zT~Yf3^wl}tN2AiIze*32GmWf;1?c(NAYwm$&^m1_=P3V!@hk(YN9)!7+(j@r8o z)`C;C6=0y0bU@I948~?l>fYvFsC)E$*U{|q=Iyid?P`oS{GIZs?Wz%a49yGh(|?>w zvaFKqLzBk=UJq_`^e^|0O;wW$PZd0P2K3kh?SO*(~ zyQ+}qxq6&*Wn!Mv7W|JO^4AIcVcBdVZYHk?#i6?WKIOYvz~P&W#xp%Kw)x^-&7rO5 zEca)YG~iE%u{eIaM5|)! zTXaCLCq$jK1&1NPTIzTz(A2o7lT0C z%@0irfQ}KqZQ=9Zpx@T-r&P+S4eX{2mFOrufeFX2B+u};`RP06zvdySH`IrWT*YuAzvTxMVBI9kD=qQjC2fzxdq!$@M@GLzA`=-aMB ztWxHgVHL-y<{`mEnI zjH((s!WL>48m5xa0XSN6OKj3Ytn$ir$simh7LYws20c<-o&zHLGr@?S2u@XSY&J+!D(gY7 zodPDB3SNcCIJB~X7|d3?Fg3NHOAS7OkntR=n5EWEhjszVB-DS(H791d(j8jvfpR`r z%DeF%3lH~S@2UL>Fd9YzCc5Wcc{N8rKxzE&?RO~wvTjQ_}$ z&~zhwuJdc{o4(;;Tj{ai?vDGQYj9gb@p?%b4P7$K;5D$zmuk`{YWuY)-6<5RtdL$3 zKcDk#EYdZ0DQ|vWs2DS?8appoE-pDnDL6IyF|?t6Ew`PSZ|MIHPFY|vn_R(w9HY>+ zDB(}c%ploF@0T1Sm%m_(_fIlmi<=PxevyLz0k#N^`78JQfqH3yoyF#~+FK@Zamep_ zmz0K32cRti7v94eR##Az=djQ-3uDD0>eCL$*-pIL09lq-1`7PQq8}Sn3sJFsHzy=& zC;vQHN&vnYP|wFeM6?0#M0f2WXt&4ZUGq!15Im9(-<@p{diG-~EWi)0d4?+sWkD+bp!gg*c^A9jcm-v6YNVTWz|~O;BP*hp6?Vx>woO>oeKJ* z_Y2G(F!_Xj24_| z!?eUIkUlt#ZZ5W0TK38q#_n$5dw^qejCQAzhGps(lJcoV?4Z+~w}E3m`svXdQ><8Z zZbE@DKXZ?aIKeYC+rh>iLZs0q8&Aa+CB@dQDPf^Qga8JR39)7INjm4QVjGp1$e|G8 zl$2o30^30B5>#xjo=cfJ3w0Wq3uf!o-2A0sd+ka5r4H~DZ`syPwt~vVP~!>JYW^$5 z5y;E+Ys6-t#$r>i>BLnVVvvKRlP|LU>Mtd>`Q(9@N{gY_86EIUNKiS}7YdZ<%+2wS z;=R+;{0UVVg&eg6O5g10i4QSLoikF*V&K(wko7jU3(t$!KFm+b$_=A_gV1K==^p%D_@C@v7 zrvnUW?zbC)7Ks2{=(aaKqI0%KZzYZzDHZZoRV+Wh3@nC71swD~k*9o84J*)WZv+D;sS+r}R^B-hecg3q^T(}FSu zWU-HGz2?jG@DsTEHWn%B6_IZyE4jr*-ruk{921yC8UZm@Odai{A#?Y7!UVdlNj~D* zO(8tj5pWX#3A_{^?z`e36A19j=}RUxfXh63NRF)n$n1yqMFRb$cYI!TS^3AB{rcFa z4~j{(`htW4yUg8h&vGR#1ppy!mSIf%rlY!DxWV%a&bjyXEv3K}&*;Z)m4=$t+L~V&+!_Y7(E(?O@S)NL zY5h!I1ncMi;n7M#FRA!uom1fQ_q}H#VoL=S+0G+^GVMk+YNn7Let<-G9<*e^YsQ_+ z?nQna&5LkDLiht72KN}C^`^7|!hYL^Tb`X)`aCa;vq!J1*LP!l;ruo0=hY)txJi(C z4iHYAN6+n13e@BPaN4{>+6l-T8zAozn@Z=F{1X6GKw%lrblh)+C`XnM6Z!?ex^5iB_Z@);^h(TS02toFBV9VPeZHq4=$J6r7(?LhgcOlQiHa z-tJj46mC5@(bgGZp|>(#-4oj|Y_rB(g!?Z40Z7^iH9xo$BR6tsrhQ?vwK(bB z#biiujU`}lk&QwLYP*zp$3`PMpd(*tVRZIb_Ry&6KmKA5vj9Uz%E{aKj(= zDQ5~KNT0c+2Tf3zqUL~I=q3QB-wPYqF(dYgUIgqo6$?f^p8Jd3YBF1q>Aljnvpw4W z8}py8FM+Hv6D^6h^8lQ(4#p@YcIiOHgjn)y@BU14C&*2-EM! z6TU#hwoNNoM!Wx3lQkKyw4x-JB04K0zFk&aktMK;PJLt#3k4MBRz zf0sYx+uQm3MeRokQml&(dcxQKELkEI;r-IwItA!Z^h$W z_Wp8J}4S)_KO(YbD z|I~#_M#3VFuvhlsO`w>mreM6=GS`o{R$Q;#Z^3D}EB5M&&!0u9dF?lD8F%gGe9hEj zU+7N09liX~%~U)ADwuG;RGfL=dVre*xDw{;C&|!e_YKE;M3p*I8<%7%F?&vLJx3~ zUiOck{Gbqmy7`X+I{n6LGwp=og3%vuX;O@;__#cw=wBqPV3wHaMe(X>DCCL(Ot^hx`8xjU_qt literal 0 HcmV?d00001 diff --git a/src/data/tr/articles.json b/src/data/tr/articles.json index fe51488..5a5299b 100644 --- a/src/data/tr/articles.json +++ b/src/data/tr/articles.json @@ -1 +1,12 @@ -[] +[ + { + "title": "Linear Regression Nedir?", + "slug": "linear-regression-nedir", + "category": "machine-learning" + }, + { + "title": "Supervised Learning Nedir?", + "slug": "supervised-learning-nedir", + "category": "machine-learning" + } +] diff --git a/src/data/tr/categories.json b/src/data/tr/categories.json index fe51488..1bc8c99 100644 --- a/src/data/tr/categories.json +++ b/src/data/tr/categories.json @@ -1 +1,10 @@ -[] +[ + { + "id": "1", + "name": "machine-learning", + "slug": "machine-learning", + "description": "", + "color": "", + "articleCount": 2 + } +] diff --git a/src/data/tr/tags.json b/src/data/tr/tags.json index fe51488..b5baf42 100644 --- a/src/data/tr/tags.json +++ b/src/data/tr/tags.json @@ -1 +1,23 @@ -[] +[ + { + "id": "1", + "name": "linear-regression", + "slug": "linear-regression", + "articleCount": 1, + "description": "" + }, + { + "id": "2", + "name": "supervised-learning", + "slug": "supervised-learning", + "articleCount": 2, + "description": "" + }, + { + "id": "3", + "name": "teori", + "slug": "teori", + "articleCount": 2, + "description": "" + } +] From 5b87ce0dd4547500449331c1eedf680a91e7283f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 16:41:44 +0300 Subject: [PATCH 12/15] chore: update categories-tags descriptions --- src/data/tr/categories.json | 16 +++++++------- src/data/tr/tags.json | 42 ++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/data/tr/categories.json b/src/data/tr/categories.json index 1bc8c99..003aadb 100644 --- a/src/data/tr/categories.json +++ b/src/data/tr/categories.json @@ -1,10 +1,10 @@ [ - { - "id": "1", - "name": "machine-learning", - "slug": "machine-learning", - "description": "", - "color": "", - "articleCount": 2 - } + { + "id": "1", + "name": "Machine Learning", + "slug": "machine-learning", + "description": "Sadece kural yazmak yerine, makinelere veriden kendi kurallarını çıkarmayı öğrettiğimiz alan.", + "color": "#6366F1", + "articleCount": 2 + } ] diff --git a/src/data/tr/tags.json b/src/data/tr/tags.json index b5baf42..1e46a4e 100644 --- a/src/data/tr/tags.json +++ b/src/data/tr/tags.json @@ -1,23 +1,23 @@ [ - { - "id": "1", - "name": "linear-regression", - "slug": "linear-regression", - "articleCount": 1, - "description": "" - }, - { - "id": "2", - "name": "supervised-learning", - "slug": "supervised-learning", - "articleCount": 2, - "description": "" - }, - { - "id": "3", - "name": "teori", - "slug": "teori", - "articleCount": 2, - "description": "" - } + { + "id": "1", + "name": "linear-regression", + "slug": "linear-regression", + "articleCount": 1, + "description": "Sürekli sayısal değerleri tahmin etmek için kullanılan, değişkenler arasındaki doğrusal ilişkiyi matematiksel olarak modelleyen temel makine öğrenmesi algoritması." + }, + { + "id": "2", + "name": "supervised-learning", + "slug": "supervised-learning", + "articleCount": 2, + "description": "Modelin önceden etiketlenmiş eğitim verileriyle (girdi ve doğru çıktı eşleşmeleri) eğitilerek, yeni ve görülmemiş veriler üzerinde tahmin yapmasını sağlayan öğrenme yaklaşımı." + }, + { + "id": "3", + "name": "teori", + "slug": "teori", + "articleCount": 2, + "description": "Algoritmaların sadece nasıl kodlandığını değil; arkasındaki matematiği, çalışma mantığını ve 'neden' sorusunun cevabını detaylandıran kavramsal içerikler." + } ] From 00e57c42b239bd31c903b0d16fa922333ff8967d Mon Sep 17 00:00:00 2001 From: omerfdmrl <62325045+omerfdmrl@users.noreply.github.com> Date: Sun, 22 Mar 2026 13:42:16 +0000 Subject: [PATCH 13/15] chore: auto-generate data and og images --- src/data/tr/categories.json | 16 +++++++------- src/data/tr/tags.json | 42 ++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/data/tr/categories.json b/src/data/tr/categories.json index 003aadb..913fc77 100644 --- a/src/data/tr/categories.json +++ b/src/data/tr/categories.json @@ -1,10 +1,10 @@ [ - { - "id": "1", - "name": "Machine Learning", - "slug": "machine-learning", - "description": "Sadece kural yazmak yerine, makinelere veriden kendi kurallarını çıkarmayı öğrettiğimiz alan.", - "color": "#6366F1", - "articleCount": 2 - } + { + "id": "2", + "name": "machine-learning", + "slug": "machine-learning", + "description": "", + "color": "", + "articleCount": 2 + } ] diff --git a/src/data/tr/tags.json b/src/data/tr/tags.json index 1e46a4e..85b9bb8 100644 --- a/src/data/tr/tags.json +++ b/src/data/tr/tags.json @@ -1,23 +1,23 @@ [ - { - "id": "1", - "name": "linear-regression", - "slug": "linear-regression", - "articleCount": 1, - "description": "Sürekli sayısal değerleri tahmin etmek için kullanılan, değişkenler arasındaki doğrusal ilişkiyi matematiksel olarak modelleyen temel makine öğrenmesi algoritması." - }, - { - "id": "2", - "name": "supervised-learning", - "slug": "supervised-learning", - "articleCount": 2, - "description": "Modelin önceden etiketlenmiş eğitim verileriyle (girdi ve doğru çıktı eşleşmeleri) eğitilerek, yeni ve görülmemiş veriler üzerinde tahmin yapmasını sağlayan öğrenme yaklaşımı." - }, - { - "id": "3", - "name": "teori", - "slug": "teori", - "articleCount": 2, - "description": "Algoritmaların sadece nasıl kodlandığını değil; arkasındaki matematiği, çalışma mantığını ve 'neden' sorusunun cevabını detaylandıran kavramsal içerikler." - } + { + "id": "1", + "name": "linear-regression", + "slug": "linear-regression", + "articleCount": 1, + "description": "Sürekli sayısal değerleri tahmin etmek için kullanılan, değişkenler arasındaki doğrusal ilişkiyi matematiksel olarak modelleyen temel makine öğrenmesi algoritması." + }, + { + "id": "2", + "name": "supervised-learning", + "slug": "supervised-learning", + "articleCount": 2, + "description": "Modelin önceden etiketlenmiş eğitim verileriyle (girdi ve doğru çıktı eşleşmeleri) eğitilerek, yeni ve görülmemiş veriler üzerinde tahmin yapmasını sağlayan öğrenme yaklaşımı." + }, + { + "id": "3", + "name": "teori", + "slug": "teori", + "articleCount": 2, + "description": "Algoritmaların sadece nasıl kodlandığını değil; arkasındaki matematiği, çalışma mantığını ve 'neden' sorusunun cevabını detaylandıran kavramsal içerikler." + } ] From fffcc4543521c19cfdb789df0ab226f3cd71fe6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Demirel?= Date: Sun, 22 Mar 2026 16:44:26 +0300 Subject: [PATCH 14/15] fix: data generation script to use slug instead name in categories --- scripts/update-data.mjs | 2 +- src/data/tr/categories.json | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/update-data.mjs b/scripts/update-data.mjs index c7b2dcf..6386e1e 100755 --- a/scripts/update-data.mjs +++ b/scripts/update-data.mjs @@ -64,7 +64,7 @@ const writeJSON = async (file, data) => await fs.writeFile(file, JSON.stringify( const catName = data.category?.trim() if (catName) { - const existing = categories.find((c) => c.name === catName) + const existing = categories.find((c) => c.slug === catName) if (existing) { existing.articleCount = (existing.articleCount ?? 0) + 1 } else { diff --git a/src/data/tr/categories.json b/src/data/tr/categories.json index 913fc77..003aadb 100644 --- a/src/data/tr/categories.json +++ b/src/data/tr/categories.json @@ -1,10 +1,10 @@ [ - { - "id": "2", - "name": "machine-learning", - "slug": "machine-learning", - "description": "", - "color": "", - "articleCount": 2 - } + { + "id": "1", + "name": "Machine Learning", + "slug": "machine-learning", + "description": "Sadece kural yazmak yerine, makinelere veriden kendi kurallarını çıkarmayı öğrettiğimiz alan.", + "color": "#6366F1", + "articleCount": 2 + } ] From 30b1cc76db94791a9d3b274cd43a820f53708aeb Mon Sep 17 00:00:00 2001 From: omerfdmrl <62325045+omerfdmrl@users.noreply.github.com> Date: Sun, 22 Mar 2026 13:44:56 +0000 Subject: [PATCH 15/15] chore: auto-generate data and og images --- src/data/tr/categories.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/data/tr/categories.json b/src/data/tr/categories.json index 003aadb..2e489e4 100644 --- a/src/data/tr/categories.json +++ b/src/data/tr/categories.json @@ -1,10 +1,10 @@ [ - { - "id": "1", - "name": "Machine Learning", - "slug": "machine-learning", - "description": "Sadece kural yazmak yerine, makinelere veriden kendi kurallarını çıkarmayı öğrettiğimiz alan.", - "color": "#6366F1", - "articleCount": 2 - } + { + "id": "1", + "name": "Machine Learning", + "slug": "machine-learning", + "description": "Sadece kural yazmak yerine, makinelere veriden kendi kurallarını çıkarmayı öğrettiğimiz alan.", + "color": "#6366F1", + "articleCount": 2 + } ]