|
1 | 1 | # Agent Guidelines for vxcore |
2 | 2 |
|
| 3 | +## Project Overview |
| 4 | + |
| 5 | +vxcore is a cross-platform C/C++ library providing notebook management functionality for VNote. It offers a stable C ABI for embedding in desktop (Qt), iOS (Swift), and Android (Kotlin) applications. |
| 6 | + |
| 7 | +**Key Features**: Notebook management, folder/file operations, tag system, full-text search, JSON-based configuration. |
| 8 | + |
3 | 9 | ## Build Commands |
4 | | -- **Configure**: `cmake -B build -DVXCORE_BUILD_TESTS=ON` |
5 | | -- **Build**: `cmake --build build` |
6 | | -- **Run all tests**: `ctest --test-dir build -C Debug` (Windows) or `ctest --test-dir build` (Unix) |
7 | | -- **Run single module**: `ctest --test-dir build -C Debug -R test_core` or `test_notebook` |
8 | | -- **Run with verbose**: `ctest --test-dir build -C Debug -V` |
| 10 | + |
| 11 | +```bash |
| 12 | +# Configure |
| 13 | +cmake -B build -DVXCORE_BUILD_TESTS=ON |
| 14 | + |
| 15 | +# Build |
| 16 | +cmake --build build |
| 17 | + |
| 18 | +# Run all tests |
| 19 | +ctest --test-dir build -C Debug # Windows |
| 20 | +ctest --test-dir build # Unix |
| 21 | + |
| 22 | +# Run single test module |
| 23 | +ctest --test-dir build -C Debug -R test_core |
| 24 | +ctest --test-dir build -C Debug -R test_notebook |
| 25 | +ctest --test-dir build -C Debug -R test_folder |
| 26 | +ctest --test-dir build -C Debug -R test_search |
| 27 | +ctest --test-dir build -C Debug -R test_db |
| 28 | + |
| 29 | +# Run with verbose output |
| 30 | +ctest --test-dir build -C Debug -V |
| 31 | +``` |
9 | 32 |
|
10 | 33 | ## Test Structure |
11 | | -- Each module (core, notebook) has its own test executable |
12 | | -- Individual test cases are tracked within each module's main() |
13 | | -- Add new test cases to existing test_*.cpp files or create new modules as needed |
| 34 | + |
| 35 | +- Each module has its own test executable: `test_core`, `test_notebook`, `test_folder`, `test_search`, `test_db`, etc. |
| 36 | +- Individual test cases tracked within each module's `main()` |
| 37 | +- Test macros defined in `tests/test_utils.h`: `ASSERT`, `ASSERT_EQ`, `ASSERT_NE`, `ASSERT_TRUE`, `ASSERT_FALSE`, `ASSERT_NULL`, `ASSERT_NOT_NULL`, `RUN_TEST` |
14 | 38 | - **Test Mode**: All tests call `vxcore_set_test_mode(1)` to isolate test data |
15 | 39 | - Enabled: Uses `%TEMP%\vxcore_test` (Windows) or `/tmp/vxcore_test` (Unix) |
16 | 40 | - Disabled: Uses real AppData paths (`%APPDATA%\VNote`, `%LOCALAPPDATA%\VNote`) |
17 | 41 | - CLI tools do NOT enable test mode (use production paths) |
18 | 42 |
|
19 | 43 | ## Code Style |
| 44 | + |
20 | 45 | - **Language**: C++17 with C ABI for public API |
21 | 46 | - **Formatting**: Use `.clang-format` based on Google style (indent 2 spaces, 100 col limit, pointer alignment right) |
22 | 47 | - **Headers**: Sort includes; public API in `include/vxcore/`, implementation in `src/` |
23 | | -- **Naming** (following Google C++ Style Guide): |
24 | | - - **C API**: `snake_case` (e.g., `vxcore_context_create`) |
25 | | - - **Types** (classes/structs/enums): `PascalCase` (e.g., `NotebookManager`, `VxCoreError`) |
26 | | - - **Functions/Methods**: `PascalCase` (e.g., `GetNotebook()`, `CreateContext()`) |
27 | | - - **Variables** (local/global): `snake_case` (e.g., `notebook_id`, `root_folder`) |
28 | | - - **Class data members**: `snake_case_` with trailing underscore (e.g., `config_`, `notebooks_`) |
29 | | - - **Struct data members**: `snake_case` without trailing underscore (e.g., `assets_folder`, `last_opened_timestamp`) |
30 | | - - **Constants**: `kPascalCase` with leading k (e.g., `kMaxPathLength`, `kDefaultTimeout`) |
31 | | - - **Enumerators**: `kPascalCase` with leading k (e.g., `kSuccess`, `kErrorInvalidArgument`) |
32 | | - - **Macros**: `UPPER_CASE` (e.g., `VXCORE_API`, `RUN_TEST`) |
33 | | - - **Namespaces**: `snake_case` (e.g., `vxcore`) |
34 | | -- **Types**: Opaque handles (`VxCoreContextHandle`), enums for errors (`VxCoreError`) |
35 | | -- **Error handling**: Return `VxCoreError` codes; check null pointers; use `(void)param` for unused |
36 | | -- **Memory**: Caller frees strings with `vxcore_string_free()`; context owns handles |
37 | | -- **Platform**: Use `VXCORE_API` macro for exports; Windows DLL-safe; no Qt dependencies |
38 | | -- **JSON**: Communication boundary uses JSON over C ABI for cross-platform stability |
39 | | - - **JSON keys**: Always use `camelCase` (e.g., `createdUtc`, `modifiedUtc`, `assetsFolder`) |
40 | | - - **C++ struct members**: Use `snake_case` (e.g., `created_utc`, `modified_utc`, `assets_folder`) |
41 | | - - **Rationale**: User-facing files (config.json, vx.json) follow JavaScript conventions |
42 | | -- **Namespaces**: C++ code in `namespace vxcore`; close with `// namespace vxcore` |
43 | | -- **File Format**: Use Unix line ending |
| 48 | +- **File Format**: Unix line endings |
| 49 | + |
| 50 | +### Naming Conventions (Google C++ Style Guide) |
| 51 | + |
| 52 | +| Element | Convention | Examples | |
| 53 | +|---------|------------|----------| |
| 54 | +| C API functions | `snake_case` | `vxcore_context_create`, `vxcore_notebook_open` | |
| 55 | +| Types (classes/structs/enums) | `PascalCase` | `NotebookManager`, `VxCoreError`, `FolderConfig` | |
| 56 | +| Functions/Methods | `PascalCase` | `GetNotebook()`, `CreateFolder()`, `ToJson()` | |
| 57 | +| Variables (local/global) | `snake_case` | `notebook_id`, `root_folder`, `out_config_json` | |
| 58 | +| Class data members | `snake_case_` (trailing underscore) | `config_`, `notebooks_`, `folder_manager_` | |
| 59 | +| Struct data members | `snake_case` (no trailing underscore) | `assets_folder`, `created_utc`, `parent_id` | |
| 60 | +| Constants | `kPascalCase` | `kMaxPathLength`, `kConfigFileName` | |
| 61 | +| Enumerators | `kPascalCase` | `kSuccess`, `kTrace`, `kDebug` | |
| 62 | +| Macros | `UPPER_CASE` | `VXCORE_API`, `RUN_TEST`, `VXCORE_LOG_INFO` | |
| 63 | +| Namespaces | `snake_case` | `vxcore`, `vxcore::db` | |
| 64 | + |
| 65 | +### JSON Conventions |
| 66 | + |
| 67 | +- **JSON keys**: Always use `camelCase` (e.g., `createdUtc`, `modifiedUtc`, `assetsFolder`, `rootFolder`) |
| 68 | +- **C++ struct members**: Use `snake_case` (e.g., `created_utc`, `modified_utc`, `assets_folder`) |
| 69 | +- **Rationale**: User-facing files (config.json, session.json) follow JavaScript conventions |
| 70 | + |
| 71 | +### Error Handling |
| 72 | + |
| 73 | +- Return `VxCoreError` codes from C API functions |
| 74 | +- Check null pointers; use `(void)param` for unused parameters |
| 75 | +- Caller frees strings with `vxcore_string_free()` |
| 76 | +- Context owns handles; destroy context to free all resources |
| 77 | + |
| 78 | +### Platform |
| 79 | + |
| 80 | +- Use `VXCORE_API` macro for exports; Windows DLL-safe |
| 81 | +- No Qt dependencies in core library |
| 82 | +- Namespaces: C++ code in `namespace vxcore`; close with `// namespace vxcore` |
44 | 83 |
|
45 | 84 | ## Folder Structure |
46 | | -- **`include/vxcore/`**: Public C API headers (`vxcore.h`, `vxcore_types.h`, `vxcore_events.h`) |
47 | | -- **`src/api/`**: C API implementation (`vxcore_api.cpp`, `vxcore_*_api.cpp`, `error_handler.h`, `handle_manager.h`) |
48 | | -- **`src/core/`**: Core C++ logic (`context.h`, `config_manager.h`, `notebook_manager.h`, `notebook.h`) |
49 | | - - **Notebook Implementation**: `notebook.h` contains `NotebookConfig`, `NotebookRecord`, and `Notebook` class |
50 | | - - `NotebookConfig`: Persistent configuration (id, name, description, folders, metadata) |
51 | | - - `NotebookRecord`: Session metadata (id, root_folder, type) |
52 | | - - `Notebook`: Represents a single notebook instance (bundled or raw type) |
53 | | - - **NotebookManager**: Thread-safe manager for notebook lifecycle (create, open, close, list) |
54 | | - - Uses `std::mutex` for thread-safety across all public methods |
55 | | - - Manages in-memory notebook instances (`std::unordered_map<std::string, std::unique_ptr<Notebook>>`) |
56 | | - - Syncs notebook records with session config via `session_config_updater_` callback |
57 | | - - Bundled notebooks store config in `<root>/vx_notebook/config.json` |
58 | | - - Raw notebooks store config in session config only |
59 | | -- **`src/platform/`**: Platform-specific utilities (`path_provider.h`) |
60 | | -- **`src/utils/`**: General utilities (`utils.h`) |
61 | | -- **`cli/`**: CLI tool implementation (`main.cpp`, `notebook_cmd.h`, `json_helpers.h`) |
62 | | -- **`tests/`**: Test executables (`test_core.cpp`, `test_notebook.cpp`) |
63 | | -- **`third_party/`**: External dependencies (`nlohmann/json.hpp`) |
| 85 | + |
| 86 | +``` |
| 87 | +vxcore/ |
| 88 | +├── include/vxcore/ # Public C API headers |
| 89 | +│ ├── vxcore.h # Main API (context, notebook, folder, file, tag, search) |
| 90 | +│ ├── vxcore_types.h # Error codes, handles, version struct |
| 91 | +│ ├── vxcore_events.h # Event types and callbacks |
| 92 | +│ └── vxcore_log.h # Logging API |
| 93 | +│ |
| 94 | +├── src/ |
| 95 | +│ ├── api/ # C API implementation layer |
| 96 | +│ │ ├── vxcore_api.cpp # Context, version, test mode |
| 97 | +│ │ ├── vxcore_notebook_api.cpp # Notebook operations |
| 98 | +│ │ ├── vxcore_folder_api.cpp # Folder/file operations |
| 99 | +│ │ ├── vxcore_tag_api.cpp # Tag operations |
| 100 | +│ │ ├── vxcore_search_api.cpp # Search operations |
| 101 | +│ │ ├── api_utils.h # API helper utilities |
| 102 | +│ │ ├── error_handler.h/.cpp # Error handling utilities |
| 103 | +│ │ └── handle_manager.h/.cpp # Handle management |
| 104 | +│ │ |
| 105 | +│ ├── core/ # Core C++ business logic |
| 106 | +│ │ ├── context.h # VxCoreContext: owns ConfigManager + NotebookManager |
| 107 | +│ │ ├── config_manager.h/.cpp # VxCoreConfig + VxCoreSessionConfig management |
| 108 | +│ │ ├── vxcore_config.h/.cpp # Application config (vxcore.json) |
| 109 | +│ │ ├── vxcore_session_config.h/.cpp # Session config (session.json) |
| 110 | +│ │ ├── notebook_manager.h/.cpp # Notebook lifecycle (create, open, close, list) |
| 111 | +│ │ ├── notebook.h/.cpp # Notebook base class, NotebookConfig, NotebookRecord, TagNode |
| 112 | +│ │ ├── bundled_notebook.h/.cpp # Bundled notebook (config in vx_notebook/config.json) |
| 113 | +│ │ ├── raw_notebook.h/.cpp # Raw notebook (config in session only) |
| 114 | +│ │ ├── folder_manager.h # Abstract folder/file operations interface |
| 115 | +│ │ ├── bundled_folder_manager.h/.cpp # Folder ops for bundled notebooks |
| 116 | +│ │ ├── raw_folder_manager.h/.cpp # Folder ops for raw notebooks |
| 117 | +│ │ └── folder.h/.cpp # FolderConfig, FolderRecord, FileRecord |
| 118 | +│ │ |
| 119 | +│ ├── db/ # SQLite database layer |
| 120 | +│ │ ├── db_manager.h/.cpp # Database lifecycle, schema, transactions |
| 121 | +│ │ ├── db_schema.h # Schema definitions (tables, indexes, FTS5) |
| 122 | +│ │ ├── file_db.h/.cpp # File/folder CRUD operations |
| 123 | +│ │ ├── tag_db.h/.cpp # Tag CRUD and queries |
| 124 | +│ │ └── sync_manager.h/.cpp # Filesystem ↔ database synchronization |
| 125 | +│ │ |
| 126 | +│ ├── search/ # Search subsystem |
| 127 | +│ │ ├── search_manager.h/.cpp # Search orchestration |
| 128 | +│ │ ├── search_backend.h # ISearchBackend interface |
| 129 | +│ │ ├── simple_search_backend.h/.cpp # Built-in search implementation |
| 130 | +│ │ ├── rg_search_backend.h/.cpp # Ripgrep integration |
| 131 | +│ │ ├── search_query.h/.cpp # SearchQuery, SearchScope, SearchOption |
| 132 | +│ │ └── search_file_info.h/.cpp # SearchFileInfo struct |
| 133 | +│ │ |
| 134 | +│ ├── platform/ # Platform-specific code |
| 135 | +│ │ ├── path_provider.h/.cpp # AppData, LocalData paths per platform |
| 136 | +│ │ └── process_utils.h/.cpp # External process execution (for rg) |
| 137 | +│ │ |
| 138 | +│ └── utils/ # General utilities |
| 139 | +│ ├── utils.h/.cpp # GenerateUUID(), GetCurrentTimestampMillis(), HasFlag() |
| 140 | +│ ├── file_utils.h/.cpp # CleanPath(), ConcatenatePaths(), LoadJsonFile() |
| 141 | +│ ├── string_utils.h/.cpp # ToLowerString(), MatchesPattern(), exclude patterns |
| 142 | +│ └── logger.h/.cpp # VXCORE_LOG_* macros, Logger singleton |
| 143 | +│ |
| 144 | +├── cli/ # Command-line interface |
| 145 | +│ ├── main.cpp # Entry point, command dispatch |
| 146 | +│ ├── args.h/.cpp # Argument parsing |
| 147 | +│ ├── notebook_cmd.h/.cpp # notebook subcommand |
| 148 | +│ ├── tag_cmd.h/.cpp # tag subcommand |
| 149 | +│ ├── config_cmd.h/.cpp # config subcommand |
| 150 | +│ └── json_helpers.h/.cpp # JSON formatting utilities |
| 151 | +│ |
| 152 | +├── tests/ # Test executables |
| 153 | +│ ├── test_utils.h # Test macros and helpers |
| 154 | +│ ├── test_core.cpp # Context, config tests |
| 155 | +│ ├── test_notebook.cpp # Notebook CRUD tests |
| 156 | +│ ├── test_folder.cpp # Folder/file operation tests |
| 157 | +│ ├── test_search.cpp # Search tests |
| 158 | +│ ├── test_db.cpp # Database layer tests |
| 159 | +│ └── ... |
| 160 | +│ |
| 161 | +├── third_party/ # External dependencies (DO NOT MODIFY) |
| 162 | +│ ├── nlohmann/json.hpp # JSON library |
| 163 | +│ └── sqlite/ # SQLite amalgamation |
| 164 | +│ |
| 165 | +└── data/ |
| 166 | + └── vxcore.json # Default configuration template |
| 167 | +``` |
64 | 168 |
|
65 | 169 | ## Architecture Notes |
66 | | -- C ABI for cross-platform embedding (desktop/iOS/Android) |
67 | | -- File-based notes with SQLite metadata (WAL + FTS5) |
68 | | -- Event-driven with typed JSON notifications |
69 | | -- Core plugins: Lua (logic); UI plugins: Python (Qt customization) |
| 170 | + |
| 171 | +### Layered Architecture |
| 172 | + |
| 173 | +``` |
| 174 | +┌─────────────────────────────────────────────────┐ |
| 175 | +│ C API Layer (include/vxcore/, src/api/) │ ← Stable ABI for FFI |
| 176 | +├─────────────────────────────────────────────────┤ |
| 177 | +│ Core Layer (src/core/) │ ← Business logic |
| 178 | +├─────────────────────────────────────────────────┤ |
| 179 | +│ Database Layer (src/db/) │ ← SQLite metadata |
| 180 | +├──────────────────────┬──────────────────────────┤ |
| 181 | +│ Platform (src/platform/) │ Utils (src/utils/)│ ← Cross-cutting concerns |
| 182 | +└──────────────────────┴──────────────────────────┘ |
| 183 | +``` |
| 184 | + |
| 185 | +### Key Design Patterns |
| 186 | + |
| 187 | +1. **Context-based API**: All operations go through `VxCoreContextHandle` |
| 188 | +2. **Opaque handles**: C API uses opaque pointers for type safety |
| 189 | +3. **JSON boundaries**: All complex data crosses C ABI as JSON strings |
| 190 | +4. **Two notebook types**: |
| 191 | + - **Bundled**: Config stored in `<root>/vx_notebook/config.json`, metadata in `<root>/vx_notebook/` |
| 192 | + - **Raw**: Config stored in session config only, metadata in local data folder |
| 193 | +5. **FolderManager abstraction**: `BundledFolderManager` and `RawFolderManager` implement different storage strategies |
| 194 | +6. **Search backends**: Pluggable via `ISearchBackend` interface (ripgrep, simple built-in) |
| 195 | + |
| 196 | +### Thread Safety |
| 197 | + |
| 198 | +- `NotebookManager` operations are NOT thread-safe; caller must synchronize |
| 199 | +- `DbManager`, `FileDb`, `TagDb` are NOT thread-safe; designed for single-threaded use per notebook |
| 200 | +- `Logger` is thread-safe (uses mutex) |
| 201 | + |
| 202 | +### Data Flow |
| 203 | + |
| 204 | +``` |
| 205 | +User Code |
| 206 | + │ |
| 207 | + ▼ |
| 208 | +C API (vxcore_*) |
| 209 | + │ |
| 210 | + ▼ |
| 211 | +VxCoreContext |
| 212 | + ├── ConfigManager (VxCoreConfig, VxCoreSessionConfig) |
| 213 | + └── NotebookManager |
| 214 | + └── Notebook (Bundled/Raw) |
| 215 | + ├── NotebookConfig (id, name, tags, metadata) |
| 216 | + ├── FolderManager (folder/file operations) |
| 217 | + │ └── DbManager + FileDb + TagDb |
| 218 | + └── SearchManager (search operations) |
| 219 | + └── ISearchBackend (rg/simple) |
| 220 | +``` |
| 221 | + |
| 222 | +## Important Implementation Details |
| 223 | + |
| 224 | +### Notebook Types |
| 225 | + |
| 226 | +| Aspect | Bundled | Raw | |
| 227 | +|--------|---------|-----| |
| 228 | +| Config location | `<root>/vx_notebook/config.json` | Session config only | |
| 229 | +| Metadata folder | `<root>/vx_notebook/` | `<local_data>/notebooks/<id>/` | |
| 230 | +| Database location | `<metadata_folder>/metadata.db` | `<metadata_folder>/metadata.db` | |
| 231 | +| Portable | Yes (self-contained) | No (external metadata) | |
| 232 | + |
| 233 | +### Path Handling |
| 234 | + |
| 235 | +- Always use `CleanPath()` or `CleanFsPath()` to normalize paths |
| 236 | +- Paths use forward slashes internally (even on Windows) |
| 237 | +- Relative paths within notebook are relative to notebook root |
| 238 | +- Use `ConcatenatePaths()` to join path components |
| 239 | + |
| 240 | +### Utility Functions (check before implementing) |
| 241 | + |
| 242 | +- `src/utils/utils.h`: `GenerateUUID()`, `GetCurrentTimestampMillis()`, `HasFlag()` |
| 243 | +- `src/utils/file_utils.h`: `CleanPath()`, `LoadJsonFile()`, `ConcatenatePaths()`, `SplitPath()`, `RelativePath()` |
| 244 | +- `src/utils/string_utils.h`: `ToLowerString()`, `MatchesPattern()`, `MatchesPatterns()` |
| 245 | +- `src/utils/logger.h`: `VXCORE_LOG_TRACE/DEBUG/INFO/WARN/ERROR/FATAL` |
| 246 | + |
| 247 | +### Logging |
| 248 | + |
| 249 | +```cpp |
| 250 | +#include "utils/logger.h" |
| 251 | + |
| 252 | +VXCORE_LOG_INFO("Opening notebook: %s", path.c_str()); |
| 253 | +VXCORE_LOG_ERROR("Failed to open database: %s", db_path.c_str()); |
| 254 | +``` |
| 255 | +
|
| 256 | +## Common Patterns |
| 257 | +
|
| 258 | +### Adding a New C API Function |
| 259 | +
|
| 260 | +1. Declare in `include/vxcore/vxcore.h` |
| 261 | +2. Implement in appropriate `src/api/vxcore_*_api.cpp` |
| 262 | +3. Add tests in `tests/test_*.cpp` |
| 263 | +
|
| 264 | +### Adding a New Core Feature |
| 265 | +
|
| 266 | +1. Implement in `src/core/` |
| 267 | +2. Expose through existing managers or create new manager |
| 268 | +3. Add C API wrapper in `src/api/` |
| 269 | +4. Add tests |
| 270 | +
|
| 271 | +### Working with JSON |
| 272 | +
|
| 273 | +```cpp |
| 274 | +#include <nlohmann/json.hpp> |
| 275 | +
|
| 276 | +// Parsing |
| 277 | +nlohmann::json j = nlohmann::json::parse(json_string); |
| 278 | +auto config = NotebookConfig::FromJson(j); |
| 279 | +
|
| 280 | +// Serializing |
| 281 | +nlohmann::json j = config.ToJson(); |
| 282 | +std::string json_string = j.dump(); |
| 283 | +``` |
| 284 | + |
| 285 | +### Error Handling Pattern |
| 286 | + |
| 287 | +```cpp |
| 288 | +VxCoreError MyFunction(const char* input, char** output) { |
| 289 | + if (!input || !output) { |
| 290 | + return VXCORE_ERR_NULL_POINTER; |
| 291 | + } |
| 292 | + |
| 293 | + try { |
| 294 | + // ... implementation ... |
| 295 | + *output = strdup(result.c_str()); |
| 296 | + return VXCORE_OK; |
| 297 | + } catch (const std::exception& e) { |
| 298 | + VXCORE_LOG_ERROR("MyFunction failed: %s", e.what()); |
| 299 | + return VXCORE_ERR_UNKNOWN; |
| 300 | + } |
| 301 | +} |
| 302 | +``` |
0 commit comments