This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
vxcore is a cross-platform C/C++ library providing notebook management for VNote. It exposes a stable C ABI (via extern "C") for embedding in desktop (Qt), iOS (Swift), and Android (Kotlin) applications. Internally implemented in C++17. No Qt dependency.
# Configure (standalone, with tests enabled)
cmake -B build -DVXCORE_BUILD_TESTS=ON
# Build
cmake --build build
# Run all tests
ctest --test-dir build -C Debug # Windows
ctest --test-dir build # Unix
# Run a single test module
ctest --test-dir build -C Debug -R test_core
# Run with verbose output
ctest --test-dir build -C Debug -V| Option | Default | Description |
|---|---|---|
VXCORE_BUILD_SHARED |
ON | Build shared library (DLL/SO) |
VXCORE_BUILD_CLI |
ON | Build vxcore_cli command-line tool |
VXCORE_BUILD_TESTS |
ON | Build test executables |
VXCORE_INSTALL |
ON | Enable install targets |
When built as a VNote submodule, the parent CMake sets VXCORE_BUILD_TESTS=OFF, VXCORE_BUILD_CLI=OFF, and VXCORE_BUILD_SHARED=OFF (static lib). Build vxcore standalone for testing.
Custom test framework in tests/test_utils.h. Tests are plain C++ executables returning 0 on success.
Test mode: All tests must call vxcore_set_test_mode(1) before vxcore_context_create() to redirect data to temp directories instead of real AppData paths.
#include "test_utils.h"
#include "vxcore/vxcore.h"
int test_something() {
std::cout << " Running test_something..." << std::endl;
// ... test logic using ASSERT, ASSERT_EQ, ASSERT_NE, etc.
ASSERT_EQ(err, VXCORE_OK);
std::cout << " ✓ test_something passed" << std::endl;
return 0;
}
int main() {
vxcore_set_test_mode(1);
vxcore_clear_test_directory();
RUN_TEST(test_something);
std::cout << "All tests passed!" << std::endl;
return 0;
}For tests that link the full vxcore library, use the helper in tests/CMakeLists.txt:
add_vxcore_test(test_mymodule)For tests that link only specific source files (unit-level isolation), add a manual add_executable block (see test_db, test_rg_search_backend in tests/CMakeLists.txt for examples).
ASSERT(cond), ASSERT_EQ(a, b), ASSERT_NE(a, b), ASSERT_TRUE(cond), ASSERT_FALSE(cond), ASSERT_NULL(ptr), ASSERT_NOT_NULL(ptr), RUN_TEST(func).
test_core, test_notebook, test_folder, test_node, test_search, test_tag_sync, test_filetype, test_workspace, test_buffer, test_file_utils, test_process_utils, test_rg_search_backend, test_simple_search_backend, test_db, test_metadata_store, test_folder_manager.
C API Layer (include/vxcore/, src/api/) ← Stable ABI, extern "C"
│
Core Layer (src/core/) ← C++ business logic
│
Database Layer (src/db/) ← SQLite metadata store
│
Platform (src/platform/) + Utils (src/utils/) ← Cross-cutting concerns
C API (vxcore_*)
→ VxCoreContext
├── ConfigManager (VxCoreConfig + VxCoreSessionConfig)
└── NotebookManager
└── Notebook (Bundled or Raw)
├── FolderManager (folder/file CRUD)
│ └── DbManager + FileDb + TagDb
├── BufferManager (open file handles)
├── WorkspaceManager (split pane state)
└── SearchManager → ISearchBackend (rg / simple)
- Context-based API: All operations go through
VxCoreContextHandle(opaque pointer) - JSON boundaries: All complex data crosses the C ABI as JSON strings (using nlohmann/json internally)
- Caller-frees strings: C API returns
char*that the caller must free withvxcore_string_free() - Unified Node API:
vxcore_node_*functions work on both files and folders, returning a"type"field in JSON - Two notebook types:
- Bundled: Self-contained, config in
<root>/vx_notebook/config.json, metadata in<root>/vx_notebook/ - Raw: Config in session only, metadata in
<local_data>/notebooks/<id>/
- Bundled: Self-contained, config in
- FolderManager abstraction:
BundledFolderManagerandRawFolderManagerimplement different storage strategies behind a common interface - Search backends: Pluggable via
ISearchBackend(ripgrep for performance, simple built-in as fallback) - Buffer providers:
StandardBufferProviderfor notebook files,ExternalBufferProviderfor standalone files
| Header | Contents |
|---|---|
vxcore.h |
All C API functions (context, notebook, folder, file, node, tag, search, buffer, workspace) |
vxcore_types.h |
Error codes (VxCoreError), handles (VxCoreContextHandle), enums, version struct |
vxcore_log.h |
Log level control and file/console output |
NotebookManager,DbManager,FileDb,TagDb: NOT thread-safe; designed for single-threaded use per notebookLogger: Thread-safe (uses mutex)
Google C++ Style Guide base, enforced by .clang-format. Key conventions:
- C++17, 2-space indent, 100-char line limit, pointer alignment right
- C API functions:
vxcore_module_actionsnake_case (e.g.,vxcore_notebook_open) - C++ types:
PascalCase(e.g.,NotebookManager,FolderConfig) - C++ methods:
PascalCase(e.g.,GetNotebook(),CreateFolder()) - Variables:
snake_case(e.g.,notebook_id,root_folder) - Class members:
snake_case_with trailing underscore (e.g.,config_,notebooks_) - Struct members:
snake_casewithout trailing underscore (e.g.,assets_folder,created_utc) - Constants/Enumerators:
kPascalCase(e.g.,kMaxPathLength,kSuccess) - Macros:
UPPER_CASE(e.g.,VXCORE_API,RUN_TEST) - JSON keys:
camelCase(e.g.,createdUtc,assetsFolder) — user-facing files follow JavaScript conventions
VxCoreError MyFunction(const char *input, char **output) {
if (!input || !output) return VXCORE_ERR_NULL_POINTER;
try {
// ...
*output = strdup(result.c_str());
return VXCORE_OK;
} catch (const std::exception &e) {
VXCORE_LOG_ERROR("MyFunction failed: %s", e.what());
return VXCORE_ERR_UNKNOWN;
}
}#include "utils/logger.h"
VXCORE_LOG_INFO("Opening notebook: %s", path.c_str());
VXCORE_LOG_ERROR("Failed: %s", err_msg.c_str());- nlohmann/json — JSON parsing/serialization (
third_party/nlohmann/) - SQLite — Embedded database via amalgamation (
third_party/sqlite/)
Do NOT modify files in third_party/.
- Declare in
include/vxcore/vxcore.hwithVXCORE_APIprefix - Implement in appropriate
src/api/vxcore_*_api.cpp - Add tests in
tests/test_*.cpp - Use
vxcore_string_free()convention for returned strings
- Always use
CleanPath()orCleanFsPath()fromsrc/utils/file_utils.hto normalize - Paths use forward slashes internally (even on Windows)
- Relative paths within notebooks are relative to notebook root
- Use
ConcatenatePaths()to join path components