Skip to content

feat(image): paste pipeline, Feishu-style toolbar, lightbox & base64 migration - #74

Closed
moyu12-ae wants to merge 6 commits into
marswaveai:mainfrom
moyu12-ae:feat/image-pipeline
Closed

moyu12-ae wants to merge 6 commits into
marswaveai:mainfrom
moyu12-ae:feat/image-pipeline

Conversation

@moyu12-ae

Copy link
Copy Markdown
Contributor

Summary

Reopens #21 (removed before v1.9.0 for reliability) with a full image experience, designed against Typora / Obsidian / Feishu / VS Code and stress-tested.

Image input (fixes the base64 leak)

  • Paste, drag-and-drop, and 编辑 → 插入图片…(⌘⇧I) save images into an assets/ folder beside the document (SHA-256 content-hash filenames, deduplicated) and insert relative-path references
  • Clipboard images and rich-text data: URIs (WeChat / browsers) are intercepted, so base64 never reaches the markdown source
  • Untitled documents are asked to save first (VS Code rule); assets reuse the existing resolveImagePaths / restoreImagePaths round-trip
  • Instant feedback: a placeholder image is inserted synchronously and swapped to the final path once written

Image interaction (Feishu-style)

  • Hovering or selecting an image floats a toolbar centered above it: left/center/right alignment, caption, replace, copy, reveal-in-folder, fullscreen, delete
  • Four corner dot handles resize proportionally from any corner; sizing and alignment persist as inline <img … width style> (Typora model), and dropping every HTML-specific feature restores plain ![]()
  • The caption (standard markdown alt text) renders below the image, always centered relative to the picture itself
  • Double-click opens a fullscreen viewer: fit-to-window default, cursor-centered wheel zoom, drag pan, arrow-key navigation, Esc
  • Broken references render an inline action box (relocate / reveal) instead of a silent broken glyph

Legacy document cleanup

  • Opening a document containing inline base64 shows a one-click "extract to local files" banner (40 URIs / 2 MB migrate in ~4 ms; 200 URIs dedupe to 2 files)
  • Source mode wraps long tokens instead of scrolling sideways

Stability hardening (found by stress testing)

  • Save race: autosave / ⌘S / close snapshots wait for in-flight image writes, so placeholder blob: URLs never reach disk
  • Undo boundary: document loads no longer enter the undo stack — ⌘Z spam previously rolled the editor back to the welcome document and autosave then overwrote the user's file with it (reproduced on disk during testing)
  • Exports hide the toolbar/handles first

Verification

  • tsc (main/preload/renderer), electron-vite build, theme color contract: all green
  • Node-level stress suite: migration throughput, hash dedup, escaping, garbage-URI tolerance — 14/14
  • GUI E2E against the real app: paste → assets + relative ref, dedup (3 pastes → 1 file), migration banner → extraction → autosave, broken-image box, resize persistence, lightbox open/close, undo boundary
  • Design reference licenses: MarkText & VS Code & Milkdown (MIT, implementation ideas), Zettlr (GPL, product ideas only)

Docs

  • design.md §图片 (new), docs/feature-requests.md (功能需求 #21 → implemented), resources/demo/changelog.md (Unreleased)

Closes #21

@moyu12-ae

Copy link
Copy Markdown
Contributor Author

效果如下:
image
image
image

@orange2ai

Copy link
Copy Markdown
Collaborator

感谢提交!粘贴流水线 + 工具条 + lightbox 的方向我们很认可,这会是图片功能的主体实现。

合并前有三个必改项:

  1. autosave 数据安全(必须):runAutosave() 目前没有等待 whenImageWritesSettled()(saveCurrent 和关闭快照都等了)。粘贴大图后 1 秒内自动保存会把 blob: 死引用持久化进用户文件。这个是数据安全级别的问题,必须修复。
  2. 源码模式破图:源码模式插入用的是 asset.relPath,切回所见即所得后刚插入的图片渲染失败,需要统一路径表示。
  3. rebase 到 release/2.0.2 并接入集中式 i18n:main.ts/base.css 有实测冲突(大纲/侧栏重构);另外新增的 UI 文案请使用 ui-language.ts 的 isChinese() 而不是各文件硬编码 navigator.language,否则语言切换不会生效。

修完这三项我们会尽快再审一轮。

…gration

Image input now saves to an assets/ folder beside the document instead of
leaking base64 into the source (marswaveai#21):

- Intercept clipboard and drops (incl. rich-text data: URIs from WeChat or
  browsers), persist images with SHA-256 content-hash names, insert
  relative-path references; untitled docs are asked to save first
- Floating toolbar on selection: width presets, drag-resize handle,
  caption/alt editing, replace, copy, reveal-in-folder, delete; applied
  widths persist as inline <img … width> (Typora model)
- Double-click opens a fullscreen viewer: wheel zoom, drag pan, arrow-key
  navigation across document images, Esc to close
- Broken image references render an inline action box (relocate/reveal)
  instead of a silent broken glyph
- Legacy documents with inline base64 get a one-click extract-to-files
  banner; source mode wraps long tokens instead of scrolling sideways

Design reference: MarkText imageAction hook and hash naming, VS Code
markdown copyFiles untitled rule (both MIT), Zettlr clipboard intent rule
(ideas only, GPL). Registered in design.md §图片 and feature-requests.md.
Interaction rework after side-by-side comparison with Feishu:
- Toolbar floats in on hover (no selection needed), centered above the
  image; percentage width presets removed
- Four corner dot handles resize proportionally from any corner (left
  handles shrink toward drag direction); a plain click on a handle no
  longer rewrites the document
- Caption (alt) renders centered below the image in both node view and
  inline-HTML view instead of an overlay input
- Lightbox: fixed double-shrink (CSS max-width stacked with the fit
  transform, opening huge images at ~29%); fit is now transform-only and
  fills the viewport

Stability hardening from stress testing:
- Save guard: autosave/⌘S/close snapshots wait for in-flight image asset
  writes so placeholder blob: URLs never reach disk
- Exports hide the toolbar/handles first
- Undo can no longer cross the document-load boundary (⌘Z spam used to
  roll the editor back to the welcome document and autosave would then
  overwrite the user's file with it) — found by GUI stress testing

Node-level stress suite: 40-URI (2 MB) migration in 4 ms, 200-URI dedup
to 2 files, sha256 hash naming/dedup, escaping, garbage-URI tolerance.
…mage

Shrinking an image exposed the missing alignment story: a narrow image
sat hard-left and the caption drifted with it. Following Feishu:

- Toolbar gains an alignment trio (left/center/right, linear SVG icons,
  current state highlighted) ahead of the caption group
- Alignment persists as inline HTML (style="display:block;margin:...");
  width+align mutations now parse the html value, patch one field and
  rebuild it (no regex residue), and dropping every HTML-specific feature
  (no width, left-aligned) restores plain markdown image syntax
- Inline-HTML image wrappers become block units so the caption sits under
  the image and follows its alignment (fixes the caption drifting beside
  resized images)
- Every in-place html update rebinds the toolbar to the rebuilt node view
  - previously the overlay kept pointing at the detached img, so the
  alignment highlight went stale
Alignment was written as the CSS shorthand (margin:0 0 0 auto) but read
back with a margin-left:auto regex, so after a right-align the toolbar
kept highlighting left and the caption stayed put. Read the alignment
via CSSOM (img.style.marginLeft), which expands shorthands, and refresh
the feature-request notes to match the shipped interaction.
Right-aligning an image dragged the caption to the column edge and
left-aligning pinned it to the far left — the caption was aligned to
the text column instead of the picture. Inline-HTML images now render
img + caption inside a shrink-to-fit frame that owns the alignment
margins (moved off the img at render time), so the caption is always
centered under the picture whatever the alignment is. The persisted
img style is untouched, and the caption is still just the standard
markdown alt text.
@orange2ai

Copy link
Copy Markdown
Collaborator

维护者决定不做这个方案,关闭本 PR。你之前的 #68、#70 都合了、质量很扎实,所以这次的判断需要说清楚——不是实现问题,是方向问题:

  • 悬浮工具栏与产品定位不符:ColaMD 的界面只有标题栏、编辑器、文件面板三个区域,「不加不必要 UI 元素」是第一原则。飞书风格的图片浮动工具栏、lightbox 属于把应用做成「功能完整的富文本编辑器」的路子,会让 ColaMD 比 Obsidian 这类工具更重,这不是我们想去的方向
  • 复杂度:2500 行、五个 image 子模块,换来的是图片编辑体验的完整度;ColaMD 现阶段不做图片管理子系统
  • 与 feat: configurable image imports with inline editing, scaling and alignment #73 撞车:同一时间有两份完整图片方案,两案都不合并

其中 base64 → assets 本地化这一件事本身有价值(内嵌图片会让 .md 快速膨胀,也影响 Agent 读写)。如果将来重启,会先开 issue 对齐最小范围:默认零配置、只做落盘 + 相对路径引用、不新增常驻 UI 或工具栏。

感谢这份投入,抱歉方向没对上。

@orange2ai orange2ai closed this Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

功能需求

2 participants