4b0bbc77b0
* fix(reader): open TXT files shared via "Open with" by converting to EPUB
The Android "Open with Readest" (VIEW intent) transient path hands the
reader the original .txt file (its filePath points at the content:// URI),
unlike the managed library which stores the already-converted EPUB. The
DocumentLoader had no branch for a raw .txt, so open() returned
{ book: null } and initViewState crashed with
"TypeError: Cannot read properties of null (reading 'metadata')",
leaving the user stuck on the library splash.
Add an isTxt() check that converts the raw .txt to EPUB in-memory (the
same TxtToEpubConverter the import path runs) and parses that. The
converter emits a .epub-named file, so the importer's own
DocumentLoader.open() on the converted file is unaffected.
Verified on-device (emulator, warm + cold start): the TXT now opens and
renders in the reader instead of crashing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(settings): allow adjusting highlight opacity in e-ink mode
Drop the isEink prop that disabled the highlight Opacity slider under
e-ink. Opacity is still meaningful on e-ink, so let users change it.
Removes the prop from HighlightColorsEditor, its ColorPanel call site,
and the test render helper.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(send): mock fetch to fix flaky article conversion test
The article/page conversion paths fetch a favicon + author image for the
synthetic cover via globalThis.fetch. In jsdom that hit the real network:
a live fetch to the sample URL can hang up to faviconFetcher's 6s timeout,
exceeding the 5s test timeout and intermittently failing the suite. Stub
fetch so the cover falls back to its initial-letter tile (the pattern other
tests in this suite already use). Article test: ~5003ms hang -> ~80ms.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(agent): update annotation-share-toolbar memory
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
4.4 KiB
4.4 KiB
name, description, metadata
| name | description | metadata | ||||||
|---|---|---|---|---|---|---|---|---|
| annotation-share-toolbar-4014 | Share intent in the selection toolbar + drag-and-drop toolbar customizer (#4014) |
|
#4014 (PR #4570) — added a native "Share" tool to the in-reader text-selection toolbar
plus a drag-and-drop customizer (show/hide + reorder tools). Branch
feat/annotation-share-toolbar-4014; spec + plan in
docs/superpowers/{specs,plans}/2026-06-13-annotation-share-toolbar*.
Key facts / gotchas:
src/utils/share.tsis dual-purpose — it already held share-LINK helpers (buildShareUrl/parseShareDeepLinkfor the/s/{token}feature). Text-share was added there:shareSelectedText(text, position?, appService?)andcanShareText(appService).- Native share is gated to mobile + macOS only (
isMobileApp || isMacOSApp). Windows/Linux desktop are excluded because@choochmeque/tauri-plugin-sharekit-api's share UI can FREEZE the app on Windows (issue #4343) —nativeAppService.saveFilegatesshareFilethe same way. Ladder: native →navigator.share→ clipboard.canShareText= that OR webnavigator.share; used to gate Share's visibility in toolbar + customizer + the quick-action dropdown. - Toolbar order is a view setting:
AnnotatorConfig.annotationToolbarItems(src/types/book.ts), default inDEFAULT_ANNOTATOR_CONFIG= the original 8 tools, Share hidden by default (starts in the "Available" tray). No migration needed: the{...getDefaultViewSettings(ctx), ...saved}merge insettingsService.ts+getToolbarToolTypes(undefined,...)fallback both yield the default. - Pure helpers in
src/utils/annotationToolbar.ts(unit-tested) own all order/visibility logic:getToolbarToolTypes/getAvailableToolTypes(canShare-gated, dedup, drop-unknown),add/remove/reorderToolbar.ALL_ANNOTATION_TOOL_TYPESis asserted to match theannotationToolButtonsregistry order by a test. - Customizer =
src/components/settings/AnnotationToolbarCustomizer.tsx, a sub-page offControlPanel(Behavior panel) viaNavigationRow. Two@dnd-kitzones; chips are tap-to-toggle AND drag. Design evolved heavily during live browser testing (see gotchas):- WYSIWYG: "In toolbar" renders a faithful preview of the real selection popup —
selection-popup bg-gray-600 text-white, icon-only 32×32 buttons (mirrorsAnnotationToolButton),w-fit max-w-full(content-width, start-aligned). "Available" tools are labeled icon+text chips. Zone content usespx-4to align withSubPageHeader. - dnd-kit multiple-containers pattern (NOT the simple single-list one): single
{toolbar, available}state;onDragOverlive-reparents across zones; customcollisionDetection=pointerWithin→rectIntersectionfallback, snapping a zone-id hit to the closest inner chip (plainclosestCorners/closestCenterCANNOT drop into an empty zone).rectSortingStrategy(NOThorizontalListSortingStrategy, which breaks wrapped layouts). - NO
DragOverlay— the settings modal is a CSS-transformcontainer, so aposition:fixedoverlay is offset from the cursor. In-placeuseSortabletransform (relative translate) tracks correctly. itemsRefstale-closure fix: dnd-kit callsonDragEndwith the handler captured at drag START, so the closed-overitemsis stale → a cross-zone drag would bounce back on release. Read live state fromitemsRef.currentinhandleDragEnd/tap handlers.- Add all (rebuilds in canonical
ALL_ANNOTATION_TOOL_TYPESorder, NOT prior order) / Clear all header buttons. - Cross-platform guard: when editing on a
!canSharedevice,persistre-appends asharethat was synced-in but hidden, so it isn't dropped for share-capable devices.
- WYSIWYG: "In toolbar" renders a faithful preview of the real selection popup —
- Empty toolbar suppresses the popup: when
getToolbarToolTypesyields [] (user cleared all),Annotator.tsxdoes NOT render theAnnotationPopupon a plain selection (gated ontoolButtons.length > 0 || highlightOptionsVisible || annotationNotes.length > 0) — no empty bar, but highlight-edit/notes popups still work. (Earlier tried fallback-to-default; user wanted full suppression instead.) - Adding a tool to the union (
AnnotationToolType) is compile-checked: thecreateAnnotationToolButtonsgeneric inAnnotationTools.tsxrequires every member.