From f51cda4a936c5aafff9a2bc9b611885311c66a41 Mon Sep 17 00:00:00 2001 From: Orual Date: Sat, 20 Dec 2025 22:16:22 -0500 Subject: [PATCH] editor bugfixes, RTL text support --- Cargo.lock | 1 + .../weaver-app/assets/styling/cards-base.css | 6 +- crates/weaver-app/assets/styling/editor.css | 64 +++- .../assets/styling/entry-actions.css | 2 +- .../weaver-app/assets/styling/entry-card.css | 24 +- crates/weaver-app/assets/styling/entry.css | 16 +- crates/weaver-app/assets/styling/home.css | 6 +- crates/weaver-app/assets/styling/invites.css | 2 +- crates/weaver-app/assets/styling/navbar.css | 8 +- .../assets/styling/notebook-card.css | 24 +- .../assets/styling/notebook-cover.css | 4 +- .../assets/styling/profile-actions.css | 4 +- crates/weaver-app/assets/styling/profile.css | 8 +- .../weaver-app/assets/styling/record-view.css | 88 ++--- .../src/components/editor/beforeinput.rs | 57 ++- .../src/components/editor/dom_sync.rs | 15 +- .../weaver-app/src/components/editor/mod.rs | 9 + .../src/components/editor/publish.rs | 2 + .../src/components/editor/render.rs | 11 + ...components__editor__tests__blockquote.snap | 36 +- ..._app__components__editor__tests__bold.snap | 14 +- ...omponents__editor__tests__bold_italic.snap | 18 +- ...nts__editor__tests__code_block_fenced.snap | 4 +- ...ts__editor__tests__gap_between_blocks.snap | 34 +- ...components__editor__tests__hard_break.snap | 12 +- ...components__editor__tests__heading_h1.snap | 8 +- ...onents__editor__tests__heading_levels.snap | 112 ++++-- ...omponents__editor__tests__inline_code.snap | 10 +- ...pp__components__editor__tests__italic.snap | 14 +- ...s__editor__tests__mixed_unicode_ascii.snap | 6 +- ...__editor__tests__multiple_blank_lines.snap | 32 +- ...ditor__tests__multiple_inline_formats.snap | 22 +- ...omponents__editor__tests__nested_list.snap | 78 +++- ...mponents__editor__tests__ordered_list.snap | 18 +- ...ents__editor__tests__single_paragraph.snap | 6 +- ...ents__editor__tests__three_paragraphs.snap | 62 +-- ...ditor__tests__trailing_double_newline.snap | 8 +- ...ditor__tests__trailing_single_newline.snap | 8 +- ...onents__editor__tests__two_paragraphs.snap | 32 +- ...omponents__editor__tests__unicode_cjk.snap | 6 +- ...ponents__editor__tests__unicode_emoji.snap | 6 +- ...onents__editor__tests__unordered_list.snap | 18 +- .../weaver-app/src/components/editor/tests.rs | 65 +++- .../src/components/editor/writer.rs | 353 ++++++++---------- .../src/components/editor/writer/tags.rs | 189 ++++++---- crates/weaver-app/src/lib.rs | 1 - crates/weaver-app/src/main.rs | 5 +- crates/weaver-common/src/agent.rs | 8 +- .../src/clickhouse/queries/edit.rs | 21 +- crates/weaver-renderer/Cargo.toml | 1 + ..._atproto__tests__blockquote_rendering.snap | 4 +- ...tproto__tests__footnote_in_blockquote.snap | 4 +- ...er__atproto__tests__footnote_multiple.snap | 6 +- ...derer__atproto__tests__footnote_named.snap | 2 +- ...roto__tests__footnote_sidenote_inline.snap | 2 +- ..._atproto__tests__footnote_traditional.snap | 2 +- ...ests__footnote_with_inline_formatting.snap | 2 +- ...derer__atproto__tests__math_rendering.snap | 4 +- ...__atproto__tests__paragraph_rendering.snap | 4 +- ...roto__tests__weaver_block_aside_class.snap | 2 +- ...tests__weaver_block_before_blockquote.snap | 2 +- ...o__tests__weaver_block_before_heading.snap | 2 +- ...tests__weaver_block_custom_attributes.snap | 2 +- ...oto__tests__weaver_block_custom_class.snap | 2 +- ..._tests__weaver_block_multiple_classes.snap | 2 +- ...__weaver_block_no_effect_on_following.snap | 4 +- ...to__tests__weaver_block_with_footnote.snap | 2 +- crates/weaver-renderer/src/atproto/writer.rs | 37 +- crates/weaver-renderer/src/base_html.rs | 31 +- crates/weaver-renderer/src/css.rs | 84 ++--- ...tic_site__tests__blockquote_rendering.snap | 4 +- ...c_site__tests__footnote_in_blockquote.snap | 4 +- ...static_site__tests__footnote_multiple.snap | 6 +- ...r__static_site__tests__footnote_named.snap | 2 +- ...site__tests__footnote_sidenote_inline.snap | 2 +- ...tic_site__tests__footnote_traditional.snap | 2 +- ...ests__footnote_with_inline_formatting.snap | 2 +- ...r__static_site__tests__math_rendering.snap | 6 +- ...atic_site__tests__paragraph_rendering.snap | 4 +- ...site__tests__weaver_block_aside_class.snap | 2 +- ...tests__weaver_block_before_blockquote.snap | 2 +- ...e__tests__weaver_block_before_heading.snap | 2 +- ...tests__weaver_block_custom_attributes.snap | 2 +- ...ite__tests__weaver_block_custom_class.snap | 2 +- ..._tests__weaver_block_multiple_classes.snap | 2 +- ...__weaver_block_no_effect_on_following.snap | 4 +- ...te__tests__weaver_block_with_footnote.snap | 2 +- .../weaver-renderer/src/static_site/writer.rs | 36 +- crates/weaver-renderer/src/utils.rs | 51 ++- weaver_notes/test-sidenotes.md | 1 - 90 files changed, 1165 insertions(+), 731 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3941f1..f420a61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11846,6 +11846,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "unicode-bidi", "unicode-normalization", "url", "weaver-api", diff --git a/crates/weaver-app/assets/styling/cards-base.css b/crates/weaver-app/assets/styling/cards-base.css index 80aa50a..4c2fbee 100644 --- a/crates/weaver-app/assets/styling/cards-base.css +++ b/crates/weaver-app/assets/styling/cards-base.css @@ -95,8 +95,8 @@ font-size: 0.875rem; font-weight: 600; margin-top: 0; - margin-left: 0; - margin-right: 0; + margin-inline-start: 0; + margin-inline-end: 0; } .card-preview p { @@ -122,7 +122,7 @@ ========================================================================== */ .card-hover-border:hover { - border-left-color: var(--color-secondary); + border-inline-start-color: var(--color-secondary); } .card-hover-title:hover .card-title { diff --git a/crates/weaver-app/assets/styling/editor.css b/crates/weaver-app/assets/styling/editor.css index 3388629..b1c6ed8 100644 --- a/crates/weaver-app/assets/styling/editor.css +++ b/crates/weaver-app/assets/styling/editor.css @@ -159,6 +159,8 @@ background: var(--color-base); border: 1px solid var(--color-overlay); color: var(--color-text); + /* break-spaces ensures trailing whitespace takes up space and allows cursor placement */ + white-space-collapse: break-spaces; } .editor-content:focus { @@ -201,7 +203,7 @@ .editor-debug { padding: 8px; - padding-right: 0; + padding-inline-end: 0; background: var(--color-base); font-family: var(--font-mono); font-size: 12px; @@ -296,7 +298,7 @@ li:has(.md-syntax-block:not(.hidden)) { /* Editor page header with report button */ .editor-header { - padding-left: 6rem; + padding-inline-start: 6rem; background: var(--color-base); } @@ -323,9 +325,8 @@ li:has(.md-syntax-block:not(.hidden)) { .report-dialog-overlay { position: fixed; + inset-inline: 0; top: 0; - left: 0; - right: 0; bottom: 0; background: rgba(0, 0, 0, 0.6); display: flex; @@ -440,7 +441,7 @@ li:has(.md-syntax-block:not(.hidden)) { display: flex; align-items: center; gap: 12px; - margin-left: auto; + margin-inline-start: auto; flex-shrink: 0; } @@ -466,9 +467,8 @@ li:has(.md-syntax-block:not(.hidden)) { .publish-dialog-overlay { position: fixed; + inset-inline: 0; top: 0; - left: 0; - right: 0; bottom: 0; background: rgba(0, 0, 0, 0.6); display: flex; @@ -795,7 +795,7 @@ li:has(.md-syntax-block:not(.hidden)) { font-weight: 500; font-family: var(--font-mono); text-transform: uppercase; - margin-left: -6px; + margin-inline-start: -6px; position: relative; overflow: hidden; } @@ -808,7 +808,7 @@ li:has(.md-syntax-block:not(.hidden)) { } .collab-avatar:first-child { - margin-left: 0; + margin-inline-start: 0; } .collab-avatar.collab-overflow { @@ -832,9 +832,8 @@ li:has(.md-syntax-block:not(.hidden)) { /* Collaborators panel overlay */ .collaborators-overlay { position: fixed; + inset-inline: 0; top: 0; - left: 0; - right: 0; bottom: 0; background: rgba(0, 0, 0, 0.4); display: flex; @@ -953,9 +952,8 @@ li:has(.md-syntax-block:not(.hidden)) { .invite-dialog-overlay { position: fixed; + inset-inline: 0; top: 0; - left: 0; - right: 0; bottom: 0; background: rgba(0, 0, 0, 0.4); display: flex; @@ -1123,7 +1121,7 @@ li:has(.md-syntax-block:not(.hidden)) { margin: 6px 0 0 0; padding: 8px; background: var(--color-overlay); - border-left: 2px solid var(--color-border); + border-inline-start: 2px solid var(--color-border); } .invite-actions, @@ -1174,9 +1172,8 @@ li:has(.md-syntax-block:not(.hidden)) { .remote-cursors-overlay { position: absolute; + inset-inline: 0; top: 0; - left: 0; - right: 0; bottom: 0; pointer-events: none; z-index: 10; @@ -1226,3 +1223,38 @@ li:has(.md-syntax-block:not(.hidden)) { opacity: 0.5; } } + +/* ========================================================================== + Footnotes (Editor Mode) - styled but visible, no reordering + ========================================================================== */ + +/* Inline footnote reference [^name] */ +.footnote-ref { + font-family: var(--font-mono); + font-size: 0.9rem; + color: var(--color-subtle); + background: color-mix(in srgb, var(--color-surface) 10%, transparent); + padding: 0 2px; +} + +/* Footnote definition wrapper - stays in place, no reordering */ +.footnote-def-editor { + margin-bottom: 1rem; + margin-top: -1rem; + margin-inline-start: 2rem; + padding: 0 0.5rem; + border-inline-start: 2px solid var(--color-border); +} + +/* The [^name]: prefix in definitions */ +.footnote-def-syntax { + font-family: var(--font-mono); + font-size: 0.9rem; + color: var(--color-subtle); +} + +/* Inner paragraphs in footnote defs */ +.footnote-def-editor p { + margin: 0; + display: inline; +} diff --git a/crates/weaver-app/assets/styling/entry-actions.css b/crates/weaver-app/assets/styling/entry-actions.css index c9d4965..ac5ad76 100644 --- a/crates/weaver-app/assets/styling/entry-actions.css +++ b/crates/weaver-app/assets/styling/entry-actions.css @@ -53,7 +53,7 @@ display: block; width: 100%; padding: 0.5rem 0.75rem; - text-align: left; + text-align: start; background: none; border: none; cursor: pointer; diff --git a/crates/weaver-app/assets/styling/entry-card.css b/crates/weaver-app/assets/styling/entry-card.css index d3314d6..95e8b15 100644 --- a/crates/weaver-app/assets/styling/entry-card.css +++ b/crates/weaver-app/assets/styling/entry-card.css @@ -12,7 +12,7 @@ display: block; background: var(--color-surface); box-shadow: 0 1px 2px color-mix(in srgb, var(--color-text) 6%, transparent); - border-left: 2px solid transparent; + border-inline-start: 2px solid transparent; padding: 1.25rem; text-decoration: none; color: var(--color-text); @@ -23,7 +23,7 @@ .entry-card-link:hover { box-shadow: 0 2px 4px color-mix(in srgb, var(--color-text) 10%, transparent); - border-left-color: var(--color-secondary); + border-inline-start-color: var(--color-secondary); } .entry-card-link:hover .entry-card-title { @@ -83,8 +83,8 @@ } .feed-entry-card:hover { - border-left: 2px solid var(--color-secondary); - margin-left: -1px; + border-inline-start: 2px solid var(--color-secondary); + margin-inline-start: -1px; } .feed-entry-card:hover .entry-card-title { @@ -103,12 +103,12 @@ } .feed-entry-card .entry-card-byline .entry-card-date { - margin-left: auto; + margin-inline-start: auto; } /* Date in header (no author) aligns right */ .feed-entry-card .entry-card-header .entry-card-date { - margin-left: auto; + margin-inline-start: auto; } .entry-card-stats { @@ -121,7 +121,7 @@ .entry-card-stats .word-count::after { content: "·"; - margin-left: 0.5rem; + margin-inline-start: 0.5rem; } .entry-card-tags { @@ -178,8 +178,8 @@ font-size: 0.875rem; font-weight: 600; margin-top: 0.5rem; - margin-left: 0; - margin-right: 0; + margin-inline-start: 0; + margin-inline-end: 0; } .entry-card-preview p { @@ -201,10 +201,10 @@ .feed-entry-card { box-shadow: none; border-top: 1px solid var(--color-border); - border-right: 1px solid var(--color-border); + border-inline-end: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); - border-left: 1px solid var(--color-border); - /* Keep border-left as accent */ + border-inline-start: 1px solid var(--color-border); + /* Keep border-inline-start as accent */ } } diff --git a/crates/weaver-app/assets/styling/entry.css b/crates/weaver-app/assets/styling/entry.css index 82600e1..f7388ac 100644 --- a/crates/weaver-app/assets/styling/entry.css +++ b/crates/weaver-app/assets/styling/entry.css @@ -104,13 +104,13 @@ .entry-footer-nav .nav-button-prev { align-items: flex-start; - text-align: left; + text-align: start; } .entry-footer-nav .nav-button-next { align-items: flex-end; - text-align: right; - margin-left: auto; + text-align: end; + margin-inline-start: auto; } .entry-footer-nav .nav-arrow { @@ -176,17 +176,17 @@ .entry-reading-stats { color: var(--color-subtle); - margin-left: auto; + margin-inline-start: auto; margin-top: 0.25rem; } .entry-reading-stats .word-count::after { content: "·"; - margin-left: 0.5rem; + margin-inline-start: 0.5rem; } .entry-date { - margin-left: auto; + margin-inline-start: auto; font-weight: 400; align-items: last baseline; color: var(--color-subtle); @@ -255,7 +255,7 @@ } .entry-content-main blockquote { - border-left-color: var(--color-secondary); + border-inline-start-color: var(--color-secondary); background: var(--color-surface); } @@ -292,7 +292,7 @@ .entry-header .nav-button-next { order: 1; - margin-left: auto; + margin-inline-start: auto; } } diff --git a/crates/weaver-app/assets/styling/home.css b/crates/weaver-app/assets/styling/home.css index faa5841..e9da87d 100644 --- a/crates/weaver-app/assets/styling/home.css +++ b/crates/weaver-app/assets/styling/home.css @@ -25,12 +25,12 @@ .pinned-items .entry-card, .pinned-items .feed-entry-card, .pinned-items .notebook-card { - border-left: 3px solid var(--color-primary) !important; + border-inline-start: 3px solid var(--color-primary) !important; } .pinned-items .feed-entry-card:hover { - border-left: 3px solid var(--color-secondary) !important; - margin-left: 0; + border-inline-start: 3px solid var(--color-secondary) !important; + margin-inline-start: 0; } .feed-section { diff --git a/crates/weaver-app/assets/styling/invites.css b/crates/weaver-app/assets/styling/invites.css index 2bac89d..32ae5ef 100644 --- a/crates/weaver-app/assets/styling/invites.css +++ b/crates/weaver-app/assets/styling/invites.css @@ -72,7 +72,7 @@ margin: 0.5rem 0 0 0; padding: 0.5rem; background: var(--color-background); - border-left: 3px solid var(--color-primary); + border-inline-start: 3px solid var(--color-primary); font-style: italic; } diff --git a/crates/weaver-app/assets/styling/navbar.css b/crates/weaver-app/assets/styling/navbar.css index 5f1503e..b3e5894 100644 --- a/crates/weaver-app/assets/styling/navbar.css +++ b/crates/weaver-app/assets/styling/navbar.css @@ -2,9 +2,9 @@ display: flex; flex-direction: row; justify-content: space-between; - padding-left: 1rem; + padding-inline-start: 1rem; padding-top: 1rem; - padding-right: 1rem; + padding-inline-end: 1rem; } .breadcrumbs { @@ -48,8 +48,8 @@ display: flex; align-items: center; gap: 1rem; - margin-left: auto; - margin-right: 1rem; + margin-inline-start: auto; + margin-inline-end: 1rem; } .nav-tool-link { diff --git a/crates/weaver-app/assets/styling/notebook-card.css b/crates/weaver-app/assets/styling/notebook-card.css index 9d5a268..0bea03e 100644 --- a/crates/weaver-app/assets/styling/notebook-card.css +++ b/crates/weaver-app/assets/styling/notebook-card.css @@ -19,14 +19,14 @@ display: block; text-decoration: none; color: var(--color-text); - border-left: 3px solid transparent; - padding-left: 0.75rem; - margin-left: -0.75rem; + border-inline-start: 3px solid transparent; + padding-inline-start: 0.75rem; + margin-inline-start: -0.75rem; transition: border-color 0.2s ease; } .notebook-card-header-link:hover { - border-left-color: var(--color-primary); + border-inline-start-color: var(--color-primary); } .notebook-card-header-link:hover .notebook-card-title { @@ -130,7 +130,7 @@ .notebook-card-date { color: var(--color-muted); font-size: 0.85rem; - margin-left: 0.25rem; + margin-inline-start: 0.25rem; } .notebook-card-authors { @@ -157,12 +157,12 @@ display: block; text-decoration: none; color: var(--color-text); - border-left: 2px solid transparent; + border-inline-start: 2px solid transparent; border-top: 1px solid var(--color-border); - padding-left: 0.625rem; /* 0.5 grid */ + padding-inline-start: 0.625rem; /* 0.5 grid */ padding-top: 1.25rem; - margin-left: -0.625rem; - transition: border-left-color 0.2s ease; + margin-inline-start: -0.625rem; + transition: border-inline-start-color 0.2s ease; } .notebook-entry-preview-link:first-child { @@ -171,7 +171,7 @@ } .notebook-entry-preview-link:hover { - border-left-color: var(--color-secondary); + border-inline-start-color: var(--color-secondary); } .notebook-entry-preview-link:hover .entry-preview-title { @@ -255,8 +255,8 @@ font-size: 0.875rem; font-weight: 600; margin-top: 0; - margin-left: 0; - margin-right: 0; + margin-inline-start: 0; + margin-inline-end: 0; } .entry-preview-content code { diff --git a/crates/weaver-app/assets/styling/notebook-cover.css b/crates/weaver-app/assets/styling/notebook-cover.css index 192d5d4..2debdd3 100644 --- a/crates/weaver-app/assets/styling/notebook-cover.css +++ b/crates/weaver-app/assets/styling/notebook-cover.css @@ -8,7 +8,7 @@ @media (min-width: 1400px) { .notebook-cover { border-top: 1px solid var(--color-border); - border-right: 1px solid var(--color-border); + border-inline-end: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); padding: 1.25rem; } @@ -105,6 +105,6 @@ .notebook-cover-actions .button { width: 100%; - text-align: left; + text-align: start; border-radius: 0; } diff --git a/crates/weaver-app/assets/styling/profile-actions.css b/crates/weaver-app/assets/styling/profile-actions.css index 4e8799a..ed8c1d7 100644 --- a/crates/weaver-app/assets/styling/profile-actions.css +++ b/crates/weaver-app/assets/styling/profile-actions.css @@ -7,7 +7,7 @@ .profile-actions-container { padding-top: 2.25rem; - padding-right: 5rem; + padding-inline-end: 5rem; } .profile-actions-title { @@ -33,7 +33,7 @@ /* Match notebook-add-entry styling */ .profile-actions-list .button { width: 100%; - text-align: left; + text-align: start; font-size: 0.85rem; color: var(--color-primary); background: transparent; diff --git a/crates/weaver-app/assets/styling/profile.css b/crates/weaver-app/assets/styling/profile.css index fb5f045..5b44c2c 100644 --- a/crates/weaver-app/assets/styling/profile.css +++ b/crates/weaver-app/assets/styling/profile.css @@ -153,8 +153,8 @@ } .profile-extras { - padding-left: 1rem; - border-left: 1.5px dashed var(--color-border); + padding-inline-start: 1rem; + border-inline-start: 1.5px dashed var(--color-border); } .profile-block { @@ -175,7 +175,7 @@ } .profile-name-section { - margin-left: 1rem; + margin-inline-start: 1rem; margin-top: -0.5rem; } } @@ -191,7 +191,7 @@ @media (min-width: 1400px) { .profile-display { border-top: 1.5px solid var(--color-border); - border-right: 1.5px solid var(--color-border); + border-inline-end: 1.5px solid var(--color-border); } } diff --git a/crates/weaver-app/assets/styling/record-view.css b/crates/weaver-app/assets/styling/record-view.css index 74c362e..f0049d9 100644 --- a/crates/weaver-app/assets/styling/record-view.css +++ b/crates/weaver-app/assets/styling/record-view.css @@ -73,7 +73,7 @@ .metadata-label::after { content: ":"; font-size: 0.8rem; - margin-left: 0.25rem; + margin-inline-start: 0.25rem; } .metadata-value { @@ -140,11 +140,11 @@ } .tab-button.edit-button { - margin-left: auto; + margin-inline-start: auto; } .action-buttons-group { - margin-left: auto; + margin-inline-start: auto; display: flex; gap: 0; align-items: center; @@ -182,7 +182,7 @@ padding: 0.5rem 1rem; background: transparent; border: none; - text-align: left; + text-align: start; cursor: pointer; color: var(--color-text); font-family: var(--font-mono); @@ -200,22 +200,22 @@ display: flex; flex-direction: column; align-items: flex-start; - border-left: 1px dashed var(--color-subtle); + border-inline-start: 1px dashed var(--color-subtle); } .record-field { display: flex; flex-direction: column; padding: 0rem 0 0rem 1rem; - padding-right: 1rem; - margin-left: -1px; - border-left: 2px solid var(--color-secondary); + padding-inline-end: 1rem; + margin-inline-start: -1px; + border-inline-start: 2px solid var(--color-secondary); border-bottom: 1px dashed var(--color-subtle); z-index: 1; } .record-field.field-error { - border-left-color: var(--color-error); + border-inline-start-color: var(--color-error); background-color: rgba(255, 107, 107, 0.05); } @@ -224,11 +224,11 @@ font-size: 0.85rem; color: var(--color-error); padding: 0.25rem 0; - border-left: 2px solid var(--color-error); + border-inline-start: 2px solid var(--color-error); border-bottom: 1px dashed var(--color-error); - padding-left: 0.5rem; + padding-inline-start: 0.5rem; word-wrap: break-word; - margin-left: -1px; + margin-inline-start: -1px; word-break: break-word; overflow-wrap: break-word; } @@ -254,7 +254,7 @@ color: var(--color-subtle); font-size: 0.9rem; font-weight: 400; - padding-left: 0.125rem; + padding-inline-start: 0.125rem; padding-top: 0.5rem; } @@ -285,18 +285,18 @@ .record-section { position: relative; - margin-left: 1.5rem; - border-left: 1px dashed var(--color-border); + margin-inline-start: 1.5rem; + border-inline-start: 1px dashed var(--color-border); } .section-content .record-section { position: relative; - border-left: 1px dashed var(--color-border); + border-inline-start: 1px dashed var(--color-border); } .array-item .record-section { position: relative; - border-left: 1px dashed var(--color-border); + border-inline-start: 1px dashed var(--color-border); } .section-label { @@ -304,11 +304,11 @@ color: var(--color-primary); font-size: 1rem; font-weight: 600; - padding-left: 1rem; + padding-inline-start: 1rem; padding-top: 0.5rem; padding-bottom: 0.25rem; - margin-left: -1px; - border-left: 2px solid var(--color-primary); + margin-inline-start: -1px; + border-inline-start: 2px solid var(--color-primary); border-bottom: 1px dashed var(--color-muted); } @@ -317,21 +317,21 @@ color: var(--color-tertiary); font-size: 0.9rem; font-weight: 600; - padding-left: 1rem; + padding-inline-start: 1rem; padding-top: 0.5rem; - margin-left: -1px; - border-left: 2px solid var(--color-secondary); + margin-inline-start: -1px; + border-inline-start: 2px solid var(--color-secondary); } .section-content { display: flex; flex-direction: column; align-items: flex-start; - padding-right: 1rem; + padding-inline-end: 1rem; } .section-content .record-field { - border-left-color: var(--color-secondary); + border-inline-start-color: var(--color-secondary); opacity: 0.95; align-self: stretch; width: 100%; @@ -536,8 +536,8 @@ .validation-errors .error { padding: 0.25rem 0; - border-left: 2px solid var(--color-error); - padding-left: 0.5rem; + border-inline-start: 2px solid var(--color-error); + padding-inline-start: 0.5rem; margin: 0.25rem 0; word-wrap: break-word; word-break: break-word; @@ -574,7 +574,7 @@ width: 100%; display: flex; flex-direction: column; - margin-left: 1.48rem; + margin-inline-start: 1.48rem; } /* Pretty Editor Input Fields */ @@ -627,7 +627,7 @@ .record-field textarea::-webkit-resizer { background: transparent; border: 2px solid var(--color-border); - border-left: none; + border-inline-start: none; border-bottom: none; border-top: none; } @@ -657,7 +657,7 @@ background: var(--color-surface); border: 1px solid var(--color-border); padding: 0.25rem 0.25rem; - margin-right: 0.5rem; + margin-inline-end: 0.5rem; margin-bottom: 0.2rem; cursor: pointer; transition: @@ -694,8 +694,8 @@ align-items: center; padding: 0.75rem 0 0rem 0rem; margin-bottom: 1rem; - margin-left: 1.5rem; - border-left: 1px solid var(--color-border); + margin-inline-start: 1.5rem; + border-inline-start: 1px solid var(--color-border); } .add-field-widget input[type="text"] { @@ -705,7 +705,7 @@ background: var(--color-background-alt, rgba(0, 0, 0, 0.2)); border: 1px solid var(--color-border); padding: 0.3rem 0.5rem; - margin-left: -1px; + margin-inline-start: -1px; outline: none; } @@ -719,7 +719,7 @@ text-transform: uppercase; letter-spacing: 0.05em; padding: 0.3rem 0.75rem; - margin-left: -1px; + margin-inline-start: -1px; background: transparent; border: 1px solid var(--color-border); color: var(--color-primary); @@ -733,7 +733,7 @@ text-transform: uppercase; letter-spacing: 0.05em; padding: 0.36rem 0.75rem; - margin-left: -1px; + margin-inline-start: -1px; background: transparent; border: 1px solid var(--color-primary); color: var(--color-primary); @@ -799,7 +799,7 @@ background: var(--color-surface); border: 1px dashed var(--color-border); padding: 0.25rem 0.5rem; - margin-right: 0.5rem; + margin-inline-end: 0.5rem; margin-bottom: -0.2rem; cursor: pointer; transition: @@ -933,19 +933,19 @@ .accordion-content { grid-template-rows: 1fr; - padding-left: 46px; + padding-inline-start: 46px; } .accordion-content .section-content { - margin-left: -1px; + margin-inline-start: -1px; } .accordion-content .record-field { - margin-left: 0px; + margin-inline-start: 0px; } .accordion-content .array-item { - margin-left: 0px; + margin-inline-start: 0px; } .accordion-content .array-item .section-content { @@ -953,15 +953,15 @@ } .accordion-content .array-item .record-section { - margin-left: 1.5rem; + margin-inline-start: 1.5rem; } .accordion-content .array-item .record-section .record-field { - margin-left: -1px; + margin-inline-start: -1px; } .accordion-content .array-item .record-section .accordion-trigger .section-label { - margin-left: -2px; + margin-inline-start: -2px; } .accordion-trigger { @@ -969,5 +969,5 @@ } .accordion { - margin-left: -46px; + margin-inline-start: -46px; } diff --git a/crates/weaver-app/src/components/editor/beforeinput.rs b/crates/weaver-app/src/components/editor/beforeinput.rs index 5649c57..5f444b1 100644 --- a/crates/weaver-app/src/components/editor/beforeinput.rs +++ b/crates/weaver-app/src/components/editor/beforeinput.rs @@ -288,23 +288,40 @@ pub fn handle_beforeinput( // === Insertion === InputType::InsertText => { if let Some(text) = ctx.data { - // Simple text insert - update model, let browser handle DOM - // This mirrors the simple delete handling: we track in model, - // browser handles visual update, DOM sync skips innerHTML for - // cursor paragraph when syntax is unchanged + use super::FORCE_INNERHTML_UPDATE; + let action = EditorAction::Insert { text: text.clone(), range, }; execute_action(doc, &action); - tracing::trace!( - text_len = text.len(), - range_start = range.start, - range_end = range.end, - cursor_after = doc.cursor.read().offset, - "insertText: updated model, PassThrough to browser" - ); - BeforeInputResult::PassThrough + + // Log model content after insert to detect ZWC contamination + if tracing::enabled!(tracing::Level::TRACE) { + let content = doc.content(); + tracing::trace!( + text_len = text.len(), + range_start = range.start, + range_end = range.end, + cursor_after = doc.cursor.read().offset, + model_len = content.len(), + model_chars = content.chars().count(), + model_content = %content.escape_debug(), + force_innerhtml = FORCE_INNERHTML_UPDATE, + "insertText: updated model" + ); + } + + // When FORCE_INNERHTML_UPDATE is true, dom_sync will always replace + // innerHTML. We must preventDefault to avoid browser's default action + // racing with our innerHTML update and causing double-insertion. + if FORCE_INNERHTML_UPDATE { + BeforeInputResult::Handled + } else { + // PassThrough: browser handles DOM, we just track in model. + // dom_sync will skip innerHTML for cursor paragraph when syntax unchanged. + BeforeInputResult::PassThrough + } } else { BeforeInputResult::PassThrough } @@ -372,7 +389,8 @@ pub fn handle_beforeinput( }; if needs_special_handling { - // Complex delete - we handle everything, prevent browser default + // Handle fully when: complex delete OR when dom_sync will replace innerHTML + // (FORCE_INNERHTML_UPDATE). PassThrough + innerHTML causes double-deletion. let action = EditorAction::DeleteBackward { range }; execute_action(doc, &action); BeforeInputResult::Handled @@ -388,7 +406,11 @@ pub fn handle_beforeinput( doc.selection.set(None); } tracing::debug!("deleteContentBackward: after model update, returning PassThrough"); - BeforeInputResult::PassThrough + if super::FORCE_INNERHTML_UPDATE { + BeforeInputResult::Handled + } else { + BeforeInputResult::PassThrough + } } } @@ -404,6 +426,7 @@ pub fn handle_beforeinput( }; if needs_special_handling { + // Handle fully when: complex delete OR when dom_sync will replace innerHTML let action = EditorAction::DeleteForward { range }; execute_action(doc, &action); BeforeInputResult::Handled @@ -413,7 +436,11 @@ pub fn handle_beforeinput( let _ = doc.remove_tracked(range.start, 1); doc.selection.set(None); } - BeforeInputResult::PassThrough + if super::FORCE_INNERHTML_UPDATE { + BeforeInputResult::Handled + } else { + BeforeInputResult::PassThrough + } } } diff --git a/crates/weaver-app/src/components/editor/dom_sync.rs b/crates/weaver-app/src/components/editor/dom_sync.rs index 4f635ab..d1bc22c 100644 --- a/crates/weaver-app/src/components/editor/dom_sync.rs +++ b/crates/weaver-app/src/components/editor/dom_sync.rs @@ -496,9 +496,7 @@ pub fn update_paragraph_dom( } if needs_update { - // TESTING: Force innerHTML update to measure timing cost - // TODO: Remove this flag after benchmarking - const FORCE_INNERHTML_UPDATE: bool = true; + use super::FORCE_INNERHTML_UPDATE; // For cursor paragraph: only update if syntax/formatting changed // This prevents destroying browser selection during fast typing @@ -549,6 +547,17 @@ pub fn update_paragraph_dom( // Update hash - browser native editing has the correct content let _ = existing_elem.set_attribute("data-hash", &new_hash); } else { + // Log old innerHTML before replacement to see what browser did + if tracing::enabled!(tracing::Level::TRACE) { + let old_inner = existing_elem.inner_html(); + tracing::trace!( + para_id = %para_id, + old_inner = %old_inner.escape_debug(), + new_html = %new_para.html.escape_debug(), + "update_paragraph_dom: replacing innerHTML" + ); + } + // Timing instrumentation for innerHTML update cost let start = web_sys::window() .and_then(|w| w.performance()) diff --git a/crates/weaver-app/src/components/editor/mod.rs b/crates/weaver-app/src/components/editor/mod.rs index be1609d..bec51bd 100644 --- a/crates/weaver-app/src/components/editor/mod.rs +++ b/crates/weaver-app/src/components/editor/mod.rs @@ -32,6 +32,15 @@ mod writer; #[cfg(test)] mod tests; +/// When true, always update innerHTML even for cursor paragraph during typing. +/// This ensures syntax/formatting changes are immediately visible, but requires +/// using `Handled` (preventDefault) for InsertText to avoid double-insertion +/// from browser's default action racing with our innerHTML update. +/// +/// TODO: Replace with granular detection of syntax/formatting changes to allow +/// PassThrough optimization when only text content changes. +pub(crate) const FORCE_INNERHTML_UPDATE: bool = true; + // Main component pub use component::MarkdownEditor; diff --git a/crates/weaver-app/src/components/editor/publish.rs b/crates/weaver-app/src/components/editor/publish.rs index ca25019..a1503ad 100644 --- a/crates/weaver-app/src/components/editor/publish.rs +++ b/crates/weaver-app/src/components/editor/publish.rs @@ -250,6 +250,7 @@ pub async fn publish_entry( .title(doc.title()) .path(path) .created_at(Datetime::now()) + .updated_at(Datetime::now()) .maybe_tags(tags) .maybe_embeds(entry_embeds) .build(); @@ -400,6 +401,7 @@ pub async fn publish_entry( .title(doc.title()) .path(path) .created_at(Datetime::now()) + .updated_at(Datetime::now()) .maybe_tags(tags) .maybe_embeds(entry_embeds) .build(); diff --git a/crates/weaver-app/src/components/editor/render.rs b/crates/weaver-app/src/components/editor/render.rs index 85f86b5..98bfcf8 100644 --- a/crates/weaver-app/src/components/editor/render.rs +++ b/crates/weaver-app/src/components/editor/render.rs @@ -184,6 +184,17 @@ pub fn render_paragraphs_incremental( let fn_start = crate::perf::now(); let source = text.to_string(); + // Log source entering renderer to detect ZWC/space issues + if tracing::enabled!(target: "weaver::render", tracing::Level::TRACE) { + tracing::trace!( + target: "weaver::render", + source_len = source.len(), + source_chars = source.chars().count(), + source_content = %source.escape_debug(), + "render_paragraphs: source entering renderer" + ); + } + // Handle empty document if source.is_empty() { let empty_node_id = "n0".to_string(); diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__blockquote.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__blockquote.snap index 93cf634..ecac4e7 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__blockquote.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__blockquote.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 18 - html: "
\n

> This is a quote\n

\n
\n" + html: "
\n

> This is a quote\n

\n" offset_map: - byte_range: - 2 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 1 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 1 @@ -36,7 +36,7 @@ expression: result char_range: - 1 - 2 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 1 child_index: ~ utf16_len: 1 @@ -46,7 +46,7 @@ expression: result char_range: - 2 - 17 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 2 child_index: ~ utf16_len: 15 @@ -56,7 +56,7 @@ expression: result char_range: - 17 - 18 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 17 child_index: ~ utf16_len: 1 @@ -86,25 +86,35 @@ expression: result char_range: - 22 - 41 - html: "

With multiple lines

\n" + html: ">\n> \n

With multiple lines

\n" offset_map: - byte_range: - - 0 - - 0 + - 18 + - 22 char_range: + - 18 - 22 + node_id: p-1-n0 + char_offset_in_node: 0 + child_index: ~ + utf16_len: 4 + - byte_range: - 22 - node_id: n1 + - 22 + char_range: + - 22 + - 22 + node_id: p-1-n1 char_offset_in_node: 0 child_index: 0 utf16_len: 0 - byte_range: - - 0 - - 19 + - 22 + - 41 char_range: - 22 - 41 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 0 child_index: ~ utf16_len: 19 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold.snap index c14d540..323f2f1 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 18 - html: "

Some **bold** text

\n" + html: "

Some **bold** text

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 5 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 5 @@ -36,7 +36,7 @@ expression: result char_range: - 5 - 7 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 5 child_index: ~ utf16_len: 2 @@ -46,7 +46,7 @@ expression: result char_range: - 7 - 11 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 7 child_index: ~ utf16_len: 4 @@ -56,7 +56,7 @@ expression: result char_range: - 11 - 13 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 11 child_index: ~ utf16_len: 2 @@ -66,7 +66,7 @@ expression: result char_range: - 13 - 18 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 13 child_index: ~ utf16_len: 5 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold_italic.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold_italic.snap index 3f66287..94d4d8d 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold_italic.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__bold_italic.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 27 - html: "

Some ***bold italic*** text

\n" + html: "

Some ***bold italic*** text

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 5 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 5 @@ -36,7 +36,7 @@ expression: result char_range: - 5 - 6 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 5 child_index: ~ utf16_len: 1 @@ -46,7 +46,7 @@ expression: result char_range: - 6 - 8 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 6 child_index: ~ utf16_len: 2 @@ -56,7 +56,7 @@ expression: result char_range: - 8 - 19 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 8 child_index: ~ utf16_len: 11 @@ -66,7 +66,7 @@ expression: result char_range: - 19 - 21 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 19 child_index: ~ utf16_len: 2 @@ -76,7 +76,7 @@ expression: result char_range: - 21 - 22 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 21 child_index: ~ utf16_len: 1 @@ -86,7 +86,7 @@ expression: result char_range: - 22 - 27 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 22 child_index: ~ utf16_len: 5 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__code_block_fenced.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__code_block_fenced.snap index 6233f74..c2495e8 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__code_block_fenced.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__code_block_fenced.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 24 - html: "```rust\n
fn main() {}\n
```" + html: "```rust\n
fn main() {}\n
```" offset_map: - byte_range: - 8 @@ -16,7 +16,7 @@ expression: result char_range: - 8 - 21 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 13 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__gap_between_blocks.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__gap_between_blocks.snap index d5b69bd..597e5d5 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__gap_between_blocks.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__gap_between_blocks.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 10 - html: "

# Heading\n

\n" + html: "

# Heading\n

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 2 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 2 @@ -36,7 +36,7 @@ expression: result char_range: - 2 - 9 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 2 child_index: ~ utf16_len: 7 @@ -46,7 +46,7 @@ expression: result char_range: - 9 - 10 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 9 child_index: ~ utf16_len: 1 @@ -57,25 +57,35 @@ expression: result char_range: - 11 - 26 - html: "

Paragraph below

\n" + html: "\n\n

Paragraph below

\n" offset_map: - byte_range: - - 0 - - 0 + - 10 + - 11 + char_range: + - 10 + - 11 + node_id: p-1-n0 + char_offset_in_node: 0 + child_index: ~ + utf16_len: 1 + - byte_range: + - 11 + - 11 char_range: - 11 - 11 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 0 child_index: 0 utf16_len: 0 - byte_range: - - 0 - - 15 + - 11 + - 26 char_range: - 11 - 26 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 0 child_index: ~ utf16_len: 15 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__hard_break.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__hard_break.snap index 7853751..556c922 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__hard_break.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__hard_break.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 19 - html: "

Line one
Line two

\n" + html: "

Line one
​Line two

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 8 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 8 @@ -36,7 +36,7 @@ expression: result char_range: - 8 - 10 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 8 child_index: ~ utf16_len: 2 @@ -46,7 +46,7 @@ expression: result char_range: - 10 - 11 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 10 child_index: ~ utf16_len: 1 @@ -56,7 +56,7 @@ expression: result char_range: - 11 - 19 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 11 child_index: ~ utf16_len: 8 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_h1.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_h1.snap index 9b9566b..14c269c 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_h1.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_h1.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 11 - html: "

# Heading 1

\n" + html: "

# Heading 1

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 2 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 2 @@ -36,7 +36,7 @@ expression: result char_range: - 2 - 11 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 2 child_index: ~ utf16_len: 9 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_levels.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_levels.snap index 4740ffb..922ba3b 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_levels.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__heading_levels.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 5 - html: "

# H1\n

\n" + html: "

# H1\n

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 2 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 2 @@ -36,7 +36,7 @@ expression: result char_range: - 2 - 4 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 2 child_index: ~ utf16_len: 2 @@ -46,7 +46,7 @@ expression: result char_range: - 4 - 5 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 4 child_index: ~ utf16_len: 1 @@ -57,45 +57,55 @@ expression: result char_range: - 6 - 12 - html: "

## H2\n

\n" + html: "\n\n

## H2\n

\n" offset_map: - byte_range: - - 0 - - 0 + - 5 + - 6 char_range: + - 5 - 6 + node_id: p-1-n0 + char_offset_in_node: 0 + child_index: ~ + utf16_len: 1 + - byte_range: - 6 - node_id: n1 + - 6 + char_range: + - 6 + - 6 + node_id: p-1-n1 char_offset_in_node: 0 child_index: 0 utf16_len: 0 - byte_range: - - 0 - - 3 + - 6 + - 9 char_range: - 6 - 9 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 0 child_index: ~ utf16_len: 3 - byte_range: - - 3 - - 5 + - 9 + - 11 char_range: - 9 - 11 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 3 child_index: ~ utf16_len: 2 - byte_range: - - 5 - - 6 + - 11 + - 12 char_range: - 11 - 12 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 5 child_index: ~ utf16_len: 1 @@ -106,45 +116,55 @@ expression: result char_range: - 13 - 20 - html: "

### H3\n

\n" + html: "\n\n

### H3\n

\n" offset_map: - byte_range: - - 0 - - 0 + - 12 + - 13 char_range: + - 12 + - 13 + node_id: p-2-n0 + char_offset_in_node: 0 + child_index: ~ + utf16_len: 1 + - byte_range: - 13 - 13 - node_id: n2 + char_range: + - 13 + - 13 + node_id: p-2-n1 char_offset_in_node: 0 child_index: 0 utf16_len: 0 - byte_range: - - 0 - - 4 + - 13 + - 17 char_range: - 13 - 17 - node_id: n2 + node_id: p-2-n1 char_offset_in_node: 0 child_index: ~ utf16_len: 4 - byte_range: - - 4 - - 6 + - 17 + - 19 char_range: - 17 - 19 - node_id: n2 + node_id: p-2-n1 char_offset_in_node: 4 child_index: ~ utf16_len: 2 - byte_range: - - 6 - - 7 + - 19 + - 20 char_range: - 19 - 20 - node_id: n2 + node_id: p-2-n1 char_offset_in_node: 6 child_index: ~ utf16_len: 1 @@ -155,35 +175,45 @@ expression: result char_range: - 21 - 28 - html: "

#### H4

\n" + html: "\n\n

#### H4

\n" offset_map: - byte_range: - - 0 - - 0 + - 20 + - 21 + char_range: + - 20 + - 21 + node_id: p-3-n0 + char_offset_in_node: 0 + child_index: ~ + utf16_len: 1 + - byte_range: + - 21 + - 21 char_range: - 21 - 21 - node_id: n3 + node_id: p-3-n1 char_offset_in_node: 0 child_index: 0 utf16_len: 0 - byte_range: - - 0 - - 5 + - 21 + - 26 char_range: - 21 - 26 - node_id: n3 + node_id: p-3-n1 char_offset_in_node: 0 child_index: ~ utf16_len: 5 - byte_range: - - 5 - - 7 + - 26 + - 28 char_range: - 26 - 28 - node_id: n3 + node_id: p-3-n1 char_offset_in_node: 5 child_index: ~ utf16_len: 2 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__inline_code.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__inline_code.snap index 8758feb..e719850 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__inline_code.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__inline_code.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 16 - html: "

Some `code` here

\n" + html: "

Some `code` here

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 5 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 5 @@ -36,7 +36,7 @@ expression: result char_range: - 6 - 10 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 5 child_index: ~ utf16_len: 6 @@ -46,7 +46,7 @@ expression: result char_range: - 11 - 16 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 11 child_index: ~ utf16_len: 5 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__italic.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__italic.snap index 0cb4391..4b3bde2 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__italic.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__italic.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 18 - html: "

Some *italic* text

\n" + html: "

Some *italic* text

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 5 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 5 @@ -36,7 +36,7 @@ expression: result char_range: - 5 - 6 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 5 child_index: ~ utf16_len: 1 @@ -46,7 +46,7 @@ expression: result char_range: - 6 - 12 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 6 child_index: ~ utf16_len: 6 @@ -56,7 +56,7 @@ expression: result char_range: - 12 - 13 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 12 child_index: ~ utf16_len: 1 @@ -66,7 +66,7 @@ expression: result char_range: - 13 - 18 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 13 child_index: ~ utf16_len: 5 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__mixed_unicode_ascii.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__mixed_unicode_ascii.snap index 03f705b..3be09e2 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__mixed_unicode_ascii.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__mixed_unicode_ascii.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 16 - html: "

Hello 你好 world 🎉

\n" + html: "

Hello 你好 world 🎉

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 16 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 17 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_blank_lines.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_blank_lines.snap index 3915940..1c8e396 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_blank_lines.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_blank_lines.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 6 - html: "

First\n

\n" + html: "

First\n

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 5 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 5 @@ -36,7 +36,7 @@ expression: result char_range: - 5 - 6 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 5 child_index: ~ utf16_len: 1 @@ -66,25 +66,35 @@ expression: result char_range: - 9 - 15 - html: "

Second

\n" + html: "\n\n\n\n

Second

\n" offset_map: - byte_range: - - 0 - - 0 + - 6 + - 9 + char_range: + - 6 + - 9 + node_id: p-1-n0 + char_offset_in_node: 0 + child_index: ~ + utf16_len: 3 + - byte_range: + - 9 + - 9 char_range: - 9 - 9 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 0 child_index: 0 utf16_len: 0 - byte_range: - - 0 - - 6 + - 9 + - 15 char_range: - 9 - 15 - node_id: n1 + node_id: p-1-n1 char_offset_in_node: 0 child_index: ~ utf16_len: 6 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_inline_formats.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_inline_formats.snap index 9e845fa..2061656 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_inline_formats.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__multiple_inline_formats.snap @@ -8,7 +8,7 @@ expression: result char_range: - 0 - 32 - html: "

**Bold** and *italic* and `code`

\n" + html: "

**Bold** and *italic* and `code`

\n" offset_map: - byte_range: - 0 @@ -16,7 +16,7 @@ expression: result char_range: - 0 - 0 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: 0 utf16_len: 0 @@ -26,7 +26,7 @@ expression: result char_range: - 0 - 2 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 0 child_index: ~ utf16_len: 2 @@ -36,7 +36,7 @@ expression: result char_range: - 2 - 6 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 2 child_index: ~ utf16_len: 4 @@ -46,7 +46,7 @@ expression: result char_range: - 6 - 8 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 6 child_index: ~ utf16_len: 2 @@ -56,7 +56,7 @@ expression: result char_range: - 8 - 13 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 8 child_index: ~ utf16_len: 5 @@ -66,7 +66,7 @@ expression: result char_range: - 13 - 14 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 13 child_index: ~ utf16_len: 1 @@ -76,7 +76,7 @@ expression: result char_range: - 14 - 20 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 14 child_index: ~ utf16_len: 6 @@ -86,7 +86,7 @@ expression: result char_range: - 20 - 21 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 20 child_index: ~ utf16_len: 1 @@ -96,7 +96,7 @@ expression: result char_range: - 21 - 26 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 21 child_index: ~ utf16_len: 5 @@ -106,7 +106,7 @@ expression: result char_range: - 27 - 31 - node_id: n0 + node_id: p-0-n0 char_offset_in_node: 26 child_index: ~ utf16_len: 6 diff --git a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__nested_list.snap b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__nested_list.snap index 43bce36..45eb510 100644 --- a/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__nested_list.snap +++ b/crates/weaver-app/src/components/editor/snapshots/weaver_app__components__editor__tests__nested_list.snap @@ -27,65 +27,105 @@ expression: result char_range: - 11 - 33 - html: "\n" + html: "