diff --git a/src/lib.rs b/src/lib.rs index 1a25bd3..52fab86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,14 @@ impl ChartManager { "".to_string() } + // Recommends a possible solution to an error from method `test_func` + pub fn error_recommend(error_string: String) -> String { + match error_string.as_str() { + "Parse error: Unexpected token x." => "Always explicitly state multiplication using an asterisk.", + _ => "Make sure you're using proper syntax! Check the 'Frequent issues' for more info" + }.to_string() + } + fn get_func(&self) -> impl Fn(f64) -> f64 { let expr: Expr = self.func_str.parse().unwrap(); let func = expr.bind("x").unwrap(); @@ -88,7 +96,7 @@ impl ChartManager { #[inline] fn draw( - &mut self, element: HtmlCanvasElement, + &mut self, element: HtmlCanvasElement, dark_mode: bool ) -> DrawResult<(impl Fn((i32, i32)) -> Option<(f32, f32)>, f32)> { let func = self.get_func(); @@ -96,7 +104,11 @@ impl ChartManager { let root = backend.into_drawing_area(); let font: FontDesc = ("sans-serif", 20.0).into(); - root.fill(&WHITE)?; + if dark_mode { + root.fill(&RGBColor(28, 28, 28))?; + } else { + root.fill(&WHITE)?; + } let mut chart = ChartBuilder::on(&root) .margin(20.0) @@ -105,7 +117,13 @@ impl ChartManager { .y_label_area_size(30.0) .build_cartesian_2d(self.min_x..self.max_x, self.min_y..self.max_y)?; - chart.configure_mesh().x_labels(3).y_labels(3).draw()?; + let light_line_color = if dark_mode { + RGBColor(254, 254, 254) + } else { + RGBColor(28, 28, 28) + }; + + chart.configure_mesh().x_labels(3).y_labels(3).light_line_style(&light_line_color).draw()?; let absrange = (self.max_x - self.min_x).abs(); let data: Vec<(f32, f32)> = match self.back_cache.is_valid() { @@ -176,7 +194,7 @@ impl ChartManager { #[allow(clippy::too_many_arguments)] pub fn update( &mut self, canvas: HtmlCanvasElement, func_str: &str, min_x: f32, max_x: f32, min_y: f32, - max_y: f32, num_interval: usize, resolution: i32, + max_y: f32, num_interval: usize, resolution: i32, dark_mode: bool ) -> Result { let underlying_update = (*func_str != self.func_str) | (min_x != self.min_x) @@ -214,7 +232,7 @@ impl ChartManager { self.num_interval = num_interval; self.resolution = resolution; - let draw_output = self.draw(canvas).map_err(|err| err.to_string())?; + let draw_output = self.draw(canvas, dark_mode).map_err(|err| err.to_string())?; let map_coord = draw_output.0; let chart_output = ChartOutput { diff --git a/www/index.html b/www/index.html index 7f550f7..95fb905 100644 --- a/www/index.html +++ b/www/index.html @@ -15,7 +15,7 @@
Loading WebAssembly...
- Supported Expressions +

@@ -35,4 +35,18 @@ -

I'm Open Source! (and licensed under AGPLv3)

\ No newline at end of file +

I’m Open Source! (and licensed under AGPLv3)

+ +

Frequent Issues

+ +

Not using explicit Multiplication

+ + + +

What Expressions can I use?

+ + diff --git a/www/index.js b/www/index.js index 0970ae9..61eabfc 100644 --- a/www/index.js +++ b/www/index.js @@ -13,6 +13,8 @@ const num_interval = document.getElementById("num_interval"); const area_msg = document.getElementById("area-msg"); const resolution = document.getElementById("resolution"); +let darkMode = false; + let chart = null; let chart_manager = null; @@ -30,6 +32,18 @@ export function setup(WasmChart) { /** Add event listeners. */ function setupUI() { + + // Handles browser color preferences + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + darkMode = true; + } + + // Watches for changes in color preferences + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + darkMode = event.matches; + updatePlot(); + }); + status.innerText = "WebAssembly loaded!"; math_function.addEventListener("change", updatePlot); minX.addEventListener("input", updatePlot); @@ -39,6 +53,7 @@ function setupUI() { num_interval.addEventListener("input", updatePlot); resolution.addEventListener("input", updatePlot); + window.addEventListener("resize", setupCanvas); window.addEventListener("mousemove", onMouseMove); } @@ -80,14 +95,19 @@ function updatePlot() { const test_result = ChartManager.test_func(math_function.value); if (test_result != "") { + const error_recommendation = ChartManager.error_recommend(test_result); status.style.color = "red"; - status.innerText = test_result; + if (error_recommendation == "") { + status.innerText = test_result; + } else { + status.innerText = `${test_result}\nTip: ${error_recommendation}` + } return; } try { const start = performance.now(); - chart = chart_manager.update(canvas, math_function.value, Number(minX.value), Number(maxX.value), Number(minY.value), Number(maxY.value), Number(num_interval.value), Number(resolution.value)); + chart = chart_manager.update(canvas, math_function.value, Number(minX.value), Number(maxX.value), Number(minY.value), Number(maxY.value), Number(num_interval.value), Number(resolution.value), false); // TODO: improve darkmode support const end = performance.now(); area_msg.innerText = `Estimated Area: ${chart.get_area()}`; diff --git a/www/style.css.new b/www/style.css.new new file mode 100644 index 0000000..09d9c92 --- /dev/null +++ b/www/style.css.new @@ -0,0 +1,496 @@ +html { + font-size: 100%; + overflow-y: scroll; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +@media (prefers-color-scheme: dark) { + body { + color: #c9d1d9; + font-family: 'Open Sans', sans-serif; + font-size: 12px; + line-height: 1.5em; + padding: 1em; + margin: auto; + max-width: 52em; + /* github dark color: + background: #0d1117; */ + + /* duckduckgo dark color */ + background: #1c1c1c; + } +} + +@media (prefers-color-scheme: light) { + body { + color: #444; + font-family: 'Open Sans', sans-serif; + font-size: 12px; + line-height: 1.5em; + padding: 1em; + margin: auto; + max-width: 52em; + background: #fefefe; + } +} + + +@media (prefers-color-scheme: dark) { + /* Links */ + a { + color: #58a6ff; + text-decoration: none; + } + + /* Visited Links */ + a:visited { + color: #58a6ff; + } + + /* Links that are being hovered over */ + a:hover { + color: #58a6ff; + cursor:pointer; + } +} + +@media (prefers-color-scheme: light) { + /* Links */ + a { + color: #0645ad; + text-decoration: none; + } + + /* Visited Links */ + a:visited { + color: #0b0080; + } + + /* Links that are being hovered over */ + a:hover { + color: #06e; + cursor:pointer; + } +} + + +a:active { + color: #faa700; +} + +a:focus { + outline: thin dotted; +} + +a:hover, +a:active { + outline: 0; +} + +/* Paragraph selected (Legacy firefox 61 and below) */ +::-moz-selection { + background: rgba(255, 255, 0, 0.3); + color: #000; +} + +/* Paragraph selected */ +::selection { + background: rgba(255, 255, 0, 0.3); + color: #000; +} + +/* Paragraph selected (Legacy firefox 61 and below) */ +a::-moz-selection { + background: rgba(255, 255, 0, 0.3); + color: #0645ad; +} + +/* Paragraph selected */ +a::selection { + background: rgba(255, 255, 0, 0.3); + color: #0645ad; +} + + +p { + margin: 1em 0; +} + +img { + max-width: 100%; +} + + +/* Inline code snippets */ +@media (prefers-color-scheme: dark) { + code { + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border: 1px solid #2f333a; + padding: 2px; + } +} + +/* Inline code snippets */ +@media (prefers-color-scheme: light) { + code { + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border: 1px solid #BCBEC0; + padding: 2px; + } +} + +/* Post-Inline code snippets */ +pre code { + border-radius: 0px; + -moz-border-radius: 0px; + -webkit-border-radius: 0px; + border: 0px; + padding: 2px; +} + + +/* Headers */ +@media (prefers-color-scheme: dark) { + h1, + h2, + h3, + h4, + h5, + h6 { + font-weight: 600; + color: #c9d1d9; + line-height: 1em; + } +} + +/* Headers */ +@media (prefers-color-scheme: light) { + h1, + h2, + h3, + h4, + h5, + h6 { + font-weight: 600; + color: #111; + line-height: 1em; + } +} + +h4, +h5, +h6 { + font-weight: bold; +} + +h1 { + font-size: 2em; +} + +h2 { + font-size: 1.3em; + padding-top: 30px; +} + +h3 { + font-size: 1.1em; + padding-top: 10px; +} + +h4 { + font-size: 0.9em; + padding-top: 5px; +} + +h5 { + font-size: 0.9em; +} + +h6 { + font-size: 0.9em; +} + + +blockquote { + color: #666666; + margin: 0; + padding-left: 3em; + border-left: 0.5em #eee solid; +} + +hr { + display: block; + border: 0; + border-top: 1px solid #aaa; + border-bottom: 1px solid #eee; + margin: 1em 0; + padding: 0; +} + +pre, +code, +kbd, +samp { + font-family: 'Fira Code', monospace; + font-size: 0.98em; +} + +/* Markdown code block */ +@media (prefers-color-scheme: dark) { + pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; + /* Use with "github dark" background: */ + /* background-color: #1f2227; */ + /* Use with "duckduckgo dark" background: */ + background-color: #202325; + padding: 10px 15px; + } +} + +/* Markdown code block */ +@media (prefers-color-scheme: light) { + pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; + background-color: #eee; + padding: 10px 15px; + } +} + +b, +strong { + font-weight: bold; +} + +dfn { + font-style: italic; +} + +ins { + background: #ff9; + color: #000; + text-decoration: none; +} + +mark { + background: #ff0; + color: #000; + font-style: italic; + font-weight: bold; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +ul, +ol { + margin: 1em 0; + padding: 0 0 0 2em; +} + +li p:last-child { + margin: 0; +} + +dd { + margin: 0 0 0 2em; +} + +img { + border: 0; + -ms-interpolation-mode: bicubic; + vertical-align: middle; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td { + vertical-align: top; +} + +@media only screen and (min-width: 480px) { + body { + font-size: 14px; + } + .logo { + max-height: 30px + } +} + +@media only screen and (min-width: 768px) { + body { + font-size: 16px; + } + .logo { + max-height: 40px; + } + article { + margin: 50px 0; + } +} + +@media print { + * { + background: transparent !important; + color: black !important; + filter: none !important; + -ms-filter: none !important; + } + + body { + font-size: 12pt; + max-width: 100%; + } + + a, + a:visited { + text-decoration: underline; + } + + hr { + height: 1px; + border: 0; + border-bottom: 1px solid black; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + padding-right: 1em; + page-break-inside: avoid; + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + @page :left { + margin: 15mm 20mm 15mm 10mm; + } + + @page :right { + margin: 15mm 10mm 15mm 20mm; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} + +nav ul { + margin: 0; + padding: 0; + list-style-type: none; + overflow: hidden; +} + +nav ul li { + /* This allow us to arrange list items in a row, without using float */ + display: inline-block; + list-style-type: none; +} +/* Create a style for the first level items */ +nav > div > ul > li > a { + color: #333 !important; + display: block; + line-height: 2em; + padding: 0.5em 0em; + text-decoration: none; +} + +nav > div.nav-right > ul > li > a { + padding: 0.5em 0.5em; +} + +nav > div > ul > li > a:hover { + color: #aaa !important; +} + +.nav-left { + float: left; +} + +.nav-left ul li { + float: left; +} + +.nav-right ul li { + float: right; +} + +.logo { + margin-right: 0.5em +} + +article img { + margin: 1em 0; +} + +p.date { + font-size: 13px; + color: #666; +} + +ul.articles { + list-style: none; + padding: 0; +} + +#Articles { + margin-top: 2em; +} + +footer { + font-size: 13px; +}