statamic / cms

The core Laravel CMS Composer package
https://statamic.com
Other
3.99k stars 526 forks source link

Issue with highlighting and editing content in Bard #7078

Closed cwyrwas closed 1 year ago

cwyrwas commented 1 year ago

Bug description

When highlighting and editing text using Bard, if I attempt to edit multiple lines, the editor does not maintain the highlighted state. It will frequently attempt to highlight multiple nodes and does not keep the content highlighted.

If I attempt to delete the highlighted text, the editor will restore the text as soon as I click off of the node.

It's a bit difficult to describe this issue, but please check the gif below for a reference: bard-error

I've attempted to disable various settings in the Blueprint to no avail.

How to reproduce

Here is the YAML file containing the entry that I'm referring to, but this happens frequently on any entry in this collection:

`
id: e89473ba-2393-4069-a201-8933d4bcc802
blueprint: blog
title: 'What it Really Takes to Live Best Practices'
featured_image: images/live-salon-spa-best-practices.png
main_content:
  -
    type: paragraph
    content:
      -
        type: text
        text: 'It’s no wonder why many salon/spa owners have an insatiable appetite for learning about “best practices.”'
      -
        type: hard_break
      -
        type: hard_break
      -
        type: text
        text: 'Best practices are best described as the methodologies, systems and behaviors that distinguish the “great” salons/spas from your “not-so-great” counterparts.'
      -
        type: hard_break
      -
        type: hard_break
      -
        type: text
        text: 'But learning about best practices, then implementing them, and then getting these transformative business approaches to stick, is another story.'
      -
        type: hard_break
      -
        type: hard_break
      -
        type: text
        text: 'It’s tough to get best practices to stick in a service business like a salon/spa.'
      -
        type: hard_break
      -
        type: hard_break
      -
        type: text
        text: 'Why?'
      -
        type: hard_break
      -
        type: hard_break
      -
        type: text
        text: 'Because nearly every aspect of the customer service experience is human dependent.'
        marks:
          -
            type: bold
      -
        type: hard_break
      -
        type: hard_break
      -
        type: text
        text: 'And because best practices are human dependent, living them consistently every day is 100% dependent on you, the leader.'
      -
        type: hard_break
      -
        type: hard_break
      -
        type: text
        text: 'As the leader of your salon/spa …'
      -
        type: hard_break
  -
    type: bullet_list
    content:
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'You are the keeper of your company vision.'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'You create and protect your culture.'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'You maintain accountability'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'You inspire your team to achieve breakthroughs'
      -
        type: hard_break
  -
    type: hard_break
  -
    type: text
    text: 'Here are five No-Compromise Leadership strategies to help you and your team implement and live best practices in your salon/spa every day.'
  -
    type: hard_break
  -
    type: ordered_list
    attrs:
      order: 1
    content:
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'They are change initiatives:'
                marks:
                  -
                    type: bold
              -
                type: text
                text: ' Each and every best practice that you want to implement must be regarded as a change initiative. Procedures will need to change. Behaviors will need to change. Responsibilities will need to change. More importantly, each best practice that you implement creates incremental refinements in your culture. '
              -
                type: text
                text: 'KEY: As leader, your prime responsibility is to communicate and clarify the why, what and how each best practice will benefit your company, your employees and your customers. Without clarity and team buy-in, a best practice doesn’t stand a chance of succeeding. '
                marks:
                  -
                    type: italic
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Intimidate yourself:'
                marks:
                  -
                    type: bold
              -
                type: text
                text: ' Implementing a best practice means pushing yourself and your team out of your comfort zones. A best practice means doing what you do at an entirely new level of execution. Bringing out the best in yourself and your team should be challenging. '
              -
                type: text
                text: 'KEY: Motivation requires energy. Inspiration to overcome requires leadership. Confidence comes from practiced discipline. Leadership is about challenging your team to attempt something extraordinary. Something they weren’t sure they could do. This is what living best practices requires.'
                marks:
                  -
                    type: italic
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Break the mold:'
                marks:
                  -
                    type: bold
              -
                type: text
                text: ' Breakthroughs happen when you smash the mold, raise the bar, and discover new possibilities. The mold is about conformity and doing things the same way. Yesterday''s mold may have achieved yesterday''s extraordinary, but yesterday''s extraordinary is today''s ordinary. '
              -
                type: text
                text: 'KEY: When was the last time you broke your company’s trusted mold? It''s probably past its useful life. Invent a new best practices mold to achieve a new breakthrough. Just remember, you''ll have to break that mold too one day'
                marks:
                  -
                    type: italic
              -
                type: text
                text: .
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Choose wisely: '
                marks:
                  -
                    type: bold
              -
                type: text
                text: 'What do you really want your salon/spa to achieve in the next five years? What’s that one thing you want your brand to be recognized for? What levels of performance will make you feel strong, proud and worthy? Answering these questions means selecting the best-of-the-best business practices that will take your company to extraordinary new places. It means locking them into your company''s thinking and culture. '
              -
                type: text
                text: 'KEY: Select and embrace best practices that will create the company you always envisioned. Go beyond the numbers and select those best practices that will make a difference in people’s lives. Remember that achieving benchmarks and amazing critical numbers are a reflection of leadership, culture and behavior. '
                marks:
                  -
                    type: italic
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Believe in yourself:'
                marks:
                  -
                    type: bold
              -
                type: text
                text: ' Life is a roller coaster full of ups and downs. There are times when you need to reach deep to find those morsels of courage to keep you in the game and moving forward. It sure helps to have people around you who believe in you, but in the end, you must believe in your own ability to implement and live best practices every day. '
              -
                type: text
                text: 'KEY: We all have the ability to do better. Not everyone has the courage. Nothing bad ever comes from giving your best effort to something you believe in. Believing is the heart of leadership.'
                marks:
                  -
                    type: italic
      -
        type: hard_break
  -
    type: hard_break
  -
    type: text
    text: 'Here’s my challenge to you:'
    marks:
      -
        type: bold
  -
    type: text
    text: ' Chances are pretty darn good that your salon/spa has a few basic, yet essential, best practices in desperate need of reviving.'
  -
    type: hard_break
  -
    type: bullet_list
    content:
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Does every client go to check out with a service prebook date and retail recommendation?'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Are service providers working within time standards?'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Are your service prices based on cost-per-hour + profit margin?'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Are performance reviews scheduled and thorough?'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Do your financial reports reflect your cash-flow pan?'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Is everyone responsible for every hour available for sale?'
      -
        type: hard_break
      -
        type: list_item
        content:
          -
            type: paragraph
            content:
              -
                type: text
                text: 'Is pay based on overall performance or just individual sales?'
      -
        type: hard_break
  -
    type: hard_break
  -
    type: text
    text: 'These are just a few best practices that every salon/spa needs to be living every day. More importantly, these best practices simply establish the foundation to support more aggressive best practices.'
  -
    type: hard_break
  -
    type: hard_break
  -
    type: text
    text: 'To learn more about inner workings of best practices, '
  -
    type: text
    text: 'register now for our upcoming month-long Onlive course, '
    marks:
      -
        type: bold
  -
    type: text
    text: 'Salon/Spa Best Practices: The Ultimate Client Experience,'
    marks:
      -
        type: bold
      -
        type: link
        attrs:
          href: 'http://go.strategies.com/best-practices-onlive?utm_campaign=onlive-best-practices-july-2019&utm_medium=post&utm_source=blog-link&utm_content=live-best-practices-6-24-19&utm_term=traffic'
  -
    type: text
    text: ' beginning July 8, 2019.'
  -
    type: hard_break
  -
    type: hard_break
  -
    type: text
    text: 'Onlive training events'
    marks:
      -
        type: link
        attrs:
          href: 'https://strategies.com/onlive/?utm_campaign=onlive-best-practices-july-2019&utm_medium=post&utm_source=blog-link&utm_content=live-best-practices-6-24-19&utm_term=traffic'
  -
    type: text
    text: ' are live, interactive, online salon/spa business training designed to give you the support and accountability you need to learn, design, refine and launch your project in one month.'
  -
    type: hard_break
  -
    type: hard_break
  -
    type: text
    text: 'No travel required. All Onlive events are broadcast live from Strategies video studio.'
  -
    type: hard_break
  -
    type: hard_break
  -
    type: text
    text: '{{adrotate banner="28"}}'
category:
  - productivity
tags: false
author: 2
seotamic_title: custom
seotamic_custom_title: 'What it Really Takes to Live Best Practices'
seotamic_title_prepend: false
seotamic_title_append: false
seotamic_meta_description: custom
seotamic_custom_meta_description: 'Imagine if everything about your salon/spa was a reflection of “best practices.” In this blog post, we''ll give you some important strategies to not only implement best practices, but to live them every day in your salon/spa.'
seotamic_open_graph_title: custom
seotamic_custom_open_graph_title: 'What it Really Takes to Live Best Practices'
seotamic_open_graph_description: custom
seotamic_custom_open_graph_description: 'Imagine if everything about your salon/spa was a reflection of “best practices.” In this blog post, we''ll give you some important strategies to not only implement best practices, but to live them every day in your salon/spa.'
twitter_title: general
twitter_description: general

`

Here is the Blueprint for the collection:

`title: Blog
sections:
  main:
    display: Main
    fields:
      -
        handle: title
        field:
          type: text
          required: true
          validate:
            - required
      -
        handle: featured_image
        field:
          mode: list
          container: uploads
          folder: images
          restrict: true
          allow_uploads: true
          show_filename: true
          display: 'Featured Image'
          type: assets
          icon: assets
          listable: hidden
          instructions_position: above
          max_files: 1
      -
        handle: main_content
        field:
          always_show_set_button: false
          buttons:
            - h2
            - h3
            - bold
            - italic
            - unorderedlist
            - orderedlist
            - removeformat
            - quote
            - anchor
            - image
            - table
            - strikethrough
            - code
          container: assets
          save_html: false
          toolbar_mode: fixed
          link_noopener: false
          link_noreferrer: false
          target_blank: false
          reading_time: true
          fullscreen: true
          allow_source: true
          type: bard
          listable: hidden
          display: 'Main Content'
          sets:
            code_block:
              display: 'Code Block'
              fields:
                -
                  handle: code
                  field:
                    theme: material
                    mode: php
                    indent_type: tabs
                    indent_size: 4
                    key_map: default
                    line_numbers: true
                    line_wrapping: true
                    type: code
                    listable: hidden
                    display: Code
          instructions_position: above
          link_collections:
            - advertisement
          enable_input_rules: true
          enable_paste_rules: true
          antlers: true
          visibility: visible
          always_save: false
          collapse: false
          previews: true
          remove_empty_nodes: true
  sidebar:
    display: Sidebar
    fields:
      -
        handle: slug
        field:
          type: slug
          localizable: true
          validate:
            - required
            - 'unique_entry_value:{collection},{id},{site}'
      -
        handle: date
        field:
          type: date
          required: true
          validate:
            - required
      -
        handle: author
        field:
          max_items: 1
          mode: select
          type: users
          listable: true
          display: Author
          default: current
          instructions_position: above
          visibility: visible
          always_save: false
      -
        handle: category
        field:
          mode: default
          create: true
          taxonomies:
            - mmwu_categories
          display: Category
          type: terms
          icon: taxonomy
          listable: hidden
          instructions_position: above
      -
        handle: tags
        field:
          mode: default
          create: true
          taxonomies:
            - mmwu_tags
          display: Tags
          type: terms
          icon: taxonomy
          listable: hidden
          instructions_position: above
      -
        handle: mmwu_categories
        field:
          type: terms
          taxonomies:
            - mmwu_categories
          display: 'MMWU Categories'
          mode: select
      -
        handle: mmwu_tags
        field:
          type: terms
          taxonomies:
            - mmwu_tags
          display: 'MMWU Tags'
          mode: select
  SEO:
    display: SEO
    fields:
      -
        handle: seotamic_meta
        field:
          display: Meta
          listable: hidden
          type: section
          localizable: true
      -
        handle: seotamic_title
        field:
          options:
            title: Title
            custom: Custom
          clearable: false
          multiple: false
          searchable: true
          taggable: false
          push_tags: false
          cast_booleans: false
          type: select
          localizable: true
          listable: hidden
          default: title
          display: Title
          instructions: 'It can be used to determine the title used on search engine results pages.'
      -
        handle: seotamic_custom_title
        field:
          input_type: text
          character_limit: 100
          type: text
          localizable: true
          listable: hidden
          display: 'Custom Meta Title'
          if:
            seotamic_title: 'equals custom'
      -
        handle: seotamic_title_prepend
        field:
          type: toggle
          instructions: 'Prepends to title the text set in General SEO settings'
          localizable: true
          default: true
          width: 50
          listable: hidden
          display: 'Title prepend'
      -
        handle: seotamic_title_append
        field:
          type: toggle
          localizable: true
          instructions: 'Appends to title the text set in General SEO settings'
          width: 50
          listable: hidden
          default: true
          display: 'Title append'
      -
        handle: seotamic_meta_description
        field:
          options:
            empty: Empty
            general: General
            custom: Custom
          clearable: false
          default: empty
          multiple: false
          searchable: true
          taggable: false
          push_tags: false
          cast_booleans: false
          type: select
          instructions: 'It can be used to determine the text used under the title on search engine results pages. If empty, search engines will automatically generate this text.'
          localizable: true
          listable: hidden
          display: 'Meta description'
      -
        handle: seotamic_custom_meta_description
        field:
          input_type: text
          character_limit: 200
          type: textarea
          localizable: true
          listable: hidden
          display: 'Custom Meta description'
          if:
            seotamic_meta_description: 'equals custom'
      -
        handle: seotamic_canonical
        field:
          type: link
          instructions: 'By default it will be set to the page url'
          localizable: true
          listable: hidden
          display: Canonical
      -
        handle: seotamic_social
        field:
          type: section
          localizable: true
          listable: hidden
          display: Social
      -
        handle: seotamic_open_graph_title
        field:
          options:
            title: Title
            general: General
            custom: Custom
          clearable: false
          default: title
          multiple: false
          searchable: true
          taggable: false
          push_tags: false
          cast_booleans: false
          type: select
          localizable: true
          listable: hidden
          display: 'Open Graph title'
      -
        handle: seotamic_custom_open_graph_title
        field:
          input_type: text
          character_limit: 100
          type: text
          localizable: true
          listable: hidden
          display: 'Custom Open Graph title'
          if:
            seotamic_open_graph_title: 'equals custom'
      -
        handle: seotamic_open_graph_description
        field:
          options:
            meta: 'Meta description'
            general: 'General description'
            custom: Custom
          clearable: false
          default: general
          multiple: false
          searchable: true
          taggable: false
          push_tags: false
          cast_booleans: false
          type: select
          localizable: true
          listable: hidden
          display: 'Open Graph description'
      -
        handle: seotamic_custom_open_graph_description
        field:
          input_type: text
          character_limit: 200
          type: textarea
          localizable: true
          listable: hidden
          display: 'Custom Open Graph description'
          if:
            seotamic_open_graph_description: 'equals custom'
      -
        handle: seotamic_twitter_title
        field:
          options:
            title: Title
            general: General
            custom: Custom
          clearable: false
          default: title
          multiple: false
          searchable: true
          taggable: false
          push_tags: false
          cast_booleans: false
          type: select
          localizable: true
          listable: hidden
          display: 'Twitter title'
      -
        handle: seotamic_custom_twitter_title
        field:
          input_type: text
          character_limit: 100
          type: text
          localizable: true
          listable: hidden
          display: 'Custom Twitter title'
          if:
            seotamic_twitter_title: 'equals custom'
      -
        handle: seotamic_twitter_description
        field:
          options:
            meta: 'Meta description'
            general: 'General description'
            custom: Custom
          clearable: false
          default: general
          multiple: false
          searchable: true
          taggable: false
          push_tags: false
          cast_booleans: false
          type: select
          localizable: true
          listable: hidden
          display: 'Twitter description'
      -
        handle: seotamic_custom_twitter_description
        field:
          input_type: text
          character_limit: 200
          type: textarea
          localizable: true
          listable: hidden
          display: 'Custom Twitter description'
          if:
            seotamic_twitter_description: 'equals custom'
      -
        handle: seotamic_image
        field:
          container: uploads
          mode: grid
          restrict: false
          allow_uploads: true
          max_files: 1
          type: assets
          instructions: "If not set, the general image will be used. It's best to use an image with a 1.91:1 aspect ratio that is at least 1200px wide for universal support. The image will be resized to 1200 width."
          localizable: true
          listable: hidden
          display: Image
          instructions_position: above
          visibility: visible
          always_save: false
          show_filename: true
`

### Logs

```shell
There's nothing in the logs referring to this. It appears to be a JS issue.

Environment

This happens both on my local server and production environment.

Environment
Application Name: Strategies
Laravel Version: 9.26.1
PHP Version: 8.1.6
Composer Version: 2.4.1
Environment: local
Debug Mode: ENABLED
URL: localhost
Maintenance Mode: OFF

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: statamic
Database: mysql
Logs: stack / single
Mail: ses
Queue: sync
Session: file

Statamic
Addons: 7
Antlers: runtime
Version: 3.3.56 PRO

Statamic Addons
aryehraber/statamic-captcha: 1.9.0
cnj/seotamic: 2.1.0
doublethreedigital/duplicator: 2.3.2
jacksleight/statamic-bard-mutator: 1.1.2
jonassiewertsen/statamic-livewire: 2.9.0
optimoapps/statamic-bard-text-align: 1.0.2
visuellverstehen/statamic-classify: 2.4.0

### Installation

Existing Laravel app

### Antlers Parser

runtime (new)

### Additional details

_No response_
jasonvarga commented 1 year ago

What browser are you using?

jasonvarga commented 1 year ago

Nevermind, I get it in any browser when I use your exact content. 👍

It seems to only bug out if you highlight towards the end of the content. I can highlight like crazy towards the beginning and it's fine.

cwyrwas commented 1 year ago

I'm using Firefox as my default, but I get the issue in both Safari, Firefox, and Chrome.

I'm glad to see that I'm not crazy! I've been trying to debug this for days now. It's definitely a strange one.

jacksleight commented 1 year ago

It'll be because of all those text and hard_break nodes at the root of the data, they're not valid for the schema and need to be inside paragraph nodes. Run into that before.

Easiest fix is Select All, Cut, Paste, and Tiptap will restructure it to match the schema.

jasonvarga commented 1 year ago

That fixed it here. Nice one, Jack.

I thought those line breaks looked odd.

I wonder how you got the content into that state to begin with. 🤔

jacksleight commented 1 year ago

I've only ever run into that when importing external HTML with html-to-prosemirror. Unfortunately that doesn't follow the schema and will put text nodes at the root if there's text at the root of the original HTML.

cwyrwas commented 1 year ago

THANK YOU!

That was probably my fault. I wrote a migration command to migrate old Wordpress posts/comments into Statamic Entries. It was a huge project which consisted of 10+ years of data. All those hard_breaks must have come from that conversion script since it used the html-to-prosemirror package

For now, I'll just pass along to the company that they can Select All and then paste until I can work out how to clean up the schema that it generates.

jacksleight commented 1 year ago

You could scan through the output from the renderer and tidy those up, see https://github.com/statamic/cms/issues/7050#issuecomment-1314032292. You'd need to expand on that to cover hard_break as well.

jasonvarga commented 1 year ago

Great detective work. Closing this one down. 🕵️ 🔎

cwyrwas commented 1 year ago

Awesome, thank you again! You guys rock.