diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..8b3d575 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,63 @@ +name: Bug Report +description: Something behaves, draws or builds wrong. +title: "[BUG REPORT] " +labels: [bug] +body: + - type: dropdown + id: subject + attributes: + label: App + options: + - ElyOffice + - ElyDoc + - ElyTable + - ElyShow + - ElyPDF + - ElyMark + - ElyTree + validations: + required: true + - type: dropdown + id: where + attributes: + label: Where + options: + - macOS + - Windows + - Linux + validations: + required: true + - type: textarea + id: what + attributes: + label: What happened + description: A screenshot or a short recording helps most. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + placeholder: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + id: expected + attributes: + label: What you expected + validations: + required: true + - type: input + id: version + attributes: + label: "Version" + placeholder: "0.1.8" + - type: textarea + id: logs + attributes: + label: Logs + description: Terminal output or error messages. + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..2a54e2f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ely Office + url: https://office.elygpui.com + about: "Downloads and what each app does." diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..d661c90 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,42 @@ +name: Feature Request +description: Something new, or something that should do more. +title: "[FEATURE REQUEST] " +labels: [enhancement] +body: + - type: dropdown + id: subject + attributes: + label: App + options: + - ElyOffice + - ElyDoc + - ElyTable + - ElyShow + - ElyPDF + - ElyMark + - ElyTree + validations: + required: false + - type: textarea + id: use + attributes: + label: What it is for + description: The problem, and the moment you need it in. + validations: + required: true + - type: textarea + id: behavior + attributes: + label: How it should work + - type: textarea + id: references + attributes: + label: References + description: Links or screenshots of it elsewhere. + - type: checkboxes + id: searched + attributes: + label: Checked + options: + - label: I searched the open issues for the same request. + required: true diff --git a/.github/ISSUE_TEMPLATE/other.yml b/.github/ISSUE_TEMPLATE/other.yml new file mode 100644 index 0000000..42b381e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.yml @@ -0,0 +1,11 @@ +name: Other +description: Questions, docs, ideas, anything else. +title: "[Other] " +labels: [question] +body: + - type: textarea + id: what + attributes: + label: What's on your mind + validations: + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c822346 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,35 @@ + + +## Type + +- [ ] **[FEATURE REQUEST]**: a new feature or an improvement +- [ ] **[BUG REPORT]**: a bug fix +- [ ] **[Other]**: docs, refactoring, CI, dependencies or chores + +## What and why + + + +## Bug details + + + +- Steps to reproduce: +- Expected: +- Actual: +- Cause and fix: + +## How it was tested + + + +## Checklist + +- [ ] The title starts with `[FEATURE REQUEST]`, `[BUG REPORT]` or `[Other]` +- [ ] Tests and the build pass locally +- [ ] Docs are updated if behaviour changed diff --git a/crates/ely-lok/src/view/render.rs b/crates/ely-lok/src/view/render.rs index 7e83263..42a76e1 100644 --- a/crates/ely-lok/src/view/render.rs +++ b/crates/ely-lok/src/view/render.rs @@ -46,7 +46,9 @@ impl Render for LokView { let focus = self.focus.clone(); let colors = &cx.theme().colors; let paper = super::paper(); - let (selection_color, caret_color) = (colors.selection, colors.focus); + // Opaque overlays hide glyphs already painted in the tiles. + let selection_color = colors.selection.alpha(colors.selection.a.min(0.3)); + let caret_color = colors.focus; let canvas_color = colors.sunken; let caret_width = cx.theme().caret_width().to_pixels(window.rem_size()); self.handle_size = cx.theme().handle_hit().to_pixels(window.rem_size()); diff --git a/crates/ely-lok/src/view/tests/theme.rs b/crates/ely-lok/src/view/tests/theme.rs index 2ca2413..16d5d6b 100644 --- a/crates/ely-lok/src/view/tests/theme.rs +++ b/crates/ely-lok/src/view/tests/theme.rs @@ -83,6 +83,93 @@ fn a_dark_app_paints_the_page_white() { assert!(light > 240, "the view paints the page white: {light}"); } +#[test] +fn selection_keeps_document_text_readable_across_themes() { + use ely_gpui_component::{ + devtools::{contrast, over}, + theme::{ActiveTheme, Mode, Palette, Theme}, + }; + + let Some(install) = crate::client::tests::install() else { + eprintln!("SKIP: no pinned LOK build"); + return; + }; + let mut cx = TestAppContext::single(); + cx.update(ely_gpui_component::init_for_tests); + let (view, worker) = cx.update(|app| live_view(app, &install)); + let doc = cx.update(|app| view.read(app).doc.clone()); + let shown = view.clone(); + let window = cx.add_window(move |_, _| Host(shown)); + worker + .lock() + .expect("the worker") + .run(&doc, "edit.select_all", &[]) + .expect("select all"); + assert!( + settle(&cx, |app| !view.read(app).selection.is_empty()), + "the selection arrives" + ); + + let mut themes = vec![ + ("dark".to_string(), Mode::Dark, None), + ("light".to_string(), Mode::Light, None), + ]; + for (name, mode, color) in [ + ("omarchy dark", Mode::Dark, 0x292e42), + ("omarchy light", Mode::Light, 0xccd0da), + ] { + let mut palette = match mode { + Mode::Dark => Palette::dark(false), + Mode::Light => Palette::light(false), + }; + palette.selection = gpui::rgb(color).into(); + themes.push((name.to_string(), mode, Some(palette))); + } + let mut translucent = Palette::dark(false); + translucent.selection = gpui::rgb(0x292e42).into(); + translucent.selection.a = 0.15; + themes.push(("translucent".to_string(), Mode::Dark, Some(translucent))); + + for (name, mode, palette) in themes { + cx.update(|app| { + Theme::set_palette(mode, palette, app); + Theme::set_mode_now(mode, app); + }); + cx.update_window(window.into(), |_, window, app| { + window.draw(app).clear(app); + let source = app.theme().colors.selection; + let selected: Vec<_> = window + .painted_quads() + .into_iter() + .filter_map(|quad| quad.background.as_solid()) + .filter(|color| color.alpha(1.0) == source.alpha(1.0)) + .collect(); + assert!(!selected.is_empty(), "{name}: the selection is painted"); + for color in selected { + for (text, background) in [(gpui::black(), paper()), (gpui::white(), gpui::black())] + { + let ratio = contrast(over(color, text), over(color, background)); + assert!( + ratio >= 4.5, + "{name}: selected text stays readable: {ratio}" + ); + } + assert!( + color.a > 0.0 && color.a <= source.a, + "{name}: the theme's opacity is respected" + ); + if source.a < 0.3 { + assert_eq!( + color.a, source.a, + "{name}: a translucent theme stays unchanged" + ); + } + } + }) + .expect("the window lives"); + } +} + #[test] fn a_failed_tile_reports_and_retries_on_a_move() { let Some(install) = crate::client::tests::install() else {