From 67f36886a1b85ca752c0dcefb77955814eda010d Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Thu, 27 Nov 2025 06:53:03 +0100 Subject: [PATCH 1/5] Update lexxy to latest Color highlighter, more code languages... --- Gemfile.lock | 2 +- app/assets/stylesheets/_global.css | 21 +++++++ app/assets/stylesheets/lexxy.css | 65 ++++++++++++++++++++ app/assets/stylesheets/rich-text-content.css | 4 ++ 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index b9c3489b5..57f59eabb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -237,7 +237,7 @@ GEM logger (~> 1.6) letter_opener (1.10.0) launchy (>= 2.2, < 4) - lexxy (0.1.20.beta) + lexxy (0.1.22.beta) rails (>= 8.0.2) lint_roller (1.1.0) logger (1.7.0) diff --git a/app/assets/stylesheets/_global.css b/app/assets/stylesheets/_global.css index 414d4c696..13988f027 100644 --- a/app/assets/stylesheets/_global.css +++ b/app/assets/stylesheets/_global.css @@ -219,6 +219,27 @@ --color-card-7: oklch(var(--lch-purple-medium)); --color-card-8: oklch(var(--lch-pink-medium)); + /* Colors: Highlighter */ + --highlight-1: rgb(136, 118, 38); + --highlight-2: rgb(185, 94, 6); + --highlight-3: rgb(207, 0, 0); + --highlight-4: rgb(216, 28, 170); + --highlight-5: rgb(144, 19, 254); + --highlight-6: rgb(5, 98, 185); + --highlight-7: rgb(17, 138, 15); + --highlight-8: rgb(148, 82, 22); + --highlight-9: rgb(102, 102, 102); + + --highlight-bg-1: rgba(229, 223, 6, 0.3); + --highlight-bg-2: rgba(255, 185, 87, 0.3); + --highlight-bg-3: rgba(255, 118, 118, 0.3); + --highlight-bg-4: rgba(248, 137, 216, 0.3); + --highlight-bg-5: rgba(190, 165, 255, 0.3); + --highlight-bg-6: rgba(124, 192, 252, 0.3); + --highlight-bg-7: rgba(140, 255, 129, 0.3); + --highlight-bg-8: rgba(221, 170, 123, 0.3); + --highlight-bg-9: rgba(200, 200, 200, 0.3); + /* Colors: Syntax highlighting */ --color-code-token__att: oklch(var(--lch-blue-dark)); --color-code-token__comment: oklch(var(--lch-ink-medium)); diff --git a/app/assets/stylesheets/lexxy.css b/app/assets/stylesheets/lexxy.css index e3f8f8cbd..84225a55b 100644 --- a/app/assets/stylesheets/lexxy.css +++ b/app/assets/stylesheets/lexxy.css @@ -167,6 +167,71 @@ flex: 1; } + :where(.lexxy-highlight-dialog) { + .lexxy-highlight-dialog-content { + display: flex; + flex-direction: column; + gap: 0.75ch; + + [data-button-group] { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 0.5ch; + } + + button { + appearance: none; + background: var(--color-canvas); + block-size: 3.5ch; + border: none; + border-radius: 0.5ch; + color: var(--color-ink); + cursor: pointer; + display: grid; + font-size: inherit; + inline-size: 3.5ch; + place-items: center; + position: relative; + outline: none; + + &:after { + align-self: center; + content: "Aa"; + display: inline-block; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + position: absolute; + inset-block-start: 0; + inset-block-end: 0; + inset-inline-end: 0; + inset-inline-start: 0; + } + + &:hover { + opacity: 0.8; + } + + &[aria-pressed="true"] { + box-shadow: 0 0 0 1px var(--color-canvas), 0 0 0 3px var(--color-link); + + &:after { + content: "✓"; + } + } + } + + .lexxy-highlight-dialog-reset { + inline-size: 100%; + + &[disabled] { + display: none; + } + + &:after { display: none; } + } + } + } + /* Based on .input, .input--select */ .lexxy-code-language-picker { -webkit-appearance: none; diff --git a/app/assets/stylesheets/rich-text-content.css b/app/assets/stylesheets/rich-text-content.css index 2d465c935..9f106fbd6 100644 --- a/app/assets/stylesheets/rich-text-content.css +++ b/app/assets/stylesheets/rich-text-content.css @@ -40,6 +40,10 @@ text-decoration: line-through; } + :where(mark, .lexxy-content__highlight) { + background-color: transparent; + color: inherit; + } :where(p, blockquote) { letter-spacing: -0.005ch; From 48ef7d0b983443c0ef8af101a8cc640717866343 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Fri, 5 Dec 2025 11:24:52 +0100 Subject: [PATCH 2/5] Update Lexxy and hide the highlighter for now --- Gemfile.lock | 2 +- Gemfile.saas.lock | 2 +- app/assets/stylesheets/lexxy.css | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 57f59eabb..43d4e718b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -237,7 +237,7 @@ GEM logger (~> 1.6) letter_opener (1.10.0) launchy (>= 2.2, < 4) - lexxy (0.1.22.beta) + lexxy (0.1.23.beta) rails (>= 8.0.2) lint_roller (1.1.0) logger (1.7.0) diff --git a/Gemfile.saas.lock b/Gemfile.saas.lock index 5cd308155..39af96f95 100644 --- a/Gemfile.saas.lock +++ b/Gemfile.saas.lock @@ -313,7 +313,7 @@ GEM logger (~> 1.6) letter_opener (1.10.0) launchy (>= 2.2, < 4) - lexxy (0.1.20.beta) + lexxy (0.1.23.beta) rails (>= 8.0.2) lint_roller (1.1.0) logger (1.7.0) diff --git a/app/assets/stylesheets/lexxy.css b/app/assets/stylesheets/lexxy.css index 84225a55b..a737cc590 100644 --- a/app/assets/stylesheets/lexxy.css +++ b/app/assets/stylesheets/lexxy.css @@ -152,6 +152,11 @@ } } + /* Temporarily hide the highlighter button until we tackle the styles */ + [name="strikethrough"] + .lexxy-editor__toolbar-dropdown { + display: none; + } + .lexxy-editor__toolbar-overflow-menu { background-color: var(--color-canvas); border-radius: 0.5ch; From 80a0eb3a4d90df6653695ac65965729c322b0222 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Fri, 5 Dec 2025 11:30:58 +0100 Subject: [PATCH 3/5] Fix system test --- test/system/smoke_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/system/smoke_test.rb b/test/system/smoke_test.rb index e4bc9cae9..539921b93 100644 --- a/test/system/smoke_test.rb +++ b/test/system/smoke_test.rb @@ -24,7 +24,7 @@ class SmokeTest < ApplicationSystemTestCase within("form lexxy-editor figure.attachment[data-content-type='image/jpeg']") do assert_selector "img[src*='/rails/active_storage']" - assert_selector "figcaption input[placeholder='moon.jpg']" + assert_selector "figcaption textarea[placeholder='moon.jpg']" end click_on "Post" From 1ac5867d5cea26e8ff8a27ca4c5412499c43231f Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Fri, 5 Dec 2025 11:41:11 +0100 Subject: [PATCH 4/5] These are textareas now --- app/assets/stylesheets/lexxy.css | 15 +++++++++++++++ app/assets/stylesheets/rich-text-content.css | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/lexxy.css b/app/assets/stylesheets/lexxy.css index a737cc590..939a3fb5e 100644 --- a/app/assets/stylesheets/lexxy.css +++ b/app/assets/stylesheets/lexxy.css @@ -237,6 +237,21 @@ } } + .attachment__caption { + textarea { + background-color: var(--input-background, transparent); + border-radius: var(--input-border-radius, 0.5em); + border: var(--input-border-size, 1px) solid var(--input-border-color, var(--color-ink-medium)); + color: var(--input-color, var(--color-ink)); + font-size: max(16px, 1em); + inline-size: 100%; + line-height: inherit; + max-inline-size: 100%; + padding: var(--input-padding, 0.5em 0.8em); + resize: none; + } + } + /* Based on .input, .input--select */ .lexxy-code-language-picker { -webkit-appearance: none; diff --git a/app/assets/stylesheets/rich-text-content.css b/app/assets/stylesheets/rich-text-content.css index 9f106fbd6..ca3715cba 100644 --- a/app/assets/stylesheets/rich-text-content.css +++ b/app/assets/stylesheets/rich-text-content.css @@ -205,7 +205,7 @@ color: color-mix(in oklch, var(--color-ink) 66%, transparent); font-size: var(--text-small); - .input { + textarea { --input-border-radius: 0.3em; --input-border-size: 0; --input-padding: 0; From 2cf718835b715190f78016eba0c0c6612854830f Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 5 Dec 2025 11:16:11 -0600 Subject: [PATCH 5/5] Style updates for latest Lexxy version Style nested lists, highlight dropdown, and link dropdown. Also organizes the CSS file a bit better. --- app/assets/stylesheets/lexxy.css | 331 +++++++++---------- app/assets/stylesheets/rich-text-content.css | 14 +- 2 files changed, 172 insertions(+), 173 deletions(-) diff --git a/app/assets/stylesheets/lexxy.css b/app/assets/stylesheets/lexxy.css index 939a3fb5e..7b647caca 100644 --- a/app/assets/stylesheets/lexxy.css +++ b/app/assets/stylesheets/lexxy.css @@ -1,61 +1,39 @@ @layer components { + /* Editor + /* ------------------------------------------------------------------------ */ + lexxy-editor { display: block; position: relative; overflow: visible; figure.node--selected { - &:not(:has(img)) { - outline: var(--focus-ring-size) solid var(--focus-ring-color); - outline-offset: var(--focus-ring-offset); - } - &:has(img) { img { outline: var(--focus-ring-size) solid var(--focus-ring-color); outline-offset: var(--focus-ring-offset); } } - } - /* Lexical still uses the `lexxy-editor--empty` class if you have a list - started with no other characters. Here, we won't show the placeholder if - you've clicked the List button in the toolbar. */ - &.lexxy-editor--empty { - .lexxy-editor__content:not(:has(ul, ol))::before { - content: attr(placeholder); - color: currentColor; - cursor: text; - opacity: 0.66; - pointer-events: none; - position: absolute; - white-space: pre-line; + &:not(:has(img)) { + outline: var(--focus-ring-size) solid var(--focus-ring-color); + outline-offset: var(--focus-ring-offset); } } } - .lexxy-dialog-actions { - display: flex; - font-size: var(--text-x-small); - flex: 1 1 0%; - gap: var(--inline-space-half); - margin-block-start: var(--block-space-half); - - .btn { - --radius: 0.3em; - - inline-size: 100%; - justify-content: center; - - &:is([type="submit"]) { - --btn-background: var(--card-color, var(--color-link)); - --btn-color: var(--color-ink-inverted); - --focus-ring-color: var(--card-color, var(--color-link)); - } - } - - span { - inline-size: 100%; + /* Lexical uses the `lexxy-editor--empty` class even if you have a list + * started but haven't added other characters. Here, we hide the placeholder + * when you click the List button in the toolbar. */ + .lexxy-editor--empty { + .lexxy-editor__content:not(:has(ul, ol))::before { + content: attr(placeholder); + color: currentColor; + cursor: text; + opacity: 0.66; + pointer-events: none; + position: absolute; + white-space: pre-line; } } @@ -64,14 +42,9 @@ min-block-size: calc(7lh + var(--block-space)); outline: 0; - * { - &:first-child { - margin-block-start: 0; - } - - &:last-child { - margin-block-end: 0; - } + /* Allow color highlights to show through a bit */ + ::selection { + background: oklch(var(--lch-blue-light) / 0.5); } } @@ -81,6 +54,40 @@ outline: 2px dashed var(--color-selected-dark); } + .lexxy-code-language-picker { + -webkit-appearance: none; + appearance: none; + background-color: var(--color-canvas); + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m12 19.5c-.7 0-1.3-.3-1.7-.8l-9.8-11.1c-.7-.8-.6-1.9.2-2.6.8-.6 1.9-.6 2.5.2l8.6 9.8c0 .1.2.1.4 0l8.6-9.8c.7-.8 1.8-.9 2.6-.2s.9 1.8.2 2.6l-9.8 11.1c-.4.5-1.1.8-1.7.8z' fill='%23000'/%3E%3C/svg%3E"); + background-position: center right 0.9em; + background-repeat: no-repeat; + background-size: 0.5em; + border: 1px solid var(--color-ink-light); + border-radius: 99rem; + color: var(--color-ink); + cursor: pointer; + font-family: var(--font-base); + font-size: var(--text-x-small); + font-weight: 500; + inset-inline-end: 0; + line-height: 1.15lh; + margin: 0.75ch 0.75ch 0 0; + padding-inline: 1.5ch 1.8em; + text-align: start; + + @media (prefers-color-scheme: dark) { + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m12 19.5c-.7 0-1.3-.3-1.7-.8l-9.8-11.1c-.7-.8-.6-1.9.2-2.6.8-.6 1.9-.6 2.5.2l8.6 9.8c0 .1.2.1.4 0l8.6-9.8c.7-.8 1.8-.9 2.6-.2s.9 1.8.2 2.6l-9.8 11.1c-.4.5-1.1.8-1.7.8z' fill='%23fff'/%3E%3C/svg%3E"); + } + + option { + background-color: var(--color-canvas); + color: var(--color-ink); + } + } + + /* Toolbar + /* ------------------------------------------------------------------------ */ + lexxy-toolbar { --lexxy-toolbar-icon-size: 1em; @@ -96,21 +103,6 @@ position: sticky; inset-block-start: 0; z-index: 1; - - dialog { - background-color: var(--color-canvas); - border: 1px solid var(--color-ink-lighter); - border-radius: 0.5em; - box-shadow: var(--shadow); - color: var(--color-ink); - padding: var(--block-space) calc(var(--inline-space) * 1.5); - position: absolute; - z-index: 1; - - .input[type="url"] { - min-inline-size: 30ch; - } - } } .lexxy-editor__toolbar-button { @@ -152,11 +144,6 @@ } } - /* Temporarily hide the highlighter button until we tackle the styles */ - [name="strikethrough"] + .lexxy-editor__toolbar-dropdown { - display: none; - } - .lexxy-editor__toolbar-overflow-menu { background-color: var(--color-canvas); border-radius: 0.5ch; @@ -168,122 +155,122 @@ z-index: 1; } - :where(.lexxy-editor__toolbar-spacer) { + .lexxy-editor__toolbar-spacer { flex: 1; } - :where(.lexxy-highlight-dialog) { - .lexxy-highlight-dialog-content { - display: flex; - flex-direction: column; - gap: 0.75ch; + /* Dropdowns + /* ------------------------------------------------------------------------ */ - [data-button-group] { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 0.5ch; - } + .lexxy-editor__toolbar-dropdown-content { + --lexxy-dropdown-padding: 0.75rem; + --lexxy-dropdown-btn-size: 2rem; - button { - appearance: none; - background: var(--color-canvas); - block-size: 3.5ch; - border: none; - border-radius: 0.5ch; - color: var(--color-ink); - cursor: pointer; - display: grid; - font-size: inherit; - inline-size: 3.5ch; - place-items: center; - position: relative; - outline: none; - - &:after { - align-self: center; - content: "Aa"; - display: inline-block; - font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - position: absolute; - inset-block-start: 0; - inset-block-end: 0; - inset-inline-end: 0; - inset-inline-start: 0; - } - - &:hover { - opacity: 0.8; - } - - &[aria-pressed="true"] { - box-shadow: 0 0 0 1px var(--color-canvas), 0 0 0 3px var(--color-link); - - &:after { - content: "✓"; - } - } - } - - .lexxy-highlight-dialog-reset { - inline-size: 100%; - - &[disabled] { - display: none; - } - - &:after { display: none; } - } - } - } - - .attachment__caption { - textarea { - background-color: var(--input-background, transparent); - border-radius: var(--input-border-radius, 0.5em); - border: var(--input-border-size, 1px) solid var(--input-border-color, var(--color-ink-medium)); - color: var(--input-color, var(--color-ink)); - font-size: max(16px, 1em); - inline-size: 100%; - line-height: inherit; - max-inline-size: 100%; - padding: var(--input-padding, 0.5em 0.8em); - resize: none; - } - } - - /* Based on .input, .input--select */ - .lexxy-code-language-picker { - -webkit-appearance: none; - appearance: none; background-color: var(--color-canvas); - background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m12 19.5c-.7 0-1.3-.3-1.7-.8l-9.8-11.1c-.7-.8-.6-1.9.2-2.6.8-.6 1.9-.6 2.5.2l8.6 9.8c0 .1.2.1.4 0l8.6-9.8c.7-.8 1.8-.9 2.6-.2s.9 1.8.2 2.6l-9.8 11.1c-.4.5-1.1.8-1.7.8z' fill='%23000'/%3E%3C/svg%3E"); - background-position: center right 0.9em; - background-repeat: no-repeat; - background-size: 0.5em; border: 1px solid var(--color-ink-lighter); - border-radius: 0.3em; + border-radius: 0.5em; + box-shadow: var(--shadow); color: var(--color-ink); - font-family: var(--font-base); - font-size: var(--text-x-small); - font-weight: 500; - inset-inline-end: 0; - line-height: inherit; - margin: 0.3em 0.3em 0 0; - padding: 0.5em 1.8em 0.5em 1.2em; - text-align: start; + font-size: var(--text-small); + padding: var(--lexxy-dropdown-padding); + position: absolute; + z-index: 1; - @media (prefers-color-scheme: dark) { - background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m12 19.5c-.7 0-1.3-.3-1.7-.8l-9.8-11.1c-.7-.8-.6-1.9.2-2.6.8-.6 1.9-.6 2.5.2l8.6 9.8c0 .1.2.1.4 0l8.6-9.8c.7-.8 1.8-.9 2.6-.2s.9 1.8.2 2.6l-9.8 11.1c-.4.5-1.1.8-1.7.8z' fill='%23fff'/%3E%3C/svg%3E"); - } - - option { - background-color: var(--color-canvas); - color: var(--color-ink); + button { + block-size: var(--lexxy-dropdown-btn-size); } } - /* Prompt + .lexxy-editor__toolbar-dropdown-actions { + display: flex; + font-size: var(--text-x-small); + flex: 1 1 0%; + gap: var(--lexxy-dropdown-padding); + margin-block-start: var(--lexxy-dropdown-padding); + + .btn { + --radius: 99rem; + + inline-size: 100%; + } + + .btn[type="submit"] { + --btn-background: var(--card-color, var(--color-link)); + --btn-color: var(--color-ink-inverted); + --focus-ring-color: var(--card-color, var(--color-link)); + } + + span { + inline-size: 100%; + } + } + + lexxy-link-dropdown { + .input { + min-inline-size: 30ch; + } + } + + lexxy-highlight-dropdown { + --gap: 0.5ch; + + [data-button-group] { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: var(--gap); + + + & { + margin-block-start: var(--gap); + } + } + } + + .lexxy-highlight-button { + --outline-color: oklch(var(--lch-ink-darkest) / 0.2); + + appearance: none; + background: var(--color-canvas); + border: none; + border-radius: 0.5ch; + display: grid; + font-weight: 500; + inline-size: var(--lexxy-dropdown-btn-size); + place-items: center; + position: relative; + outline: none; + + &:after { + content: "Aa"; + } + + &:hover, + &[aria-pressed="true"] { + box-shadow: 0 0 0 1px var(--color-canvas), 0 0 0 3px var(--outline-color); + } + + &[aria-pressed="true"] { + --outline-color: var(--color-link); + + &:after { + content: "✓"; + } + } + } + + .lexxy-editor__toolbar-dropdown-reset { + background: var(--color-canvas); + border: 1px solid var(--color-ink-light); + border-radius: 99rem; + inline-size: 100%; + margin-block-start: var(--lexxy-dropdown-padding); + + &[disabled] { + display: none; + } + } + + /* Prompt ,enu (@mentions, etc.) /* ------------------------------------------------------------------------ */ .lexxy-prompt-menu { diff --git a/app/assets/stylesheets/rich-text-content.css b/app/assets/stylesheets/rich-text-content.css index ca3715cba..fdb8224fc 100644 --- a/app/assets/stylesheets/rich-text-content.css +++ b/app/assets/stylesheets/rich-text-content.css @@ -28,6 +28,14 @@ } } + :where(ol, ul) { + padding-inline-start: 3ch; + } + + :where(li:has(li)) { + list-style: none; + } + :where(b, strong, .lexxy-content__bold) { font-weight: 700; } @@ -210,9 +218,12 @@ --input-border-size: 0; --input-padding: 0; + background-color: var(--input-background, transparent); + border: none; color: inherit; inline-size: 100%; max-inline-size: 100%; + resize: none; text-align: center; &:focus { @@ -278,7 +289,8 @@ display: flex; flex-wrap: wrap; gap: 1ch; - inline-size: auto; + inline-size: 100%; + margin-inline: 0; .attachment__caption { display: grid;