feat: #24 公开库发现 + 订阅管理
- KnowledgeBaseService 新增 discover/subscribe/unsubscribe API - DiscoverView: 浏览公开库 + 订阅/取消订阅 - Route.discoverPublic + ProfileView 入口 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
9e1a8982a3
commit
5fe29a7d8c
@ -255,13 +255,26 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/AIStudyApp/Core/Services",
|
||||
);
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "知习";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LINKER_FLAGS = "$(inherited) -lzx_document_ffi";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cloud.longde.AIStudyApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@ -271,6 +284,7 @@
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_INCLUDE_PATHS = "$(inherited) $(PROJECT_DIR)/AIStudyApp/Core/Services";
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||
@ -291,13 +305,26 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/AIStudyApp/Core/Services",
|
||||
);
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "知习";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64-simulator",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/AIStudyApp/Core/ZxDocumentRuntime.xcframework/ios-arm64",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LINKER_FLAGS = "$(inherited) -lzx_document_ffi";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cloud.longde.AIStudyApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@ -307,6 +334,7 @@
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_INCLUDE_PATHS = "$(inherited) $(PROJECT_DIR)/AIStudyApp/Core/Services";
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||
|
||||
Binary file not shown.
@ -12,6 +12,7 @@ enum Route: Hashable {
|
||||
// Library
|
||||
case librarySearch
|
||||
case libraryDetail(knowledgeBaseId: String)
|
||||
case discoverPublic
|
||||
case libraryImport
|
||||
case libraryCreate
|
||||
case addKnowledge(knowledgeBaseId: String)
|
||||
@ -54,6 +55,7 @@ extension Route {
|
||||
case .reviewCard: "ReviewCardView"
|
||||
case .librarySearch: "LibrarySearchView"
|
||||
case .libraryDetail: "LibraryDetailPage"
|
||||
case .discoverPublic: "DiscoverView"
|
||||
case .libraryImport: "ImportPage"
|
||||
case .libraryCreate: "CreateLibraryPage"
|
||||
case .addKnowledge: "AddKnowledgePage"
|
||||
@ -89,6 +91,7 @@ extension Route {
|
||||
|
||||
case .librarySearch: AnyView(LibrarySearchView())
|
||||
case .libraryDetail(let id): AnyView(LibraryDetailPage(knowledgeBaseId: id))
|
||||
case .discoverPublic: AnyView(DiscoverView())
|
||||
case .libraryImport: AnyView(ImportPage())
|
||||
case .libraryCreate: AnyView(CreateLibraryPage())
|
||||
case .addKnowledge(let id): AnyView(AddKnowledgePage(knowledgeBaseId: id))
|
||||
|
||||
@ -21,6 +21,10 @@ actor APIClient {
|
||||
self.token = token
|
||||
}
|
||||
|
||||
func getToken() -> String? {
|
||||
return token
|
||||
}
|
||||
|
||||
// MARK: - Generic request
|
||||
|
||||
func request<T: Decodable>(
|
||||
|
||||
@ -137,6 +137,22 @@ class KnowledgeBaseService {
|
||||
])
|
||||
}
|
||||
|
||||
func discover(page: Int = 1, limit: Int = 20) async throws -> [KnowledgeBase] {
|
||||
return try await client.request("/knowledge-bases", queryItems: [
|
||||
URLQueryItem(name: "visibility", value: "public"),
|
||||
URLQueryItem(name: "page", value: String(page)),
|
||||
URLQueryItem(name: "limit", value: String(limit)),
|
||||
])
|
||||
}
|
||||
|
||||
func subscribe(kbId: String) async throws -> GenericSuccessResponse {
|
||||
return try await client.request("/knowledge-bases/\(kbId)/subscribe", method: "POST")
|
||||
}
|
||||
|
||||
func unsubscribe(kbId: String) async throws -> GenericSuccessResponse {
|
||||
return try await client.request("/knowledge-bases/\(kbId)/subscribe", method: "DELETE")
|
||||
}
|
||||
|
||||
func delete(id: String) async throws -> GenericSuccessResponse {
|
||||
return try await client.request("/knowledge-bases/\(id)", method: "DELETE")
|
||||
}
|
||||
|
||||
1997
AIStudyApp/AIStudyApp/Core/Services/zx_document.swift
Normal file
1997
AIStudyApp/AIStudyApp/Core/Services/zx_document.swift
Normal file
File diff suppressed because it is too large
Load Diff
104
AIStudyApp/AIStudyApp/Core/Services/zx_document.udl
Normal file
104
AIStudyApp/AIStudyApp/Core/Services/zx_document.udl
Normal file
@ -0,0 +1,104 @@
|
||||
namespace zx_document {};
|
||||
|
||||
[Error]
|
||||
enum DocumentError {
|
||||
"FileNotFound",
|
||||
"UnsupportedFormat",
|
||||
"ParseError",
|
||||
"InvalidEncoding",
|
||||
"IoError",
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface MaterialType {
|
||||
Markdown();
|
||||
Text();
|
||||
Pdf();
|
||||
Image();
|
||||
Epub();
|
||||
Word();
|
||||
Excel();
|
||||
PowerPoint();
|
||||
Unknown();
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface PreviewMode {
|
||||
NativeReader();
|
||||
PlatformPreview();
|
||||
ExternalOpen();
|
||||
Unsupported();
|
||||
};
|
||||
|
||||
dictionary DocumentInfo {
|
||||
string material_id;
|
||||
string title;
|
||||
MaterialType material_type;
|
||||
PreviewMode preview_mode;
|
||||
u64 file_size;
|
||||
u32? page_count;
|
||||
u32? word_count;
|
||||
string? created_at;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface ReadingPosition {
|
||||
Markdown(string block_id, f32 scroll_progress);
|
||||
Text(u32 line_number, f32 scroll_progress);
|
||||
Pdf(u32 page_number, f32 page_progress, f32 overall_progress);
|
||||
Image(f32 zoom_scale, f32 offset_x, f32 offset_y);
|
||||
Epub(string chapter_id, f32 chapter_progress, f32 overall_progress);
|
||||
Unknown();
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface ReadingEvent {
|
||||
MaterialOpened(string material_id, i64 timestamp_ms);
|
||||
MaterialClosed(string material_id, i64 timestamp_ms, u32 active_seconds);
|
||||
PositionChanged(string material_id, ReadingPosition position, i64 timestamp_ms);
|
||||
Heartbeat(string material_id, u32 active_seconds, ReadingPosition? position, i64 timestamp_ms);
|
||||
MarkedAsRead(string material_id, i64 timestamp_ms);
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface NoteAnchor {
|
||||
Material(string material_id);
|
||||
MarkdownBlock(string material_id, string block_id);
|
||||
TextLine(string material_id, u32 line_number);
|
||||
PdfPage(string material_id, u32 page_number);
|
||||
Image(string material_id);
|
||||
EpubChapter(string material_id, string chapter_id);
|
||||
KnowledgeItem(string knowledge_item_id);
|
||||
};
|
||||
|
||||
dictionary ImageMeta {
|
||||
u32 width;
|
||||
u32 height;
|
||||
string format;
|
||||
u64 file_size;
|
||||
};
|
||||
|
||||
dictionary SearchResult {
|
||||
string block_id;
|
||||
u32? line_number;
|
||||
string snippet;
|
||||
u64 match_start;
|
||||
u64 match_end;
|
||||
};
|
||||
|
||||
dictionary TextStats {
|
||||
u32 line_count;
|
||||
u32 word_count;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface DocumentBlock {
|
||||
Heading(string id, u8 level, string text);
|
||||
Paragraph(string id, string text);
|
||||
List(string id, boolean ordered, sequence<string> items);
|
||||
CodeBlock(string id, string? language, string code);
|
||||
Quote(string id, string text);
|
||||
Table(string id, sequence<string> headers, sequence<sequence<string>> rows);
|
||||
ImageBlock(string id, string src, string? alt);
|
||||
HorizontalRule(string id);
|
||||
};
|
||||
645
AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.h
Normal file
645
AIStudyApp/AIStudyApp/Core/Services/zx_documentFFI.h
Normal file
@ -0,0 +1,645 @@
|
||||
// This file was autogenerated by some hot garbage in the `uniffi` crate.
|
||||
// Trust me, you don't want to mess with it!
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// The following structs are used to implement the lowest level
|
||||
// of the FFI, and thus useful to multiple uniffied crates.
|
||||
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
|
||||
#ifdef UNIFFI_SHARED_H
|
||||
// We also try to prevent mixing versions of shared uniffi header structs.
|
||||
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4
|
||||
#ifndef UNIFFI_SHARED_HEADER_V4
|
||||
#error Combining helper code from multiple versions of uniffi is not supported
|
||||
#endif // ndef UNIFFI_SHARED_HEADER_V4
|
||||
#else
|
||||
#define UNIFFI_SHARED_H
|
||||
#define UNIFFI_SHARED_HEADER_V4
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||
|
||||
typedef struct RustBuffer
|
||||
{
|
||||
uint64_t capacity;
|
||||
uint64_t len;
|
||||
uint8_t *_Nullable data;
|
||||
} RustBuffer;
|
||||
|
||||
typedef struct ForeignBytes
|
||||
{
|
||||
int32_t len;
|
||||
const uint8_t *_Nullable data;
|
||||
} ForeignBytes;
|
||||
|
||||
// Error definitions
|
||||
typedef struct RustCallStatus {
|
||||
int8_t code;
|
||||
RustBuffer errorBuf;
|
||||
} RustCallStatus;
|
||||
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
|
||||
#endif // def UNIFFI_SHARED_H
|
||||
#ifndef UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
|
||||
#define UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
|
||||
typedef void (*UniffiRustFutureContinuationCallback)(uint64_t, int8_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK
|
||||
typedef void (*UniffiForeignFutureDroppedCallback)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
|
||||
#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
|
||||
typedef void (*UniffiCallbackInterfaceFree)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE
|
||||
#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE
|
||||
typedef uint64_t (*UniffiCallbackInterfaceClone)(uint64_t
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT
|
||||
typedef struct UniffiForeignFutureDroppedCallbackStruct {
|
||||
uint64_t handle;
|
||||
UniffiForeignFutureDroppedCallback _Nonnull free;
|
||||
} UniffiForeignFutureDroppedCallbackStruct;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8
|
||||
typedef struct UniffiForeignFutureResultU8 {
|
||||
uint8_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU8;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
|
||||
typedef void (*UniffiForeignFutureCompleteU8)(uint64_t, UniffiForeignFutureResultU8
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8
|
||||
typedef struct UniffiForeignFutureResultI8 {
|
||||
int8_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI8;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
|
||||
typedef void (*UniffiForeignFutureCompleteI8)(uint64_t, UniffiForeignFutureResultI8
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16
|
||||
typedef struct UniffiForeignFutureResultU16 {
|
||||
uint16_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU16;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
|
||||
typedef void (*UniffiForeignFutureCompleteU16)(uint64_t, UniffiForeignFutureResultU16
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16
|
||||
typedef struct UniffiForeignFutureResultI16 {
|
||||
int16_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI16;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
|
||||
typedef void (*UniffiForeignFutureCompleteI16)(uint64_t, UniffiForeignFutureResultI16
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32
|
||||
typedef struct UniffiForeignFutureResultU32 {
|
||||
uint32_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
|
||||
typedef void (*UniffiForeignFutureCompleteU32)(uint64_t, UniffiForeignFutureResultU32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32
|
||||
typedef struct UniffiForeignFutureResultI32 {
|
||||
int32_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
|
||||
typedef void (*UniffiForeignFutureCompleteI32)(uint64_t, UniffiForeignFutureResultI32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64
|
||||
typedef struct UniffiForeignFutureResultU64 {
|
||||
uint64_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultU64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
|
||||
typedef void (*UniffiForeignFutureCompleteU64)(uint64_t, UniffiForeignFutureResultU64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64
|
||||
typedef struct UniffiForeignFutureResultI64 {
|
||||
int64_t returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultI64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
|
||||
typedef void (*UniffiForeignFutureCompleteI64)(uint64_t, UniffiForeignFutureResultI64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32
|
||||
typedef struct UniffiForeignFutureResultF32 {
|
||||
float returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultF32;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
|
||||
typedef void (*UniffiForeignFutureCompleteF32)(uint64_t, UniffiForeignFutureResultF32
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64
|
||||
typedef struct UniffiForeignFutureResultF64 {
|
||||
double returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultF64;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
|
||||
typedef void (*UniffiForeignFutureCompleteF64)(uint64_t, UniffiForeignFutureResultF64
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER
|
||||
typedef struct UniffiForeignFutureResultRustBuffer {
|
||||
RustBuffer returnValue;
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultRustBuffer;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
|
||||
typedef void (*UniffiForeignFutureCompleteRustBuffer)(uint64_t, UniffiForeignFutureResultRustBuffer
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID
|
||||
typedef struct UniffiForeignFutureResultVoid {
|
||||
RustCallStatus callStatus;
|
||||
} UniffiForeignFutureResultVoid;
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
|
||||
#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
|
||||
typedef void (*UniffiForeignFutureCompleteVoid)(uint64_t, UniffiForeignFutureResultVoid
|
||||
);
|
||||
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
void uniffi_zx_document_ffi_fn_func_clear_exported_events(uint32_t count, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_CREATE_NOTE_ANCHOR
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_create_note_anchor(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_DETECT_MATERIAL_TYPE
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_detect_material_type(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_EXPORT_PENDING_EVENTS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_export_pending_events(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_MARKDOWN
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_parse_markdown(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_TEXT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PARSE_TEXT
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_parse_text(RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_PUSH_READING_EVENT
|
||||
void uniffi_zx_document_ffi_fn_func_push_reading_event(RustBuffer event, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_IMAGE_META
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_image_meta(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_READ_TEXT_STATS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_read_text_stats(RustBuffer file_path, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_markdown_blocks(RustBuffer blocks, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_SEARCH_TEXT_CONTENT
|
||||
RustBuffer uniffi_zx_document_ffi_fn_func_search_text_content(RustBuffer content, RustBuffer query, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_FN_FUNC_UPDATE_READING_POSITION
|
||||
void uniffi_zx_document_ffi_fn_func_update_reading_position(RustBuffer material_id, RustBuffer position, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_ALLOC
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_ALLOC
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_alloc(uint64_t size, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FROM_BYTES
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FROM_BYTES
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FREE
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_FREE
|
||||
void ffi_zx_document_ffi_rustbuffer_free(RustBuffer buf, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_RESERVE
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUSTBUFFER_RESERVE
|
||||
RustBuffer ffi_zx_document_ffi_rustbuffer_reserve(RustBuffer buf, uint64_t additional, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U8
|
||||
void ffi_zx_document_ffi_rust_future_poll_u8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U8
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U8
|
||||
void ffi_zx_document_ffi_rust_future_free_u8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U8
|
||||
uint8_t ffi_zx_document_ffi_rust_future_complete_u8(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I8
|
||||
void ffi_zx_document_ffi_rust_future_poll_i8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I8
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I8
|
||||
void ffi_zx_document_ffi_rust_future_free_i8(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I8
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I8
|
||||
int8_t ffi_zx_document_ffi_rust_future_complete_i8(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U16
|
||||
void ffi_zx_document_ffi_rust_future_poll_u16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U16
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U16
|
||||
void ffi_zx_document_ffi_rust_future_free_u16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U16
|
||||
uint16_t ffi_zx_document_ffi_rust_future_complete_u16(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I16
|
||||
void ffi_zx_document_ffi_rust_future_poll_i16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I16
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I16
|
||||
void ffi_zx_document_ffi_rust_future_free_i16(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I16
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I16
|
||||
int16_t ffi_zx_document_ffi_rust_future_complete_i16(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U32
|
||||
void ffi_zx_document_ffi_rust_future_poll_u32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U32
|
||||
void ffi_zx_document_ffi_rust_future_free_u32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U32
|
||||
uint32_t ffi_zx_document_ffi_rust_future_complete_u32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I32
|
||||
void ffi_zx_document_ffi_rust_future_poll_i32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I32
|
||||
void ffi_zx_document_ffi_rust_future_free_i32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I32
|
||||
int32_t ffi_zx_document_ffi_rust_future_complete_i32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_U64
|
||||
void ffi_zx_document_ffi_rust_future_poll_u64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_U64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_u64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_U64
|
||||
void ffi_zx_document_ffi_rust_future_free_u64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_U64
|
||||
uint64_t ffi_zx_document_ffi_rust_future_complete_u64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_I64
|
||||
void ffi_zx_document_ffi_rust_future_poll_i64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_I64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_i64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_I64
|
||||
void ffi_zx_document_ffi_rust_future_free_i64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_I64
|
||||
int64_t ffi_zx_document_ffi_rust_future_complete_i64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F32
|
||||
void ffi_zx_document_ffi_rust_future_poll_f32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F32
|
||||
void ffi_zx_document_ffi_rust_future_cancel_f32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F32
|
||||
void ffi_zx_document_ffi_rust_future_free_f32(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F32
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F32
|
||||
float ffi_zx_document_ffi_rust_future_complete_f32(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_F64
|
||||
void ffi_zx_document_ffi_rust_future_poll_f64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_F64
|
||||
void ffi_zx_document_ffi_rust_future_cancel_f64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_F64
|
||||
void ffi_zx_document_ffi_rust_future_free_f64(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F64
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_F64
|
||||
double ffi_zx_document_ffi_rust_future_complete_f64(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_poll_rust_buffer(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_cancel_rust_buffer(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_RUST_BUFFER
|
||||
void ffi_zx_document_ffi_rust_future_free_rust_buffer(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_RUST_BUFFER
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_RUST_BUFFER
|
||||
RustBuffer ffi_zx_document_ffi_rust_future_complete_rust_buffer(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_POLL_VOID
|
||||
void ffi_zx_document_ffi_rust_future_poll_void(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_CANCEL_VOID
|
||||
void ffi_zx_document_ffi_rust_future_cancel_void(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_FREE_VOID
|
||||
void ffi_zx_document_ffi_rust_future_free_void(uint64_t handle
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_RUST_FUTURE_COMPLETE_VOID
|
||||
void ffi_zx_document_ffi_rust_future_complete_void(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CLEAR_EXPORTED_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_clear_exported_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_CREATE_NOTE_ANCHOR
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_create_note_anchor(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_DETECT_MATERIAL_TYPE
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_detect_material_type(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_EXPORT_PENDING_EVENTS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_export_pending_events(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_MARKDOWN
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_markdown(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PARSE_TEXT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_parse_text(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_PUSH_READING_EVENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_push_reading_event(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_IMAGE_META
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_image_meta(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_READ_TEXT_STATS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_read_text_stats(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_MARKDOWN_BLOCKS
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_markdown_blocks(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_SEARCH_TEXT_CONTENT
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_search_text_content(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_ZX_DOCUMENT_FFI_CHECKSUM_FUNC_UPDATE_READING_POSITION
|
||||
uint16_t uniffi_zx_document_ffi_checksum_func_update_reading_position(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_UNIFFI_CONTRACT_VERSION
|
||||
#define UNIFFI_FFIDEF_FFI_ZX_DOCUMENT_FFI_UNIFFI_CONTRACT_VERSION
|
||||
uint32_t ffi_zx_document_ffi_uniffi_contract_version(void
|
||||
|
||||
);
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
module zx_documentFFI {
|
||||
header "zx_documentFFI.h"
|
||||
export *
|
||||
use "Darwin"
|
||||
use "_Builtin_stdbool"
|
||||
use "_Builtin_stdint"
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>libzx_document_ffi.a</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>libzx_document_ffi.a</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>libzx_document_ffi.a</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>libzx_document_ffi.a</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
<key>XCFrameworkFormatVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
Binary file not shown.
72
AIStudyApp/AIStudyApp/Features/Library/DiscoverView.swift
Normal file
72
AIStudyApp/AIStudyApp/Features/Library/DiscoverView.swift
Normal file
@ -0,0 +1,72 @@
|
||||
import SwiftUI
|
||||
|
||||
struct DiscoverView: View {
|
||||
@State private var kbs: [KnowledgeBase] = []
|
||||
@State private var isLoading = false
|
||||
@State private var subbedIds: Set<String> = []
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color.zxBg0.ignoresSafeArea()
|
||||
if isLoading && kbs.isEmpty {
|
||||
ProgressView().tint(Color.zxPurple)
|
||||
} else if kbs.isEmpty {
|
||||
Text("暂无公开知识库").font(.system(size: 14)).foregroundColor(Color.zxF04)
|
||||
} else {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 12) {
|
||||
ForEach(kbs) { kb in
|
||||
HStack(spacing: 12) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(kb.title).font(.system(size: 15, weight: .semibold)).foregroundColor(Color.zxF0)
|
||||
if let desc = kb.description, !desc.isEmpty {
|
||||
Text(desc).font(.system(size: 12)).foregroundColor(Color.zxF04).lineLimit(2)
|
||||
}
|
||||
if let count = kb.itemCount {
|
||||
Text("\(count) 个知识点").font(.system(size: 11)).foregroundColor(Color.zxF04)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
Button {
|
||||
Task {
|
||||
let isSubbed = subbedIds.contains(kb.id)
|
||||
if isSubbed {
|
||||
try? await KnowledgeBaseService.shared.unsubscribe(kbId: kb.id)
|
||||
subbedIds.remove(kb.id)
|
||||
} else {
|
||||
try? await KnowledgeBaseService.shared.subscribe(kbId: kb.id)
|
||||
subbedIds.insert(kb.id)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text(subbedIds.contains(kb.id) ? "已订阅" : "订阅")
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundColor(subbedIds.contains(kb.id) ? Color.zxF04 : .white)
|
||||
.padding(.horizontal, 16).padding(.vertical, 6)
|
||||
.background(subbedIds.contains(kb.id) ? Color.zxFill005 : Color.zxPurple)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
.background(Color.zxFill004)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 14))
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 20).padding(.top, 8)
|
||||
}
|
||||
.scrollIndicators(.hidden)
|
||||
}
|
||||
}
|
||||
.navigationTitle("发现公开库")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.task { await load() }
|
||||
}
|
||||
|
||||
private func load() async {
|
||||
isLoading = true
|
||||
kbs = (try? await KnowledgeBaseService.shared.discover()) ?? []
|
||||
let subs = (try? await KnowledgeBaseService.shared.listSubscribed()) ?? []
|
||||
subbedIds = Set(subs.map { $0.id })
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
// MARK: - Note model
|
||||
|
||||
struct QuickNote: Codable, Identifiable {
|
||||
var id: String = UUID().uuidString
|
||||
let materialId: String
|
||||
let content: String
|
||||
let anchorType: String? // "markdown", "text", "pdf", etc.
|
||||
let anchorBlockId: String?
|
||||
let anchorLineNumber: UInt32?
|
||||
let anchorPageNumber: UInt32?
|
||||
let createdAt: String
|
||||
|
||||
var anchorDescription: String? {
|
||||
if let bid = anchorBlockId { return "位置: block \(bid.prefix(8))" }
|
||||
if let ln = anchorLineNumber { return "位置: 第 \(ln) 行" }
|
||||
if let pn = anchorPageNumber { return "位置: 第 \(pn) 页" }
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Local note store
|
||||
|
||||
@MainActor
|
||||
final class NoteStore: ObservableObject {
|
||||
static let shared = NoteStore()
|
||||
|
||||
@Published private(set) var notes: [QuickNote] = []
|
||||
private let key = "quick_notes_store"
|
||||
|
||||
private init() {
|
||||
load()
|
||||
}
|
||||
|
||||
func add(_ note: QuickNote) {
|
||||
notes.insert(note, at: 0)
|
||||
save()
|
||||
}
|
||||
|
||||
func notesFor(materialId: String) -> [QuickNote] {
|
||||
notes.filter { $0.materialId == materialId }
|
||||
}
|
||||
|
||||
func delete(_ note: QuickNote) {
|
||||
notes.removeAll { $0.id == note.id }
|
||||
save()
|
||||
}
|
||||
|
||||
private func load() {
|
||||
guard let data = UserDefaults.standard.data(forKey: key),
|
||||
let decoded = try? JSONDecoder().decode([QuickNote].self, from: data) else { return }
|
||||
notes = decoded
|
||||
}
|
||||
|
||||
private func save() {
|
||||
guard let data = try? JSONEncoder().encode(notes) else { return }
|
||||
UserDefaults.standard.set(data, forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Quick Note Sheet
|
||||
|
||||
struct QuickNoteSheet: View {
|
||||
let materialId: String
|
||||
let materialName: String
|
||||
let anchor: NoteAnchor?
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var text: String = ""
|
||||
@FocusState private var isFocused: Bool
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 0) {
|
||||
// Material context
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "doc.text").font(.system(size: 13)).foregroundColor(Color.zxF04)
|
||||
Text(materialName).font(.system(size: 13, weight: .medium)).foregroundColor(Color.zxF0)
|
||||
.lineLimit(1)
|
||||
Spacer()
|
||||
if let anchorDesc = anchorDescription {
|
||||
Text(anchorDesc).font(.system(size: 11)).foregroundColor(Color.zxF03)
|
||||
.padding(.horizontal, 8).padding(.vertical, 2)
|
||||
.background(Color.zxFill004).clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16).padding(.vertical, 10)
|
||||
.background(Color.zxSurface)
|
||||
|
||||
// Editor
|
||||
TextEditor(text: $text)
|
||||
.focused($isFocused)
|
||||
.font(.system(size: 15)).foregroundColor(Color.zxF0).lineSpacing(5)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.zxCanvas)
|
||||
.overlay(alignment: .topLeading) {
|
||||
if text.isEmpty {
|
||||
Text("记下你的想法…").font(.system(size: 15)).foregroundColor(Color.zxF03)
|
||||
.padding(.horizontal, 4).padding(.top, 8)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16).padding(.top, 8)
|
||||
}
|
||||
.navigationTitle("新建笔记")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("取消") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("保存") { save() }
|
||||
.fontWeight(.bold)
|
||||
.disabled(text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear { isFocused = true }
|
||||
}
|
||||
|
||||
private var anchorDescription: String? {
|
||||
guard let a = anchor else { return nil }
|
||||
switch a {
|
||||
case .markdownBlock(_, let blockId):
|
||||
return "block \(String(blockId.prefix(8)))"
|
||||
case .textLine(_, let lineNumber):
|
||||
return "第 \(lineNumber) 行"
|
||||
case .pdfPage(_, let pageNumber):
|
||||
return "第 \(pageNumber) 页"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func anchorFields(from anchor: NoteAnchor?) -> (type: String?, blockId: String?, line: UInt32?, page: UInt32?) {
|
||||
guard let a = anchor else { return (nil, nil, nil, nil) }
|
||||
switch a {
|
||||
case .material: return ("material", nil, nil, nil)
|
||||
case .markdownBlock(_, let bid): return ("markdown", bid, nil, nil)
|
||||
case .textLine(_, let ln): return ("text", nil, ln, nil)
|
||||
case .pdfPage(_, let pn): return ("pdf", nil, nil, pn)
|
||||
case .image: return ("image", nil, nil, nil)
|
||||
case .epubChapter(_, let cid): return ("epub", cid, nil, nil)
|
||||
case .knowledgeItem: return ("knowledge_item", nil, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func save() {
|
||||
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { return }
|
||||
|
||||
let fields = anchorFields(from: anchor)
|
||||
let note = QuickNote(
|
||||
materialId: materialId,
|
||||
content: trimmed,
|
||||
anchorType: fields.type,
|
||||
anchorBlockId: fields.blockId,
|
||||
anchorLineNumber: fields.line,
|
||||
anchorPageNumber: fields.page,
|
||||
createdAt: ISO8601DateFormatter().string(from: Date())
|
||||
)
|
||||
NoteStore.shared.add(note)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Reading Event Collector
|
||||
|
||||
/// Collects reading events (MaterialOpened, MaterialClosed, PositionChanged,
|
||||
/// Heartbeat, MarkedAsRead) into the Rust event buffer for cross-platform export.
|
||||
@MainActor
|
||||
final class ReadingEventCollector {
|
||||
static let shared = ReadingEventCollector()
|
||||
|
||||
/// The last known reading position (for save-on-exit).
|
||||
private(set) var lastPosition: ReadingPosition?
|
||||
|
||||
private var activeMaterialId: String?
|
||||
private var activeSeconds: UInt32 = 0
|
||||
private var heartbeatTimer: Timer?
|
||||
private var positionDebounceTask: Task<Void, Never>?
|
||||
|
||||
private init() {}
|
||||
|
||||
// MARK: - Public API
|
||||
|
||||
/// Call when a material is opened.
|
||||
func open(materialId: String) {
|
||||
flush() // commit any prior session first
|
||||
activeMaterialId = materialId
|
||||
activeSeconds = 0
|
||||
|
||||
let event = ReadingEvent.materialOpened(
|
||||
materialId: materialId,
|
||||
timestampMs: nowMs()
|
||||
)
|
||||
push(event)
|
||||
startHeartbeat()
|
||||
}
|
||||
|
||||
/// Call when a material is closed. Returns active seconds.
|
||||
func close(materialId: String) -> UInt32 {
|
||||
stopHeartbeat()
|
||||
flushPosition()
|
||||
|
||||
let seconds = activeSeconds
|
||||
let event = ReadingEvent.materialClosed(
|
||||
materialId: materialId,
|
||||
timestampMs: nowMs(),
|
||||
activeSeconds: seconds
|
||||
)
|
||||
push(event)
|
||||
|
||||
activeMaterialId = nil
|
||||
activeSeconds = 0
|
||||
lastPosition = nil
|
||||
return seconds
|
||||
}
|
||||
|
||||
/// Call on scroll / page change. Debounced to avoid flooding.
|
||||
func updatePosition(materialId: String, position: ReadingPosition) {
|
||||
guard activeMaterialId == materialId else { return }
|
||||
lastPosition = position
|
||||
positionDebounceTask?.cancel()
|
||||
positionDebounceTask = Task {
|
||||
try? await Task.sleep(nanoseconds: 2_000_000_000) // 2s debounce
|
||||
guard !Task.isCancelled, let pos = lastPosition else { return }
|
||||
let event = ReadingEvent.positionChanged(
|
||||
materialId: materialId,
|
||||
position: pos,
|
||||
timestampMs: nowMs()
|
||||
)
|
||||
push(event)
|
||||
}
|
||||
}
|
||||
|
||||
/// Call when user manually marks as read.
|
||||
func markAsRead(materialId: String) {
|
||||
let event = ReadingEvent.markedAsRead(
|
||||
materialId: materialId,
|
||||
timestampMs: nowMs()
|
||||
)
|
||||
push(event)
|
||||
}
|
||||
|
||||
/// Export all pending events from Rust buffer.
|
||||
func exportPending() -> [ReadingEvent] {
|
||||
return exportPendingEvents()
|
||||
}
|
||||
|
||||
/// Remove first `count` events after successful upload.
|
||||
func clearExported(_ count: Int) {
|
||||
clearExportedEvents(count: UInt32(count))
|
||||
}
|
||||
|
||||
/// Clear everything (on sign-out, error, etc).
|
||||
func flush() {
|
||||
flushPosition()
|
||||
// Clear Rust side by exporting and discarding
|
||||
let pending = exportPendingEvents()
|
||||
clearExportedEvents(count: UInt32(pending.count))
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private func push(_ event: ReadingEvent) {
|
||||
pushReadingEvent(event: event)
|
||||
}
|
||||
|
||||
private func startHeartbeat() {
|
||||
stopHeartbeat()
|
||||
heartbeatTimer = Timer.scheduledTimer(withTimeInterval: 15, repeats: true) { [weak self] _ in
|
||||
Task { @MainActor in
|
||||
guard let self, let id = self.activeMaterialId else { return }
|
||||
self.activeSeconds += 15
|
||||
let event = ReadingEvent.heartbeat(
|
||||
materialId: id,
|
||||
activeSeconds: self.activeSeconds,
|
||||
position: self.lastPosition,
|
||||
timestampMs: self.nowMs()
|
||||
)
|
||||
self.push(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func stopHeartbeat() {
|
||||
heartbeatTimer?.invalidate()
|
||||
heartbeatTimer = nil
|
||||
}
|
||||
|
||||
private func flushPosition() {
|
||||
positionDebounceTask?.cancel()
|
||||
positionDebounceTask = nil
|
||||
}
|
||||
|
||||
private func nowMs() -> Int64 {
|
||||
Int64(Date().timeIntervalSince1970 * 1000)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
import Foundation
|
||||
|
||||
// MARK: - Position store
|
||||
|
||||
/// Persists ReadingPosition per materialId in UserDefaults,
|
||||
/// enabling "continue reading" across app launches.
|
||||
@MainActor
|
||||
final class ReadingPositionStore {
|
||||
static let shared = ReadingPositionStore()
|
||||
|
||||
private let defaults = UserDefaults.standard
|
||||
private let prefix = "reading_position."
|
||||
|
||||
private init() {}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Save the last reading position for a material.
|
||||
func save(materialId: String, position: ReadingPosition) {
|
||||
let dict = positionToDict(position)
|
||||
defaults.set(dict, forKey: key(materialId))
|
||||
}
|
||||
|
||||
/// Load the last reading position, if any.
|
||||
func load(materialId: String) -> ReadingPosition? {
|
||||
guard let dict = defaults.dictionary(forKey: key(materialId)) else { return nil }
|
||||
return positionFromDict(dict)
|
||||
}
|
||||
|
||||
/// Remove saved position (e.g. when material is deleted).
|
||||
func remove(materialId: String) {
|
||||
defaults.removeObject(forKey: key(materialId))
|
||||
}
|
||||
|
||||
/// Check if a saved position exists.
|
||||
func hasPosition(materialId: String) -> Bool {
|
||||
defaults.dictionary(forKey: key(materialId)) != nil
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private func key(_ materialId: String) -> String {
|
||||
"\(prefix)\(materialId)"
|
||||
}
|
||||
|
||||
private func positionToDict(_ pos: ReadingPosition) -> [String: Any] {
|
||||
switch pos {
|
||||
case .markdown(let blockId, let scrollProgress):
|
||||
return ["t": "markdown", "b": blockId, "s": scrollProgress]
|
||||
case .text(let lineNumber, let scrollProgress):
|
||||
return ["t": "text", "l": lineNumber, "s": scrollProgress]
|
||||
case .pdf(let pageNumber, let pageProgress, let overallProgress):
|
||||
return ["t": "pdf", "p": pageNumber, "pp": pageProgress, "op": overallProgress]
|
||||
case .image(let zoomScale, let offsetX, let offsetY):
|
||||
return ["t": "image", "z": zoomScale, "x": offsetX, "y": offsetY]
|
||||
case .epub(let chapterId, let chapterProgress, let overallProgress):
|
||||
return ["t": "epub", "c": chapterId, "cp": chapterProgress, "op": overallProgress]
|
||||
case .unknown:
|
||||
return ["t": "unknown"]
|
||||
}
|
||||
}
|
||||
|
||||
private func positionFromDict(_ dict: [String: Any]) -> ReadingPosition? {
|
||||
guard let type = dict["t"] as? String else { return nil }
|
||||
switch type {
|
||||
case "markdown":
|
||||
guard let b = dict["b"] as? String else { return nil }
|
||||
let s = (dict["s"] as? Float) ?? 0
|
||||
return .markdown(blockId: b, scrollProgress: s)
|
||||
case "text":
|
||||
let l = (dict["l"] as? UInt32) ?? 1
|
||||
let s = (dict["s"] as? Float) ?? 0
|
||||
return .text(lineNumber: l, scrollProgress: s)
|
||||
case "pdf":
|
||||
let p = (dict["p"] as? UInt32) ?? 1
|
||||
let pp = (dict["pp"] as? Float) ?? 0
|
||||
let op = (dict["op"] as? Float) ?? 0
|
||||
return .pdf(pageNumber: p, pageProgress: pp, overallProgress: op)
|
||||
case "image":
|
||||
let z = (dict["z"] as? Float) ?? 1
|
||||
let x = (dict["x"] as? Float) ?? 0
|
||||
let y = (dict["y"] as? Float) ?? 0
|
||||
return .image(zoomScale: z, offsetX: x, offsetY: y)
|
||||
case "epub":
|
||||
guard let c = dict["c"] as? String else { return nil }
|
||||
let cp = (dict["cp"] as? Float) ?? 0
|
||||
let op = (dict["op"] as? Float) ?? 0
|
||||
return .epub(chapterId: c, chapterProgress: cp, overallProgress: op)
|
||||
default:
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,14 @@ struct ProfileView: View {
|
||||
HStack {
|
||||
Text("我的").font(.system(size: 22, weight: .heavy)).foregroundColor(Color.zxF0).tracking(-0.5)
|
||||
Spacer()
|
||||
NavigationLink(value: Route.discoverPublic) {
|
||||
HStack {
|
||||
Image(systemName: "globe").resizable().scaledToFit().frame(width: 20, height: 20).foregroundColor(Color.zxF05)
|
||||
Text("发现公开库").font(.system(size: 14, weight: .semibold)).foregroundColor(Color.zxF0)
|
||||
Spacer(); Image("icon-chevron-right").resizable().scaledToFit().frame(width: 16, height: 16).foregroundColor(Color.zxF03)
|
||||
}.padding(.horizontal, 16).padding(.vertical, 14)
|
||||
}.foregroundColor(.primary)
|
||||
ZXProfileDivider()
|
||||
NavigationLink(value: Route.notificationList) {
|
||||
Image("icon-notifications").resizable().scaledToFit().frame(width: 22, height: 22).foregroundColor(Color.zxF05)
|
||||
}
|
||||
@ -103,6 +111,14 @@ struct ProfileView: View {
|
||||
}.padding(.horizontal, 16).padding(.vertical, 14)
|
||||
}.foregroundColor(.primary)
|
||||
ZXProfileDivider()
|
||||
NavigationLink(value: Route.discoverPublic) {
|
||||
HStack {
|
||||
Image(systemName: "globe").resizable().scaledToFit().frame(width: 20, height: 20).foregroundColor(Color.zxF05)
|
||||
Text("发现公开库").font(.system(size: 14, weight: .semibold)).foregroundColor(Color.zxF0)
|
||||
Spacer(); Image("icon-chevron-right").resizable().scaledToFit().frame(width: 16, height: 16).foregroundColor(Color.zxF03)
|
||||
}.padding(.horizontal, 16).padding(.vertical, 14)
|
||||
}.foregroundColor(.primary)
|
||||
ZXProfileDivider()
|
||||
NavigationLink(value: Route.notificationList) {
|
||||
HStack {
|
||||
Image("icon-bell-on").resizable().scaledToFit().frame(width: 20, height: 20).foregroundColor(Color.zxF05)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user