Extract a compact text summary from HTML widget content. Keeps: text content, data values, labels, chart config hints. Removes: HTML tags, CSS, boilerplate.
(html: &str)
| 1783 | /// Keeps: text content, data values, labels, chart config hints. |
| 1784 | /// Removes: HTML tags, CSS, boilerplate. |
| 1785 | fn summarize_html_widget(html: &str) -> String { |
| 1786 | let mut summary = String::new(); |
| 1787 | |
| 1788 | // Extract title if present |
| 1789 | if let Some(cap) = regex::Regex::new(r"<title>([^<]+)</title>") |
| 1790 | .ok() |
| 1791 | .and_then(|re| re.captures(html)) |
| 1792 | { |
| 1793 | summary.push_str(&format!("title: {}. ", &cap[1])); |
| 1794 | } |
| 1795 | |
| 1796 | // Extract visible text from key elements (h1-h3, labels, spans with data) |
| 1797 | let text_re = regex::Regex::new( |
| 1798 | r"<(?:h[1-3]|label|th|td)[^>]*>([^<]{1,100})<" |
| 1799 | ).ok(); |
| 1800 | if let Some(re) = text_re { |
| 1801 | let mut texts: Vec<&str> = Vec::new(); |
| 1802 | for cap in re.captures_iter(html) { |
| 1803 | let t = cap.get(1).map_or("", |m| m.as_str()).trim(); |
| 1804 | if !t.is_empty() && !texts.contains(&t) { |
| 1805 | texts.push(t); |
| 1806 | } |
| 1807 | } |
| 1808 | if !texts.is_empty() { |
| 1809 | summary.push_str(&format!("content: {}. ", texts.join(", "))); |
| 1810 | } |
| 1811 | } |
| 1812 | |
| 1813 | // Extract Chart.js datasets info |
| 1814 | if html.contains("Chart(") || html.contains("chart.js") || html.contains("Chart.js") { |
| 1815 | // Try to find labels and dataset labels |
| 1816 | let labels_re = regex::Regex::new(r"labels:\s*\[([^\]]{1,300})\]").ok(); |
| 1817 | let dataset_re = regex::Regex::new(r#"label:\s*['"]([^'"]{1,50})['"]"#).ok(); |
| 1818 | let data_re = regex::Regex::new(r"data:\s*\[([^\]]{1,300})\]").ok(); |
| 1819 | let bg_re = regex::Regex::new(r#"backgroundColor:\s*['"]([^'"]{1,30})['"]"#).ok(); |
| 1820 | |
| 1821 | if let Some(re) = labels_re { |
| 1822 | if let Some(cap) = re.captures(html) { |
| 1823 | summary.push_str(&format!("chart labels: [{}]. ", &cap[1])); |
| 1824 | } |
| 1825 | } |
| 1826 | if let Some(re) = dataset_re { |
| 1827 | let names: Vec<&str> = re.captures_iter(html).filter_map(|c| c.get(1).map(|m| m.as_str())).collect(); |
| 1828 | if !names.is_empty() { |
| 1829 | summary.push_str(&format!("datasets: {}. ", names.join(", "))); |
| 1830 | } |
| 1831 | } |
| 1832 | if let Some(re) = data_re { |
| 1833 | for (i, cap) in re.captures_iter(html).enumerate() { |
| 1834 | if i < 3 { |
| 1835 | summary.push_str(&format!("data[{}]: [{}]. ", i, &cap[1])); |
| 1836 | } |
| 1837 | } |
| 1838 | } |
| 1839 | if let Some(re) = bg_re { |
| 1840 | let colors: Vec<&str> = re.captures_iter(html).filter_map(|c| c.get(1).map(|m| m.as_str())).collect(); |
| 1841 | if !colors.is_empty() { |
| 1842 | summary.push_str(&format!("colors: {}. ", colors.join(", "))); |
no outgoing calls
no test coverage detected