From 5e21ae8875dbd336d4c3ce7e40396749dad39edd Mon Sep 17 00:00:00 2001 From: edenliu Date: Sat, 15 Aug 2026 09:58:24 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E9=97=B4=E8=B7=9D=E4=B8=8E=E9=A1=B6=E9=83=A8?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/features/editor/Editor.test.tsx | 12 +- .../editor/MarkdownNoteHeader.test.tsx | 38 +++- .../features/editor/MarkdownNoteHeader.tsx | 164 +++++++++++------- .../src/features/editor/editorExtensions.ts | 6 +- apps/desktop/src/index.css | 9 +- 5 files changed, 154 insertions(+), 75 deletions(-) diff --git a/apps/desktop/src/features/editor/Editor.test.tsx b/apps/desktop/src/features/editor/Editor.test.tsx index 0c678a47..32b92105 100644 --- a/apps/desktop/src/features/editor/Editor.test.tsx +++ b/apps/desktop/src/features/editor/Editor.test.tsx @@ -2593,9 +2593,15 @@ describe("Editor", () => { renderComponent(); const firstView = getEditorView(); - expect(firstView.scrollDOM.firstElementChild).toHaveClass( - "cm-lp-scroll-header", - ); + const firstScrollHeader = firstView.scrollDOM + .firstElementChild as HTMLElement; + expect(firstScrollHeader).toHaveClass("cm-lp-scroll-header"); + expect(getComputedStyle(firstScrollHeader).display).toBe("contents"); + expect( + firstScrollHeader.querySelector( + '[data-editor-note-toolbar="true"]', + ), + ).toHaveStyle({ position: "sticky", top: "0px" }); await act(async () => { useEditorStore.getState().switchTab("tab-2"); diff --git a/apps/desktop/src/features/editor/MarkdownNoteHeader.test.tsx b/apps/desktop/src/features/editor/MarkdownNoteHeader.test.tsx index 1fab3393..95ec888c 100644 --- a/apps/desktop/src/features/editor/MarkdownNoteHeader.test.tsx +++ b/apps/desktop/src/features/editor/MarkdownNoteHeader.test.tsx @@ -45,6 +45,15 @@ function renderHeader(lineWrapping: boolean) { ); return { + spacer: document.querySelector( + '[data-editor-note-toolbar-spacer="true"]', + ) as HTMLElement | null, + toolbar: document.querySelector( + '[data-editor-note-toolbar="true"]', + ) as HTMLElement | null, + toolbarInner: document.querySelector( + '[data-editor-note-toolbar-inner="true"]', + ) as HTMLElement | null, outer: document.querySelector( '[data-editor-note-header="true"]', ) as HTMLElement | null, @@ -64,13 +73,29 @@ describe("MarkdownNoteHeader", () => { }); it("keeps the centered reading layout when line wrapping is enabled", () => { - const { outer, inner } = renderHeader(true); + const { spacer, toolbar, toolbarInner, outer, inner } = + renderHeader(true); + expect(spacer).toHaveStyle({ height: "32px", flex: "0 0 100%" }); + expect(toolbar).toHaveAttribute("data-line-wrapping", "true"); + expect(toolbar).toHaveStyle({ + position: "sticky", + top: "0px", + width: "100%", + flex: "0 0 100%", + padding: "6px var(--editor-horizontal-inset)", + }); + expect(toolbarInner).toHaveStyle({ + width: "min(100%, var(--editor-content-width))", + maxWidth: "var(--editor-content-width)", + margin: "0 auto", + }); expect(outer).not.toBeNull(); expect(inner).not.toBeNull(); expect(outer).toHaveAttribute("data-line-wrapping", "true"); expect(outer).toHaveStyle({ width: "100%", - padding: "40px var(--editor-horizontal-inset) 0", + padding: "14px var(--editor-horizontal-inset) 0", + flex: "0 0 100%", }); expect(inner).toHaveStyle({ width: "min(100%, var(--editor-content-width))", @@ -82,13 +107,18 @@ describe("MarkdownNoteHeader", () => { }); it("switches to a left-aligned layout when line wrapping is disabled", () => { - const { outer, inner } = renderHeader(false); + const { toolbarInner, outer, inner } = renderHeader(false); expect(outer).not.toBeNull(); expect(inner).not.toBeNull(); expect(outer).toHaveAttribute("data-line-wrapping", "false"); expect(outer).toHaveStyle({ width: "100%", - padding: "40px var(--editor-horizontal-inset) 0", + padding: "14px var(--editor-horizontal-inset) 0", + }); + expect(toolbarInner).toHaveStyle({ + width: "100%", + maxWidth: "none", + margin: "0px", }); expect(inner).toHaveStyle({ width: "100%", diff --git a/apps/desktop/src/features/editor/MarkdownNoteHeader.tsx b/apps/desktop/src/features/editor/MarkdownNoteHeader.tsx index 26cb44ec..584f084a 100644 --- a/apps/desktop/src/features/editor/MarkdownNoteHeader.tsx +++ b/apps/desktop/src/features/editor/MarkdownNoteHeader.tsx @@ -182,38 +182,56 @@ export function MarkdownNoteHeader({ const bannerCopy = status ? BANNER_COPY[status] : undefined; + const readingWidth = lineWrapping + ? "min(100%, var(--editor-content-width))" + : "100%"; + const readingMaxWidth = lineWrapping + ? "var(--editor-content-width)" + : "none"; + const readingMargin = lineWrapping ? "0 auto" : "0"; + return ( -
+ <> + {statusMenu && ( @@ -340,7 +380,7 @@ export function MarkdownNoteHeader({ ]} /> )} -
+ ); } diff --git a/apps/desktop/src/features/editor/editorExtensions.ts b/apps/desktop/src/features/editor/editorExtensions.ts index 4b6c6fba..9e16bd2d 100644 --- a/apps/desktop/src/features/editor/editorExtensions.ts +++ b/apps/desktop/src/features/editor/editorExtensions.ts @@ -75,8 +75,10 @@ export const baseTheme = EditorView.theme({ overflowY: "auto", }, ".cm-lp-scroll-header": { - flex: "0 0 100%", - boxSizing: "border-box", + // Let the spacer, sticky action bar, and document header participate + // directly in the scroller's flex layout. A sticky descendant would + // otherwise stop sticking at the bottom of this short wrapper. + display: "contents", }, ".cm-content": { flex: "1 1 0%", diff --git a/apps/desktop/src/index.css b/apps/desktop/src/index.css index 1ce350d6..d0d1086b 100644 --- a/apps/desktop/src/index.css +++ b/apps/desktop/src/index.css @@ -758,6 +758,7 @@ html[data-desktop-platform="windows"].dark { --code-fence-radius: 6px; --code-fence-inline-size: min(100%, 68ch); --editor-code-fence-inline-size: min(100%, 88ch); + --editor-code-card-gap: 0.75rem; } .chat-code-frame { @@ -796,7 +797,7 @@ html[data-desktop-platform="windows"].dark { } .cm-code-block-header-shell { - padding-top: 0.25rem; + padding-top: var(--editor-code-card-gap); font-family: var(--font-mono), ui-monospace, monospace; font-size: 0.93em; } @@ -820,14 +821,14 @@ html[data-desktop-platform="windows"].dark { .cm-code-block-header-shell-unlabeled { position: relative; z-index: 10; - height: 0; + height: var(--editor-code-card-gap); padding-top: 0; pointer-events: none; } .cm-code-block-header-unlabeled { position: absolute; - top: 0.55em; + top: calc(100% + 0.55em); right: 0.55em; min-height: 0; padding: 0; @@ -841,7 +842,7 @@ html[data-desktop-platform="windows"].dark { .cm-code-block-header-shell-unlabeled-empty { height: auto; - padding-top: 0.25rem; + padding-top: var(--editor-code-card-gap); } .cm-code-block-header-unlabeled.cm-code-block-header-only { From 55d2c1c65feef74156c48a895f808c73a10ce1bc Mon Sep 17 00:00:00 2001 From: edenliu Date: Sat, 15 Aug 2026 12:18:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=8F=91=E5=B8=83=20BifrostWrite=201.2.8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +++++++ apps/desktop/native-backend/Cargo.toml | 2 +- apps/desktop/package-lock.json | 4 ++-- apps/desktop/package.json | 2 +- apps/desktop/src-tauri/Cargo.toml | 2 +- apps/desktop/src-tauri/tauri.conf.json | 2 +- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd75c74f..25e35a24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,13 @@ refactors, dependency updates, CI changes, and code cleanup do not belong here. --- +## [1.2.8] - 2026-08-15 + +### Changed + +- Added consistent spacing between adjacent rendered code cards in Markdown live preview. +- Kept document location, status, live-preview, and line-wrapping controls pinned to the top of the editor while scrolling. + ## [1.2.7] - 2026-08-14 ### Changed diff --git a/apps/desktop/native-backend/Cargo.toml b/apps/desktop/native-backend/Cargo.toml index 68e7d283..6478585e 100644 --- a/apps/desktop/native-backend/Cargo.toml +++ b/apps/desktop/native-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neverwrite-native-backend" -version = "1.2.7" +version = "1.2.8" edition = "2021" [dependencies] diff --git a/apps/desktop/package-lock.json b/apps/desktop/package-lock.json index 3e15ece7..a1c43e9f 100644 --- a/apps/desktop/package-lock.json +++ b/apps/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "bifrostwrite-desktop", - "version": "1.2.7", + "version": "1.2.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bifrostwrite-desktop", - "version": "1.2.7", + "version": "1.2.8", "hasInstallScript": true, "dependencies": { "@codemirror/commands": "^6.10.3", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 38d44612..c2e0d981 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "bifrostwrite-desktop", "private": true, - "version": "1.2.7", + "version": "1.2.8", "homepage": "https://github.com/bifrost-proxy/BifrostWrite", "type": "module", "engines": { diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 20f59f6e..b670a543 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bifrostwrite-desktop" -version = "1.2.7" +version = "1.2.8" edition = "2021" description = "Tauri desktop shell for BifrostWrite" license = "Apache-2.0" diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 53d8d734..566da2c1 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "BifrostWrite", - "version": "1.2.7", + "version": "1.2.8", "identifier": "com.bifrostwrite.desktop", "build": { "beforeDevCommand": "npm run renderer:dev",