forked from akai/readest
912e97cb82
* feat(send): iOS share-extension picker + App Group queue + reliable host launch
Rework the iOS Share Extension to a Zotero-style sheet: URL preview row +
library group picker + "Save & Open". Queues each save into the shared
App Group container and best-effort launches the host app via the
Chrome-style responder-chain trick (IMP cast against
`openURL:options:completionHandler:`). The host plugin drains the queue
on `applicationDidBecomeActive`, so if the launch ever fails the article
still ingests next time Readest is opened.
A `WKScriptMessageHandler` named `readestShareBridge` lets the JS hook
post `{type:'ready'}` on mount, fixing the cold-start race when the
extension wakes the app before the React side has loaded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(send): cover generator, favicon fetcher, share-extension polish, locale sync
Builds on the previous commit (iOS share-extension picker + App Group queue +
reliable host launch) with three further additions to the send-to-Readest
pipeline:
* **Cover generator** — `services/send/conversion/coverGenerator.ts` renders a
deterministic cover image into clipped EPUBs (favicon + page title + host).
Hooked into `buildEpub.ts` so every clip path (desktop, mobile, browser
extension) produces the same cover for the same source.
* **Favicon fetcher** — `services/send/conversion/faviconFetcher.ts` resolves
the best-available site icon (Open Graph image → apple-touch-icon →
/favicon.ico), with size + format normalization. Feeds the cover generator.
* **Unified page conversion** — `convertToEpub({kind:'page', ...})` replaces
the older `convertPageToEpub(html, url)` so the share extension, /send page,
and browser extension share one entry point. Test: `send-convert-page-unified`.
* **Share-extension project.yml comment** — clarifies why the ShareExtension
target carries no `.lproj` files (system bar buttons + JS-supplied "Default"
label, no per-locale strings to wire).
* **Locale sync** — 33 translation.json files updated with new "Default",
"Saving article…", and cover-generator strings extracted by i18next-scanner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
170 lines
7.5 KiB
YAML
170 lines
7.5 KiB
YAML
name: Readest
|
|
options:
|
|
bundleIdPrefix: com.bilingify.readest
|
|
deploymentTarget:
|
|
iOS: 15.0
|
|
fileGroups: [../../src]
|
|
configs:
|
|
debug: debug
|
|
release: release
|
|
settingGroups:
|
|
app:
|
|
base:
|
|
PRODUCT_NAME: Readest
|
|
PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest
|
|
DEVELOPMENT_TEAM: J5W48D69VR
|
|
targetTemplates:
|
|
app:
|
|
type: application
|
|
sources:
|
|
- path: Sources
|
|
scheme:
|
|
environmentVariables:
|
|
RUST_BACKTRACE: full
|
|
RUST_LOG: info
|
|
settings:
|
|
groups: [app]
|
|
targets:
|
|
Readest_iOS:
|
|
type: application
|
|
platform: iOS
|
|
# NOTE on In-App Purchase capability
|
|
# ----------------------------------
|
|
# IAP is enabled at three layers:
|
|
# 1. App ID `com.bilingify.readest` services (Apple Developer
|
|
# portal) — must have "In-App Purchase" service ON.
|
|
# 2. Provisioning profile — pulled automatically by Xcode when
|
|
# DEVELOPMENT_TEAM is set; inherits IAP from the App ID.
|
|
# 3. StoreKit framework — linked transitively by the
|
|
# tauri-plugin-native-bridge plugin's iap_* commands.
|
|
# The `SystemCapabilities` pbxproj entry that Xcode writes when you
|
|
# add IAP via Signing & Capabilities is informational only; it
|
|
# doesn't gate the actual capability. xcodegen 2.45 can't
|
|
# round-trip nested dictionaries into pbxproj attributes (it
|
|
# serializes them as quoted strings), so we omit the entry rather
|
|
# than ship a malformed one. IAP works without it.
|
|
sources:
|
|
- path: Sources
|
|
- path: Assets.xcassets
|
|
# Only reference Externals as a file group (no compile/resource
|
|
# phases) — the actual libapp.a lookup happens via the
|
|
# `framework: libapp.a` dependency + LIBRARY_SEARCH_PATHS pointing
|
|
# at `Externals/arm64/$(CONFIGURATION)`. Listing the whole folder
|
|
# as a source caused xcodegen to add copy-resource phases for
|
|
# both `debug/libapp.a` and `release/libapp.a`, conflicting at
|
|
# build time:
|
|
# "Multiple commands produce Readest.app/libapp.a"
|
|
- path: Externals
|
|
buildPhase: none
|
|
- path: Readest_iOS
|
|
- path: assets
|
|
buildPhase: resources
|
|
type: folder
|
|
- path: LaunchScreen.storyboard
|
|
# Info.plist and entitlements live on disk with hand-tuned content
|
|
# (CFBundleDocumentTypes, UTExportedTypeDeclarations, locales,
|
|
# CFBundleURLTypes for readest://, com.apple.developer.applesignin,
|
|
# associated-domains for Universal Links). Reference them via
|
|
# INFOPLIST_FILE / CODE_SIGN_ENTITLEMENTS instead of `info:` /
|
|
# `entitlements:` so xcodegen treats them as opaque inputs and
|
|
# doesn't rewrite them on regeneration.
|
|
scheme:
|
|
environmentVariables:
|
|
RUST_BACKTRACE: full
|
|
RUST_LOG: info
|
|
settings:
|
|
base:
|
|
INFOPLIST_FILE: Readest_iOS/Info.plist
|
|
CODE_SIGN_ENTITLEMENTS: Readest_iOS/Readest_iOS.entitlements
|
|
ENABLE_BITCODE: false
|
|
ARCHS: [arm64]
|
|
VALID_ARCHS: arm64
|
|
LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
|
|
LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
|
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true
|
|
# Tell Xcode the app embeds Swift code (the new ShareExtension
|
|
# is a Swift app-extension target). Without this the
|
|
# swift-stdlib-tool phase that copies libswiftCore.dylib +
|
|
# friends into Readest.app/Frameworks runs incorrectly under
|
|
# Xcode 26 — the app launches and immediately dyld-fails with
|
|
# "libswiftCore.dylib: no such file".
|
|
EMBEDDED_CONTENT_CONTAINS_SWIFT_CODE: YES
|
|
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64
|
|
groups: [app]
|
|
dependencies:
|
|
- framework: libapp.a
|
|
embed: false
|
|
- target: ShareExtension
|
|
embed: true
|
|
codeSign: true
|
|
- sdk: CoreGraphics.framework
|
|
- sdk: Metal.framework
|
|
- sdk: MetalKit.framework
|
|
- sdk: QuartzCore.framework
|
|
- sdk: Security.framework
|
|
- sdk: UIKit.framework
|
|
- sdk: WebKit.framework
|
|
preBuildScripts:
|
|
- script: pnpm tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}
|
|
name: Build Rust Code
|
|
basedOnDependencyAnalysis: false
|
|
outputFiles:
|
|
- $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a
|
|
- $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a
|
|
|
|
# Share-sheet extension that lets users send article URLs from Safari /
|
|
# Chrome / etc. directly to Readest. The extension grabs the URL via
|
|
# NSExtensionContext, encodes it into `readest://share?url=...`, and
|
|
# opens the main app — which catches the deep-link via
|
|
# `tauri-plugin-deep-link` and runs the clip-and-import pipeline.
|
|
ShareExtension:
|
|
type: app-extension
|
|
platform: iOS
|
|
# The Share Extension uses iOS-system-localized bar button items
|
|
# (`barButtonSystemItem: .cancel` / `.save`) plus a JS-supplied
|
|
# "Default" group name (passed through the App Group), so the target
|
|
# carries no per-locale `.strings` files — no `.lproj` directories
|
|
# to wire up.
|
|
sources:
|
|
- path: ShareExtension
|
|
info:
|
|
path: ShareExtension/Info.plist
|
|
properties:
|
|
CFBundleDisplayName: Readest
|
|
NSExtension:
|
|
NSExtensionPointIdentifier: com.apple.share-services
|
|
NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).ShareViewController
|
|
NSExtensionAttributes:
|
|
NSExtensionActivationRule:
|
|
NSExtensionActivationSupportsWebURLWithMaxCount: 1
|
|
NSExtensionActivationSupportsText: true
|
|
# Entitlements referenced via CODE_SIGN_ENTITLEMENTS build setting
|
|
# rather than xcodegen's `entitlements:` block — otherwise xcodegen
|
|
# may overwrite the file (even with no properties declared) during
|
|
# regeneration, and Xcode rejects the build with
|
|
# "Entitlements file ... was modified during the build"
|
|
settings:
|
|
base:
|
|
PRODUCT_NAME: ShareExtension
|
|
PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest.ShareExtension
|
|
DEVELOPMENT_TEAM: J5W48D69VR
|
|
CODE_SIGN_ENTITLEMENTS: ShareExtension/ShareExtension.entitlements
|
|
# Permit Xcode's productPackagingUtility to write the auto-derived
|
|
# signing entitlements (application-identifier, team-identifier,
|
|
# get-task-allow) back to the source entitlements file at build
|
|
# time. Without this, Xcode 16+ refuses the modification it
|
|
# itself just performed and the build fails with:
|
|
# "Entitlements file ... was modified during the build"
|
|
# Safe for this target because the entitlements file is empty —
|
|
# the only modifications Xcode performs are the team / app-id
|
|
# auto-population every iOS extension target needs anyway.
|
|
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION: YES
|
|
SWIFT_VERSION: "5.0"
|
|
IPHONEOS_DEPLOYMENT_TARGET: 15.0
|
|
ARCHS: [arm64]
|
|
VALID_ARCHS: arm64
|
|
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64
|
|
ENABLE_BITCODE: false
|
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: false
|
|
SKIP_INSTALL: true
|