feat(share): refine image themes and persist settings across clients

This commit is contained in:
tianma-if committed 2026-10-16 21:57:49 +08:00
1 parent 70d486e4d7
commit 776fc3b566
410 files changed
+5671 -4478

No files matched your search

@@ -57,6 +57,7 @@
88A1141343A4E255A8050CB6 /* RevisionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328A2DAC7E7DC89F4782B5FB /* RevisionsView.swift */; };
88C20914237162E295798B54 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 1B167394120674FECA86D42F /* GRDB */; };
8C761DABADB93F2F15E21E80 /* EditFabButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2829CE5DF5C15ACF41CC17DA /* EditFabButton.swift */; };
8E1CB597E6534EBFAD4B43FE /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 01D34117A78B1F5C367ACDBB /* assets */; };
90FFFFD00B2D19F96EA299C8 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5126510AFB9AFBF7ABADB45 /* Models.swift */; };
93556FDCB87BB93990221E01 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8691859094F8C08AE49FE27D /* Assets.xcassets */; };
94456E5BCAB7496FAD4685A6 /* Pow in Frameworks */ = {isa = PBXBuildFile; productRef = E37BD605D826474D3E842F31 /* Pow */; };
@@ -120,6 +121,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
01D34117A78B1F5C367ACDBB /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = EdgeEver/Resources/EditorBundle/assets; sourceTree = SOURCE_ROOT; };
033565CE2E96EC2D23105188 /* ListActionsSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListActionsSheet.swift; sourceTree = "<group>"; };
0531FC6AA81DAF0A55B42362 /* AppDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDatabase.swift; sourceTree = "<group>"; };
0EF7E97C950002FA22EFB7C8 /* AiDirectStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiDirectStream.swift; sourceTree = "<group>"; };
@@ -342,6 +344,7 @@
821234A55C8B7CF544B28EE6 /* EditorBundle */ = {
isa = PBXGroup;
children = (
01D34117A78B1F5C367ACDBB /* assets */,
AB02424D0412FE36C4BCD116 /* index.html */,
);
path = EditorBundle;
@@ -618,6 +621,7 @@
93556FDCB87BB93990221E01 /* Assets.xcassets in Resources */,
06D2661CAB0ADED27F322649 /* Localizable.xcstrings in Resources */,
C37DA99E090A783562506CDA /* PrivacyInfo.xcprivacy in Resources */,
8E1CB597E6534EBFAD4B43FE /* assets in Resources */,
08D0159B8F194E04C02B69BF /* index.html in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -32,6 +32,55 @@ enum AppUILocale {
}
}
/// Local image-share choices, independent of the current note or sheet lifetime.
struct NoteImageSharePreferences: Codable, Equatable {
var theme: String = "gradient"
var fontStyle: String = "auto"
var fontSize: String = "lg"
var cardWidth: String = "wide"
var format: String = "png"
var showTitle: Bool = true
var showNotebook: Bool = false
var showTags: Bool = false
var showUpdatedAt: Bool = true
var showBranding: Bool = true
init() {}
private enum CodingKeys: String, CodingKey {
case theme, fontStyle, fontSize, cardWidth, format, showTitle, showNotebook, showTags, showUpdatedAt, showBranding
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if let value = try? values.decode(String.self, forKey: .theme),
["gradient", "glass", "quote", "book", "memo", "bento", "black-sun", "frame", "handwritten", "code"].contains(value) {
theme = value
}
if let value = try? values.decode(String.self, forKey: .fontStyle),
["auto", "serif", "sans", "mono", "kai"].contains(value) {
fontStyle = value
}
if let value = try? values.decode(String.self, forKey: .fontSize),
["sm", "md", "lg"].contains(value) {
fontSize = value
}
if let value = try? values.decode(String.self, forKey: .cardWidth),
["compact", "standard", "wide"].contains(value) {
cardWidth = value
}
if let value = try? values.decode(String.self, forKey: .format),
["png", "jpeg"].contains(value) {
format = value
}
showTitle = (try? values.decode(Bool.self, forKey: .showTitle)) ?? true
showNotebook = (try? values.decode(Bool.self, forKey: .showNotebook)) ?? false
showTags = (try? values.decode(Bool.self, forKey: .showTags)) ?? false
showUpdatedAt = (try? values.decode(Bool.self, forKey: .showUpdatedAt)) ?? true
showBranding = (try? values.decode(Bool.self, forKey: .showBranding)) ?? true
}
}
@Observable
@MainActor
final class PreferencesStore {
@@ -64,8 +113,22 @@ final class PreferencesStore {
didSet { persistAiAssistantLastAction() }
}
var noteImageShare: NoteImageSharePreferences {
didSet {
if let data = try? JSONEncoder().encode(noteImageShare) {
defaults.set(data, forKey: Keys.noteImageShare)
}
}
}
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
if let data = defaults.data(forKey: Keys.noteImageShare),
let saved = try? JSONDecoder().decode(NoteImageSharePreferences.self, from: data) {
self.noteImageShare = saved
} else {
self.noteImageShare = NoteImageSharePreferences()
}
self.localeCode = defaults.string(forKey: Keys.locale) ?? "system"
self.useCompression = defaults.object(forKey: Keys.compression) as? Bool ?? true
self.showDescendantNotes = defaults.object(forKey: Keys.showDescendantNotes) as? Bool ?? true
@@ -157,6 +220,7 @@ final class PreferencesStore {
}
private enum Keys {
static let noteImageShare = "edgeever.ios.noteImagePreferences"
static let locale = "edgeever.ios.locale"
static let compression = "edgeever.ios.imageCompression"
static let showDescendantNotes = "edgeever.ios.showDescendantNotes"
@@ -23,16 +23,6 @@ struct MemoDetailView: View {
@State private var imageExportRequestId: String?
@State private var imageExportBuffer = MemoImageExportBuffer()
@State private var imageShareOptionsOpen = false
@State private var imageShareFormat = "png"
@State private var imageShareTheme = "mint"
@State private var imageShareFontStyle = "auto"
@State private var imageShareFontSize = "lg"
@State private var imageShareCardWidth = "wide"
@State private var imageShareTitle = true
@State private var imageShareNotebook = false
@State private var imageShareTags = false
@State private var imageShareUpdatedAt = true
@State private var imageShareBranding = true
@State private var imageExportIntent: MemoImageExportIntent = .share
@State private var error: String?
@State private var conflictItem: OutboxItem?
@@ -56,7 +46,8 @@ struct MemoDetailView: View {
@State private var initialShareImageHandled = false
var body: some View {
VStack(spacing: 0) {
@Bindable var preferences = env.preferences
return VStack(spacing: 0) {
detailHeader
if syncStatus == .conflict, memo != nil {
conflictBanner
@@ -187,19 +178,22 @@ struct MemoDetailView: View {
NavigationStack {
Form {
Section(env.preferences.t("主题风格", en: "Theme", pl: "Motyw")) {
Picker(env.preferences.t("主题风格", en: "Theme", pl: "Motyw"), selection: $imageShareTheme) {
Text(env.preferences.t("纯白", en: "Pure White", pl: "Czysta biel")).tag("slate")
Text(env.preferences.t("薄荷", en: "Mint", pl: "Mięta")).tag("mint")
Text(env.preferences.t("极光渐变", en: "Aurora", pl: "Zorza")).tag("aurora")
Text(env.preferences.t("暮色晚霞", en: "Sunset", pl: "Zachód słońca")).tag("sunset")
Text(env.preferences.t("黑白编辑", en: "Black & White", pl: "Czerń i biel")).tag("editorial")
Text(env.preferences.t("极客终端", en: "Terminal", pl: "Terminal")).tag("terminal")
Text(env.preferences.t("书页", en: "Book Page", pl: "Strona książki")).tag("film")
Picker(env.preferences.t("主题风格", en: "Theme", pl: "Motyw"), selection: $preferences.noteImageShare.theme) {
Text(env.preferences.t("默认", en: "Classic", pl: "Klasyczny")).tag("gradient")
Text(env.preferences.t("透明", en: "Glass", pl: "Szkło")).tag("glass")
Text(env.preferences.t("金句", en: "Quote", pl: "Cytat")).tag("quote")
Text(env.preferences.t("书摘", en: "Book excerpt", pl: "Fragment książki")).tag("book")
Text(env.preferences.t("备忘录", en: "Memo", pl: "Notatka")).tag("memo")
Text(env.preferences.t("便当", en: "Bento", pl: "Bento")).tag("bento")
Text(env.preferences.t("黑日", en: "Black Sun", pl: "Czarne słońce")).tag("black-sun")
Text(env.preferences.t("框界", en: "Frame", pl: "Ramka")).tag("frame")
Text(env.preferences.t("手写", en: "Handwritten", pl: "Pismo odręczne")).tag("handwritten")
Text(env.preferences.t("代码", en: "Code", pl: "Kod")).tag("code")
}
.pickerStyle(.menu)
}
Section(env.preferences.t("字体风格", en: "Typography", pl: "Typografia")) {
Picker(env.preferences.t("字体风格", en: "Typography", pl: "Typografia"), selection: $imageShareFontStyle) {
Picker(env.preferences.t("字体风格", en: "Typography", pl: "Typografia"), selection: $preferences.noteImageShare.fontStyle) {
Text(env.preferences.t("跟随主题", en: "Follow Theme", pl: "Zgodnie z motywem")).tag("auto")
Text(env.preferences.t("文艺衬线", en: "Serif", pl: "Szeryfowa")).tag("serif")
Text(env.preferences.t("现代无衬线", en: "Sans", pl: "Bezszeryfowa")).tag("sans")
@@ -208,7 +202,7 @@ struct MemoDetailView: View {
.pickerStyle(.menu)
}
Section(env.preferences.t("字号大小", en: "Font size", pl: "Rozmiar czcionki")) {
Picker(env.preferences.t("字号大小", en: "Font size", pl: "Rozmiar czcionki"), selection: $imageShareFontSize) {
Picker(env.preferences.t("字号大小", en: "Font size", pl: "Rozmiar czcionki"), selection: $preferences.noteImageShare.fontSize) {
Text(env.preferences.t("紧凑", en: "Compact", pl: "Kompaktowy")).tag("sm")
Text(env.preferences.t("标准", en: "Standard", pl: "Standardowy")).tag("md")
Text(env.preferences.t("舒适", en: "Comfortable", pl: "Wygodny")).tag("lg")
@@ -216,7 +210,7 @@ struct MemoDetailView: View {
.pickerStyle(.segmented)
}
Section(env.preferences.t("卡片宽度", en: "Card width", pl: "Szerokość karty")) {
Picker(env.preferences.t("卡片宽度", en: "Card width", pl: "Szerokość karty"), selection: $imageShareCardWidth) {
Picker(env.preferences.t("卡片宽度", en: "Card width", pl: "Szerokość karty"), selection: $preferences.noteImageShare.cardWidth) {
Text(env.preferences.t("紧凑", en: "Compact", pl: "Kompaktowy")).tag("compact")
Text(env.preferences.t("标准", en: "Standard", pl: "Standardowy")).tag("standard")
Text(env.preferences.t("宽屏", en: "Wide", pl: "Szeroka")).tag("wide")
@@ -224,14 +218,14 @@ struct MemoDetailView: View {
.pickerStyle(.segmented)
}
Section(env.preferences.t("显示内容", en: "Content elements", pl: "Elementy treści")) {
Toggle(env.preferences.t("笔记标题", en: "Note title", pl: "Tytuł notatki"), isOn: $imageShareTitle)
Toggle(env.preferences.t("笔记本", en: "Notebook", pl: "Notatnik"), isOn: $imageShareNotebook)
Toggle(env.preferences.t("标签", en: "Tags", pl: "Tagi"), isOn: $imageShareTags)
Toggle(env.preferences.t("更新时间", en: "Updated time", pl: "Czas aktualizacji"), isOn: $imageShareUpdatedAt)
Toggle(env.preferences.t("EdgeEver 品牌标识", en: "EdgeEver branding", pl: "Oznaczenie EdgeEver"), isOn: $imageShareBranding)
Toggle(env.preferences.t("笔记标题", en: "Note title", pl: "Tytuł notatki"), isOn: $preferences.noteImageShare.showTitle)
Toggle(env.preferences.t("笔记本", en: "Notebook", pl: "Notatnik"), isOn: $preferences.noteImageShare.showNotebook)
Toggle(env.preferences.t("标签", en: "Tags", pl: "Tagi"), isOn: $preferences.noteImageShare.showTags)
Toggle(env.preferences.t("更新时间", en: "Updated time", pl: "Czas aktualizacji"), isOn: $preferences.noteImageShare.showUpdatedAt)
Toggle(env.preferences.t("EdgeEver 品牌标识", en: "EdgeEver branding", pl: "Oznaczenie EdgeEver"), isOn: $preferences.noteImageShare.showBranding)
}
Section(env.preferences.t("图片格式", en: "Image format", pl: "Format obrazu")) {
Picker(env.preferences.t("图片格式", en: "Image format", pl: "Format obrazu"), selection: $imageShareFormat) {
Picker(env.preferences.t("图片格式", en: "Image format", pl: "Format obrazu"), selection: $preferences.noteImageShare.format) {
Text(env.preferences.t("PNG · 超清无损", en: "PNG · Best for text", pl: "PNG · Najlepszy do tekstu")).tag("png")
Text(env.preferences.t("JPEG · 体积小", en: "JPEG · Smaller file", pl: "JPEG · Mniejszy plik")).tag("jpeg")
}
@@ -250,16 +244,16 @@ struct MemoDetailView: View {
imageShareOptionsOpen = false
exportMemoImage(
memo,
format: imageShareFormat,
theme: imageShareTheme,
fontStyle: imageShareFontStyle,
fontSize: imageShareFontSize,
cardWidth: imageShareCardWidth,
showTitle: imageShareTitle,
showNotebook: imageShareNotebook,
showTags: imageShareTags,
showUpdatedAt: imageShareUpdatedAt,
showBranding: imageShareBranding,
format: env.preferences.noteImageShare.format,
theme: env.preferences.noteImageShare.theme,
fontStyle: env.preferences.noteImageShare.fontStyle,
fontSize: env.preferences.noteImageShare.fontSize,
cardWidth: env.preferences.noteImageShare.cardWidth,
showTitle: env.preferences.noteImageShare.showTitle,
showNotebook: env.preferences.noteImageShare.showNotebook,
showTags: env.preferences.noteImageShare.showTags,
showUpdatedAt: env.preferences.noteImageShare.showUpdatedAt,
showBranding: env.preferences.noteImageShare.showBranding,
intent: .preview
)
}
Loaded 100 of 410 files, more files were not shown because too many files have changed in this diff. Show more