chore: add docs
336
CHANGELOG.md
Normal file
@ -0,0 +1,336 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to the Knowledge Base project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
#### BREAKING: MCP Protocol Rebranding
|
||||
- **All MCP URIs, tool names, and prompts updated to KOGRAL namespace**
|
||||
- **URI Scheme**: `kb://` → `kogral://`
|
||||
- `kb://project/notes` → `kogral://project/notes`
|
||||
- `kb://project/decisions` → `kogral://project/decisions`
|
||||
- `kb://project/guidelines` → `kogral://project/guidelines`
|
||||
- `kb://project/patterns` → `kogral://project/patterns`
|
||||
- `kb://shared/guidelines` → `kogral://shared/guidelines`
|
||||
- `kb://shared/patterns` → `kogral://shared/patterns`
|
||||
- **Tool Names**: `kb/*` → `kogral/*`
|
||||
- `kb/search` → `kogral/search`
|
||||
- `kb/add_note` → `kogral/add_note`
|
||||
- `kb/add_decision` → `kogral/add_decision`
|
||||
- `kb/link` → `kogral/link`
|
||||
- `kb/get_guidelines` → `kogral/get_guidelines`
|
||||
- `kb/list_graphs` → `kogral/list_graphs`
|
||||
- `kb/export` → `kogral/export`
|
||||
- `kb/find_blocks` → `kogral/find_blocks`
|
||||
- `kb/find_todos` → `kogral/find_todos`
|
||||
- `kb/find_cards` → `kogral/find_cards`
|
||||
- **Prompt Names**: `kb/*` → `kogral/*`
|
||||
- `kb/summarize_project` → `kogral/summarize_project`
|
||||
- `kb/find_related` → `kogral/find_related`
|
||||
- **Server Name**: `kb-mcp` → `kogral-mcp`
|
||||
- **Migration Required**: Users must update `~/.config/claude/config.json`:
|
||||
|
||||
```diff
|
||||
{
|
||||
"mcpServers": {
|
||||
- "kb-mcp": {
|
||||
- "command": "/path/to/kb-mcp",
|
||||
+ "kogral-mcp": {
|
||||
+ "command": "/path/to/kogral-mcp",
|
||||
- "env": { "KB_DIR": "/path/to/.kb" }
|
||||
+ "env": { "KOGRAL_DIR": "/path/to/.kogral" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Rationale**: Complete branding consistency across all interfaces. Pre-release status (v0.x) allows breaking changes without user pain.
|
||||
- **Files updated**:
|
||||
- `crates/kb-mcp/src/resources.rs`: All URI definitions and parsing
|
||||
- `crates/kb-mcp/src/tools.rs`: All tool name definitions and dispatch
|
||||
- `crates/kb-mcp/src/prompts.rs`: All prompt name definitions and dispatch
|
||||
- `crates/kb-mcp/src/main.rs`: Server name registration
|
||||
- `crates/kb-mcp/tests/integration_test.rs`: Test assertions updated
|
||||
- `docs/apps/mcp-quickguide.md`: All examples and references
|
||||
- `docs/kogral/core-concepts.md`: MCP protocol documentation
|
||||
- `docs/api/mcp-tools.md`: API reference documentation
|
||||
- `README.md`: MCP integration examples
|
||||
|
||||
#### Branding
|
||||
- **Rebranded to KOGRAL** (**KO**wledge **GRA**phs, **L**ocal-first)
|
||||
- Updated project name from "Knowledge Base" to "KOGRAL" across all documentation
|
||||
- New tagline: "Git-native knowledge graphs for developer teams"
|
||||
- Updated description emphasizing structured knowledge management for developers
|
||||
- Documentation folder renamed: `docs/kb/` → `docs/kogral/`
|
||||
- Documentation files renamed:
|
||||
- `docs/kogral/what-is-kb.md` → `what-is-kogral.md`
|
||||
- `docs/kogral/why-kb.md` → `why-kogral.md`
|
||||
- Technical names maintained for compatibility: `kb-core`, `kb-cli`, `kb-mcp` (crate names), `kb` (CLI binary)
|
||||
- Hybrid naming strategy: KOGRAL as primary brand, `kb.vapora.dev` as future alias
|
||||
- Files updated:
|
||||
- `README.md`: Title, description, and branding
|
||||
- `Cargo.toml` (workspace): Homepage, description, repository metadata
|
||||
- `crates/kb-core/Cargo.toml`: Package description
|
||||
- `crates/kb-cli/Cargo.toml`: Package description
|
||||
- `crates/kb-mcp/Cargo.toml`: Package description
|
||||
- `crates/kb-core/src/lib.rs`: Module documentation comments
|
||||
- `crates/kb-core/src/models.rs`: Type documentation comments
|
||||
- `crates/kb-cli/src/main.rs`: CLI description and messages
|
||||
- `docs/kogral/what-is-kogral.md`: Full KOGRAL branding with acronym explanation
|
||||
- `docs/kogral/core-concepts.md`: All "KB" references replaced with "KOGRAL"
|
||||
- `docs/README.md`: Title and section headers
|
||||
- `docs/book.toml`: mdBook title and description
|
||||
- `docs/SUMMARY.md`: Navigation titles
|
||||
- `.claude/CLAUDE.md`: Project description and documentation links
|
||||
|
||||
#### BREAKING: Filesystem Paths and Environment Variables
|
||||
- **Directory structure renamed from `.kb/` to `.kogral/`**
|
||||
- Project directory: `.kb/` → `.kogral/`
|
||||
- Shared directory: `.kb-shared/` → `.kogral-shared/`
|
||||
- Configuration paths updated throughout codebase
|
||||
- All storage paths use `.kogral/` prefix
|
||||
- **Environment variable renamed from `KB_DIR` to `KOGRAL_DIR`**
|
||||
- MCP server configuration requires `KOGRAL_DIR` environment variable
|
||||
- CLI tools respect `KOGRAL_DIR` for custom directory location
|
||||
- **Migration Required**:
|
||||
1. Rename existing directory: `mv .kb .kogral`
|
||||
2. Update environment variables: `KB_DIR` → `KOGRAL_DIR`
|
||||
3. Update config files referencing `.kb/` paths
|
||||
- **Rationale**: Complete branding consistency across all filesystem operations. Pre-release status allows breaking changes.
|
||||
- **Files updated**:
|
||||
- All Rust source files in `crates/`: Path constants and variable names
|
||||
- `crates/kb-core/src/config/loader.rs`: Configuration path discovery
|
||||
- `crates/kb-core/src/storage/filesystem.rs`: Storage paths
|
||||
- `crates/kb-cli/src/main.rs`: CLI messages and examples
|
||||
- `crates/kb-mcp/src/main.rs`: Environment variable handling
|
||||
- All documentation in `docs/`: Examples and instructions
|
||||
- `README.md`: Setup and usage examples
|
||||
- `.claude/CLAUDE.md`: Project configuration paths
|
||||
|
||||
### Added
|
||||
|
||||
#### Core Infrastructure
|
||||
- Initial project structure with Cargo workspace (kb-core, kb-cli, kb-mcp)
|
||||
- Core graph models (Node, Edge, Graph) with 6 node types and 6 relationship types
|
||||
- Config-driven architecture using Nickel schemas (NCL → JSON → Rust)
|
||||
- Markdown parser with Logseq compatibility (YAML frontmatter + wikilinks)
|
||||
- Storage backends: Filesystem (default), In-Memory, SurrealDB (feature-gated)
|
||||
- Embedding integration via rig-core (OpenAI, Claude, Ollama) + fastembed local fallback
|
||||
- Tera template engine for document generation and export
|
||||
|
||||
#### Logseq Blocks Support (ADR-004)
|
||||
- **Block Data Structures** (`crates/kb-core/src/models.rs`):
|
||||
- `TaskStatus` enum: TODO, DOING, DONE, LATER, NOW, WAITING, CANCELLED
|
||||
- `BlockProperties` struct: tags, task status, custom properties, block/page references
|
||||
- `Block` struct: hierarchical content blocks with properties and children
|
||||
- `Node.blocks` field: Optional cached block structure (lazy parsing)
|
||||
- **BlockParser** (`crates/kb-core/src/block_parser.rs`):
|
||||
- Bidirectional parser: Markdown outliner ↔ Block structures
|
||||
- Supports: task markers, #tags, custom properties (key:: value), ((block-refs)), [[page-refs]]
|
||||
- Hierarchical parsing based on indentation
|
||||
- Round-trip fidelity for Logseq import/export
|
||||
- 15 comprehensive tests covering parsing, serialization, and round-trips
|
||||
- **Block Query API**:
|
||||
- `Node::get_blocks()`: Lazy parsing from content with caching
|
||||
- `Node::find_blocks_by_tag()`: Find blocks with specific tag
|
||||
- `Node::find_all_todos()`: Find all task blocks
|
||||
- `Node::find_blocks_by_status()`: Find blocks by task status
|
||||
- `Node::find_blocks_by_property()`: Find blocks with custom property
|
||||
- `Graph::find_blocks_by_tag()`: Search blocks across all nodes
|
||||
- `Graph::find_all_todos()`: Find TODOs across entire graph
|
||||
- 6 integration tests for Node and Graph block queries
|
||||
- **SurrealDB Schema** (`schemas/surrealdb/blocks.surql`):
|
||||
- `block` table with indexes on node_id, block_id, tags, status, parent_id
|
||||
- Full-text search index on content
|
||||
- Support for hierarchical queries and tag-based filtering
|
||||
- Migration queries and example usage patterns
|
||||
- **Logseq Import/Export** (`crates/kb-core/src/import/logseq.rs`, `crates/kb-core/src/export/logseq.rs`):
|
||||
- Round-trip import: Logseq markdown → KB Node with blocks
|
||||
- Property parsing: key:: value format, tags ([[tag]] and #tag), references, timestamps
|
||||
- Export to Logseq page/journal formats with block serialization
|
||||
- 12 comprehensive tests covering import/export round-trips
|
||||
- **MCP Block Tools** (`crates/kb-mcp/src/tools.rs`):
|
||||
- `kb/find_blocks`: Search blocks by tag, task status, or custom property
|
||||
- `kb/find_todos`: Find all TODO blocks across KB
|
||||
- `kb/find_cards`: Find all flashcard blocks (#card tag)
|
||||
- Fixed borrowing issues with two-phase data collection pattern
|
||||
- **Configuration Schema** (`schemas/kb-config.ncl`, `config/*.ncl`):
|
||||
- Added `BlocksConfig` schema with 4 settings:
|
||||
- `enabled`: Enable/disable Logseq blocks parsing (default: false, opt-in)
|
||||
- `parse_on_import`: Auto-parse blocks from Logseq imports (default: true)
|
||||
- `serialize_on_export`: Serialize blocks to outliner format on export (default: true)
|
||||
- `enable_mcp_tools`: Enable block-related MCP tools (default: true)
|
||||
- Updated all config files (defaults.ncl, production.ncl, minimal.ncl)
|
||||
- Validated: All configs export to JSON successfully
|
||||
- **Documentation**:
|
||||
- Complete technical design: `docs/architecture/logseq-blocks-design.md`
|
||||
- Architectural Decision Record: `docs/architecture/adrs/004-logseq-blocks-support.md`
|
||||
- 6-phase implementation plan documented
|
||||
- Use cases and examples for flashcards, task tracking, and block references
|
||||
|
||||
#### Applications
|
||||
- CLI tool (kb-cli) with 13 commands (init, add, search, link, sync, serve, etc.)
|
||||
- MCP server (kb-mcp) with 10 tools (7 core + 3 block tools), 6 resources, 2 prompts for Claude Code integration
|
||||
- NuShell maintenance scripts (6 scripts: sync, backup, reindex, import/export Logseq, stats)
|
||||
|
||||
#### Configuration System (Nickel)
|
||||
- Complete Nickel configuration schema following typedialog/provisioning pattern
|
||||
- Schema contracts (`schemas/kb/contracts.ncl`) - Type definitions with validation
|
||||
- Default values (`schemas/kb/defaults.ncl`) - Base configuration values
|
||||
- Composition helpers (`schemas/kb/helpers.ncl`) - merge_with_override, compose_config
|
||||
- Mode overlays (`schemas/kb/modes/{dev,prod,test}.ncl`) - Environment-specific tuning
|
||||
- Runtime configurations (`.kb-config/core/kb.ncl`, `.kb-config/platform/*.ncl`)
|
||||
- Double validation pattern: Nickel contracts + serde deserialization
|
||||
- Config export to JSON for Rust consumption (`.kb-config/targets/*.json`)
|
||||
|
||||
#### Templates
|
||||
- Tera templates for 6 document types (note, decision, guideline, pattern, journal, execution)
|
||||
- 4 export format templates (Logseq page/journal, summary, JSON)
|
||||
- Customizable template system with frontmatter generation
|
||||
|
||||
#### Documentation (mdBook)
|
||||
- Complete mdBook documentation structure with SUMMARY.md navigation
|
||||
- 4 SVG diagrams for visual guides:
|
||||
- `architecture-overview.svg` - Complete system architecture
|
||||
- `core-concepts.svg` - Node types and relationships
|
||||
- `config-composition.svg` - Configuration flow (Schema → Defaults → Mode → User → JSON)
|
||||
- `storage-architecture.svg` - Hybrid storage strategy
|
||||
- 11 major documentation sections (kb/, guides/, architecture/, setup/, config/, storage/, ai/, templates/, cli/, apps/, api/, contributing/)
|
||||
- ADR documentation (ADR-001: Nickel vs TOML, ADR-002: FastEmbed + AI providers, ADR-003: Hybrid storage)
|
||||
- Use cases documentation (8 real-world scenarios)
|
||||
- MCP quick guide (5-minute setup for Claude Code)
|
||||
- Config-driven architecture deep dive
|
||||
- Storage architecture deep dive with code examples
|
||||
- docs/README.md - Comprehensive guide for using and building documentation
|
||||
|
||||
#### Build System
|
||||
- Modular justfile system (7 modules: build, test, dev, ci, nickel, docs, scripts)
|
||||
- Nickel validation and export recipes (`just nickel::validate-all`, `just nickel::export-all`)
|
||||
- mdBook documentation recipes (`just docs::serve`, `just docs::build`, `just docs::test`)
|
||||
- CI/CD pipeline recipes (format, lint, test, build validation)
|
||||
- NuShell script validation and execution recipes
|
||||
|
||||
#### Project Documentation
|
||||
- Enhanced README.md with complete documentation links and visual diagram references
|
||||
- Directory structure documentation with detailed explanations
|
||||
- Quick start guide with MCP integration
|
||||
- Integration guides (Vapora, Logseq, Claude Code)
|
||||
- CHANGELOG.md following Keep a Changelog format
|
||||
|
||||
### Changed
|
||||
- Updated README.md with comprehensive documentation links (mdBook, guides, architecture, config)
|
||||
- Enhanced directory structure documentation with .kb-config/ and schemas/kb/ details
|
||||
- Improved documentation section with visual diagrams and just recipe references
|
||||
- **Blocks Feature Documentation**:
|
||||
- README.md: Added Logseq blocks section with examples, features, and configuration
|
||||
- README.md: Updated architecture diagram to include BlockParser
|
||||
- README.md: Added 3 new MCP tools to integration section
|
||||
- docs/kb/core-concepts.md: Added comprehensive "Logseq Content Blocks" section with examples, use cases, and queries
|
||||
- docs/api/mcp-tools.md: Added "Block Tools" section documenting kb/find_blocks, kb/find_todos, kb/find_cards
|
||||
- docs/api/mcp-tools.md: Updated overview from 7 to 10 tools
|
||||
|
||||
### Infrastructure
|
||||
- Cargo workspace with 3 crates
|
||||
- Feature flags: `filesystem` (default), `surrealdb`, `fastembed`, `full`
|
||||
- 90 tests across workspace (81 kb-core + 5 kb-mcp + 4 kb-cli)
|
||||
- 15 tests for block parser
|
||||
- 12 tests for Logseq import/export
|
||||
- 6 tests for block queries
|
||||
- mdBook integration for documentation (serves on [http://localhost:3000](http://localhost:3000))
|
||||
- Nickel → JSON → Rust configuration pipeline
|
||||
- Justfile modules: build, test, dev, ci, nickel, docs, scripts
|
||||
- CI/CD pipeline recipes (format, lint, test, build validation)
|
||||
- mdBook documentation structure with SUMMARY.md
|
||||
- NuShell script validation recipes
|
||||
|
||||
## [1.0.0] - TBD
|
||||
|
||||
Initial release (planned).
|
||||
|
||||
### Planned Features
|
||||
- Full FilesystemStorage implementation with file watching
|
||||
- Complete Query Engine with semantic search
|
||||
- Guideline inheritance resolution (shared → local override priority)
|
||||
- SurrealDB schema and complete backend implementation
|
||||
- Sync mechanism: bidirectional Filesystem ↔ SurrealDB
|
||||
- FastEmbed local embeddings integration
|
||||
- API provider integrations (OpenAI, Claude, Ollama) for embeddings
|
||||
- Cross-graph queries (project + shared knowledge bases)
|
||||
- Template customization system
|
||||
- Export to multiple formats (Logseq, JSON, Markdown)
|
||||
- Import from Logseq graphs
|
||||
- MCP protocol compliance testing
|
||||
- Production-ready CLI tool
|
||||
- Performance benchmarks
|
||||
- Security audit
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
### Version Numbering
|
||||
|
||||
This project follows semantic versioning (MAJOR.MINOR.PATCH):
|
||||
|
||||
- **MAJOR**: Incompatible API changes
|
||||
- **MINOR**: New features (backward compatible)
|
||||
- **PATCH**: Bug fixes (backward compatible)
|
||||
|
||||
### Release Schedule
|
||||
|
||||
- **Alpha** (0.x.x): API unstable, features incomplete
|
||||
- **Beta** (1.0.0-beta.x): API stable, features complete, testing phase
|
||||
- **Stable** (1.0.0+): Production ready
|
||||
|
||||
### Feature Roadmap
|
||||
|
||||
#### Phase 1: Foundation ✅ (Completed)
|
||||
- [x] Core models and graph structure
|
||||
- [x] Nickel configuration system
|
||||
- [x] Markdown parser with Logseq compatibility
|
||||
- [x] Storage trait abstraction
|
||||
- [x] CLI structure with clap
|
||||
- [x] MCP server protocol implementation
|
||||
- [x] NuShell maintenance scripts
|
||||
- [x] Documentation structure
|
||||
|
||||
#### Phase 2: Integration (In Progress)
|
||||
- [ ] Full FilesystemStorage with file watching
|
||||
- [ ] SurrealDB backend implementation
|
||||
- [ ] Query engine with text + semantic search
|
||||
- [ ] Embedding provider integrations
|
||||
- [ ] Guideline inheritance resolution
|
||||
- [ ] Sync mechanism implementation
|
||||
|
||||
#### Phase 3: Polish (Planned)
|
||||
- [ ] Performance optimization
|
||||
- [ ] Comprehensive error handling
|
||||
- [ ] Production testing
|
||||
- [ ] Security audit
|
||||
- [ ] API stabilization
|
||||
- [ ] Release preparation
|
||||
|
||||
---
|
||||
|
||||
## Migration Guides
|
||||
|
||||
### Upgrading from 0.x to 1.0
|
||||
|
||||
(To be documented when 1.0 is released)
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](docs/contributing/development.md) for development setup and guidelines.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
(To be determined)
|
||||
105
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,105 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We, as members, contributors, and leaders, pledge to make participation in our project and
|
||||
community a harassment-free experience for everyone, regardless of:
|
||||
|
||||
- Age
|
||||
- Body size
|
||||
- Visible or invisible disability
|
||||
- Ethnicity
|
||||
- Sex characteristics
|
||||
- Gender identity and expression
|
||||
- Level of experience
|
||||
- Education
|
||||
- Socioeconomic status
|
||||
- Nationality
|
||||
- Personal appearance
|
||||
- Race
|
||||
- Caste
|
||||
- Color
|
||||
- Religion
|
||||
- Sexual identity and orientation
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by mistakes
|
||||
- Focusing on what is best not just for us as individuals, but for the overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery
|
||||
- Trolling, insulting, or derogatory comments
|
||||
- Personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information (doxing)
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying and enforcing our standards of acceptable
|
||||
behavior and will take appropriate corrective action in response to unacceptable behavior.
|
||||
|
||||
Maintainers have the right and responsibility to:
|
||||
|
||||
- Remove, edit, or reject comments, commits, code, and other contributions
|
||||
- Ban contributors for behavior they deem inappropriate, threatening, or harmful
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies to:
|
||||
|
||||
- All community spaces (GitHub, forums, chat, events, etc.)
|
||||
- Official project channels and representations
|
||||
- Interactions between community members related to the project
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to project maintainers:
|
||||
|
||||
- Email: [project contact]
|
||||
- GitHub: Private security advisory
|
||||
- Issues: Report with `conduct` label (public discussions only)
|
||||
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
### Enforcement Guidelines
|
||||
|
||||
**1. Correction**
|
||||
- Community impact: Use of inappropriate language or unwelcoming behavior
|
||||
- Action: Private written warning with explanation and clarity on impact
|
||||
- Consequence: Warning and no further violations
|
||||
|
||||
**2. Warning**
|
||||
- Community impact: Violation through single incident or series of actions
|
||||
- Action: Written warning with severity consequences for continued behavior
|
||||
- Consequence: Suspension from community interaction
|
||||
|
||||
**3. Temporary Ban**
|
||||
- Community impact: Serious violation of standards
|
||||
- Action: Temporary ban from community interaction
|
||||
- Consequence: Revocation of ban after reflection period
|
||||
|
||||
**4. Permanent Ban**
|
||||
- Community impact: Pattern of violating community standards
|
||||
- Action: Permanent ban from community interaction
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
||||
|
||||
For answers to common questions about this code of conduct, see the [FAQ](https://www.contributor-covenant.org/faq).
|
||||
|
||||
---
|
||||
|
||||
**Thank you for being part of our community!**
|
||||
|
||||
We believe in creating a welcoming and inclusive space where everyone can contribute their best work. Together, we make this project better.
|
||||
130
CONTRIBUTING.md
Normal file
@ -0,0 +1,130 @@
|
||||
# Contributing
|
||||
|
||||
Thank you for your interest in contributing! This document provides guidelines and instructions for contributing to this project.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project adheres to a Code of Conduct. By participating, you are expected to uphold this code.
|
||||
Please see [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for details.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Rust 1.70+ (if project uses Rust)
|
||||
- NuShell (if project uses Nushell scripts)
|
||||
- Git
|
||||
|
||||
### Development Setup
|
||||
|
||||
1. Fork the repository
|
||||
2. Clone your fork: `git clone <your-fork-url>`
|
||||
3. Add upstream: `git remote add upstream <upstream-url>`
|
||||
4. Create a branch: `git checkout -b feature/your-feature`
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Before You Code
|
||||
|
||||
- Check existing issues and pull requests to avoid duplication
|
||||
- Create an issue to discuss major changes before implementing
|
||||
- Assign yourself to let others know you're working on it
|
||||
|
||||
### Code Standards
|
||||
|
||||
#### Rust
|
||||
|
||||
- Run `cargo fmt --all` before committing
|
||||
- All code must pass `cargo clippy -- -D warnings`
|
||||
- Write tests for new functionality
|
||||
- Maintain 100% documentation coverage for public APIs
|
||||
|
||||
#### Nushell
|
||||
|
||||
- Validate scripts with `nu --ide-check 100 script.nu`
|
||||
- Follow consistent naming conventions
|
||||
- Use type hints where applicable
|
||||
|
||||
#### Nickel
|
||||
|
||||
- Type check schemas with `nickel typecheck`
|
||||
- Document schema fields with comments
|
||||
- Test schema validation
|
||||
|
||||
### Commit Guidelines
|
||||
|
||||
- Write clear, descriptive commit messages
|
||||
- Reference issues with `Fixes #123` or `Related to #123`
|
||||
- Keep commits focused on a single concern
|
||||
- Use imperative mood: "Add feature" not "Added feature"
|
||||
|
||||
### Testing
|
||||
|
||||
All changes must include tests:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
cargo test --workspace
|
||||
|
||||
# Run with coverage
|
||||
cargo llvm-cov --all-features --lcov
|
||||
|
||||
# Run locally before pushing
|
||||
just ci-full
|
||||
```
|
||||
|
||||
### Pull Request Process
|
||||
|
||||
1. Update documentation for any changed functionality
|
||||
2. Add tests for new code
|
||||
3. Ensure all CI checks pass
|
||||
4. Request review from maintainers
|
||||
5. Be responsive to feedback and iterate quickly
|
||||
|
||||
## Review Process
|
||||
|
||||
- Maintainers will review your PR within 3-5 business days
|
||||
- Feedback is constructive and meant to improve the code
|
||||
- All discussions should be respectful and professional
|
||||
- Once approved, maintainers will merge the PR
|
||||
|
||||
## Reporting Bugs
|
||||
|
||||
Found a bug? Please file an issue with:
|
||||
|
||||
- **Title**: Clear, descriptive title
|
||||
- **Description**: What happened and what you expected
|
||||
- **Steps to reproduce**: Minimal reproducible example
|
||||
- **Environment**: OS, Rust version, etc.
|
||||
- **Screenshots**: If applicable
|
||||
|
||||
## Suggesting Enhancements
|
||||
|
||||
Have an idea? Please file an issue with:
|
||||
|
||||
- **Title**: Clear feature title
|
||||
- **Description**: What, why, and how
|
||||
- **Use cases**: Real-world scenarios where this would help
|
||||
- **Alternative approaches**: If you've considered any
|
||||
|
||||
## Documentation
|
||||
|
||||
- Keep README.md up to date
|
||||
- Document public APIs with rustdoc comments
|
||||
- Add examples for non-obvious functionality
|
||||
- Update CHANGELOG.md with your changes
|
||||
|
||||
## Release Process
|
||||
|
||||
Maintainers handle releases following semantic versioning:
|
||||
- MAJOR: Breaking changes
|
||||
- MINOR: New features (backward compatible)
|
||||
- PATCH: Bug fixes
|
||||
|
||||
## Questions
|
||||
|
||||
- Check existing documentation and issues
|
||||
- Ask in discussions or open an issue
|
||||
- Join our community channels
|
||||
|
||||
Thank you for contributing!
|
||||
98
SECURITY.md
Normal file
@ -0,0 +1,98 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
This project provides security updates for the following versions:
|
||||
|
||||
| Version | Supported |
|
||||
|---------|-----------|
|
||||
| 1.x | ✅ Yes |
|
||||
| 0.x | ❌ No |
|
||||
|
||||
Only the latest major version receives security patches. Users are encouraged to upgrade to the latest version.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
**Do not open public GitHub issues for security vulnerabilities.**
|
||||
|
||||
Instead, please report security issues to the maintainers privately:
|
||||
|
||||
### Reporting Process
|
||||
|
||||
1. Email security details to the maintainers (see project README for contact)
|
||||
2. Include:
|
||||
- Description of the vulnerability
|
||||
- Steps to reproduce (if possible)
|
||||
- Potential impact
|
||||
- Suggested fix (if you have one)
|
||||
|
||||
3. Expect acknowledgment within 48 hours
|
||||
4. We will work on a fix and coordinate disclosure timing
|
||||
|
||||
### Responsible Disclosure
|
||||
|
||||
- Allow reasonable time for a fix before public disclosure
|
||||
- Work with us to understand and validate the issue
|
||||
- Maintain confidentiality until the fix is released
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### For Users
|
||||
|
||||
- Keep dependencies up to date
|
||||
- Use the latest version of this project
|
||||
- Review security advisories regularly
|
||||
- Report vulnerabilities responsibly
|
||||
|
||||
### For Contributors
|
||||
|
||||
- Run `cargo audit` before submitting PRs
|
||||
- Use `cargo deny` to check license compliance
|
||||
- Follow secure coding practices
|
||||
- Don't hardcode secrets or credentials
|
||||
- Validate all external inputs
|
||||
|
||||
## Dependency Security
|
||||
|
||||
We use automated tools to monitor dependencies:
|
||||
|
||||
- **cargo-audit**: Scans for known security vulnerabilities
|
||||
- **cargo-deny**: Checks licenses and bans unsafe dependencies
|
||||
|
||||
These run in CI on every push and PR.
|
||||
|
||||
## Code Review
|
||||
|
||||
All code changes go through review before merging:
|
||||
- At least one maintainer review required
|
||||
- Security implications considered
|
||||
- Tests required for all changes
|
||||
- CI checks must pass
|
||||
|
||||
## Known Vulnerabilities
|
||||
|
||||
We maintain transparency about known issues:
|
||||
- Documented in GitHub security advisories
|
||||
- Announced in release notes
|
||||
- Tracked in issues with `security` label
|
||||
|
||||
## Security Contact
|
||||
|
||||
For security inquiries, please contact:
|
||||
- Email: [project maintainers]
|
||||
- Issue: Open a private security advisory on GitHub
|
||||
|
||||
## Changelog
|
||||
|
||||
Security fixes are highlighted in CHANGELOG.md with [SECURITY] prefix.
|
||||
|
||||
## Resources
|
||||
|
||||
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
||||
- [CWE: Common Weakness Enumeration](https://cwe.mitre.org/)
|
||||
- [Rust Security](https://www.rust-lang.org/governance/security-disclosures)
|
||||
- [npm Security](https://docs.npmjs.com/about-npm/security)
|
||||
|
||||
## Questions
|
||||
|
||||
If you have security questions (not vulnerabilities), open a discussion or issue with the `security` label.
|
||||
147
assets/branding/README.md
Normal file
@ -0,0 +1,147 @@
|
||||
# KOGRAL Branding Assets
|
||||
|
||||
Complete branding system for KOGRAL (KOwledge GRAphs, Local-first) - Git-native knowledge graphs for developer teams.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```text
|
||||
branding/
|
||||
├── README.md # This file
|
||||
├── index.html # Quick reference showcase
|
||||
└── kogral-assets-showcase.html # Comprehensive assets catalog
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Open either HTML file in your browser to view the interactive branding showcase:
|
||||
|
||||
- **`index.html`** - Compact version with dark/light mode toggle
|
||||
- **`kogral-assets-showcase.html`** - Full-featured showcase with navigation, comparison tables, and detailed guidelines
|
||||
|
||||
## Logo Variants
|
||||
|
||||
All logos are available in the `../logos/` directory with these suffixes:
|
||||
|
||||
| Suffix | Variant | Dimensions | Use Case |
|
||||
|--------|---------|------------|----------|
|
||||
| (none) | Full logo | 610×200px | Primary branding |
|
||||
| `-h` | Horizontal | 1000×300px | Banners, headers |
|
||||
| `-s` | Square | 200×200px | Apps, UI, icons |
|
||||
| `-h-s` | Horizontal small | 500×150px | Navbars, sidebars |
|
||||
| `-bn` | Black & white | 610×200px | Print, documents |
|
||||
|
||||
## Icon Variants
|
||||
|
||||
All icons are available in the `../icons/` directory:
|
||||
|
||||
| Variant | Dimensions | Use Case |
|
||||
|---------|------------|----------|
|
||||
| `kogral-icon.svg` | 200×200px | Standard favicon, UI elements |
|
||||
| `kogral-icon-s.svg` | 100×100px | Compact spaces, mobile UI |
|
||||
| `kogral-icon-bn.svg` | 200×200px | Print, monochrome contexts |
|
||||
|
||||
## Color Palette
|
||||
|
||||
Core brand colors with semantic meaning:
|
||||
|
||||
- **Primary Blue** (`#4a9eff`) - Knowledge, connectivity, main interactions
|
||||
- **Accent Green** (`#3dd68d`) - Active states, growth, highlights
|
||||
- **Gold** (`#fbbf24`) - Important elements, central focal point
|
||||
- **Slate Secondary** (`#64748b`) - Secondary elements, borders
|
||||
- **Dark Primary** (`#1a2744`) - UI backgrounds, primary dark
|
||||
- **Dark Secondary** (`#2a3f6a`) - UI emphasis, secondary dark
|
||||
|
||||
## Usage Guidelines
|
||||
|
||||
### Logo Sizing
|
||||
- Use the full logo for primary brand identification
|
||||
- Maintain minimum 20px whitespace around all sides
|
||||
- Never distort or rotate the logo
|
||||
- For small spaces, prefer square or horizontal-small variants
|
||||
|
||||
### Color Usage
|
||||
- Primary Blue for main interactions and hierarchy
|
||||
- Accent Green highlights active states and positive actions
|
||||
- Gold emphasizes central knowledge nodes and critical elements
|
||||
- Use monochrome variants on dark/light backgrounds for accessibility
|
||||
|
||||
### Digital Applications
|
||||
- All assets are SVG format for infinite scalability
|
||||
- Use in web apps, mobile interfaces, and digital signage
|
||||
- Animations are preserved and scale responsively
|
||||
- Export to PNG at 2x resolution for high-DPI displays
|
||||
|
||||
### Print Production
|
||||
- Always use black & white variants for print materials
|
||||
- Ensure minimum 1/4" clear space around logo
|
||||
- Test colors on actual materials before final production
|
||||
- Monochrome is preferred for single-color prints
|
||||
|
||||
### Social Media
|
||||
- Use square logo (200×200px) for profile pictures
|
||||
- Horizontal variants (1000×300px) for banners and covers
|
||||
- Export PNG at 2x scale for high-DPI displays
|
||||
- Use full logo for primary brand identification
|
||||
|
||||
### Accessibility
|
||||
- All color combinations meet WCAG AA standards
|
||||
- Monochrome variants ensure colorblind accessibility
|
||||
- Use descriptive alt-text: "KOGRAL - Git-native knowledge graphs"
|
||||
|
||||
## Asset Features
|
||||
|
||||
✨ **Interactive HTML Showcases**
|
||||
- Dark/light mode toggle with persistent storage
|
||||
- Responsive grid layouts
|
||||
- One-click filename copy
|
||||
- Sticky navigation (showcase version)
|
||||
- Color palette visualization
|
||||
- Comparison tables
|
||||
|
||||
🎨 **SVG Animations**
|
||||
- Pulsing central node
|
||||
- Flowing connection lines
|
||||
- Rotating hexagon outlines
|
||||
- Synchronized glow effects
|
||||
|
||||
📊 **Format & Scalability**
|
||||
- All assets in SVG format
|
||||
- Infinite scalability without quality loss
|
||||
- Animations preserved across all sizes
|
||||
- Print-ready monochrome variants
|
||||
|
||||
## File Sizes
|
||||
|
||||
- Full logos: ~3-5KB each
|
||||
- Icons: ~2-3KB each
|
||||
- HTML showcases: ~20-29KB
|
||||
|
||||
## Version Information
|
||||
|
||||
- **Last Updated:** 2026-01-18
|
||||
- **Version:** 1.0
|
||||
- **Format:** SVG + HTML5
|
||||
- **Compatibility:** All modern browsers
|
||||
|
||||
## Brand Identity
|
||||
|
||||
**KOGRAL** represents:
|
||||
- 🧠 Knowledge graphs for intelligent organization
|
||||
- 🔗 Connected nodes and relationships
|
||||
- 💾 Git-native, local-first architecture
|
||||
- 🌍 Distributed, team-friendly knowledge management
|
||||
- ✨ Elegant, animated visual identity
|
||||
|
||||
## Related Assets
|
||||
|
||||
- Logos: `/assets/logos/`
|
||||
- Icons: `/assets/icons/`
|
||||
- Main logo file: `/assets/kogral-logo.svg`
|
||||
|
||||
## Contact & Updates
|
||||
|
||||
For asset requests or updates, refer to the KOGRAL project documentation.
|
||||
|
||||
---
|
||||
|
||||
**All assets in KOGRAL branding system are optimized for scalability, accessibility, and brand consistency.**
|
||||
1304
assets/branding/index.html
Normal file
1552
assets/branding/kogral-assets-showcase.html
Normal file
221
assets/icons/kogral-icon-mono-black.svg
Normal file
@ -0,0 +1,221 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<defs>
|
||||
<radialGradient id="nodeCore-icon-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4f4f4f"/>
|
||||
<stop offset="100%" stop-color="#2d2d2d"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-icon-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#7a7a7a"/>
|
||||
<stop offset="100%" stop-color="#555555"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-icon-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#999999"/>
|
||||
<stop offset="100%" stop-color="#6b6b6b"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-icon-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#f0f0f0"/>
|
||||
<stop offset="40%" stop-color="#d0d0d0"/>
|
||||
<stop offset="100%" stop-color="#a0a0a0"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-icon-bn" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-icon-bn" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#d0d0d0" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.1); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #909090;
|
||||
stroke-width: 1;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 6 25;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 6 25;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 0.8;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 0.6;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 0.8;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 0.8;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<g id="icon" transform="translate(50, 50) scale(0.25)">
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<g id="nodes">
|
||||
<g class="node-center" filter="url(#glowGold-icon-bn)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-icon-bn)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#f0f0f0" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<g class="node-1" filter="url(#glow-icon-bn)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-icon-bn)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-2" filter="url(#glow-icon-bn)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-icon-bn)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-3" filter="url(#glow-icon-bn)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-icon-bn)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-4" filter="url(#glow-icon-bn)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-icon-bn)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-5" filter="url(#glow-icon-bn)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-icon-bn)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-6" filter="url(#glow-icon-bn)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-icon-bn)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
221
assets/icons/kogral-icon-static.svg
Normal file
@ -0,0 +1,221 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||
<defs>
|
||||
<radialGradient id="nodeCore-icon-s" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-icon-s" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-icon-s" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-icon-s" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-icon-s" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="1.5" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-icon-s" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.08); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 20; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #475569;
|
||||
stroke-width: 0.6;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 0.8;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 4 15;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.8;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 4 15;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 0.5;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.4;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 0.5;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.5;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<g id="icon" transform="translate(25, 25) scale(0.125)">
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<g id="nodes">
|
||||
<g class="node-center" filter="url(#glowGold-icon-s)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-icon-s)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<g class="node-1" filter="url(#glow-icon-s)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-icon-s)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-2" filter="url(#glow-icon-s)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-icon-s)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-3" filter="url(#glow-icon-s)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-icon-s)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-4" filter="url(#glow-icon-s)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-icon-s)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-5" filter="url(#glow-icon-s)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-icon-s)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-6" filter="url(#glow-icon-s)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-icon-s)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
228
assets/icons/kogral-icon.svg
Normal file
@ -0,0 +1,228 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore-icon" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-icon" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-icon" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-icon" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-icon" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-icon" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.1); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #475569;
|
||||
stroke-width: 1;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 6 25;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 6 25;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 0.8;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.6;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 0.8;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.8;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- ICON HEXAGON -->
|
||||
<g id="icon" transform="translate(50, 50) scale(0.25)">
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<g class="node-center" filter="url(#glowGold-icon)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-icon)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<g class="node-1" filter="url(#glow-icon)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-icon)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-2" filter="url(#glow-icon)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-icon)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-3" filter="url(#glow-icon)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-icon)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-4" filter="url(#glow-icon)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-icon)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-5" filter="url(#glow-icon)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-icon)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-6" filter="url(#glow-icon)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-icon)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
354
assets/kogral-logo.svg
Normal file
@ -0,0 +1,354 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610 200" width="610" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
.node-primary { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #475569;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base {
|
||||
fill: none;
|
||||
stroke: #cbd5e1;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border {
|
||||
fill: none;
|
||||
stroke: #475569;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON (escalado) ==================== -->
|
||||
<g id="logo" transform="translate(5, 5) scale(0.37)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<!-- Centro - DORADO -->
|
||||
<g class="node-center" filter="url(#glowGold)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g class="node-1" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g class="node-2" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g class="node-3" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g class="node-4" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g class="node-5" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g class="node-6" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== FONDO CONSTELACIÓN ==================== -->
|
||||
<g id="constellation" transform="translate(175, 25)" opacity="0.35">
|
||||
<!-- Conexiones de red - punteadas -->
|
||||
<line x1="20" y1="75" x2="100" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="100" y1="85" x2="170" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="170" y1="85" x2="220" y2="75" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="220" y1="75" x2="300" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="300" y1="85" x2="353" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<!-- Línea prolongada hasta el punto final -->
|
||||
<line x1="353" y1="70" x2="400" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
|
||||
<!-- Conexiones diagonales -->
|
||||
<line x1="60" y1="30" x2="100" y2="57" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="60" y1="120" x2="100" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="170" y1="135" x2="220" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="320" y1="60" x2="353" y2="30" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
|
||||
<!-- Pequeños nodos de constelación -->
|
||||
<circle cx="-15" cy="50" r="2" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="-10" cy="100" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="2.5s" repeatCount="indefinite" begin="0.5s"/>
|
||||
</circle>
|
||||
<circle cx="373" cy="15" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3.2s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
|
||||
<!-- Nodos intermedios -->
|
||||
<circle cx="135" cy="70" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="195" cy="60" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.3s" repeatCount="indefinite" begin="0.4s"/>
|
||||
</circle>
|
||||
<circle cx="260" cy="90" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.6s" repeatCount="indefinite" begin="0.8s"/>
|
||||
</circle>
|
||||
<circle cx="330" cy="50" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.1s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (solo tubos, sin flujo interno) ==================== -->
|
||||
<g id="text" transform="translate(175, 25)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-primary" style="transform-origin: 400px 70px" filter="url(#glowGold)">
|
||||
<circle cx="400" cy="70" r="10" fill="url(#nodeGold)"/>
|
||||
<circle cx="400" cy="70" r="6" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="400" cy="70" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
34
assets/logos/kogral-favicon-16.svg
Normal file
@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
|
||||
<defs>
|
||||
<radialGradient id="fav16-gold" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="fav16-blue" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="fav16-green" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Simplified hexagon for favicon -->
|
||||
<polygon points="8,2 13,5 13,11 8,14 3,11 3,5" fill="none" stroke="#475569" stroke-width="0.5" opacity="0.3"/>
|
||||
|
||||
<!-- Center gold node -->
|
||||
<circle cx="8" cy="8" r="3" fill="url(#fav16-gold)"/>
|
||||
<circle cx="8" cy="8" r="1.5" fill="#fef3c7" opacity="0.6"/>
|
||||
|
||||
<!-- Key outer nodes (simplified to 3) -->
|
||||
<circle cx="8" cy="3.5" r="1.2" fill="url(#fav16-blue)"/>
|
||||
<circle cx="12" cy="10.5" r="1.2" fill="url(#fav16-green)"/>
|
||||
<circle cx="4" cy="10.5" r="1.2" fill="url(#fav16-blue)"/>
|
||||
|
||||
<!-- Connection lines -->
|
||||
<line x1="8" y1="8" x2="8" y2="3.5" stroke="#4a9eff" stroke-width="0.7" opacity="0.4"/>
|
||||
<line x1="8" y1="8" x2="12" y2="10.5" stroke="#3dd68d" stroke-width="0.7" opacity="0.4"/>
|
||||
<line x1="8" y1="8" x2="4" y2="10.5" stroke="#4a9eff" stroke-width="0.7" opacity="0.4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
58
assets/logos/kogral-favicon-32.svg
Normal file
@ -0,0 +1,58 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
||||
<defs>
|
||||
<radialGradient id="fav32-gold" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="fav32-blue" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="fav32-green" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="fav32-slate" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Hexagon outline -->
|
||||
<polygon points="16,4 26,10 26,22 16,28 6,22 6,10" fill="none" stroke="#4a9eff" stroke-width="0.7" opacity="0.2"/>
|
||||
|
||||
<!-- Connection lines to center -->
|
||||
<line x1="16" y1="16" x2="16" y2="6" stroke="#475569" stroke-width="1" opacity="0.3"/>
|
||||
<line x1="16" y1="16" x2="24" y2="11" stroke="#475569" stroke-width="1" opacity="0.3"/>
|
||||
<line x1="16" y1="16" x2="24" y2="21" stroke="#475569" stroke-width="1" opacity="0.3"/>
|
||||
<line x1="16" y1="16" x2="16" y2="26" stroke="#475569" stroke-width="1" opacity="0.3"/>
|
||||
<line x1="16" y1="16" x2="8" y2="21" stroke="#475569" stroke-width="1" opacity="0.3"/>
|
||||
<line x1="16" y1="16" x2="8" y2="11" stroke="#475569" stroke-width="1" opacity="0.3"/>
|
||||
|
||||
<!-- Flow lines -->
|
||||
<line x1="16" y1="16" x2="16" y2="6" stroke="#4a9eff" stroke-width="1.2" stroke-dasharray="2 4" opacity="0.6"/>
|
||||
<line x1="16" y1="16" x2="24" y2="11" stroke="#3dd68d" stroke-width="1.2" stroke-dasharray="2 4" opacity="0.6"/>
|
||||
<line x1="16" y1="16" x2="16" y2="26" stroke="#4a9eff" stroke-width="1.2" stroke-dasharray="2 4" opacity="0.6"/>
|
||||
|
||||
<!-- Center gold node -->
|
||||
<circle cx="16" cy="16" r="4.5" fill="url(#fav32-gold)"/>
|
||||
<circle cx="16" cy="16" r="2.5" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="16" cy="16" r="1" fill="#fff" opacity="0.7"/>
|
||||
|
||||
<!-- 6 outer nodes -->
|
||||
<circle cx="16" cy="6" r="2.2" fill="url(#fav32-green)"/>
|
||||
<circle cx="24" cy="11" r="2.2" fill="url(#fav32-blue)"/>
|
||||
<circle cx="24" cy="21" r="2.2" fill="url(#fav32-slate)"/>
|
||||
<circle cx="16" cy="26" r="2.2" fill="url(#fav32-green)"/>
|
||||
<circle cx="8" cy="21" r="2.2" fill="url(#fav32-blue)"/>
|
||||
<circle cx="8" cy="11" r="2.2" fill="url(#fav32-slate)"/>
|
||||
|
||||
<!-- Highlights on outer nodes -->
|
||||
<circle cx="16" cy="6" r="0.7" fill="#fff" opacity="0.3"/>
|
||||
<circle cx="24" cy="11" r="0.7" fill="#fff" opacity="0.3"/>
|
||||
<circle cx="24" cy="21" r="0.7" fill="#fff" opacity="0.3"/>
|
||||
<circle cx="16" cy="26" r="0.7" fill="#fff" opacity="0.3"/>
|
||||
<circle cx="8" cy="21" r="0.7" fill="#fff" opacity="0.3"/>
|
||||
<circle cx="8" cy="11" r="0.7" fill="#fff" opacity="0.3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
296
assets/logos/kogral-h-static.svg
Normal file
@ -0,0 +1,296 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610 200" width="610" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore-hs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-hs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-hs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-hs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-hs" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong-hs" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-hs" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.connection-base-hs {
|
||||
stroke: #475569;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow-hs {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.connection-flow-accent-hs {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.hex-outline-hs {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.hex-outline-inner-hs {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.glow-ring-hs {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.glow-ring-accent-hs {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.text-tube-base-hs {
|
||||
fill: none;
|
||||
stroke: #cbd5e1;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border-hs {
|
||||
fill: none;
|
||||
stroke: #475569;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON (escalado) ==================== -->
|
||||
<g id="logo" transform="translate(5, 5) scale(0.37)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline-hs" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner-hs" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base-hs" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base-hs" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base-hs" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base-hs" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base-hs" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base-hs" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow-hs" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent-hs" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow-hs" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent-hs" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow-hs" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent-hs" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base-hs" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base-hs" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base-hs" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base-hs" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base-hs" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base-hs" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<!-- Centro - DORADO -->
|
||||
<g filter="url(#glowGold-hs)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-hs)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g filter="url(#glow-hs)">
|
||||
<circle class="glow-ring-accent-hs" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-hs)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g filter="url(#glow-hs)">
|
||||
<circle class="glow-ring-hs" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-hs)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g filter="url(#glow-hs)">
|
||||
<circle class="glow-ring-accent-hs" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-hs)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g filter="url(#glow-hs)">
|
||||
<circle class="glow-ring-hs" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-hs)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g filter="url(#glow-hs)">
|
||||
<circle class="glow-ring-accent-hs" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-hs)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g filter="url(#glow-hs)">
|
||||
<circle class="glow-ring-hs" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-hs)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff" opacity="0.6"/>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d" opacity="0.6"/>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff" opacity="0.6"/>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d" opacity="0.6"/>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff" opacity="0.6"/>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d" opacity="0.6"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== FONDO CONSTELACIÓN ==================== -->
|
||||
<g id="constellation" transform="translate(175, 25)" opacity="0.35">
|
||||
<!-- Conexiones de red - punteadas -->
|
||||
<line x1="20" y1="75" x2="100" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="100" y1="85" x2="170" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="170" y1="85" x2="220" y2="75" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="220" y1="75" x2="300" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="300" y1="85" x2="353" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="353" y1="70" x2="400" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
|
||||
<!-- Conexiones diagonales -->
|
||||
<line x1="60" y1="30" x2="100" y2="57" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="60" y1="120" x2="100" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="170" y1="135" x2="220" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="320" y1="60" x2="353" y2="30" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
|
||||
<!-- Pequeños nodos de constelación -->
|
||||
<circle cx="-15" cy="50" r="2" fill="#64748b" opacity="0.6"/>
|
||||
<circle cx="-10" cy="100" r="1.5" fill="#64748b" opacity="0.6"/>
|
||||
<circle cx="373" cy="15" r="1.5" fill="#64748b" opacity="0.6"/>
|
||||
|
||||
<!-- Nodos intermedios -->
|
||||
<circle cx="135" cy="70" r="1.5" fill="#4a9eff" opacity="0.5"/>
|
||||
<circle cx="195" cy="60" r="1" fill="#3dd68d" opacity="0.5"/>
|
||||
<circle cx="260" cy="90" r="1.5" fill="#4a9eff" opacity="0.5"/>
|
||||
<circle cx="330" cy="50" r="1" fill="#3dd68d" opacity="0.5"/>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (solo tubos, sin flujo interno) ==================== -->
|
||||
<g id="text" transform="translate(175, 25)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border-hs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border-hs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border-hs"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base-hs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base-hs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base-hs"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border-hs"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base-hs"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border-hs"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border-hs" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base-hs"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base-hs" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border-hs"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border-hs" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base-hs"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base-hs" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border-hs"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border-hs"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base-hs"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base-hs"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border-hs"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base-hs"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g filter="url(#glowGold-hs)">
|
||||
<circle cx="400" cy="70" r="10" fill="url(#nodeGold-hs)"/>
|
||||
<circle cx="400" cy="70" r="6" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="400" cy="70" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
354
assets/logos/kogral-h.svg
Normal file
@ -0,0 +1,354 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610 200" width="610" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
.node-primary { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #475569;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base {
|
||||
fill: none;
|
||||
stroke: #cbd5e1;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border {
|
||||
fill: none;
|
||||
stroke: #475569;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON (escalado) ==================== -->
|
||||
<g id="logo" transform="translate(5, 5) scale(0.37)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<!-- Centro - DORADO -->
|
||||
<g class="node-center" filter="url(#glowGold)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g class="node-1" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g class="node-2" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g class="node-3" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g class="node-4" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g class="node-5" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g class="node-6" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== FONDO CONSTELACIÓN ==================== -->
|
||||
<g id="constellation" transform="translate(175, 25)" opacity="0.35">
|
||||
<!-- Conexiones de red - punteadas -->
|
||||
<line x1="20" y1="75" x2="100" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="100" y1="85" x2="170" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="170" y1="85" x2="220" y2="75" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="220" y1="75" x2="300" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="300" y1="85" x2="353" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<!-- Línea prolongada hasta el punto final -->
|
||||
<line x1="353" y1="70" x2="400" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
|
||||
<!-- Conexiones diagonales -->
|
||||
<line x1="60" y1="30" x2="100" y2="57" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="60" y1="120" x2="100" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="170" y1="135" x2="220" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="320" y1="60" x2="353" y2="30" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
|
||||
<!-- Pequeños nodos de constelación -->
|
||||
<circle cx="-15" cy="50" r="2" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="-10" cy="100" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="2.5s" repeatCount="indefinite" begin="0.5s"/>
|
||||
</circle>
|
||||
<circle cx="373" cy="15" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3.2s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
|
||||
<!-- Nodos intermedios -->
|
||||
<circle cx="135" cy="70" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="195" cy="60" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.3s" repeatCount="indefinite" begin="0.4s"/>
|
||||
</circle>
|
||||
<circle cx="260" cy="90" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.6s" repeatCount="indefinite" begin="0.8s"/>
|
||||
</circle>
|
||||
<circle cx="330" cy="50" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.1s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (solo tubos, sin flujo interno) ==================== -->
|
||||
<g id="text" transform="translate(175, 25)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-primary" style="transform-origin: 400px 70px" filter="url(#glowGold)">
|
||||
<circle cx="400" cy="70" r="10" fill="url(#nodeGold)"/>
|
||||
<circle cx="400" cy="70" r="6" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="400" cy="70" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
293
assets/logos/kogral-mono-black-h.svg
Normal file
@ -0,0 +1,293 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610 200" width="610" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes en escala de grises -->
|
||||
<radialGradient id="nodeCore-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4f4f4f"/>
|
||||
<stop offset="100%" stop-color="#2d2d2d"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#7a7a7a"/>
|
||||
<stop offset="100%" stop-color="#555555"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#999999"/>
|
||||
<stop offset="100%" stop-color="#6b6b6b"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-bn" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#f0f0f0"/>
|
||||
<stop offset="40%" stop-color="#d0d0d0"/>
|
||||
<stop offset="100%" stop-color="#a0a0a0"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-bn" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-bn" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#d0d0d0" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
.node-primary { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #909090;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base {
|
||||
fill: none;
|
||||
stroke: #e0e0e0;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border {
|
||||
fill: none;
|
||||
stroke: #909090;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- LOGO HEXAGON (escalado) -->
|
||||
<g id="logo" transform="translate(5, 5) scale(0.37)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<g class="node-center" filter="url(#glowGold-bn)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-bn)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#f0f0f0" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<g class="node-1" filter="url(#glow-bn)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-bn)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-2" filter="url(#glow-bn)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-bn)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-3" filter="url(#glow-bn)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-bn)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-4" filter="url(#glow-bn)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-bn)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-5" filter="url(#glow-bn)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-bn)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-6" filter="url(#glow-bn)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-bn)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- TEXTO kogral (escala de grises) -->
|
||||
<g id="text" transform="translate(175, 25)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-primary" style="transform-origin: 400px 70px" filter="url(#glowGold-bn)">
|
||||
<circle cx="400" cy="70" r="10" fill="url(#nodeGold-bn)"/>
|
||||
<circle cx="400" cy="70" r="6" fill="#f0f0f0" opacity="0.5"/>
|
||||
<circle cx="400" cy="70" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
309
assets/logos/kogral-mono-black-v.svg
Normal file
@ -0,0 +1,309 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 0 450 315" width="450" height="315">
|
||||
<defs>
|
||||
<!-- Gradientes en escala de grises negro -->
|
||||
<radialGradient id="nodeCore-bv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4f4f4f"/>
|
||||
<stop offset="100%" stop-color="#2d2d2d"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-bv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#7a7a7a"/>
|
||||
<stop offset="100%" stop-color="#555555"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-bv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#999999"/>
|
||||
<stop offset="100%" stop-color="#6b6b6b"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-bv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#f0f0f0"/>
|
||||
<stop offset="40%" stop-color="#d0d0d0"/>
|
||||
<stop offset="100%" stop-color="#a0a0a0"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-bv" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong-bv" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-bv" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#d0d0d0" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse-bv {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow-bv {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter-bv {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate-bv {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center-bv { transform-origin: center; animation: pulse-bv 2.5s ease-in-out infinite; }
|
||||
.node-1-bv { transform-origin: center; animation: pulse-bv 2.5s ease-in-out infinite 0s; }
|
||||
.node-2-bv { transform-origin: center; animation: pulse-bv 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3-bv { transform-origin: center; animation: pulse-bv 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4-bv { transform-origin: center; animation: pulse-bv 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5-bv { transform-origin: center; animation: pulse-bv 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6-bv { transform-origin: center; animation: pulse-bv 2.5s ease-in-out infinite 2s; }
|
||||
|
||||
.connection-base-bv {
|
||||
stroke: #909090;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow-bv {
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter-bv 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent-bv {
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter-bv 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline-bv {
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate-bv 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner-bv {
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate-bv 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring-bv {
|
||||
fill: none;
|
||||
stroke: #4f4f4f;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow-bv 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent-bv {
|
||||
fill: none;
|
||||
stroke: #7a7a7a;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow-bv 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base-bv {
|
||||
fill: none;
|
||||
stroke: #e0e0e0;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border-bv {
|
||||
fill: none;
|
||||
stroke: #909090;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON ==================== -->
|
||||
<g id="logo-bv" transform="translate(45, 20) scale(0.42)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline-bv" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner-bv" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial-bv">
|
||||
<line class="connection-base-bv" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base-bv" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base-bv" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base-bv" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base-bv" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base-bv" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow-bv" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent-bv" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow-bv" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent-bv" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow-bv" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent-bv" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter-bv">
|
||||
<line class="connection-base-bv" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base-bv" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base-bv" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base-bv" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base-bv" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base-bv" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes-bv">
|
||||
<!-- Centro - DORADO -->
|
||||
<g class="node-center-bv" filter="url(#glowGold-bv)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-bv)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#f0f0f0" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g class="node-1-bv" filter="url(#glow-bv)">
|
||||
<circle class="glow-ring-accent-bv" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-bv)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g class="node-2-bv" filter="url(#glow-bv)">
|
||||
<circle class="glow-ring-bv" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-bv)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g class="node-3-bv" filter="url(#glow-bv)">
|
||||
<circle class="glow-ring-accent-bv" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-bv)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g class="node-4-bv" filter="url(#glow-bv)">
|
||||
<circle class="glow-ring-bv" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-bv)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g class="node-5-bv" filter="url(#glow-bv)">
|
||||
<circle class="glow-ring-accent-bv" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-bv)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g class="node-6-bv" filter="url(#glow-bv)">
|
||||
<circle class="glow-ring-bv" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-bv)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots-bv" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4f4f4f">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#7a7a7a">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (centered below logo) ==================== -->
|
||||
<g id="text-bv" transform="translate(-45, 175)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border-bv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border-bv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border-bv"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base-bv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base-bv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base-bv"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border-bv"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base-bv"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border-bv"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border-bv" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base-bv"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base-bv" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border-bv"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border-bv" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base-bv"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base-bv" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border-bv"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border-bv"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base-bv"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base-bv"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border-bv"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base-bv"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-center-bv" filter="url(#glowGold-bv)">
|
||||
<circle cx="393" cy="75" r="10" fill="url(#nodeGold-bv)"/>
|
||||
<circle cx="393" cy="75" r="6" fill="#f0f0f0" opacity="0.5"/>
|
||||
<circle cx="393" cy="75" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
293
assets/logos/kogral-mono-white-h.svg
Normal file
@ -0,0 +1,293 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610 200" width="610" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes en escala de grises blanco -->
|
||||
<radialGradient id="nodeCore-wh" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#f5f5f5"/>
|
||||
<stop offset="100%" stop-color="#e0e0e0"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-wh" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#ffffff"/>
|
||||
<stop offset="100%" stop-color="#f5f5f5"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-wh" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fafafa"/>
|
||||
<stop offset="100%" stop-color="#f0f0f0"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-wh" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#ffffff"/>
|
||||
<stop offset="40%" stop-color="#fefefe"/>
|
||||
<stop offset="100%" stop-color="#fcfcfc"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-wh" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-wh" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fefefe" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
.node-primary { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #f0f0f0;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #f5f5f5;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #ffffff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #f5f5f5;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #ffffff;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #f5f5f5;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #ffffff;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base {
|
||||
fill: none;
|
||||
stroke: #ffffff;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border {
|
||||
fill: none;
|
||||
stroke: #f0f0f0;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- LOGO HEXAGON (escalado) -->
|
||||
<g id="logo" transform="translate(5, 5) scale(0.37)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<g class="node-center" filter="url(#glowGold-wh)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-wh)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#ffffff" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<g class="node-1" filter="url(#glow-wh)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-wh)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-2" filter="url(#glow-wh)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-wh)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-3" filter="url(#glow-wh)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-wh)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-4" filter="url(#glow-wh)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-wh)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-5" filter="url(#glow-wh)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-wh)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<g class="node-6" filter="url(#glow-wh)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-wh)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#f5f5f5">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#ffffff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#f5f5f5">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#ffffff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#f5f5f5">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#ffffff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- TEXTO kogral (escala de grises blanco) -->
|
||||
<g id="text" transform="translate(175, 25)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-primary" style="transform-origin: 400px 70px" filter="url(#glowGold-wh)">
|
||||
<circle cx="400" cy="70" r="10" fill="url(#nodeGold-wh)"/>
|
||||
<circle cx="400" cy="70" r="6" fill="#ffffff" opacity="0.5"/>
|
||||
<circle cx="400" cy="70" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
309
assets/logos/kogral-mono-white-v.svg
Normal file
@ -0,0 +1,309 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 0 450 315" width="450" height="315">
|
||||
<defs>
|
||||
<!-- Gradientes en escala de grises blanco -->
|
||||
<radialGradient id="nodeCore-wv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#f5f5f5"/>
|
||||
<stop offset="100%" stop-color="#e0e0e0"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-wv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#ffffff"/>
|
||||
<stop offset="100%" stop-color="#f5f5f5"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-wv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fafafa"/>
|
||||
<stop offset="100%" stop-color="#f0f0f0"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-wv" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#ffffff"/>
|
||||
<stop offset="40%" stop-color="#fefefe"/>
|
||||
<stop offset="100%" stop-color="#fcfcfc"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-wv" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong-wv" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-wv" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fefefe" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse-wv {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow-wv {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter-wv {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate-wv {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center-wv { transform-origin: center; animation: pulse-wv 2.5s ease-in-out infinite; }
|
||||
.node-1-wv { transform-origin: center; animation: pulse-wv 2.5s ease-in-out infinite 0s; }
|
||||
.node-2-wv { transform-origin: center; animation: pulse-wv 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3-wv { transform-origin: center; animation: pulse-wv 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4-wv { transform-origin: center; animation: pulse-wv 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5-wv { transform-origin: center; animation: pulse-wv 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6-wv { transform-origin: center; animation: pulse-wv 2.5s ease-in-out infinite 2s; }
|
||||
|
||||
.connection-base-wv {
|
||||
stroke: #f0f0f0;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow-wv {
|
||||
stroke: #f5f5f5;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter-wv 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent-wv {
|
||||
stroke: #ffffff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter-wv 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline-wv {
|
||||
stroke: #f5f5f5;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate-wv 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner-wv {
|
||||
stroke: #ffffff;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate-wv 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring-wv {
|
||||
fill: none;
|
||||
stroke: #f5f5f5;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow-wv 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent-wv {
|
||||
fill: none;
|
||||
stroke: #ffffff;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow-wv 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base-wv {
|
||||
fill: none;
|
||||
stroke: #ffffff;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border-wv {
|
||||
fill: none;
|
||||
stroke: #f0f0f0;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON ==================== -->
|
||||
<g id="logo-wv" transform="translate(45, 20) scale(0.42)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline-wv" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner-wv" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial-wv">
|
||||
<line class="connection-base-wv" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base-wv" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base-wv" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base-wv" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base-wv" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base-wv" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow-wv" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent-wv" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow-wv" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent-wv" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow-wv" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent-wv" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter-wv">
|
||||
<line class="connection-base-wv" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base-wv" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base-wv" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base-wv" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base-wv" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base-wv" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes-wv">
|
||||
<!-- Centro - DORADO -->
|
||||
<g class="node-center-wv" filter="url(#glowGold-wv)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-wv)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#ffffff" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g class="node-1-wv" filter="url(#glow-wv)">
|
||||
<circle class="glow-ring-accent-wv" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-wv)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g class="node-2-wv" filter="url(#glow-wv)">
|
||||
<circle class="glow-ring-wv" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-wv)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g class="node-3-wv" filter="url(#glow-wv)">
|
||||
<circle class="glow-ring-accent-wv" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-wv)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g class="node-4-wv" filter="url(#glow-wv)">
|
||||
<circle class="glow-ring-wv" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-wv)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g class="node-5-wv" filter="url(#glow-wv)">
|
||||
<circle class="glow-ring-accent-wv" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-wv)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g class="node-6-wv" filter="url(#glow-wv)">
|
||||
<circle class="glow-ring-wv" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-wv)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots-wv" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#f5f5f5">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#ffffff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#f5f5f5">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#ffffff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#f5f5f5">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#ffffff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (centered below logo) ==================== -->
|
||||
<g id="text-wv" transform="translate(-45, 175)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border-wv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border-wv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border-wv"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base-wv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base-wv"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base-wv"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border-wv"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base-wv"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border-wv"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border-wv" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base-wv"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base-wv" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border-wv"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border-wv" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base-wv"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base-wv" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border-wv"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border-wv"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base-wv"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base-wv"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border-wv"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base-wv"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-center-wv" filter="url(#glowGold-wv)">
|
||||
<circle cx="393" cy="75" r="10" fill="url(#nodeGold-wv)"/>
|
||||
<circle cx="393" cy="75" r="6" fill="#ffffff" opacity="0.5"/>
|
||||
<circle cx="393" cy="75" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
267
assets/logos/kogral-v-static.svg
Normal file
@ -0,0 +1,267 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 0 450 315" width="450" height="315">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore-vs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-vs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-vs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-vs" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-vs" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong-vs" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-vs" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.connection-base-vs {
|
||||
stroke: #475569;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow-vs {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.connection-flow-accent-vs {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.hex-outline-vs {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.hex-outline-inner-vs {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.glow-ring-vs {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.glow-ring-accent-vs {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.text-tube-base-vs {
|
||||
fill: none;
|
||||
stroke: #cbd5e1;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border-vs {
|
||||
fill: none;
|
||||
stroke: #475569;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON ==================== -->
|
||||
<g id="logo-vs" transform="translate(45, 20) scale(0.42)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline-vs" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner-vs" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial-vs">
|
||||
<line class="connection-base-vs" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base-vs" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base-vs" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base-vs" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base-vs" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base-vs" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow-vs" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent-vs" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow-vs" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent-vs" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow-vs" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent-vs" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter-vs">
|
||||
<line class="connection-base-vs" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base-vs" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base-vs" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base-vs" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base-vs" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base-vs" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes-vs">
|
||||
<!-- Centro - DORADO -->
|
||||
<g filter="url(#glowGold-vs)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-vs)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g filter="url(#glow-vs)">
|
||||
<circle class="glow-ring-accent-vs" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-vs)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g filter="url(#glow-vs)">
|
||||
<circle class="glow-ring-vs" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-vs)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g filter="url(#glow-vs)">
|
||||
<circle class="glow-ring-accent-vs" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-vs)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g filter="url(#glow-vs)">
|
||||
<circle class="glow-ring-vs" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-vs)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g filter="url(#glow-vs)">
|
||||
<circle class="glow-ring-accent-vs" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-vs)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g filter="url(#glow-vs)">
|
||||
<circle class="glow-ring-vs" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-vs)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots-vs" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff" opacity="0.6"/>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d" opacity="0.6"/>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff" opacity="0.6"/>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d" opacity="0.6"/>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff" opacity="0.6"/>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d" opacity="0.6"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (centered below logo) ==================== -->
|
||||
<g id="text-vs" transform="translate(-45, 175)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border-vs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border-vs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border-vs"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base-vs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base-vs"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base-vs"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border-vs"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base-vs"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border-vs"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border-vs" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base-vs"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base-vs" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border-vs"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border-vs" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base-vs"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base-vs" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border-vs"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border-vs"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base-vs"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base-vs"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border-vs"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base-vs"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g filter="url(#glowGold-vs)">
|
||||
<circle cx="393" cy="75" r="10" fill="url(#nodeGold-vs)"/>
|
||||
<circle cx="393" cy="75" r="6" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="393" cy="75" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
309
assets/logos/kogral-v.svg
Normal file
@ -0,0 +1,309 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 0 450 315" width="450" height="315">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore-v" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent-v" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary-v" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold-v" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow-v" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong-v" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold-v" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse-v {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow-v {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter-v {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate-v {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center-v { transform-origin: center; animation: pulse-v 2.5s ease-in-out infinite; }
|
||||
.node-1-v { transform-origin: center; animation: pulse-v 2.5s ease-in-out infinite 0s; }
|
||||
.node-2-v { transform-origin: center; animation: pulse-v 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3-v { transform-origin: center; animation: pulse-v 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4-v { transform-origin: center; animation: pulse-v 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5-v { transform-origin: center; animation: pulse-v 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6-v { transform-origin: center; animation: pulse-v 2.5s ease-in-out infinite 2s; }
|
||||
|
||||
.connection-base-v {
|
||||
stroke: #475569;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow-v {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter-v 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent-v {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter-v 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline-v {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate-v 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner-v {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate-v 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring-v {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow-v 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent-v {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow-v 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base-v {
|
||||
fill: none;
|
||||
stroke: #cbd5e1;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border-v {
|
||||
fill: none;
|
||||
stroke: #475569;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON ==================== -->
|
||||
<g id="logo-v" transform="translate(45, 20) scale(0.42)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline-v" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner-v" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial-v">
|
||||
<line class="connection-base-v" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base-v" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base-v" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base-v" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base-v" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base-v" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow-v" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent-v" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow-v" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent-v" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow-v" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent-v" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter-v">
|
||||
<line class="connection-base-v" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base-v" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base-v" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base-v" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base-v" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base-v" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes-v">
|
||||
<!-- Centro - DORADO -->
|
||||
<g class="node-center-v" filter="url(#glowGold-v)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold-v)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g class="node-1-v" filter="url(#glow-v)">
|
||||
<circle class="glow-ring-accent-v" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent-v)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g class="node-2-v" filter="url(#glow-v)">
|
||||
<circle class="glow-ring-v" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore-v)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g class="node-3-v" filter="url(#glow-v)">
|
||||
<circle class="glow-ring-accent-v" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary-v)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g class="node-4-v" filter="url(#glow-v)">
|
||||
<circle class="glow-ring-v" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent-v)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g class="node-5-v" filter="url(#glow-v)">
|
||||
<circle class="glow-ring-accent-v" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore-v)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g class="node-6-v" filter="url(#glow-v)">
|
||||
<circle class="glow-ring-v" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary-v)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots-v" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (centered below logo) ==================== -->
|
||||
<g id="text-v" transform="translate(-45, 175)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border-v"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border-v"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border-v"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base-v"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base-v"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base-v"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border-v"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base-v"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border-v"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border-v" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base-v"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base-v" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border-v"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border-v" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base-v"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base-v" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border-v"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border-v"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base-v"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base-v"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border-v"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base-v"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-center-v" filter="url(#glowGold-v)">
|
||||
<circle cx="393" cy="75" r="10" fill="url(#nodeGold-v)"/>
|
||||
<circle cx="393" cy="75" r="6" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="393" cy="75" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
328
assets/web/README.md
Normal file
@ -0,0 +1,328 @@
|
||||
# KOGRAL Web Assets
|
||||
|
||||
Web-based landing page and static content for KOGRAL.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```text
|
||||
assets/web/
|
||||
├── src/
|
||||
│ ├── index.html # Source HTML (readable)
|
||||
│ └── kogral.svg # Logo for landing page
|
||||
├── index.html # Minified/Production HTML
|
||||
├── kogral.svg # Logo for landing page
|
||||
├── minify.sh # Minification script
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
### `src/index.html` - Source Version
|
||||
|
||||
- **Purpose**: Development and maintenance
|
||||
- **Content**:
|
||||
- Full formatting and indentation
|
||||
- Inline CSS and JavaScript
|
||||
- Bilingual (English/Spanish) content
|
||||
- Language-aware dynamic switching
|
||||
- Knowledge graph showcase
|
||||
- Technology stack display
|
||||
- Core components grid
|
||||
|
||||
**Use for:**
|
||||
|
||||
- Editing content
|
||||
- Understanding structure
|
||||
- Version control
|
||||
- Making translation updates
|
||||
|
||||
### `index.html` - Production Version
|
||||
|
||||
- **Purpose**: Served to browsers (fast loading)
|
||||
- **Optimizations**:
|
||||
- Removed all comments
|
||||
- Compressed CSS (removed spaces, combined rules)
|
||||
- Minified JavaScript (single line)
|
||||
- Removed whitespace between tags
|
||||
- Preserved all functionality
|
||||
|
||||
**Use for:**
|
||||
|
||||
- Production web server
|
||||
- CDN distribution
|
||||
- Browser caching
|
||||
- Fast load times
|
||||
|
||||
### `kogral.svg` - Logo
|
||||
|
||||
- **Purpose**: Landing page branding
|
||||
- **Source**: Copy of `../logos/kogral-h.svg` (horizontal logo)
|
||||
- **Dimensions**: 610×200px (viewBox 0 0 610 200)
|
||||
- **Features**: Animated knowledge graph nodes, connections, and text
|
||||
|
||||
## How to Use
|
||||
|
||||
### Development
|
||||
|
||||
Edit `src/index.html`:
|
||||
|
||||
```bash
|
||||
# Edit source file
|
||||
nano assets/web/src/index.html
|
||||
|
||||
# Regenerate minified version (see below)
|
||||
./assets/web/minify.sh
|
||||
```
|
||||
|
||||
### Update Minified Version
|
||||
|
||||
When you update `src/index.html`, regenerate `index.html`:
|
||||
|
||||
```bash
|
||||
# Using minify.sh script
|
||||
cd /path/to/kogral
|
||||
./assets/web/minify.sh
|
||||
|
||||
# Or manually
|
||||
cd /path/to/kogral
|
||||
perl -e '
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
open(my $fh, "<", "assets/web/src/index.html") or die $!;
|
||||
my $content = do { local $/; <$fh> };
|
||||
close($fh);
|
||||
|
||||
# Remove comments
|
||||
$content =~ s/<!--.*?-->//gs;
|
||||
|
||||
# Compress whitespace in style tags
|
||||
$content =~ s/(<style[^>]*>)(.*?)(<\/style>)/
|
||||
my $before = $1;
|
||||
my $style = $2;
|
||||
my $after = $3;
|
||||
$style =~ s{\/\*.*?\*\/}{}gs;
|
||||
$style =~ s{\s+}{ }gs;
|
||||
$style =~ s{\s*([{}:;,>+~])\s*}{$1}gs;
|
||||
$before . $style . $after;
|
||||
/gies;
|
||||
|
||||
# Compress whitespace in script tags
|
||||
$content =~ s/(<script[^>]*>)(.*?)(<\/script>)/
|
||||
my $before = $1;
|
||||
my $script = $2;
|
||||
my $after = $3;
|
||||
$script =~ s{\/\/.*$}{}gm;
|
||||
$script =~ s{\s+}{ }gs;
|
||||
$script =~ s{\s*([{}();,])\s*}{$1}gs;
|
||||
$before . $script . $after;
|
||||
/gies;
|
||||
|
||||
# Remove whitespace between tags
|
||||
$content =~ s/>\s+</></gs;
|
||||
$content =~ s/\s+/ /gs;
|
||||
$content =~ s/^\s+|\s+$//g;
|
||||
|
||||
open(my $out, ">", "assets/web/index.html") or die $!;
|
||||
print $out $content;
|
||||
close($out);
|
||||
|
||||
print "✅ Minified version created\n";
|
||||
'
|
||||
```
|
||||
|
||||
### Deployment
|
||||
|
||||
Serve `index.html` from your web server:
|
||||
|
||||
```bash
|
||||
# Using Rust
|
||||
cargo install static-web-server
|
||||
static-web-server -d assets/web/
|
||||
|
||||
# Using Python
|
||||
python3 -m http.server --directory assets/web
|
||||
|
||||
# Using Node.js
|
||||
npx http-server assets/web
|
||||
|
||||
# Using nginx
|
||||
# Point root to assets/web/
|
||||
# Serve index.html as default
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Responsive Design
|
||||
|
||||
- Mobile-first approach
|
||||
- Flexbox and Grid layouts
|
||||
- Media queries for mobile
|
||||
- Touch-friendly navigation
|
||||
|
||||
### Performance
|
||||
|
||||
- Inline CSS (no separate requests)
|
||||
- Inline JavaScript (no blocking external scripts)
|
||||
- Minimal dependencies (no frameworks)
|
||||
- Optimized minified size
|
||||
|
||||
### Bilingual
|
||||
|
||||
- English and Spanish
|
||||
- LocalStorage persistence
|
||||
- Data attributes for translations
|
||||
- Dynamic language switching
|
||||
|
||||
### Modern CSS
|
||||
|
||||
- CSS Gradients
|
||||
- Animations (fadeInUp)
|
||||
- Hover effects
|
||||
- Grid and Flexbox layouts
|
||||
|
||||
### Styling
|
||||
|
||||
- KOGRAL color scheme (Blue #4a9eff, Green #3dd68d, Gold #fbbf24)
|
||||
- Gradient backgrounds
|
||||
- Inter font family
|
||||
- Smooth transitions
|
||||
|
||||
## Content Sections
|
||||
|
||||
1. **Hero** - Title, tagline, logo, version badge
|
||||
2. **Problems** - 4 knowledge management problems KOGRAL solves
|
||||
- Scattered documentation
|
||||
- No version control for decisions
|
||||
- Lost context over time
|
||||
- Isolated team knowledge
|
||||
3. **How It Works** - Feature overview (Markdown-native, Semantic Search, Config-driven, Claude Code)
|
||||
4. **Technology Stack** - Tech badges (Rust, Nickel, SurrealDB, fastembed, MCP, Logseq, etc.)
|
||||
5. **Core Components** - Component showcase (kogral-core, kogral-cli, kogral-mcp, etc.)
|
||||
6. **CTA** - Call-to-action button linking to GitHub
|
||||
7. **Footer** - Credits and tagline
|
||||
|
||||
## Translations
|
||||
|
||||
All text content is bilingual. Edit data attributes in `src/index.html`:
|
||||
|
||||
```html
|
||||
<!-- English/Spanish example -->
|
||||
<span data-en="Knowledge Graphs" data-es="Grafos de Conocimiento">Knowledge Graphs</span>
|
||||
```
|
||||
|
||||
The JavaScript automatically updates based on selected language.
|
||||
|
||||
## Color Scheme
|
||||
|
||||
KOGRAL branding colors:
|
||||
|
||||
- **Primary Blue**: `#4a9eff` - Main brand color
|
||||
- **Secondary Green**: `#3dd68d` - Highlights and active states
|
||||
- **Gold Accent**: `#fbbf24` - Gradient accents
|
||||
- **Slate**: `#64748b` - Secondary text
|
||||
- **Dark Background**: `#0a0a14` - Main background
|
||||
|
||||
## Maintenance
|
||||
|
||||
- Source edits go in `src/index.html`
|
||||
- Regenerate `index.html` when source changes
|
||||
- Both files are versioned in git
|
||||
- Keep them in sync
|
||||
|
||||
## Git Workflow
|
||||
|
||||
```bash
|
||||
# Edit source
|
||||
nano assets/web/src/index.html
|
||||
|
||||
# Regenerate minified version
|
||||
./assets/web/minify.sh
|
||||
|
||||
# Commit changes
|
||||
git add assets/web/src/index.html
|
||||
git add assets/web/index.html
|
||||
git commit -m "Update landing page content"
|
||||
git push
|
||||
```
|
||||
|
||||
## File Sizes
|
||||
|
||||
Source and production versions:
|
||||
|
||||
| File | Type |
|
||||
|------|------|
|
||||
| `src/index.html` | Source (readable, formatted) |
|
||||
| `index.html` | Production (minified, optimized) |
|
||||
| `kogral.svg` | ~15KB (animated horizontal logo) |
|
||||
|
||||
## Version Information
|
||||
|
||||
- **Last Updated**: 2026-01-23
|
||||
- **Version**: 0.1.0
|
||||
- **Format**: HTML5 + CSS3 + ES6
|
||||
- **Compatibility**: All modern browsers
|
||||
- **Languages**: English, Spanish
|
||||
|
||||
## Technology Focus
|
||||
|
||||
**KOGRAL** landing page emphasizes:
|
||||
|
||||
- 📝 Git-native knowledge management
|
||||
- 🔍 Semantic search with embeddings
|
||||
- ⚙️ Config-driven architecture (Nickel)
|
||||
- 🤖 Claude Code integration (MCP)
|
||||
- 📊 6 node types, 6 relationships
|
||||
- 🗄️ Multi-backend storage (Filesystem, SurrealDB, Memory)
|
||||
|
||||
## Features Highlighted
|
||||
|
||||
### 4 Core Problems Solved
|
||||
|
||||
1. **Scattered Documentation** - Unifies notes, decisions, guidelines across tools
|
||||
2. **No Version Control** - Git-tracked ADRs with full history
|
||||
3. **Lost Context** - Semantic search and relationship tracking
|
||||
4. **Isolated Knowledge** - Multi-graph architecture with inheritance
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Markdown-Native** - YAML frontmatter, wikilinks, code references
|
||||
2. **Semantic Search** - Vector embeddings, local or cloud
|
||||
3. **Config-Driven** - Nickel schemas, 3 modes (dev/prod/test)
|
||||
4. **Claude Code** - MCP server with 7 tools, 6 resources, 2 prompts
|
||||
|
||||
### Technology Stack
|
||||
|
||||
- Rust Edition 2021
|
||||
- Nickel Config
|
||||
- SurrealDB
|
||||
- fastembed (local embeddings)
|
||||
- rig-core (cloud embeddings)
|
||||
- MCP Protocol
|
||||
- Logseq Compatible
|
||||
- Nushell Scripts
|
||||
|
||||
### Core Components
|
||||
|
||||
- **kogral-core** - Core library
|
||||
- **kogral-cli** - 13 commands
|
||||
- **kogral-mcp** - MCP server
|
||||
- **Config System** - Nickel schemas
|
||||
- **3 Storage Backends** - Filesystem, SurrealDB, Memory
|
||||
- **2 Embedding Providers** - FastEmbed, rig-core
|
||||
- **6 Node Types** - Note, Decision, Guideline, Pattern, Journal, Execution
|
||||
- **6 Relationships** - relates_to, depends_on, implements, extends, supersedes, explains
|
||||
|
||||
## Quality Metrics
|
||||
|
||||
- 3 Rust crates
|
||||
- 56 tests passing
|
||||
- 15K lines of code
|
||||
- 0 unsafe blocks
|
||||
- 100% documentation coverage
|
||||
- 0 clippy warnings
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2026-01-23
|
||||
**Version**: 0.1.0 (matches KOGRAL release)
|
||||
1
assets/web/index.html
Normal file
354
assets/web/kogral.svg
Normal file
@ -0,0 +1,354 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610 200" width="610" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
.node-primary { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #475569;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base {
|
||||
fill: none;
|
||||
stroke: #cbd5e1;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border {
|
||||
fill: none;
|
||||
stroke: #475569;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON (escalado) ==================== -->
|
||||
<g id="logo" transform="translate(5, 5) scale(0.37)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<!-- Centro - DORADO -->
|
||||
<g class="node-center" filter="url(#glowGold)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g class="node-1" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g class="node-2" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g class="node-3" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g class="node-4" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g class="node-5" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g class="node-6" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== FONDO CONSTELACIÓN ==================== -->
|
||||
<g id="constellation" transform="translate(175, 25)" opacity="0.35">
|
||||
<!-- Conexiones de red - punteadas -->
|
||||
<line x1="20" y1="75" x2="100" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="100" y1="85" x2="170" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="170" y1="85" x2="220" y2="75" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="220" y1="75" x2="300" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="300" y1="85" x2="353" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<!-- Línea prolongada hasta el punto final -->
|
||||
<line x1="353" y1="70" x2="400" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
|
||||
<!-- Conexiones diagonales -->
|
||||
<line x1="60" y1="30" x2="100" y2="57" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="60" y1="120" x2="100" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="170" y1="135" x2="220" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="320" y1="60" x2="353" y2="30" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
|
||||
<!-- Pequeños nodos de constelación -->
|
||||
<circle cx="-15" cy="50" r="2" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="-10" cy="100" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="2.5s" repeatCount="indefinite" begin="0.5s"/>
|
||||
</circle>
|
||||
<circle cx="373" cy="15" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3.2s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
|
||||
<!-- Nodos intermedios -->
|
||||
<circle cx="135" cy="70" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="195" cy="60" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.3s" repeatCount="indefinite" begin="0.4s"/>
|
||||
</circle>
|
||||
<circle cx="260" cy="90" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.6s" repeatCount="indefinite" begin="0.8s"/>
|
||||
</circle>
|
||||
<circle cx="330" cy="50" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.1s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (solo tubos, sin flujo interno) ==================== -->
|
||||
<g id="text" transform="translate(175, 25)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-primary" style="transform-origin: 400px 70px" filter="url(#glowGold)">
|
||||
<circle cx="400" cy="70" r="10" fill="url(#nodeGold)"/>
|
||||
<circle cx="400" cy="70" r="6" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="400" cy="70" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
87
assets/web/minify.sh
Executable file
@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# Minify index.html from src/ to production version
|
||||
# Usage: ./minify.sh
|
||||
|
||||
set -e
|
||||
|
||||
SRC_FILE="$(dirname "$0")/src/index.html"
|
||||
OUT_FILE="$(dirname "$0")/index.html"
|
||||
TEMP_FILE="${OUT_FILE}.tmp"
|
||||
|
||||
if [ ! -f "$SRC_FILE" ]; then
|
||||
echo "❌ Source file not found: $SRC_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔨 Minifying HTML..."
|
||||
echo " Input: $SRC_FILE"
|
||||
echo " Output: $OUT_FILE"
|
||||
|
||||
perl -e "
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
open(my \$fh, '<', '$SRC_FILE') or die \$!;
|
||||
my \$content = do { local \$/; <\$fh> };
|
||||
close(\$fh);
|
||||
|
||||
# Remove HTML comments
|
||||
\$content =~ s/<!--.*?-->//gs;
|
||||
|
||||
# Compress CSS (remove spaces and comments)
|
||||
\$content =~ s/(<style[^>]*>)(.*?)(<\/style>)/
|
||||
my \$before = \$1;
|
||||
my \$style = \$2;
|
||||
my \$after = \$3;
|
||||
\$style =~ s{\/\*.*?\*\/}{}gs;
|
||||
\$style =~ s{\s+}{ }gs;
|
||||
\$style =~ s{\s*([{}:;,>+~])\s*}{\$1}gs;
|
||||
\$before . \$style . \$after;
|
||||
/gies;
|
||||
|
||||
# Compress JavaScript (remove comments and extra spaces)
|
||||
\$content =~ s/(<script[^>]*>)(.*?)(<\/script>)/
|
||||
my \$before = \$1;
|
||||
my \$script = \$2;
|
||||
my \$after = \$3;
|
||||
\$script =~ s{\/\/.*\$}{}gm;
|
||||
\$script =~ s{\s+}{ }gs;
|
||||
\$script =~ s{\s*([{}();,])\s*}{\$1}gs;
|
||||
\$before . \$script . \$after;
|
||||
/gies;
|
||||
|
||||
# Remove whitespace between tags
|
||||
\$content =~ s/>\s+</></gs;
|
||||
|
||||
# Compress general whitespace
|
||||
\$content =~ s/\s+/ /gs;
|
||||
|
||||
# Trim
|
||||
\$content =~ s/^\s+|\s+\$//g;
|
||||
|
||||
open(my \$out, '>', '$TEMP_FILE') or die \$!;
|
||||
print \$out \$content;
|
||||
close(\$out);
|
||||
" || {
|
||||
echo "❌ Minification failed"
|
||||
rm -f "$TEMP_FILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
mv "$TEMP_FILE" "$OUT_FILE"
|
||||
|
||||
# Show statistics
|
||||
original=$(wc -c < "$SRC_FILE")
|
||||
minified=$(wc -c < "$OUT_FILE")
|
||||
saved=$((original - minified))
|
||||
percent=$((saved * 100 / original))
|
||||
|
||||
echo ""
|
||||
echo "✅ Minification complete!"
|
||||
echo ""
|
||||
echo "📊 Compression statistics:"
|
||||
printf " Original: %6d bytes\n" "$original"
|
||||
printf " Minified: %6d bytes\n" "$minified"
|
||||
printf " Saved: %6d bytes (%d%%)\n" "$saved" "$percent"
|
||||
echo ""
|
||||
echo "✅ $OUT_FILE is ready for production"
|
||||
956
assets/web/src/index.html
Normal file
@ -0,0 +1,956 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title
|
||||
data-en="KOGRAL - Git-Native Knowledge Graphs"
|
||||
data-es="KOGRAL - Grafos de Conocimiento Git-Native"
|
||||
>
|
||||
KOGRAL
|
||||
</title>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background: #0a0a14;
|
||||
color: #ffffff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.gradient-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
background:
|
||||
radial-gradient(
|
||||
circle at 20% 50%,
|
||||
rgba(74, 158, 255, 0.15) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 80% 80%,
|
||||
rgba(61, 214, 141, 0.15) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 40% 90%,
|
||||
rgba(251, 191, 36, 0.1) 0%,
|
||||
transparent 50%
|
||||
);
|
||||
}
|
||||
|
||||
.language-toggle {
|
||||
position: fixed;
|
||||
top: 2rem;
|
||||
right: 2rem;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(74, 158, 255, 0.3);
|
||||
border-radius: 20px;
|
||||
padding: 0.3rem 0.3rem;
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #94a3b8;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 18px;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.3s ease;
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.lang-btn.active {
|
||||
background: linear-gradient(135deg, #4a9eff 0%, #3dd68d 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.lang-btn:hover {
|
||||
color: #3dd68d;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
padding: 5rem 0 4rem;
|
||||
animation: fadeInUp 0.8s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
background: rgba(61, 214, 141, 0.2);
|
||||
border: 1px solid #3dd68d;
|
||||
color: #3dd68d;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 50px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.logo-container img {
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
filter: drop-shadow(0 0 30px rgba(74, 158, 255, 0.4));
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 0.95rem;
|
||||
color: #3dd68d;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.8rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 1.5rem;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#4a9eff 0%,
|
||||
#3dd68d 50%,
|
||||
#fbbf24 100%
|
||||
);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.15rem;
|
||||
color: #cbd5e1;
|
||||
max-width: 800px;
|
||||
margin: 0 auto 2rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #3dd68d;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin: 4rem 0;
|
||||
animation: fadeInUp 0.8s ease-out;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 2rem;
|
||||
color: #3dd68d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-title span {
|
||||
background: linear-gradient(135deg, #4a9eff 0%, #3dd68d 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.problems-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.problem-card {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(74, 158, 255, 0.3);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.problem-card:hover {
|
||||
transform: translateY(-5px);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-color: rgba(61, 214, 141, 0.5);
|
||||
}
|
||||
|
||||
.problem-number {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #4a9eff 0%, #3dd68d 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
line-height: 1;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.problem-card h3 {
|
||||
color: #4a9eff;
|
||||
font-size: 1.05rem;
|
||||
margin-bottom: 0.7rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.problem-card p {
|
||||
color: #cbd5e1;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.problem-card ul {
|
||||
color: #cbd5e1;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.problem-card ul li {
|
||||
padding-left: 1.2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.problem-card ul li:before {
|
||||
content: "•";
|
||||
color: #4a9eff;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.tech-stack {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tech-badge {
|
||||
background: rgba(61, 214, 141, 0.15);
|
||||
border: 1px solid #3dd68d;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
color: #3dd68d;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.feature-box {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(74, 158, 255, 0.1) 0%,
|
||||
rgba(61, 214, 141, 0.1) 100%
|
||||
);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
border-left: 4px solid #4a9eff;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-box:hover {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(74, 158, 255, 0.15) 0%,
|
||||
rgba(61, 214, 141, 0.15) 100%
|
||||
);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
color: #4a9eff;
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
|
||||
.feature-text {
|
||||
color: #cbd5e1;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.feature-text.feature-text {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.feature-text li {
|
||||
padding-left: 1.2rem;
|
||||
margin-bottom: 0.6rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.feature-text li:before {
|
||||
content: "▸";
|
||||
color: #3dd68d;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.components-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.component-item {
|
||||
background: rgba(74, 158, 255, 0.1);
|
||||
padding: 1.2rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
border: 1px solid rgba(74, 158, 255, 0.3);
|
||||
transition: all 0.2s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.component-item:hover {
|
||||
background: rgba(74, 158, 255, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.component-name {
|
||||
color: #4a9eff;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.component-role {
|
||||
color: #94a3b8;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
text-align: center;
|
||||
margin: 5rem 0 3rem;
|
||||
padding: 4rem 2rem;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(74, 158, 255, 0.1) 0%,
|
||||
rgba(61, 214, 141, 0.1) 100%
|
||||
);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(74, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.cta-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(135deg, #4a9eff 0%, #3dd68d 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#4a9eff 0%,
|
||||
#3dd68d 50%,
|
||||
#fbbf24 100%
|
||||
);
|
||||
color: #fff;
|
||||
padding: 1.1rem 2.8rem;
|
||||
border-radius: 50px;
|
||||
text-decoration: none;
|
||||
font-weight: 800;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 10px 30px rgba(74, 158, 255, 0.3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
transform: translateY(-3px) scale(1.05);
|
||||
box-shadow: 0 20px 50px rgba(74, 158, 255, 0.5);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 3rem 0 2rem;
|
||||
color: #64748b;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-top: 4rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
footer p:first-child {
|
||||
font-weight: 700;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
footer p:last-child {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.hero-subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.logo-container img {
|
||||
max-width: 320px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
.cta-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
.language-toggle {
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="gradient-bg"></div>
|
||||
|
||||
<div class="language-toggle">
|
||||
<button
|
||||
class="lang-btn active"
|
||||
data-lang="en"
|
||||
onclick="switchLanguage('en')"
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
<button class="lang-btn" data-lang="es" onclick="switchLanguage('es')">
|
||||
ES
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<span class="status-badge" data-en="✅ v0.1.0" data-es="✅ v0.1.0"
|
||||
>✅ v0.1.0</span
|
||||
>
|
||||
<div class="logo-container">
|
||||
<img src="/kogral.svg" alt="KOGRAL - Git-Native Knowledge Graphs" />
|
||||
</div>
|
||||
<p class="tagline">Git-Native Knowledge Management</p>
|
||||
<h1
|
||||
data-en="Knowledge Graphs<br>That Live in Git"
|
||||
data-es="Grafos de Conocimiento<br>Que Viven en Git"
|
||||
>
|
||||
Knowledge Graphs
|
||||
</h1>
|
||||
<p class="hero-subtitle">
|
||||
<span
|
||||
class="highlight"
|
||||
data-en="Structured knowledge management"
|
||||
data-es="Gestión estructurada de conocimiento"
|
||||
>Structured knowledge management</span
|
||||
><span
|
||||
data-en=" that scales from solo projects to organizations. Config-driven architecture for capturing architectural decisions, coding guidelines, and reusable patterns in version-controlled markdown."
|
||||
data-es=" que escala desde proyectos individuales a organizaciones. Arquitectura basada en configuración para capturar decisiones arquitectónicas, guías de código y patrones reutilizables en markdown versionado."
|
||||
> that scales from solo projects to organizations. Config-driven architecture for capturing architectural decisions, coding guidelines, and reusable patterns in version-controlled markdown.
|
||||
</span>
|
||||
<br><span><strong data-en="100% Rust. Zero compromises." data-es="100% Rust. Sin compromisos."
|
||||
>100% Rust. Zero compromises.</strong
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
<span
|
||||
data-en="The 4 Problems It Solves"
|
||||
data-es="Los 4 Problemas que Resuelve"
|
||||
>The 4 Problems It Solves</span
|
||||
>
|
||||
</h2>
|
||||
<div class="problems-grid">
|
||||
<div class="problem-card">
|
||||
<div class="problem-number">01</div>
|
||||
<h3 data-en="Scattered Documentation" data-es="Documentación Dispersa">
|
||||
Scattered Documentation
|
||||
</h3>
|
||||
<ul
|
||||
data-en="<li>Notes in Notion</li><li>Decisions in Slack</li><li>Guidelines in wikis—all disconnected</li><li>KOGRAL unifies with git-native markdown + MCP</li>"
|
||||
data-es="<li>Notas en Notion</li><li>Decisiones en Slack</li><li>Guías en wikis—todo desconectado</li><li>KOGRAL unifica con markdown git-native + MCP</li>"
|
||||
>
|
||||
<li>Notes in Notion</li>
|
||||
<li>Decisions in Slack</li>
|
||||
<li>Guidelines in wikis—all disconnected</li>
|
||||
<li>KOGRAL unifies with git-native markdown + MCP</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="problem-card">
|
||||
<div class="problem-number">02</div>
|
||||
<h3
|
||||
data-en="No Version Control for Decisions"
|
||||
data-es="Sin Control de Versiones para Decisiones"
|
||||
>
|
||||
No Version Control for Decisions
|
||||
</h3>
|
||||
<ul
|
||||
data-en="<li>Architectural decisions lost in chat history</li><li>No traceability for why code exists</li><li>KOGRAL: Git-tracked ADRs with full history</li><li>Link decisions directly to code with @file:line</li>"
|
||||
data-es="<li>Decisiones arquitectónicas perdidas en chat</li><li>Sin trazabilidad de por qué existe el código</li><li>KOGRAL: ADRs rastreados en Git con historial completo</li><li>Vincula decisiones al código con @file:line</li>"
|
||||
>
|
||||
<li>Architectural decisions lost in chat history</li>
|
||||
<li>No traceability for why code exists</li>
|
||||
<li>KOGRAL: Git-tracked ADRs with full history</li>
|
||||
<li>Link decisions directly to code with @file:line</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="problem-card">
|
||||
<div class="problem-number">03</div>
|
||||
<h3 data-en="Lost Context Over Time" data-es="Contexto Perdido con el Tiempo">
|
||||
Lost Context Over Time
|
||||
</h3>
|
||||
<ul
|
||||
data-en="<li>Team members join and can't find past decisions</li><li>Outdated documentation causes repeated mistakes</li><li>KOGRAL: Semantic search across your knowledge base</li><li>Relationship tracking shows why patterns exist</li>"
|
||||
data-es="<li>Nuevos miembros no encuentran decisiones pasadas</li><li>Documentación desactualizada causa errores repetidos</li><li>KOGRAL: Búsqueda semántica en tu base de conocimiento</li><li>Rastreo de relaciones muestra por qué existen patrones</li>"
|
||||
>
|
||||
<li>Team members join and can't find past decisions</li>
|
||||
<li>Outdated documentation causes repeated mistakes</li>
|
||||
<li>KOGRAL: Semantic search across your knowledge base</li>
|
||||
<li>Relationship tracking shows why patterns exist</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="problem-card">
|
||||
<div class="problem-number">04</div>
|
||||
<h3 data-en="Isolated Team Knowledge" data-es="Conocimiento Aislado del Equipo">
|
||||
Isolated Team Knowledge
|
||||
</h3>
|
||||
<ul
|
||||
data-en="<li>Every project reinvents the wheel</li><li>Patterns can't be shared across teams</li><li>KOGRAL: Multi-graph architecture</li><li>Shared organizational knowledge + project-specific overrides</li><li>Inheritance system for guidelines</li>"
|
||||
data-es="<li>Cada proyecto reinventa la rueda</li><li>Los patrones no se comparten entre equipos</li><li>KOGRAL: Arquitectura multi-grafo</li><li>Conocimiento organizacional compartido + sobrescrituras por proyecto</li><li>Sistema de herencia para guías</li>"
|
||||
>
|
||||
<li>Every project reinvents the wheel</li>
|
||||
<li>Patterns can't be shared across teams</li>
|
||||
<li>KOGRAL: Multi-graph architecture</li>
|
||||
<li>Shared organizational knowledge + project-specific overrides</li>
|
||||
<li>Inheritance system for guidelines</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
<span data-en="How It Works" data-es="Cómo Funciona"
|
||||
>How It Works</span
|
||||
>
|
||||
</h2>
|
||||
<div class="features-grid">
|
||||
<div class="feature-box">
|
||||
<div class="feature-icon">📝</div>
|
||||
<h3
|
||||
class="feature-title"
|
||||
data-en="Markdown-Native"
|
||||
data-es="Markdown Nativo"
|
||||
>
|
||||
Markdown-Native
|
||||
</h3>
|
||||
<ul
|
||||
class="feature-text"
|
||||
data-en="<li>YAML frontmatter for metadata</li><li>Wikilinks [[like-this]] for relationships</li><li>Code references @file.rs:42</li><li>Logseq-compatible format</li><li>Human-readable, Git-friendly</li>"
|
||||
data-es="<li>YAML frontmatter para metadatos</li><li>Wikilinks [[así]] para relaciones</li><li>Referencias de código @file.rs:42</li><li>Formato compatible con Logseq</li><li>Legible por humanos, amigable con Git</li>"
|
||||
>
|
||||
<li>YAML frontmatter for metadata</li>
|
||||
<li>Wikilinks [[like-this]] for relationships</li>
|
||||
<li>Code references @file.rs:42</li>
|
||||
<li>Logseq-compatible format</li>
|
||||
<li>Human-readable, Git-friendly</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="feature-box" style="border-left-color: #3dd68d">
|
||||
<div class="feature-icon">🔍</div>
|
||||
<h3
|
||||
class="feature-title"
|
||||
style="color: #3dd68d"
|
||||
data-en="Semantic Search"
|
||||
data-es="Búsqueda Semántica"
|
||||
>
|
||||
Semantic Search
|
||||
</h3>
|
||||
<ul
|
||||
class="feature-text"
|
||||
data-en="<li>Text search across all nodes</li><li>Vector embeddings for semantic queries</li><li>Local embeddings (fastembed, no API costs)</li><li>Cloud APIs supported (OpenAI, Claude, Ollama)</li><li>Find related knowledge automatically</li>"
|
||||
data-es="<li>Búsqueda de texto en todos los nodos</li><li>Embeddings vectoriales para consultas semánticas</li><li>Embeddings locales (fastembed, sin costos API)</li><li>APIs en la nube soportadas (OpenAI, Claude, Ollama)</li><li>Encuentra conocimiento relacionado automáticamente</li>"
|
||||
>
|
||||
<li>Text search across all nodes</li>
|
||||
<li>Vector embeddings for semantic queries</li>
|
||||
<li>Local embeddings (fastembed, no API costs)</li>
|
||||
<li>Cloud APIs supported (OpenAI, Claude, Ollama)</li>
|
||||
<li>Find related knowledge automatically</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="feature-box" style="border-left-color: #fbbf24">
|
||||
<div class="feature-icon">⚙️</div>
|
||||
<h3
|
||||
class="feature-title"
|
||||
style="color: #fbbf24"
|
||||
data-en="Config-Driven"
|
||||
data-es="Basado en Configuración"
|
||||
>
|
||||
Config-Driven
|
||||
</h3>
|
||||
<ul
|
||||
class="feature-text"
|
||||
data-en="<li>Nickel schemas with validation</li><li>3 modes: dev, prod, test</li><li>Storage backend selection (filesystem, SurrealDB, in-memory)</li><li>Embedding provider configuration</li><li>No hardcoded paths or settings</li>"
|
||||
data-es="<li>Esquemas Nickel con validación</li><li>3 modos: dev, prod, test</li><li>Selección de backend de almacenamiento (filesystem, SurrealDB, memoria)</li><li>Configuración de proveedor de embeddings</li><li>Sin rutas o configuraciones hardcodeadas</li>"
|
||||
>
|
||||
<li>Nickel schemas with validation</li>
|
||||
<li>3 modes: dev, prod, test</li>
|
||||
<li>Storage backend selection (filesystem, SurrealDB, in-memory)</li>
|
||||
<li>Embedding provider configuration</li>
|
||||
<li>No hardcoded paths or settings</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="feature-box" style="border-left-color: #64748b">
|
||||
<div class="feature-icon">🤖</div>
|
||||
<h3
|
||||
class="feature-title"
|
||||
style="color: #64748b"
|
||||
data-en="Claude Code Integration"
|
||||
data-es="Integración Claude Code"
|
||||
>
|
||||
Claude Code Integration
|
||||
</h3>
|
||||
<ul
|
||||
class="feature-text"
|
||||
data-en="<li>MCP server (Model Context Protocol)</li><li>7 tools: search, add notes/decisions, link, export</li><li>6 resources: project/shared graphs</li><li>2 prompts: summarize project, find related</li><li>JSON-RPC 2.0 over stdio</li>"
|
||||
data-es="<li>Servidor MCP (Model Context Protocol)</li><li>7 herramientas: buscar, añadir notas/decisiones, vincular, exportar</li><li>6 recursos: grafos proyecto/compartidos</li><li>2 prompts: resumir proyecto, encontrar relacionados</li><li>JSON-RPC 2.0 sobre stdio</li>"
|
||||
>
|
||||
<li>MCP server (Model Context Protocol)</li>
|
||||
<li>7 tools: search, add notes/decisions, link, export</li>
|
||||
<li>6 resources: project/shared graphs</li>
|
||||
<li>2 prompts: summarize project, find related</li>
|
||||
<li>JSON-RPC 2.0 over stdio</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
<span data-en="Technology Stack" data-es="Stack Tecnológico"
|
||||
>Technology Stack</span
|
||||
>
|
||||
</h2>
|
||||
<div class="tech-stack">
|
||||
<span class="tech-badge">Rust Edition 2021</span>
|
||||
<span class="tech-badge">Nickel Config</span>
|
||||
<span class="tech-badge">SurrealDB</span>
|
||||
<span class="tech-badge">fastembed</span>
|
||||
<span class="tech-badge">rig-core</span>
|
||||
<span class="tech-badge">MCP Protocol</span>
|
||||
<span class="tech-badge">Logseq Compatible</span>
|
||||
<span class="tech-badge">Tera Templates</span>
|
||||
<span class="tech-badge">Clap CLI</span>
|
||||
<span class="tech-badge">DashMap</span>
|
||||
<span class="tech-badge">Nushell Scripts</span>
|
||||
<span class="tech-badge">mdBook Docs</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2 class="section-title">
|
||||
<span data-en="Core Components" data-es="Componentes Principales"
|
||||
>Core Components</span
|
||||
>
|
||||
</h2>
|
||||
<div class="components-grid">
|
||||
<div class="component-item">
|
||||
<span class="component-name" data-en="kogral-core" data-es="kogral-core"
|
||||
>kogral-core</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Core library"
|
||||
data-es="Biblioteca núcleo"
|
||||
>Core library</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span class="component-name" data-en="kogral-cli" data-es="kogral-cli"
|
||||
>kogral-cli</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="13 commands"
|
||||
data-es="13 comandos"
|
||||
>13 commands</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span
|
||||
class="component-name"
|
||||
data-en="kogral-mcp"
|
||||
data-es="kogral-mcp"
|
||||
>kogral-mcp</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="MCP server"
|
||||
data-es="Servidor MCP"
|
||||
>MCP server</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span class="component-name" data-en="Config System" data-es="Sistema Config"
|
||||
>Config System</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Nickel schemas"
|
||||
data-es="Esquemas Nickel"
|
||||
>Nickel schemas</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span class="component-name" data-en="3 Storage Backends" data-es="3 Backends Storage"
|
||||
>3 Storage Backends</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Filesystem, SurrealDB, Memory"
|
||||
data-es="Filesystem, SurrealDB, Memoria"
|
||||
>Filesystem, SurrealDB, Memory</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span class="component-name" data-en="2 Embedding Providers" data-es="2 Proveedores Embeddings"
|
||||
>2 Embedding Providers</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="FastEmbed, rig-core"
|
||||
data-es="FastEmbed, rig-core"
|
||||
>FastEmbed, rig-core</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span
|
||||
class="component-name"
|
||||
data-en="6 Node Types"
|
||||
data-es="6 Tipos de Nodos"
|
||||
>6 Node Types</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Note, Decision, Guideline, Pattern, Journal, Execution"
|
||||
data-es="Nota, Decisión, Guía, Patrón, Diario, Ejecución"
|
||||
>Note, Decision, Guideline, Pattern, Journal, Execution</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span
|
||||
class="component-name"
|
||||
data-en="6 Relationships"
|
||||
data-es="6 Relaciones"
|
||||
>6 Relationships</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="relates_to, depends_on, implements, extends, supersedes, explains"
|
||||
data-es="relates_to, depends_on, implements, extends, supersedes, explains"
|
||||
>relates_to, depends_on, implements, extends, supersedes, explains</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span
|
||||
class="component-name"
|
||||
data-en="Multi-Graph"
|
||||
data-es="Multi-Grafo"
|
||||
>Multi-Graph</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Project + Shared"
|
||||
data-es="Proyecto + Compartido"
|
||||
>Project + Shared</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span class="component-name" data-en="56 Tests Passing" data-es="56 Tests Pasando"
|
||||
>56 Tests Passing</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Quality assurance"
|
||||
data-es="Aseguramiento calidad"
|
||||
>Quality assurance</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span class="component-name" data-en="0 Unsafe Code" data-es="0 Código Unsafe"
|
||||
>0 Unsafe Code</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Memory safety"
|
||||
data-es="Seguridad memoria"
|
||||
>Memory safety</span
|
||||
>
|
||||
</div>
|
||||
<div class="component-item">
|
||||
<span
|
||||
class="component-name"
|
||||
data-en="100% Doc Coverage"
|
||||
data-es="100% Cobertura Doc"
|
||||
>100% Doc Coverage</span
|
||||
><span
|
||||
class="component-role"
|
||||
data-en="Public APIs documented"
|
||||
data-es="APIs públicas documentadas"
|
||||
>Public APIs documented</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="cta-section">
|
||||
<h2
|
||||
class="cta-title"
|
||||
data-en="Ready to organize your knowledge?"
|
||||
data-es="¿Listo para organizar tu conocimiento?"
|
||||
>
|
||||
Ready to organize your knowledge?
|
||||
</h2>
|
||||
<p
|
||||
style="color: #94a3b8; margin-bottom: 2rem; font-size: 1.05rem"
|
||||
data-en="3 Crates | 56 Tests | 15K LOC | 100% Rust 🦀"
|
||||
data-es="3 Crates | 56 Tests | 15K LOC | 100% Rust 🦀"
|
||||
>
|
||||
3 Crates | 56 Tests | 15K LOC | 100% Rust 🦀
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/jesusperezlorenzo/kogral"
|
||||
class="cta-button"
|
||||
data-en="Get Started →"
|
||||
data-es="Comenzar →"
|
||||
>Get Started →</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p data-en="KOGRAL v0.1.0" data-es="KOGRAL v0.1.0">KOGRAL v0.1.0</p>
|
||||
<p
|
||||
data-en="Built with Rust • Configuration with Nickel"
|
||||
data-es="Construido con Rust • Configuración con Nickel"
|
||||
>
|
||||
Built with Rust • Configuration with Nickel
|
||||
</p>
|
||||
<p
|
||||
style="margin-top: 1rem; font-size: 0.8rem"
|
||||
data-en="Git-Native Knowledge Graphs for Developer Teams"
|
||||
data-es="Grafos de Conocimiento Git-Native para Equipos de Desarrollo"
|
||||
>
|
||||
Git-Native Knowledge Graphs for Developer Teams
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Language management
|
||||
const LANG_KEY = "kogral-lang";
|
||||
|
||||
function getCurrentLanguage() {
|
||||
return localStorage.getItem(LANG_KEY) || "en";
|
||||
}
|
||||
|
||||
function switchLanguage(lang) {
|
||||
localStorage.setItem(LANG_KEY, lang);
|
||||
|
||||
// Update language buttons
|
||||
document.querySelectorAll(".lang-btn").forEach((btn) => {
|
||||
btn.classList.remove("active");
|
||||
if (btn.dataset.lang === lang) {
|
||||
btn.classList.add("active");
|
||||
}
|
||||
});
|
||||
|
||||
// Update all translatable elements
|
||||
document.querySelectorAll("[data-en][data-es]").forEach((el) => {
|
||||
const content = el.dataset[lang];
|
||||
// Use innerHTML for headings (might contain <br>) and lists (contain <li>)
|
||||
if (
|
||||
el.tagName === "H1" ||
|
||||
el.tagName === "H2" ||
|
||||
el.tagName === "H3" ||
|
||||
el.tagName === "UL"
|
||||
) {
|
||||
el.innerHTML = content;
|
||||
} else {
|
||||
el.textContent = content;
|
||||
}
|
||||
});
|
||||
|
||||
document.documentElement.lang = lang;
|
||||
}
|
||||
|
||||
// Initialize language on page load
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const currentLang = getCurrentLanguage();
|
||||
switchLanguage(currentLang);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
354
assets/web/src/kogral.svg
Normal file
@ -0,0 +1,354 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610 200" width="610" height="200">
|
||||
<defs>
|
||||
<!-- Gradientes -->
|
||||
<radialGradient id="nodeCore" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#4a9eff"/>
|
||||
<stop offset="100%" stop-color="#2d7ad6"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeAccent" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#3dd68d"/>
|
||||
<stop offset="100%" stop-color="#28a968"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeSecondary" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#64748b"/>
|
||||
<stop offset="100%" stop-color="#475569"/>
|
||||
</radialGradient>
|
||||
|
||||
<radialGradient id="nodeGold" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fef3c7"/>
|
||||
<stop offset="40%" stop-color="#fbbf24"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</radialGradient>
|
||||
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowStrong" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="blur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="glowGold" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feGaussianBlur stdDeviation="4" result="blur"/>
|
||||
<feFlood flood-color="#fbbf24" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge>
|
||||
<feMergeNode/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.12); opacity: 0.85; }
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes flowToCenter {
|
||||
0% { stroke-dashoffset: 30; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes hexRotate {
|
||||
0%, 100% { opacity: 0.15; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.node-center { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
.node-1 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0s; }
|
||||
.node-2 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.4s; }
|
||||
.node-3 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 0.8s; }
|
||||
.node-4 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.2s; }
|
||||
.node-5 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 1.6s; }
|
||||
.node-6 { transform-origin: center; animation: pulse 2.5s ease-in-out infinite 2s; }
|
||||
.node-primary { transform-origin: center; animation: pulse 2.5s ease-in-out infinite; }
|
||||
|
||||
.connection-base {
|
||||
stroke: #475569;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-flow {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite;
|
||||
}
|
||||
|
||||
.connection-flow-accent {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 8 30;
|
||||
fill: none;
|
||||
animation: flowToCenter 1.8s linear infinite 0.9s;
|
||||
}
|
||||
|
||||
.hex-outline {
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hex-outline-inner {
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 0.75;
|
||||
fill: none;
|
||||
animation: hexRotate 3s ease-in-out infinite 1.5s;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
fill: none;
|
||||
stroke: #4a9eff;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.glow-ring-accent {
|
||||
fill: none;
|
||||
stroke: #3dd68d;
|
||||
stroke-width: 1;
|
||||
animation: pulseGlow 2.5s ease-in-out infinite 0.5s;
|
||||
}
|
||||
|
||||
.text-tube-base {
|
||||
fill: none;
|
||||
stroke: #cbd5e1;
|
||||
stroke-width: 10;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.text-tube-border {
|
||||
fill: none;
|
||||
stroke: #475569;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- ==================== LOGO HEXAGON (escalado) ==================== -->
|
||||
<g id="logo" transform="translate(5, 5) scale(0.37)">
|
||||
|
||||
<!-- Hexágono exterior sutil -->
|
||||
<polygon class="hex-outline" points="256,96 388,176 388,336 256,416 124,336 124,176"/>
|
||||
<polygon class="hex-outline-inner" points="256,136 352,196 352,316 256,376 160,316 160,196"/>
|
||||
|
||||
<!-- Conexiones al centro -->
|
||||
<g id="connections-radial">
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="256" y1="256" x2="135" y2="186"/>
|
||||
|
||||
<line class="connection-flow" x1="256" y1="256" x2="256" y2="116"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="377" y2="186"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="377" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="256" y2="396"/>
|
||||
<line class="connection-flow" x1="256" y1="256" x2="135" y2="326"/>
|
||||
<line class="connection-flow-accent" x1="256" y1="256" x2="135" y2="186"/>
|
||||
</g>
|
||||
|
||||
<!-- Conexiones perimetrales -->
|
||||
<g id="connections-perimeter">
|
||||
<line class="connection-base" x1="256" y1="116" x2="377" y2="186"/>
|
||||
<line class="connection-base" x1="377" y1="186" x2="377" y2="326"/>
|
||||
<line class="connection-base" x1="377" y1="326" x2="256" y2="396"/>
|
||||
<line class="connection-base" x1="256" y1="396" x2="135" y2="326"/>
|
||||
<line class="connection-base" x1="135" y1="326" x2="135" y2="186"/>
|
||||
<line class="connection-base" x1="135" y1="186" x2="256" y2="116"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodos -->
|
||||
<g id="nodes">
|
||||
<!-- Centro - DORADO -->
|
||||
<g class="node-center" filter="url(#glowGold)">
|
||||
<circle cx="256" cy="256" r="28" fill="url(#nodeGold)"/>
|
||||
<circle cx="256" cy="256" r="16" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="256" cy="256" r="7" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 1 - Top -->
|
||||
<g class="node-1" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="256" cy="116" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="116" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="116" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 2 - Top Right -->
|
||||
<g class="node-2" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="377" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="186" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="377" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 3 - Bottom Right -->
|
||||
<g class="node-3" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="377" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="377" cy="326" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="377" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 4 - Bottom -->
|
||||
<g class="node-4" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="256" cy="396" r="20" opacity="0.3"/>
|
||||
<circle cx="256" cy="396" r="15" fill="url(#nodeAccent)"/>
|
||||
<circle cx="256" cy="396" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 5 - Bottom Left -->
|
||||
<g class="node-5" filter="url(#glow)">
|
||||
<circle class="glow-ring-accent" cx="135" cy="326" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="326" r="15" fill="url(#nodeCore)"/>
|
||||
<circle cx="135" cy="326" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Nodo 6 - Top Left -->
|
||||
<g class="node-6" filter="url(#glow)">
|
||||
<circle class="glow-ring" cx="135" cy="186" r="20" opacity="0.3"/>
|
||||
<circle cx="135" cy="186" r="15" fill="url(#nodeSecondary)"/>
|
||||
<circle cx="135" cy="186" r="6" fill="#fff" opacity="0.2"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Puntos de sincronización -->
|
||||
<g id="sync-dots" opacity="0.7">
|
||||
<circle cx="256" cy="186" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle cx="316" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.6s"/>
|
||||
</circle>
|
||||
<circle cx="256" cy="326" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="0.9s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="291" r="2.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
<circle cx="196" cy="221" r="2.5" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.3;1;0.3" dur="1.8s" repeatCount="indefinite" begin="1.5s"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== FONDO CONSTELACIÓN ==================== -->
|
||||
<g id="constellation" transform="translate(175, 25)" opacity="0.35">
|
||||
<!-- Conexiones de red - punteadas -->
|
||||
<line x1="20" y1="75" x2="100" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="100" y1="85" x2="170" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="170" y1="85" x2="220" y2="75" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="220" y1="75" x2="300" y2="85" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<line x1="300" y1="85" x2="353" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
<!-- Línea prolongada hasta el punto final -->
|
||||
<line x1="353" y1="70" x2="400" y2="70" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="4 8"/>
|
||||
|
||||
<!-- Conexiones diagonales -->
|
||||
<line x1="60" y1="30" x2="100" y2="57" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="60" y1="120" x2="100" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="170" y1="135" x2="220" y2="113" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
<line x1="320" y1="60" x2="353" y2="30" stroke="#94a3b8" stroke-width="1" stroke-dasharray="3 6"/>
|
||||
|
||||
<!-- Pequeños nodos de constelación -->
|
||||
<circle cx="-15" cy="50" r="2" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="-10" cy="100" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="2.5s" repeatCount="indefinite" begin="0.5s"/>
|
||||
</circle>
|
||||
<circle cx="373" cy="15" r="1.5" fill="#64748b">
|
||||
<animate attributeName="opacity" values="0.3;0.8;0.3" dur="3.2s" repeatCount="indefinite" begin="0.3s"/>
|
||||
</circle>
|
||||
|
||||
<!-- Nodos intermedios -->
|
||||
<circle cx="135" cy="70" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="195" cy="60" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.3s" repeatCount="indefinite" begin="0.4s"/>
|
||||
</circle>
|
||||
<circle cx="260" cy="90" r="1.5" fill="#4a9eff">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.6s" repeatCount="indefinite" begin="0.8s"/>
|
||||
</circle>
|
||||
<circle cx="330" cy="50" r="1" fill="#3dd68d">
|
||||
<animate attributeName="opacity" values="0.2;0.7;0.2" dur="2.1s" repeatCount="indefinite" begin="1.2s"/>
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== TEXTO kogral (solo tubos, sin flujo interno) ==================== -->
|
||||
<g id="text" transform="translate(175, 25)">
|
||||
|
||||
<!-- K -->
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-border"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-border"/>
|
||||
<line x1="20" y1="30" x2="20" y2="120" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="30" class="text-tube-base"/>
|
||||
<line x1="20" y1="75" x2="60" y2="120" class="text-tube-base"/>
|
||||
|
||||
<!-- o -->
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<ellipse cx="100" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
|
||||
<!-- g -->
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-border" fill="none"/>
|
||||
<ellipse cx="170" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<path d="M190 85 L190 115 Q190 135 170 135 Q152 135 148 122" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- r -->
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-border"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-border" fill="none"/>
|
||||
<line x1="220" y1="60" x2="220" y2="113" class="text-tube-base"/>
|
||||
<path d="M220 75 Q220 58 242 58 Q258 58 262 70" class="text-tube-base" fill="none"/>
|
||||
|
||||
<!-- a -->
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-border"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-border"/>
|
||||
<ellipse cx="300" cy="85" rx="20" ry="28" class="text-tube-base"/>
|
||||
<line x1="320" y1="60" x2="320" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- l -->
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-border"/>
|
||||
<line x1="353" y1="30" x2="353" y2="113" class="text-tube-base"/>
|
||||
|
||||
<!-- Punto dorado final (nodo final de la línea central) -->
|
||||
<g class="node-primary" style="transform-origin: 400px 70px" filter="url(#glowGold)">
|
||||
<circle cx="400" cy="70" r="10" fill="url(#nodeGold)"/>
|
||||
<circle cx="400" cy="70" r="6" fill="#fef3c7" opacity="0.5"/>
|
||||
<circle cx="400" cy="70" r="2.5" fill="#fff" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
354
docs/README.md
Normal file
@ -0,0 +1,354 @@
|
||||
# KOGRAL Documentation
|
||||
|
||||
<div align="center"> <img src="../assets/kogral-logo.svg" alt="KoGraL Logo" width="600" /> </div>
|
||||
|
||||
Welcome to the KOGRAL documentation! This directory contains comprehensive documentation for KOGRAL (**KO**wledge **GRA**phs, **L**ocal-first), built with [mdBook](https://rust-lang.github.io/mdBook/).
|
||||
|
||||
## 📚 Reading the Documentation
|
||||
|
||||
You have several options for reading the documentation:
|
||||
|
||||
### Option 1: Serve Locally with mdBook (Recommended)
|
||||
|
||||
The best reading experience with navigation, search, and live reload:
|
||||
|
||||
```bash
|
||||
# Serve documentation at http://localhost:3000
|
||||
just docs::serve
|
||||
```
|
||||
|
||||
This will:
|
||||
- Build the mdBook
|
||||
- Start a local web server on port 3000
|
||||
- Open your browser automatically
|
||||
- Watch for changes and auto-reload
|
||||
|
||||
### Option 2: Build Static HTML
|
||||
|
||||
Generate static HTML files you can browse offline:
|
||||
|
||||
```bash
|
||||
# Build mdBook to docs/book/
|
||||
just docs::build
|
||||
```
|
||||
|
||||
Then open `docs/book/index.html` in your browser.
|
||||
|
||||
### Option 3: Read Markdown Files Directly
|
||||
|
||||
All documentation is written in Markdown and can be read directly:
|
||||
|
||||
- Browse via GitHub/GitLab web interface
|
||||
- Use your editor's Markdown preview
|
||||
- Read from terminal with `bat`, `glow`, or similar tools
|
||||
|
||||
**Navigation**: See [SUMMARY.md](SUMMARY.md) for the complete table of contents.
|
||||
|
||||
## 🛠️ Documentation Commands
|
||||
|
||||
We use `just` recipes for documentation tasks. All commands assume you're in the project root directory.
|
||||
|
||||
### Build and Serve
|
||||
|
||||
```bash
|
||||
# Serve documentation locally (recommended)
|
||||
just docs::serve
|
||||
|
||||
# Build static HTML
|
||||
just docs::build
|
||||
|
||||
# Watch and rebuild on file changes
|
||||
just docs::watch
|
||||
```
|
||||
|
||||
### Validation
|
||||
|
||||
```bash
|
||||
# Test code examples in documentation
|
||||
just docs::test
|
||||
|
||||
# Check for broken links
|
||||
just docs::check-links
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
# Remove build artifacts
|
||||
just docs::clean
|
||||
```
|
||||
|
||||
### View All Documentation Commands
|
||||
|
||||
```bash
|
||||
just docs::help
|
||||
```
|
||||
|
||||
## 📦 Installing mdBook
|
||||
|
||||
mdBook is required to build and serve the documentation.
|
||||
|
||||
### Install via Cargo
|
||||
|
||||
```bash
|
||||
cargo install mdbook
|
||||
```
|
||||
|
||||
### Install Optional Tools
|
||||
|
||||
For enhanced functionality:
|
||||
|
||||
```bash
|
||||
# Link checker (validates internal/external links)
|
||||
cargo install mdbook-linkcheck
|
||||
|
||||
# Mermaid diagram support
|
||||
cargo install mdbook-mermaid
|
||||
|
||||
# PlantUML diagram support
|
||||
cargo install mdbook-plantuml
|
||||
```
|
||||
|
||||
### Verify Installation
|
||||
|
||||
```bash
|
||||
mdbook --version
|
||||
# Should output: mdbook v0.4.x or later
|
||||
```
|
||||
|
||||
## 📖 Documentation Structure
|
||||
|
||||
The documentation is organized into the following sections:
|
||||
|
||||
### 1. **KOGRAL Definition** (`kogral/`)
|
||||
- What is KOGRAL and why it exists
|
||||
- Core concepts (nodes, edges, graphs)
|
||||
- Design philosophy
|
||||
|
||||
### 2. **Guides** (`guides/`)
|
||||
- Quick start (5 minutes)
|
||||
- Installation guide
|
||||
- Use cases with examples
|
||||
|
||||
### 3. **Architecture** (`architecture/`)
|
||||
- System overview with diagrams
|
||||
- Config-driven architecture
|
||||
- Graph model details
|
||||
- ADRs (Architectural Decision Records)
|
||||
|
||||
### 4. **Setup** (`setup/`)
|
||||
- Initial setup
|
||||
- Development environment
|
||||
- Production deployment
|
||||
- Testing environment
|
||||
- CI/CD integration
|
||||
|
||||
### 5. **Configuration** (`config/`)
|
||||
- Configuration overview
|
||||
- Nickel schema reference
|
||||
- Runtime configuration
|
||||
- Environment modes (dev/prod/test)
|
||||
|
||||
### 6. **Storage** (`storage/`)
|
||||
- Storage architecture (hybrid strategy)
|
||||
- Filesystem backend
|
||||
- SurrealDB backend
|
||||
- In-memory backend
|
||||
- Sync mechanism
|
||||
|
||||
### 7. **AI & Embeddings** (`ai/`)
|
||||
- Semantic search
|
||||
- Embedding providers
|
||||
- Provider comparison
|
||||
- Configuration examples
|
||||
|
||||
### 8. **Templates** (`templates/`)
|
||||
- Template system (Tera)
|
||||
- Document templates
|
||||
- Export templates
|
||||
- Custom templates
|
||||
|
||||
### 9. **CLI Reference** (`cli/`)
|
||||
- All kogral-cli commands
|
||||
- Common workflows
|
||||
- Advanced usage
|
||||
- Troubleshooting
|
||||
|
||||
### 10. **Apps & Integrations** (`apps/`)
|
||||
- MCP quick guide (Claude Code)
|
||||
- Logseq integration
|
||||
- Vapora integration
|
||||
|
||||
### 11. **API Reference** (`api/`)
|
||||
- MCP tools specification
|
||||
- Storage trait
|
||||
- Embedding trait
|
||||
- REST API (future)
|
||||
|
||||
### 12. **Contributing** (`contributing/`)
|
||||
- Development setup
|
||||
- Code guidelines
|
||||
- Testing standards
|
||||
- Documentation guidelines
|
||||
|
||||
## 🎨 Visual Diagrams
|
||||
|
||||
The documentation includes SVG diagrams for visual understanding:
|
||||
|
||||
- **[architecture-overview.svg](diagrams/architecture-overview.svg)** - Complete system architecture
|
||||
- **[core-concepts.svg](diagrams/core-concepts.svg)** - Node types and relationships
|
||||
- **[config-composition.svg](diagrams/config-composition.svg)** - Configuration flow (Nickel → JSON → Rust)
|
||||
- **[storage-architecture.svg](diagrams/storage-architecture.svg)** - Hybrid storage strategy
|
||||
|
||||
These diagrams are embedded in relevant documentation pages and can be viewed standalone in a browser.
|
||||
|
||||
## 🔍 Searching the Documentation
|
||||
|
||||
When using `just docs::serve`, you get a built-in search feature:
|
||||
|
||||
1. Click the search icon (🔍) in the top-left corner
|
||||
2. Type your query
|
||||
3. Press Enter to navigate results
|
||||
|
||||
The search indexes all documentation content including:
|
||||
- Page titles
|
||||
- Headers
|
||||
- Body text
|
||||
- Code examples (optionally)
|
||||
|
||||
## ✏️ Editing Documentation
|
||||
|
||||
### File Format
|
||||
|
||||
All documentation is written in **GitHub Flavored Markdown** with mdBook extensions.
|
||||
|
||||
See [contributing/documentation.md](contributing/documentation.md) for detailed editing guidelines.
|
||||
|
||||
### Adding a New Page
|
||||
|
||||
1. Create the markdown file in the appropriate directory
|
||||
2. Add it to `SUMMARY.md` for navigation
|
||||
3. Build to verify: `just docs::build`
|
||||
|
||||
### Adding a New Section
|
||||
|
||||
1. Create the directory
|
||||
2. Add a `README.md` for the section landing page
|
||||
3. Add section to `SUMMARY.md`
|
||||
|
||||
## 🧪 Testing Documentation
|
||||
|
||||
### Test Code Examples
|
||||
|
||||
```bash
|
||||
just docs::test
|
||||
```
|
||||
|
||||
This runs all Rust code examples in the documentation to ensure they compile.
|
||||
|
||||
### Check Links
|
||||
|
||||
```bash
|
||||
just docs::check-links
|
||||
```
|
||||
|
||||
This validates all internal and external links.
|
||||
|
||||
## 📝 Documentation Standards
|
||||
|
||||
When contributing to documentation:
|
||||
|
||||
1. **Use clear, concise language** - Write for developers and AI agents
|
||||
2. **Include code examples** - Show, don't just tell
|
||||
3. **Add diagrams where helpful** - Visual aids improve understanding
|
||||
4. **Link related concepts** - Help readers discover related content
|
||||
5. **Test code examples** - Ensure code compiles and works
|
||||
6. **Use consistent formatting** - Follow existing page structure
|
||||
7. **Update SUMMARY.md** - New pages must be in navigation
|
||||
8. **Run checks before committing**:
|
||||
|
||||
```bash
|
||||
just docs::build
|
||||
just docs::test
|
||||
just docs::check-links
|
||||
```
|
||||
|
||||
## 💡 Tips
|
||||
|
||||
### Live Reload While Writing
|
||||
|
||||
```bash
|
||||
just docs::watch
|
||||
```
|
||||
|
||||
This watches for changes and rebuilds automatically. Open <http://localhost:3000> in your browser to see updates in real-time.
|
||||
|
||||
### Markdown Preview in Editor
|
||||
|
||||
Most editors have Markdown preview:
|
||||
|
||||
- **VS Code**: `Ctrl+Shift+V` (Cmd+Shift+V on Mac)
|
||||
- **IntelliJ/CLion**: Preview pane (right side)
|
||||
- **Vim/Neovim**: Use plugins like `markdown-preview.nvim`
|
||||
|
||||
### Quick Reference
|
||||
|
||||
- **SUMMARY.md** - Table of contents (edit to add/remove pages)
|
||||
- **book.toml** - mdBook configuration
|
||||
- **theme/** - Custom CSS/JS (if needed)
|
||||
- **diagrams/** - SVG diagrams
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### mdbook command not found
|
||||
|
||||
```bash
|
||||
# Install mdBook
|
||||
cargo install mdbook
|
||||
|
||||
# Verify installation
|
||||
mdbook --version
|
||||
```
|
||||
|
||||
### Port 3000 already in use
|
||||
|
||||
```bash
|
||||
# Serve on different port
|
||||
cd docs
|
||||
mdbook serve --port 3001
|
||||
```
|
||||
|
||||
### Links broken after moving files
|
||||
|
||||
```bash
|
||||
# Check all links
|
||||
just docs::check-links
|
||||
|
||||
# Update internal links in affected files
|
||||
# Then rebuild
|
||||
just docs::build
|
||||
```
|
||||
|
||||
## 📚 Resources
|
||||
|
||||
- [mdBook User Guide](https://rust-lang.github.io/mdBook/)
|
||||
- [GitHub Flavored Markdown Spec](https://github.github.com/gfm/)
|
||||
- [Markdown Guide](https://www.markdownguide.org/)
|
||||
|
||||
## 🤝 Contributing to Documentation
|
||||
|
||||
Documentation improvements are always welcome! To contribute:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Test with `just docs::build` and `just docs::test`
|
||||
5. Submit a pull request
|
||||
|
||||
See [contributing/documentation.md](contributing/documentation.md) for detailed guidelines.
|
||||
|
||||
---
|
||||
|
||||
**Happy documenting! 📖**
|
||||
|
||||
If you have questions or need help, please open an issue or reach out to the maintainers.
|
||||
103
docs/SUMMARY.md
Normal file
@ -0,0 +1,103 @@
|
||||
# Summary
|
||||
|
||||
[Introduction](README.md)
|
||||
|
||||
# Project Definition
|
||||
|
||||
- [What is KOGRAL?](kogral/what-is-kogral.md)
|
||||
- [Core Concepts](kogral/core-concepts.md)
|
||||
- [Why KOGRAL?](kogral/why-kogral.md)
|
||||
- [Design Philosophy](kogral/design-philosophy.md)
|
||||
|
||||
# Guides
|
||||
|
||||
- [Installation](guides/installation.md)
|
||||
- [Quick Start](guides/quickstart.md)
|
||||
- [Daily Workflows](guides/daily-workflows.md)
|
||||
- [Use Cases](guides/use-cases.md)
|
||||
- [Best Practices](guides/best-practices.md)
|
||||
|
||||
# Architecture
|
||||
|
||||
- [System Overview](architecture/overview.md)
|
||||
- [Graph Model](architecture/graph-model.md)
|
||||
- [Config-Driven Design](architecture/config-driven.md)
|
||||
- [Storage Architecture](architecture/storage-architecture.md)
|
||||
- [Logseq Blocks Design](architecture/logseq-blocks-design.md)
|
||||
|
||||
## ADRs
|
||||
|
||||
- [ADR-001: Nickel vs TOML for Configuration](architecture/adrs/001-nickel-vs-toml.md)
|
||||
- [ADR-002: FastEmbed via AI Providers](architecture/adrs/002-fastembed-ai-providers.md)
|
||||
- [ADR-003: Hybrid Storage Strategy](architecture/adrs/003-hybrid-storage.md)
|
||||
- [ADR-004: Logseq Blocks Support](architecture/adrs/004-logseq-blocks-support.md)
|
||||
- [ADR-005: MCP Protocol for AI Integration](architecture/adrs/005-mcp-protocol.md)
|
||||
|
||||
# Setup
|
||||
|
||||
- [Prerequisites](setup/prerequisites.md)
|
||||
- [Installation Methods](setup/installation.md)
|
||||
- [Project Initialization](setup/project-init.md)
|
||||
- [Environment Configuration](setup/environment.md)
|
||||
- [Verification](setup/verification.md)
|
||||
|
||||
# Configuration
|
||||
|
||||
- [Configuration Overview](config/overview.md)
|
||||
- [Nickel Schemas](config/nickel-schemas.md)
|
||||
- [Graph Settings](config/graph-settings.md)
|
||||
- [Inheritance](config/inheritance.md)
|
||||
- [Examples](config/examples.md)
|
||||
|
||||
# Storage
|
||||
|
||||
- [Storage Backends](storage/backends.md)
|
||||
- [Filesystem Storage](storage/filesystem.md)
|
||||
- [SurrealDB Storage](storage/surrealdb.md)
|
||||
- [In-Memory Storage](storage/memory.md)
|
||||
- [Sync Strategies](storage/sync.md)
|
||||
|
||||
# AI & Embeddings
|
||||
|
||||
- [Embeddings Overview](ai/embeddings-overview.md)
|
||||
- [Provider Configuration](ai/providers.md)
|
||||
- [FastEmbed Local](ai/fastembed.md)
|
||||
- [OpenAI Integration](ai/openai.md)
|
||||
- [Claude Integration](ai/claude.md)
|
||||
- [Ollama Integration](ai/ollama.md)
|
||||
- [Semantic Search](ai/semantic-search.md)
|
||||
|
||||
# Templates
|
||||
|
||||
- [Template System](templates/overview.md)
|
||||
- [Document Templates](templates/documents.md)
|
||||
- [Export Templates](templates/export.md)
|
||||
- [Customization](templates/customization.md)
|
||||
|
||||
# CLI
|
||||
|
||||
- [CLI Overview](cli/overview.md)
|
||||
- [Commands Reference](cli/commands.md)
|
||||
- [Workflows](cli/workflows.md)
|
||||
- [NuShell Scripts](cli/nushell-scripts.md)
|
||||
|
||||
# Apps & Connections
|
||||
|
||||
- [MCP Quick Guide](apps/mcp-quickguide.md)
|
||||
- [Claude Code Integration](apps/claude-code.md)
|
||||
- [Logseq Integration](apps/logseq.md)
|
||||
- [Obsidian Compatibility](apps/obsidian.md)
|
||||
- [Git Workflows](apps/git.md)
|
||||
|
||||
# API Reference
|
||||
|
||||
- [MCP Protocol](api/mcp-protocol.md)
|
||||
- [Tools Reference](api/mcp-tools.md)
|
||||
- [Resources](api/mcp-resources.md)
|
||||
- [Rust API](api/rust-api.md)
|
||||
|
||||
# Contributing
|
||||
|
||||
- [Development Setup](contributing/development.md)
|
||||
- [Code Standards](contributing/standards.md)
|
||||
- [Testing](contributing/testing.md)
|
||||
1
docs/ai/embeddings-overview.md
Normal file
@ -0,0 +1 @@
|
||||
# Embeddings Overview
|
||||
1
docs/ai/fastembed.md
Normal file
@ -0,0 +1 @@
|
||||
# FastEmbed Local
|
||||
1
docs/ai/ollama.md
Normal file
@ -0,0 +1 @@
|
||||
# Ollama Integration
|
||||
1
docs/ai/openai.md
Normal file
@ -0,0 +1 @@
|
||||
# OpenAI Integration
|
||||
1
docs/ai/providers.md
Normal file
@ -0,0 +1 @@
|
||||
# Provider Configuration
|
||||
1
docs/ai/semantic-search.md
Normal file
@ -0,0 +1 @@
|
||||
# Semantic Search
|
||||
229
docs/api/README.md
Normal file
@ -0,0 +1,229 @@
|
||||
# API Reference
|
||||
|
||||
API documentation for the Knowledge Base system.
|
||||
|
||||
## Available APIs
|
||||
|
||||
- [MCP Tools](mcp-tools.md) - Model Context Protocol tools and resources for Claude Code integration
|
||||
- [Rust API](rust-api.md) - kogral-core library API for programmatic access
|
||||
|
||||
## Overview
|
||||
|
||||
The Knowledge Base system provides multiple API layers:
|
||||
|
||||
### MCP Protocol
|
||||
|
||||
JSON-RPC 2.0 protocol for Claude Code integration:
|
||||
- 7 Tools for interacting with knowledge base
|
||||
- 6 Resources for accessing knowledge graphs
|
||||
- 2 Prompts for guided workflows
|
||||
|
||||
See [MCP Tools](mcp-tools.md) for complete reference.
|
||||
|
||||
### Rust Library
|
||||
|
||||
Direct programmatic access via kogral-core:
|
||||
|
||||
```rust
|
||||
use kb_core::prelude::*;
|
||||
|
||||
// Load a graph
|
||||
let storage = FilesystemStorage::new(".kogral")?;
|
||||
let graph = storage.load_graph("default").await?;
|
||||
|
||||
// Query nodes
|
||||
let results = graph.nodes.values()
|
||||
.filter(|n| n.tags.contains(&"rust".to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
```
|
||||
|
||||
See [Rust API](rust-api.md) for complete reference.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Common MCP Operations
|
||||
|
||||
```json
|
||||
// Search knowledge base
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "kogral/search",
|
||||
"params": {
|
||||
"query": "error handling",
|
||||
"type": "guideline",
|
||||
"semantic": true
|
||||
}
|
||||
}
|
||||
|
||||
// Add a note
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "kogral/add_note",
|
||||
"params": {
|
||||
"title": "New Discovery",
|
||||
"content": "Interesting finding about...",
|
||||
"tags": ["research", "rust"]
|
||||
}
|
||||
}
|
||||
|
||||
// Get guidelines
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "kogral/get_guidelines",
|
||||
"params": {
|
||||
"language": "rust",
|
||||
"category": "error-handling"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Common Rust Operations
|
||||
|
||||
```rust
|
||||
// Create a new graph
|
||||
let mut graph = Graph::new("my-project".to_string());
|
||||
|
||||
// Add a node
|
||||
let node = Node {
|
||||
id: "note-1".to_string(),
|
||||
node_type: NodeType::Note,
|
||||
title: "Example Note".to_string(),
|
||||
content: "Note content".to_string(),
|
||||
tags: vec!["rust".to_string()],
|
||||
status: NodeStatus::Active,
|
||||
// ...
|
||||
};
|
||||
graph.add_node(node)?;
|
||||
|
||||
// Create a relationship
|
||||
let edge = Edge::new(
|
||||
"note-1".to_string(),
|
||||
"note-2".to_string(),
|
||||
EdgeType::RelatesTo,
|
||||
);
|
||||
graph.add_edge(edge)?;
|
||||
|
||||
// Save graph
|
||||
storage.save_graph(&graph).await?;
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### MCP Server
|
||||
|
||||
No authentication required for stdio transport (local use).
|
||||
|
||||
For remote access (SSE transport), configure API keys in environment:
|
||||
|
||||
```bash
|
||||
export KB_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
### Rust Library
|
||||
|
||||
No authentication needed for local filesystem access.
|
||||
|
||||
For SurrealDB backend, configure credentials:
|
||||
|
||||
```rust
|
||||
let config = SurrealDbConfig {
|
||||
url: "ws://localhost:8000".to_string(),
|
||||
namespace: "kb".to_string(),
|
||||
database: "default".to_string(),
|
||||
username: Some("root".to_string()),
|
||||
password: Some("root".to_string()),
|
||||
};
|
||||
let storage = SurrealDbStorage::new(config).await?;
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All APIs use structured error types:
|
||||
|
||||
### MCP Errors
|
||||
|
||||
JSON-RPC error responses:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"error": {
|
||||
"code": -32600,
|
||||
"message": "Invalid request",
|
||||
"data": {
|
||||
"details": "Missing required parameter: query"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Error codes:
|
||||
- `-32700`: Parse error
|
||||
- `-32600`: Invalid request
|
||||
- `-32601`: Method not found
|
||||
- `-32602`: Invalid params
|
||||
- `-32603`: Internal error
|
||||
|
||||
### Rust Errors
|
||||
|
||||
`KbError` enum with thiserror:
|
||||
|
||||
```rust
|
||||
pub enum KbError {
|
||||
#[error("Storage error: {0}")]
|
||||
Storage(String),
|
||||
|
||||
#[error("Node not found: {0}")]
|
||||
NodeNotFound(String),
|
||||
|
||||
#[error("Invalid configuration: {0}")]
|
||||
Config(String),
|
||||
|
||||
#[error("Parse error: {0}")]
|
||||
Parse(String),
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
MCP server has no rate limiting for local stdio use.
|
||||
|
||||
For production deployments with remote access, configure rate limits:
|
||||
|
||||
```nickel
|
||||
mcp = {
|
||||
rate_limit = {
|
||||
enabled = true,
|
||||
requests_per_minute = 60,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
API follows semantic versioning:
|
||||
|
||||
- **Major version**: Breaking changes to API
|
||||
- **Minor version**: New features, backward compatible
|
||||
- **Patch version**: Bug fixes, backward compatible
|
||||
|
||||
Current version: `1.0.0`
|
||||
|
||||
## Deprecation Policy
|
||||
|
||||
- Deprecated features marked in documentation
|
||||
- Minimum 1 major version before removal
|
||||
- Migration guide provided
|
||||
|
||||
## Support
|
||||
|
||||
- File issues on GitHub
|
||||
- Check documentation updates
|
||||
- Join community discussions
|
||||
|
||||
## See Also
|
||||
|
||||
- [MCP Specification](https://modelcontextprotocol.io/docs)
|
||||
- [kogral-core Documentation](../../crates/kogral-core/README.md)
|
||||
- [Configuration Reference](../user-guide/configuration.md)
|
||||
1
docs/api/mcp-protocol.md
Normal file
@ -0,0 +1 @@
|
||||
# MCP Protocol
|
||||
1
docs/api/mcp-resources.md
Normal file
@ -0,0 +1 @@
|
||||
# Resources
|
||||
902
docs/api/mcp-tools.md
Normal file
@ -0,0 +1,902 @@
|
||||
# MCP Tools API Reference
|
||||
|
||||
Complete reference for the Model Context Protocol (MCP) server tools and resources.
|
||||
|
||||
## Overview
|
||||
|
||||
The kogral-mcp server implements the MCP protocol (JSON-RPC 2.0) for Claude Code integration. It provides:
|
||||
|
||||
- **10 Tools**: Operations for querying and modifying knowledge base (7 core + 3 block tools)
|
||||
- **6 Resources**: Access to knowledge graph content via URIs
|
||||
- **2 Prompts**: Guided workflows for common tasks
|
||||
|
||||
## Server Configuration
|
||||
|
||||
### Start MCP Server
|
||||
|
||||
```bash
|
||||
# Stdio transport (local use)
|
||||
kogral serve
|
||||
|
||||
# Or run directly
|
||||
kogral-mcp serve
|
||||
```
|
||||
|
||||
### Claude Code Configuration
|
||||
|
||||
Add to `~/.config/claude/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"kogral-mcp": {
|
||||
"command": "/path/to/kogral-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"KOGRAL_DIR": "/path/to/project/.kogral"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
### kogral/search
|
||||
|
||||
Search the knowledge base using text and/or semantic similarity.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Search query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["note", "decision", "guideline", "pattern", "journal", "execution", "all"],
|
||||
"description": "Filter by node type",
|
||||
"default": "all"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Limit search to specific project graph"
|
||||
},
|
||||
"semantic": {
|
||||
"type": "boolean",
|
||||
"description": "Enable semantic similarity search",
|
||||
"default": true
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number",
|
||||
"description": "Minimum similarity threshold (0-1)",
|
||||
"default": 0.4
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of results",
|
||||
"default": 10
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "kogral/search",
|
||||
"params": {
|
||||
"query": "error handling patterns in Rust",
|
||||
"type": "pattern",
|
||||
"semantic": true,
|
||||
"threshold": 0.6,
|
||||
"limit": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 3 result(s):\n\n1. Error Handling with thiserror (pattern, score: 0.85)\n Tags: rust, error-handling\n Created: 2026-01-15\n \n2. Result Type Best Practices (guideline, score: 0.72)\n Tags: rust, error-handling, best-practices\n Created: 2026-01-10\n\n3. Custom Error Types (note, score: 0.65)\n Tags: rust, error-handling\n Created: 2026-01-08"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### kogral/add_note
|
||||
|
||||
Add a new note to the knowledge base.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Note title"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Note content (markdown)"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Tags for categorization",
|
||||
"default": []
|
||||
},
|
||||
"relates_to": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Related node IDs",
|
||||
"default": []
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Project graph name",
|
||||
"default": "default"
|
||||
}
|
||||
},
|
||||
"required": ["title", "content"]
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "kogral/add_note",
|
||||
"params": {
|
||||
"title": "Async Trait Patterns",
|
||||
"content": "Common patterns for using async traits in Rust:\n\n1. Use `async-trait` crate\n2. Box return types for flexibility\n3. Consider Send + Sync bounds",
|
||||
"tags": ["rust", "async", "patterns"],
|
||||
"relates_to": ["pattern-error-handling"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Note added successfully: note-async-trait-patterns (ID: note-abc123)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### kogral/add_decision
|
||||
|
||||
Create an Architectural Decision Record (ADR).
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Decision title"
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"description": "Decision context and background"
|
||||
},
|
||||
"decision": {
|
||||
"type": "string",
|
||||
"description": "The decision made"
|
||||
},
|
||||
"consequences": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "List of consequences",
|
||||
"default": []
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["proposed", "accepted", "rejected", "deprecated", "superseded"],
|
||||
"default": "proposed"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"required": ["title", "context", "decision"]
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "kogral/add_decision",
|
||||
"params": {
|
||||
"title": "Use SurrealDB for Knowledge Graph Storage",
|
||||
"context": "Need a scalable storage solution that supports graph queries and can handle growing knowledge base",
|
||||
"decision": "Adopt SurrealDB as the primary storage backend for production deployments",
|
||||
"consequences": [
|
||||
"Better query performance for graph traversal",
|
||||
"Native support for relationships",
|
||||
"Additional infrastructure dependency",
|
||||
"Team needs to learn SurrealDB query language"
|
||||
],
|
||||
"status": "accepted",
|
||||
"tags": ["architecture", "storage", "surrealdb"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Decision added: decision-use-surrealdb (ID: decision-xyz789)\nStatus: accepted"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### kogral/link
|
||||
|
||||
Create a relationship between two nodes.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"from": {
|
||||
"type": "string",
|
||||
"description": "Source node ID"
|
||||
},
|
||||
"to": {
|
||||
"type": "string",
|
||||
"description": "Target node ID"
|
||||
},
|
||||
"relation": {
|
||||
"type": "string",
|
||||
"enum": ["relates_to", "depends_on", "implements", "extends", "supersedes", "explains"],
|
||||
"description": "Relationship type"
|
||||
},
|
||||
"strength": {
|
||||
"type": "number",
|
||||
"description": "Relationship strength (0-1)",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"default": 1.0
|
||||
}
|
||||
},
|
||||
"required": ["from", "to", "relation"]
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"method": "kogral/link",
|
||||
"params": {
|
||||
"from": "note-async-trait-patterns",
|
||||
"to": "pattern-error-handling",
|
||||
"relation": "relates_to",
|
||||
"strength": 0.8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Link created: note-async-trait-patterns --[relates_to]--> pattern-error-handling (strength: 0.8)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Relationship Types**:
|
||||
|
||||
- `relates_to`: General conceptual relationship
|
||||
- `depends_on`: Dependency (from depends on to)
|
||||
- `implements`: Implementation of concept/pattern
|
||||
- `extends`: Inherits or extends another node
|
||||
- `supersedes`: Replaces an older version
|
||||
- `explains`: Provides documentation/clarification
|
||||
|
||||
### kogral/get_guidelines
|
||||
|
||||
Retrieve guidelines for current project with inheritance resolution.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "Programming language (e.g., rust, nushell)"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Guideline category (e.g., error-handling, testing)"
|
||||
},
|
||||
"include_base": {
|
||||
"type": "boolean",
|
||||
"description": "Include shared/base guidelines",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "kogral/get_guidelines",
|
||||
"params": {
|
||||
"language": "rust",
|
||||
"category": "error-handling",
|
||||
"include_base": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Guidelines for rust/error-handling:\n\n## Project Guidelines (priority: 150)\n\n1. Custom Error Types (guideline-custom-errors)\n - Use thiserror for error definitions\n - Implement From traits for conversions\n - Source: .kogral/guidelines/rust-errors.md\n\n## Shared Guidelines (priority: 50)\n\n1. Result Type Best Practices (guideline-result-best-practices)\n - Always use Result<T> for fallible operations\n - Never use unwrap() in production\n - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md\n\n2. Error Propagation (guideline-error-propagation)\n - Use ? operator for error propagation\n - Add context with .context()\n - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### kogral/list_graphs
|
||||
|
||||
List available knowledge graphs.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"method": "kogral/list_graphs"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Available graphs:\n\n- default (Current project)\n Path: /path/to/project/.kogral\n Nodes: 42\n Last modified: 2026-01-17T10:30:00Z\n\n- shared (Shared guidelines)\n Path: ~/Tools/.kogral-shared\n Nodes: 156\n Last modified: 2026-01-15T14:20:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### kogral/export
|
||||
|
||||
Export knowledge base to various formats.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["logseq", "json", "markdown"],
|
||||
"description": "Export format"
|
||||
},
|
||||
"output_path": {
|
||||
"type": "string",
|
||||
"description": "Output file or directory path"
|
||||
},
|
||||
"include_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["note", "decision", "guideline", "pattern", "journal", "execution"]
|
||||
},
|
||||
"description": "Node types to include",
|
||||
"default": ["note", "decision", "guideline", "pattern"]
|
||||
},
|
||||
"skip_journals": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"required": ["format", "output_path"]
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"method": "kogral/export",
|
||||
"params": {
|
||||
"format": "logseq",
|
||||
"output_path": "/Users/akasha/logseq-graph",
|
||||
"include_types": ["note", "decision", "guideline"],
|
||||
"skip_journals": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Export completed:\n\nFormat: Logseq\nOutput: /Users/akasha/logseq-graph\nExported: 42 nodes\n - 23 notes\n - 12 decisions\n - 7 guidelines\n\nLogseq graph ready to open."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Block Tools
|
||||
|
||||
Tools for querying Logseq content blocks. Requires `blocks.enable_mcp_tools = true` in configuration.
|
||||
|
||||
### kogral/find_blocks
|
||||
|
||||
Find blocks by tag, task status, or custom property across the knowledge base.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "Find blocks with this tag (e.g., 'card', 'important')"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["TODO", "DOING", "DONE", "LATER", "NOW", "WAITING", "CANCELLED"],
|
||||
"description": "Find blocks with this task status"
|
||||
},
|
||||
"property_key": {
|
||||
"type": "string",
|
||||
"description": "Custom property key to search for"
|
||||
},
|
||||
"property_value": {
|
||||
"type": "string",
|
||||
"description": "Custom property value to match"
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of results",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: Provide one of: `tag`, `status`, or both `property_key` and `property_value`.
|
||||
|
||||
**Example Request** (find blocks by tag):
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"method": "kogral/find_blocks",
|
||||
"params": {
|
||||
"tag": "high-priority",
|
||||
"limit": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 3 blocks with tag '#high-priority':\n\n**Project Tasks** (project-tasks-123)\n - TODO Implement authentication #high-priority\n - TODO Fix security vulnerability #high-priority\n\n**Sprint Planning** (sprint-planning-456)\n - DOING Refactor database layer #high-priority"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request** (find blocks by property):
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 9,
|
||||
"method": "kogral/find_blocks",
|
||||
"params": {
|
||||
"property_key": "priority",
|
||||
"property_value": "high",
|
||||
"limit": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### kogral/find_todos
|
||||
|
||||
Find all TODO blocks across the knowledge base.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of results",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 10,
|
||||
"method": "kogral/find_todos",
|
||||
"params": {
|
||||
"limit": 25
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 10,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 8 TODO blocks:\n\n**Project Tasks** (project-tasks-123)\n - TODO Implement authentication\n - TODO Write integration tests\n - TODO Update documentation\n\n**Bug Fixes** (bug-fixes-456)\n - TODO Fix race condition in cache\n - TODO Address memory leak\n\n**Research** (research-789)\n - TODO Evaluate GraphQL alternatives\n - TODO Benchmark new approach\n - TODO Document findings"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### kogral/find_cards
|
||||
|
||||
Find all flashcard blocks (blocks tagged with #card) for spaced repetition learning.
|
||||
|
||||
**Input Schema**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of flashcards",
|
||||
"default": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 11,
|
||||
"method": "kogral/find_cards",
|
||||
"params": {
|
||||
"limit": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 11,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 3 flashcards:\n\n**Rust Learning** (rust-learning-123)\n - What is Rust's ownership model? #card #rust\n - Ownership prevents data races at compile time\n - Each value has a single owner\n\n**System Design** (system-design-456)\n - What is the CAP theorem? #card #distributed-systems\n - Consistency, Availability, Partition tolerance\n - Can only guarantee 2 of 3\n\n**Algorithms** (algorithms-789)\n - What is the time complexity of quicksort? #card #algorithms\n - Average: O(n log n)\n - Worst case: O(n²)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use Cases**:
|
||||
- **kogral/find_blocks**: General block search by metadata
|
||||
- **kogral/find_todos**: Task management and tracking
|
||||
- **kogral/find_cards**: Spaced repetition learning system
|
||||
|
||||
**See Also**:
|
||||
- [Logseq Blocks Support](../kogral/core-concepts.md#logseq-content-blocks)
|
||||
- [ADR-004: Logseq Blocks Support](../architecture/adrs/004-logseq-blocks-support.md)
|
||||
|
||||
## Resources
|
||||
|
||||
Resources provide read-only access to knowledge graph content via URIs.
|
||||
|
||||
### kogral://project/notes
|
||||
|
||||
Access project notes.
|
||||
|
||||
**URI**: `kogral://project/notes` or `kogral://project/notes/{note-id}`
|
||||
|
||||
**Example**: Read all project notes
|
||||
|
||||
```text
|
||||
kogral://project/notes
|
||||
```
|
||||
|
||||
**Example**: Read specific note
|
||||
|
||||
```text
|
||||
kogral://project/notes/async-trait-patterns
|
||||
```
|
||||
|
||||
### kogral://project/decisions
|
||||
|
||||
Access project decisions (ADRs).
|
||||
|
||||
**URI**: `kogral://project/decisions` or `kogral://project/decisions/{decision-id}`
|
||||
|
||||
### kogral://project/guidelines
|
||||
|
||||
Access project-specific guidelines.
|
||||
|
||||
**URI**: `kogral://project/guidelines` or `kogral://project/guidelines/{guideline-id}`
|
||||
|
||||
### kogral://project/patterns
|
||||
|
||||
Access project patterns.
|
||||
|
||||
**URI**: `kogral://project/patterns` or `kogral://project/patterns/{pattern-id}`
|
||||
|
||||
### kogral://shared/guidelines
|
||||
|
||||
Access shared guidelines (inherited).
|
||||
|
||||
**URI**: `kogral://shared/guidelines` or `kogral://shared/guidelines/{guideline-id}`
|
||||
|
||||
### kogral://shared/patterns
|
||||
|
||||
Access shared patterns (inherited).
|
||||
|
||||
**URI**: `kogral://shared/patterns` or `kogral://shared/patterns/{pattern-id}`
|
||||
|
||||
## Prompts
|
||||
|
||||
Prompts provide guided workflows for common tasks.
|
||||
|
||||
### kogral/summarize_project
|
||||
|
||||
Generate a comprehensive project knowledge summary.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
```json
|
||||
{
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Project graph name",
|
||||
"default": "default"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"method": "kogral/summarize_project",
|
||||
"params": {
|
||||
"project": "default"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Returns**: Prompt messages with project summary including:
|
||||
- Total node counts by type
|
||||
- Recent additions
|
||||
- Top tags
|
||||
- Key decisions
|
||||
- Active patterns
|
||||
|
||||
### kogral/find_related
|
||||
|
||||
Find nodes related to a specific topic or node.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
```json
|
||||
{
|
||||
"node_id": {
|
||||
"type": "string",
|
||||
"description": "Node ID to find relations for"
|
||||
},
|
||||
"depth": {
|
||||
"type": "integer",
|
||||
"description": "Maximum traversal depth",
|
||||
"default": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 9,
|
||||
"method": "kogral/find_related",
|
||||
"params": {
|
||||
"node_id": "pattern-error-handling",
|
||||
"depth": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Returns**: Prompt messages with:
|
||||
- Direct relationships
|
||||
- Indirect relationships (depth 2+)
|
||||
- Common tags
|
||||
- Related guidelines
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error Codes
|
||||
|
||||
Standard JSON-RPC 2.0 error codes:
|
||||
|
||||
| Code | Meaning | Description |
|
||||
| ------ | --------- | ------------- |
|
||||
| -32700 | Parse error | Invalid JSON |
|
||||
| -32600 | Invalid Request | Missing required fields |
|
||||
| -32601 | Method not found | Unknown tool/resource |
|
||||
| -32602 | Invalid params | Parameter validation failed |
|
||||
| -32603 | Internal error | Server-side error |
|
||||
|
||||
### Example Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"error": {
|
||||
"code": -32602,
|
||||
"message": "Invalid params",
|
||||
"data": {
|
||||
"details": "Field 'query' is required but missing",
|
||||
"field": "query"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Semantic Search for Discovery
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "kogral/search",
|
||||
"params": {
|
||||
"query": "how to handle database connection errors",
|
||||
"semantic": true,
|
||||
"threshold": 0.5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Link Related Concepts
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "kogral/link",
|
||||
"params": {
|
||||
"from": "note-new-discovery",
|
||||
"to": "pattern-related-pattern",
|
||||
"relation": "implements"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Query Guidelines Before Implementation
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "kogral/get_guidelines",
|
||||
"params": {
|
||||
"language": "rust",
|
||||
"category": "testing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Document Decisions with ADRs
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "kogral/add_decision",
|
||||
"params": {
|
||||
"title": "Use X for Y",
|
||||
"context": "Background...",
|
||||
"decision": "We will...",
|
||||
"consequences": ["Pro 1", "Con 1"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [MCP Specification](https://modelcontextprotocol.io/docs)
|
||||
- [Quick Start Guide](../user-guide/quickstart.md)
|
||||
- [Configuration Reference](../user-guide/configuration.md)
|
||||
- [kogral-mcp Source Code](../../crates/kogral-mcp/src/)
|
||||
1
docs/api/rust-api.md
Normal file
@ -0,0 +1 @@
|
||||
# Rust API
|
||||
1
docs/apps/claude-code.md
Normal file
@ -0,0 +1 @@
|
||||
# Claude Code Integration
|
||||
1
docs/apps/git.md
Normal file
@ -0,0 +1 @@
|
||||
# Git Workflows
|
||||
1
docs/apps/logseq.md
Normal file
@ -0,0 +1 @@
|
||||
# Logseq Integration
|
||||
488
docs/apps/mcp-quickguide.md
Normal file
@ -0,0 +1,488 @@
|
||||
# MCP Quick Guide
|
||||
|
||||
Fast-track guide to integrating KOGRAL with Claude Code via the Model Context Protocol (MCP).
|
||||
|
||||
## What is MCP
|
||||
|
||||
MCP (Model Context Protocol) is a protocol that allows Claude Code to interact with external tools and data sources. The kogral-mcp server exposes your KOGRAL knowledge base to Claude Code for AI-assisted knowledge management.
|
||||
|
||||
## Quick Setup (5 Minutes)
|
||||
|
||||
### Step 1: Build MCP Server
|
||||
|
||||
```bash
|
||||
# Build kogral-mcp server
|
||||
cargo build --package kogral-mcp --release
|
||||
|
||||
# Verify binary
|
||||
ls -lh target/release/kogral-mcp
|
||||
```
|
||||
|
||||
### Step 2: Configure Claude Code
|
||||
|
||||
Add to `~/.config/claude/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"kogral-mcp": {
|
||||
"command": "/path/to/knowledge-base/target/release/kogral-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"KOGRAL_DIR": "/path/to/your/project/.kogral"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace `/path/to/knowledge-base` and `/path/to/your/project/.kogral` with your actual paths.
|
||||
|
||||
### Step 3: Initialize KOGRAL
|
||||
|
||||
```bash
|
||||
# Navigate to your project
|
||||
cd /path/to/your/project
|
||||
|
||||
# Initialize .kogral directory
|
||||
kogral init
|
||||
```
|
||||
|
||||
### Step 4: Start Claude Code
|
||||
|
||||
```bash
|
||||
# Start Claude Code (will auto-connect to kogral-mcp)
|
||||
claude-code
|
||||
```
|
||||
|
||||
### Step 5: Test Connection
|
||||
|
||||
In Claude Code, try:
|
||||
|
||||
```text
|
||||
Search for "error handling"
|
||||
```
|
||||
|
||||
Claude will use the `kogral/search` tool to query your knowledge base.
|
||||
|
||||
---
|
||||
|
||||
## Essential Commands
|
||||
|
||||
### Search Knowledge Base
|
||||
|
||||
**Natural language**:
|
||||
|
||||
```text
|
||||
Find notes about Rust error handling
|
||||
```
|
||||
|
||||
**Claude translates to**:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "kogral/search",
|
||||
"params": {
|
||||
"query": "error handling",
|
||||
"type": "note",
|
||||
"semantic": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add Note
|
||||
|
||||
**Natural language**:
|
||||
|
||||
```text
|
||||
Add a note about async Rust patterns with tags rust, async, patterns
|
||||
```
|
||||
|
||||
**Claude translates to**:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "kogral/add_note",
|
||||
"params": {
|
||||
"title": "Async Rust Patterns",
|
||||
"content": "...",
|
||||
"tags": ["rust", "async", "patterns"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add Decision
|
||||
|
||||
**Natural language**:
|
||||
|
||||
```text
|
||||
Document decision to use SurrealDB for storage
|
||||
```
|
||||
|
||||
**Claude translates to**:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "kogral/add_decision",
|
||||
"params": {
|
||||
"title": "Use SurrealDB for Storage",
|
||||
"context": "Need scalable graph database",
|
||||
"decision": "Adopt SurrealDB as primary storage backend",
|
||||
"consequences": ["Better query performance", "Additional infrastructure"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get Guidelines
|
||||
|
||||
**Natural language**:
|
||||
|
||||
```text
|
||||
What are our Rust error handling guidelines?
|
||||
```
|
||||
|
||||
**Claude translates to**:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "kogral/get_guidelines",
|
||||
"params": {
|
||||
"language": "rust",
|
||||
"category": "error-handling"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Link Concepts
|
||||
|
||||
**Natural language**:
|
||||
|
||||
```text
|
||||
Link note-async-patterns to decision-use-tokio as implements
|
||||
```
|
||||
|
||||
**Claude translates to**:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "kogral/link",
|
||||
"params": {
|
||||
"from": "note-async-patterns",
|
||||
"to": "decision-use-tokio",
|
||||
"relation": "implements"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Capture Meeting Notes
|
||||
|
||||
**You**:
|
||||
|
||||
```text
|
||||
We just decided in our meeting to migrate from REST to GraphQL.
|
||||
Context: current REST API is too chatty, requires multiple round trips.
|
||||
Consequences: better client performance, but team needs to learn GraphQL.
|
||||
Create a decision record.
|
||||
```
|
||||
|
||||
**Claude** (uses `kogral/add_decision`):
|
||||
|
||||
```text
|
||||
Created decision: decision-migrate-to-graphql
|
||||
|
||||
Status: proposed
|
||||
Context: Current REST API requires multiple round trips (chatty)
|
||||
Decision: Migrate from REST to GraphQL API
|
||||
Consequences:
|
||||
- ✅ Better client performance
|
||||
- ❌ Team learning curve for GraphQL
|
||||
```
|
||||
|
||||
### Find Related Knowledge
|
||||
|
||||
**You**:
|
||||
|
||||
```text
|
||||
I'm working on error handling. What related notes and patterns do we have?
|
||||
```
|
||||
|
||||
**Claude** (uses `kogral/search` with semantic=true):
|
||||
|
||||
```text
|
||||
Found 5 related items:
|
||||
|
||||
1. **Error Handling with thiserror** (pattern)
|
||||
Tags: rust, error-handling
|
||||
Created: 2026-01-15
|
||||
|
||||
2. **Result Type Best Practices** (guideline)
|
||||
Tags: rust, error-handling, best-practices
|
||||
Created: 2026-01-10
|
||||
|
||||
3. **Decision: Use anyhow for Application Errors** (decision)
|
||||
Status: accepted
|
||||
Created: 2026-01-08
|
||||
|
||||
4. **Custom Error Types** (note)
|
||||
Tags: rust, error-handling
|
||||
Created: 2026-01-05
|
||||
|
||||
5. **Error Propagation Patterns** (pattern)
|
||||
Tags: rust, error-handling
|
||||
Created: 2026-01-03
|
||||
|
||||
Would you like me to link your current work to any of these?
|
||||
```
|
||||
|
||||
### Document Implementation
|
||||
|
||||
**You**:
|
||||
|
||||
```text
|
||||
I just implemented the user authentication module following our security guidelines.
|
||||
Add a note and link it to our security guideline.
|
||||
```
|
||||
|
||||
**Claude** (uses `kogral/add_note` + `kogral/link`):
|
||||
|
||||
```text
|
||||
Created note: note-user-auth-implementation
|
||||
Tags: security, authentication, implementation
|
||||
|
||||
Linked:
|
||||
note-user-auth-implementation --[implements]--> guideline-security-practices
|
||||
|
||||
Done! Your implementation is now documented and connected to our guidelines.
|
||||
```
|
||||
|
||||
### Review Project Knowledge
|
||||
|
||||
**You**:
|
||||
|
||||
```text
|
||||
Summarize our project knowledge: what decisions have we made, what patterns are we using?
|
||||
```
|
||||
|
||||
**Claude** (uses `kogral/summarize_project` prompt):
|
||||
|
||||
```text
|
||||
# Project Knowledge Summary
|
||||
|
||||
**Total Nodes**: 42
|
||||
|
||||
## Recent Decisions (5)
|
||||
- Use SurrealDB for Storage (accepted, 2 days ago)
|
||||
- Adopt MCP for AI Integration (accepted, 5 days ago)
|
||||
- Nickel for Configuration (accepted, 1 week ago)
|
||||
|
||||
## Active Patterns (8)
|
||||
- Error Handling with thiserror
|
||||
- Repository Pattern for DB
|
||||
- Builder Pattern for Complex Objects
|
||||
- ...
|
||||
|
||||
## Top Tags
|
||||
1. rust (23 nodes)
|
||||
2. architecture (12 nodes)
|
||||
3. error-handling (8 nodes)
|
||||
4. patterns (7 nodes)
|
||||
|
||||
## Guideline Coverage
|
||||
- Rust: 5 guidelines
|
||||
- Testing: 3 guidelines
|
||||
- Security: 2 guidelines
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Conditional Queries
|
||||
|
||||
**You**:
|
||||
|
||||
```text
|
||||
Find all decisions made in the last week that are still proposed (not accepted)
|
||||
```
|
||||
|
||||
**Claude** (combines `kogral/search` with filtering):
|
||||
|
||||
```text
|
||||
Found 2 proposed decisions from last week:
|
||||
|
||||
1. **Migrate to GraphQL** (proposed)
|
||||
Created: 3 days ago
|
||||
Context: REST API is too chatty...
|
||||
|
||||
2. **Add Redis Caching** (proposed)
|
||||
Created: 5 days ago
|
||||
Context: Database queries are slow...
|
||||
|
||||
Would you like to review these and update their status?
|
||||
```
|
||||
|
||||
### Cross-Reference Check
|
||||
|
||||
**You**:
|
||||
|
||||
```text
|
||||
Before I implement X, check if we have any decisions or guidelines about it
|
||||
```
|
||||
|
||||
**Claude** (uses `kogral/search` + `kogral/get_guidelines`):
|
||||
|
||||
```text
|
||||
Checking existing knowledge about X...
|
||||
|
||||
**Decisions**: None found
|
||||
**Guidelines**: 1 relevant guideline found
|
||||
- guideline-module-structure: "Keep modules focused and small"
|
||||
|
||||
**Related Notes**: 2 notes mention similar topics
|
||||
- note-module-design-patterns
|
||||
- note-separation-of-concerns
|
||||
|
||||
Recommendation: Your implementation of X looks aligned with our guidelines.
|
||||
Consider linking it to guideline-module-structure when done.
|
||||
```
|
||||
|
||||
### Export for Sharing
|
||||
|
||||
**You**:
|
||||
|
||||
```text
|
||||
Export our architectural decisions to Logseq so I can review them visually
|
||||
```
|
||||
|
||||
**Claude** (uses `kogral/export`):
|
||||
|
||||
```text
|
||||
Exporting to Logseq format...
|
||||
|
||||
✓ Exported 12 decisions to /Users/akasha/logseq-export/
|
||||
- 12 decision records
|
||||
- Preserved wikilinks
|
||||
- Added Logseq properties
|
||||
|
||||
You can now open /Users/akasha/logseq-export/ in Logseq to visualize the decision graph.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
### 1. Use Natural Language
|
||||
|
||||
Don't worry about exact tool syntax. Claude understands:
|
||||
|
||||
❌ **Don't say**: "Use kogral/search with query='rust' and type='pattern'"
|
||||
✅ **Do say**: "Find Rust patterns in KOGRAL"
|
||||
|
||||
### 2. Be Specific with Tags
|
||||
|
||||
When adding notes, use consistent tags:
|
||||
|
||||
✅ **Good**: `tags: rust, error-handling, pattern`
|
||||
❌ **Bad**: `tags: Rust, ErrorHandling, patterns`
|
||||
|
||||
### 3. Link as You Go
|
||||
|
||||
After creating notes, ask Claude to link them:
|
||||
|
||||
```text
|
||||
Link this note to our error handling guideline as 'implements'
|
||||
```
|
||||
|
||||
### 4. Review Regularly
|
||||
|
||||
Ask Claude for summaries:
|
||||
|
||||
```text
|
||||
What have we documented this week?
|
||||
```
|
||||
|
||||
### 5. Use Semantic Search
|
||||
|
||||
For conceptual queries:
|
||||
|
||||
```text
|
||||
Find anything related to "making code maintainable"
|
||||
```
|
||||
|
||||
Not just keyword "maintainable", but concepts like refactoring, clean code, patterns, etc.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "MCP server not responding"
|
||||
|
||||
```bash
|
||||
# Check kogral-mcp is built
|
||||
ls target/release/kogral-mcp
|
||||
|
||||
# Test manually
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"kogral/search","params":{"query":"test"}}' \
|
||||
| target/release/kogral-mcp serve
|
||||
```
|
||||
|
||||
### "KB directory not found"
|
||||
|
||||
```bash
|
||||
# Verify .kogral exists
|
||||
ls -la /path/to/project/.kogral
|
||||
|
||||
# Initialize if missing
|
||||
cd /path/to/project
|
||||
kogral init
|
||||
```
|
||||
|
||||
### "Permission denied"
|
||||
|
||||
```bash
|
||||
# Make binary executable
|
||||
chmod +x target/release/kogral-mcp
|
||||
|
||||
# Check environment variable
|
||||
echo $KOGRAL_DIR
|
||||
```
|
||||
|
||||
### "Empty search results"
|
||||
|
||||
```bash
|
||||
# Add some test content
|
||||
kogral add note "Test Note" --content "Test content"
|
||||
|
||||
# Try search again in Claude Code
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **Read**: [MCP Tools API Reference](../api/mcp-tools.md) for all available tools
|
||||
- **Explore**: [Use Cases](../guides/use-cases.md) for more examples
|
||||
- **Configure**: [Configuration Reference](../config/overview.md) to customize behavior
|
||||
- **Integrate**: [Claude Code Integration](claude-code.md) for advanced setup
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
| Task | Say to Claude |
|
||||
| ------ | --------------- |
|
||||
| Search | "Find notes about X" |
|
||||
| Add note | "Add a note about X with tags Y, Z" |
|
||||
| Add decision | "Document decision to use X for Y" |
|
||||
| Get guidelines | "What are our X guidelines?" |
|
||||
| Link nodes | "Link A to B as implements" |
|
||||
| Summarize | "Summarize project knowledge" |
|
||||
| Export | "Export to Logseq format" |
|
||||
|
||||
---
|
||||
|
||||
**Remember**: Claude Code with MCP turns KOGRAL into an active participant in your development workflow. Ask questions, capture decisions, and let AI help you maintain your project's knowledge graph.
|
||||
1
docs/apps/obsidian.md
Normal file
@ -0,0 +1 @@
|
||||
# Obsidian Compatibility
|
||||
313
docs/architecture/adrs/001-nickel-vs-toml.md
Normal file
@ -0,0 +1,313 @@
|
||||
# ADR-001: Nickel vs TOML for Configuration
|
||||
|
||||
**Status**: Accepted
|
||||
|
||||
**Date**: 2026-01-17
|
||||
|
||||
**Deciders**: Architecture Team
|
||||
|
||||
**Context**: Configuration Strategy for Knowledge Base System
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The KOGRAL requires a flexible, type-safe configuration format that supports:
|
||||
|
||||
1. **Complex nested structures** (graph settings, storage configs, embedding providers)
|
||||
2. **Type validation** (prevent runtime errors from config mistakes)
|
||||
3. **Composition and inheritance** (shared configs, environment-specific overrides)
|
||||
4. **Documentation** (self-documenting schemas)
|
||||
5. **Validation before runtime** (catch errors early)
|
||||
|
||||
We evaluated two primary options:
|
||||
|
||||
### Option 1: TOML (Traditional Config Format)
|
||||
|
||||
**Pros**:
|
||||
- Widely adopted in Rust ecosystem (`Cargo.toml`)
|
||||
- Simple, human-readable syntax
|
||||
- Native `serde` support
|
||||
- IDE support (syntax highlighting, completion)
|
||||
|
||||
**Cons**:
|
||||
- No type system (validation only at runtime)
|
||||
- Limited composition (no imports, no functions)
|
||||
- No schema validation (errors discovered during execution)
|
||||
- Verbose for complex nested structures
|
||||
- No documentation in config files
|
||||
|
||||
**Example TOML**:
|
||||
|
||||
```toml
|
||||
[graph]
|
||||
name = "my-project"
|
||||
version = "1.0.0"
|
||||
|
||||
[storage]
|
||||
primary = "filesystem" # String, not validated as enum
|
||||
|
||||
[storage.secondary]
|
||||
enabled = true
|
||||
type = "surrealdb" # Typo would fail at runtime
|
||||
url = "ws://localhost:8000"
|
||||
|
||||
[embeddings]
|
||||
enabled = true
|
||||
provider = "openai" # No validation of valid providers
|
||||
model = "text-embedding-3-small"
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Typos in enum values (`"surrealdb"` vs `"surealdb"`) fail at runtime
|
||||
- No validation that `provider = "openai"` requires `api_key_env`
|
||||
- No documentation of valid options
|
||||
- No way to compose configs (e.g., base config + environment override)
|
||||
|
||||
### Option 2: Nickel (Functional Configuration Language)
|
||||
|
||||
**Pros**:
|
||||
- **Type system** with contracts (validate before runtime)
|
||||
- **Composition** via imports and merging
|
||||
- **Documentation** in schemas (self-documenting)
|
||||
- **Validation** at export time (catch errors early)
|
||||
- **Functions** for conditional logic
|
||||
- **Default values** in schema definitions
|
||||
|
||||
**Cons**:
|
||||
- Less familiar to Rust developers
|
||||
- Requires separate `nickel` CLI tool
|
||||
- Smaller ecosystem
|
||||
- Steeper learning curve
|
||||
|
||||
**Example Nickel**:
|
||||
|
||||
```nickel
|
||||
# schemas/kogral-config.ncl
|
||||
{
|
||||
KbConfig = {
|
||||
graph | GraphConfig,
|
||||
storage | StorageConfig,
|
||||
embeddings | EmbeddingConfig,
|
||||
},
|
||||
|
||||
StorageConfig = {
|
||||
primary | [| 'filesystem, 'memory |], # Enum validated at export
|
||||
|
||||
secondary | {
|
||||
enabled | Bool,
|
||||
type | [| 'surrealdb, 'sqlite |], # Typos caught immediately
|
||||
url | String,
|
||||
} | optional,
|
||||
},
|
||||
|
||||
EmbeddingConfig = {
|
||||
enabled | Bool,
|
||||
provider | [| 'openai, 'claude, 'fastembed |], # Valid providers enforced
|
||||
model | String,
|
||||
api_key_env | String | doc "Environment variable for API key",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Typos in enum values caught at `nickel export` time
|
||||
- Schema enforces required fields based on provider
|
||||
- Documentation embedded in schema
|
||||
- Config can be composed: `import "base.ncl" & { /* overrides */ }`
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
**We will use Nickel for configuration.**
|
||||
|
||||
**Implementation**:
|
||||
|
||||
1. Define schemas in `schemas/*.ncl` with type contracts
|
||||
2. Users write configs in `.kogral/config.ncl`
|
||||
3. Export to JSON via CLI: `nickel export --format json config.ncl`
|
||||
4. Load JSON in Rust via `serde_json` into typed structs
|
||||
|
||||
**Pattern** (double validation):
|
||||
|
||||
```text
|
||||
Nickel Config (.ncl)
|
||||
↓ [nickel export]
|
||||
JSON (validated by Nickel contracts)
|
||||
↓ [serde_json::from_str]
|
||||
Rust Struct (validated by serde)
|
||||
↓
|
||||
Runtime (guaranteed valid config)
|
||||
```
|
||||
|
||||
**Bridge Code** (`kogral-core/src/config/nickel.rs`):
|
||||
|
||||
```rust
|
||||
pub fn load_config<P: AsRef<Path>>(path: P) -> Result<KbConfig> {
|
||||
// Export Nickel to JSON
|
||||
let json = export_nickel_to_json(path)?;
|
||||
|
||||
// Deserialize to Rust struct
|
||||
let config: KbConfig = serde_json::from_str(&json)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn export_nickel_to_json<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||
let output = Command::new("nickel")
|
||||
.arg("export")
|
||||
.arg("--format").arg("json")
|
||||
.arg(path.as_ref())
|
||||
.output()?;
|
||||
|
||||
Ok(String::from_utf8(output.stdout)?)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
✅ **Type Safety**: Config errors caught before runtime
|
||||
- Invalid enum values fail at export: `'filesystm` → error
|
||||
- Missing required fields detected: no `graph.name` → error
|
||||
- Type mismatches prevented: `enabled = "yes"` → error (expects Bool)
|
||||
|
||||
✅ **Self-Documenting**: Schemas serve as documentation
|
||||
- `| doc "Environment variable for API key"` describes fields
|
||||
- Enum options visible in schema: `[| 'openai, 'claude, 'fastembed |]`
|
||||
- Default values explicit: `| default = 'filesystem`
|
||||
|
||||
✅ **Composition**: Config reuse and overrides
|
||||
|
||||
```nickel
|
||||
# base.ncl
|
||||
{ graph = { version = "1.0.0" } }
|
||||
|
||||
# project.ncl
|
||||
import "base.ncl" & { graph = { name = "my-project" } }
|
||||
```
|
||||
|
||||
✅ **Validation Before Deployment**: Catch errors in CI
|
||||
|
||||
```bash
|
||||
# CI pipeline
|
||||
nickel typecheck config.ncl
|
||||
nickel export --format json config.ncl > /dev/null
|
||||
```
|
||||
|
||||
✅ **Conditional Logic**: Environment-specific configs
|
||||
|
||||
```nickel
|
||||
let is_prod = std.string.is_match "prod" (std.env.get "ENV") in
|
||||
{
|
||||
embeddings = {
|
||||
provider = if is_prod then 'openai else 'fastembed,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Negative
|
||||
|
||||
❌ **Learning Curve**: Team must learn Nickel syntax
|
||||
- **Mitigation**: Provide comprehensive examples in `config/` directory
|
||||
- **Mitigation**: Document common patterns in `docs/config/`
|
||||
|
||||
❌ **Tool Dependency**: Requires `nickel` CLI installed
|
||||
- **Mitigation**: Document installation in setup guide
|
||||
- **Mitigation**: Check `nickel` availability in `kogral init` command
|
||||
|
||||
❌ **IDE Support**: Limited compared to TOML
|
||||
- **Mitigation**: Use LSP (nickel-lang-lsp) for VSCode/Neovim
|
||||
- **Mitigation**: Syntax highlighting available for major editors
|
||||
|
||||
❌ **Ecosystem Size**: Smaller than TOML
|
||||
- **Mitigation**: Nickel actively developed by Tweag
|
||||
- **Mitigation**: Stable language specification (v1.0+)
|
||||
|
||||
### Neutral
|
||||
|
||||
⚪ **Two-Stage Loading**: Nickel → JSON → Rust
|
||||
- Not a performance concern (config loaded once at startup)
|
||||
- Adds resilience (double validation)
|
||||
- Allows runtime config inspection (read JSON directly)
|
||||
|
||||
---
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### JSON Schema
|
||||
|
||||
**Rejected**: Not ergonomic for humans to write
|
||||
- No comments
|
||||
- Verbose syntax (`{"key": "value"}` vs `key = value`)
|
||||
- JSON Schema separate from config (duplication)
|
||||
|
||||
### YAML
|
||||
|
||||
**Rejected**: No type system, ambiguous parsing
|
||||
- Boolean confusion: `yes`/`no`/`on`/`off`/`true`/`false`
|
||||
- Indentation-sensitive (error-prone)
|
||||
- No validation without external tools
|
||||
|
||||
### Dhall
|
||||
|
||||
**Rejected**: More complex than needed
|
||||
- Turing-incomplete by design (limits use cases)
|
||||
- Smaller ecosystem than Nickel
|
||||
- Steeper learning curve
|
||||
|
||||
### KCL (KusionStack Configuration Language)
|
||||
|
||||
**Rejected**: Kubernetes-focused, less general-purpose
|
||||
- Designed for K8s manifests
|
||||
- Less mature than Nickel for general config
|
||||
|
||||
---
|
||||
|
||||
## Implementation Timeline
|
||||
|
||||
1. ✅ Define base schemas (`schemas/kogral-config.ncl`)
|
||||
2. ✅ Implement Nickel loader (`kogral-core/src/config/nickel.rs`)
|
||||
3. ✅ Create example configs (`config/defaults.ncl`, `config/production.ncl`)
|
||||
4. ✅ Document Nickel usage (`docs/config/nickel-schemas.md`)
|
||||
5. ⏳ Add LSP recommendations to setup guide
|
||||
6. ⏳ Create Nickel → TOML migration tool (for existing users)
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
**Success Criteria**:
|
||||
- Config errors caught at export time (not runtime)
|
||||
- Users can compose configs for different environments
|
||||
- Team comfortable with Nickel syntax within 2 weeks
|
||||
|
||||
**Metrics**:
|
||||
- Number of config validation errors caught before runtime
|
||||
- Time to diagnose config issues (should decrease)
|
||||
- User feedback on config complexity
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Nickel Language](https://nickel-lang.org/)
|
||||
- [Nickel User Manual](https://nickel-lang.org/user-manual/introduction)
|
||||
- [platform-config pattern](../../crates/kogral-core/src/config/README.md) (reference implementation)
|
||||
- [TOML Specification](https://toml.io/)
|
||||
|
||||
---
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change |
|
||||
| ---------- | ------------------ | ---------------- |
|
||||
| 2026-01-17 | Architecture Team | Initial decision |
|
||||
|
||||
---
|
||||
|
||||
**Next ADR**: [ADR-002: FastEmbed via AI Providers](002-fastembed-ai-providers.md)
|
||||
357
docs/architecture/adrs/002-fastembed-ai-providers.md
Normal file
@ -0,0 +1,357 @@
|
||||
# ADR-002: FastEmbed via AI Providers for Embeddings
|
||||
|
||||
**Status**: Accepted
|
||||
|
||||
**Date**: 2026-01-17
|
||||
|
||||
**Deciders**: Architecture Team
|
||||
|
||||
**Context**: Embedding Strategy for Semantic Search
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The KOGRAL requires embedding generation for semantic search capabilities. Embeddings convert text into numerical vectors that capture semantic meaning, enabling "find concepts" rather than just "find keywords".
|
||||
|
||||
**Requirements**:
|
||||
|
||||
1. **Local-First Option**: Must work offline without external API dependencies
|
||||
2. **Production Scalability**: Support cloud AI providers for large-scale deployments
|
||||
3. **Multiple Providers**: Flexibility to choose based on cost, quality, privacy
|
||||
4. **Cost-Effective Development**: Free local embeddings for development and testing
|
||||
5. **Quality**: Good enough embeddings for finding related concepts
|
||||
|
||||
**Options Evaluated**:
|
||||
|
||||
### Option 1: Only Local Embeddings (fastembed)
|
||||
|
||||
**Pros**:
|
||||
- No API costs
|
||||
- Works offline
|
||||
- Privacy-preserving (no data leaves machine)
|
||||
- Fast (local GPU acceleration possible)
|
||||
|
||||
**Cons**:
|
||||
- Limited model quality compared to cloud providers
|
||||
- Resource-intensive (requires download ~100MB models)
|
||||
- Single provider lock-in (fastembed library)
|
||||
|
||||
**Example**:
|
||||
|
||||
```rust
|
||||
use fastembed::{TextEmbedding, InitOptions};
|
||||
|
||||
let model = TextEmbedding::try_new(InitOptions {
|
||||
model_name: "BAAI/bge-small-en-v1.5",
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
let embeddings = model.embed(vec!["Hello world"], None)?;
|
||||
// Output: Vec<Vec<f32>> with 384 dimensions
|
||||
```
|
||||
|
||||
### Option 2: Only Cloud AI Providers (OpenAI, Claude, etc.)
|
||||
|
||||
**Pros**:
|
||||
- State-of-the-art embedding quality
|
||||
- No local resource usage
|
||||
- Latest models available
|
||||
- Scalable to millions of documents
|
||||
|
||||
**Cons**:
|
||||
- Requires API keys (cost per embedding)
|
||||
- Network dependency (no offline mode)
|
||||
- Privacy concerns (data sent to third parties)
|
||||
- Vendor lock-in risk
|
||||
|
||||
**Example**:
|
||||
|
||||
```rust
|
||||
use rig::providers::openai;
|
||||
|
||||
let client = openai::Client::new("sk-...");
|
||||
let embeddings = client.embeddings("text-embedding-3-small")
|
||||
.embed_documents(vec!["Hello world"]).await?;
|
||||
// Output: Vec<Vec<f32>> with 1536 dimensions
|
||||
```
|
||||
|
||||
### Option 3: Hybrid Strategy (fastembed + AI providers via rig-core)
|
||||
|
||||
**Pros**:
|
||||
- ✅ Best of both worlds: local dev, cloud production
|
||||
- ✅ User choice: privacy-first or quality-first
|
||||
- ✅ Cost flexibility: free for small projects, paid for scale
|
||||
- ✅ Unified interface via `rig-core` library
|
||||
- ✅ Easy provider switching (config-driven)
|
||||
|
||||
**Cons**:
|
||||
- ❌ More complex implementation (multiple providers)
|
||||
- ❌ Dimension mismatch between providers (384 vs 1536)
|
||||
- ❌ Additional dependencies (`rig-core`, `fastembed`)
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
**We will use a hybrid strategy: fastembed (local) + AI providers (via rig-core).**
|
||||
|
||||
**Implementation**:
|
||||
|
||||
1. **Default**: `fastembed` with `BAAI/bge-small-en-v1.5` (384 dimensions)
|
||||
2. **Optional**: OpenAI, Claude, Ollama via `rig-core` (configurable)
|
||||
3. **Interface**: `EmbeddingProvider` trait abstracts provider details
|
||||
4. **Config-Driven**: Provider selection via Nickel configuration
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait EmbeddingProvider: Send + Sync {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
|
||||
fn dimensions(&self) -> usize;
|
||||
fn model_name(&self) -> &str;
|
||||
}
|
||||
|
||||
// Local implementation
|
||||
pub struct FastEmbedProvider {
|
||||
model: TextEmbedding,
|
||||
}
|
||||
|
||||
impl FastEmbedProvider {
|
||||
pub fn new(model_name: &str) -> Result<Self> {
|
||||
let model = TextEmbedding::try_new(InitOptions {
|
||||
model_name: model_name.into(),
|
||||
..Default::default()
|
||||
})?;
|
||||
Ok(Self { model })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EmbeddingProvider for FastEmbedProvider {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
|
||||
Ok(self.model.embed(texts, None)?)
|
||||
}
|
||||
|
||||
fn dimensions(&self) -> usize { 384 }
|
||||
fn model_name(&self) -> &str { "BAAI/bge-small-en-v1.5" }
|
||||
}
|
||||
|
||||
// Cloud provider implementation (via rig-core)
|
||||
pub struct RigEmbeddingProvider {
|
||||
client: rig::Client,
|
||||
model: String,
|
||||
dimensions: usize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EmbeddingProvider for RigEmbeddingProvider {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
|
||||
let embeddings = self.client
|
||||
.embeddings(&self.model)
|
||||
.embed_documents(texts)
|
||||
.await?;
|
||||
Ok(embeddings)
|
||||
}
|
||||
|
||||
fn dimensions(&self) -> usize { self.dimensions }
|
||||
fn model_name(&self) -> &str { &self.model }
|
||||
}
|
||||
```
|
||||
|
||||
**Configuration** (Nickel):
|
||||
|
||||
```nickel
|
||||
# Local development (default)
|
||||
{
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'fastembed,
|
||||
model = "BAAI/bge-small-en-v1.5",
|
||||
dimensions = 384,
|
||||
},
|
||||
}
|
||||
|
||||
# Production with OpenAI
|
||||
{
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'openai,
|
||||
model = "text-embedding-3-small",
|
||||
dimensions = 1536,
|
||||
api_key_env = "OPENAI_API_KEY",
|
||||
},
|
||||
}
|
||||
|
||||
# Self-hosted with Ollama
|
||||
{
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'ollama,
|
||||
model = "nomic-embed-text",
|
||||
dimensions = 768,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Provider Selection** (`kogral-core/src/embeddings/mod.rs`):
|
||||
|
||||
```rust
|
||||
pub fn create_provider(config: &EmbeddingConfig) -> Result<Box<dyn EmbeddingProvider>> {
|
||||
match config.provider {
|
||||
EmbeddingProviderType::FastEmbed => {
|
||||
Ok(Box::new(FastEmbedProvider::new(&config.model)?))
|
||||
}
|
||||
EmbeddingProviderType::OpenAI => {
|
||||
let api_key = std::env::var(&config.api_key_env)?;
|
||||
Ok(Box::new(RigEmbeddingProvider::new_openai(api_key, &config.model)?))
|
||||
}
|
||||
EmbeddingProviderType::Claude => {
|
||||
let api_key = std::env::var(&config.api_key_env)?;
|
||||
Ok(Box::new(RigEmbeddingProvider::new_claude(api_key, &config.model)?))
|
||||
}
|
||||
EmbeddingProviderType::Ollama => {
|
||||
Ok(Box::new(RigEmbeddingProvider::new_ollama(&config.model)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
✅ **Development Flexibility**:
|
||||
- Developers can use `fastembed` without API keys
|
||||
- Fast feedback loop (local embeddings, no network calls)
|
||||
- Works offline (train trips, flights)
|
||||
|
||||
✅ **Production Quality**:
|
||||
- Production deployments can use OpenAI/Claude for better quality
|
||||
- Latest embedding models available
|
||||
- Scalable to millions of documents
|
||||
|
||||
✅ **Privacy Control**:
|
||||
- Privacy-sensitive projects use local embeddings
|
||||
- Public projects can use cloud providers
|
||||
- User choice via configuration
|
||||
|
||||
✅ **Cost Optimization**:
|
||||
- Small projects: free (fastembed)
|
||||
- Large projects: pay for quality (cloud providers)
|
||||
- Hybrid: important docs via cloud, bulk via local
|
||||
|
||||
✅ **Unified Interface**:
|
||||
- `EmbeddingProvider` trait abstracts provider details
|
||||
- Query code doesn't know/care about provider
|
||||
- Easy to add new providers
|
||||
|
||||
### Negative
|
||||
|
||||
❌ **Dimension Mismatch**:
|
||||
- fastembed: 384 dimensions
|
||||
- OpenAI: 1536 dimensions
|
||||
- Cannot mix in same index
|
||||
|
||||
**Mitigation**:
|
||||
- Store provider + dimensions in node metadata
|
||||
- Rebuild index when changing providers
|
||||
- Document dimension constraints
|
||||
|
||||
❌ **Model Download**:
|
||||
- First use of fastembed downloads ~100MB model
|
||||
- Slow initial startup
|
||||
|
||||
**Mitigation**:
|
||||
- Pre-download in Docker images
|
||||
- Document model download in setup guide
|
||||
- Cache models in `~/.cache/fastembed`
|
||||
|
||||
❌ **Complex Configuration**:
|
||||
- Multiple provider options may confuse users
|
||||
|
||||
**Mitigation**:
|
||||
- Sane default (fastembed)
|
||||
- Clear examples for each provider
|
||||
- Validation errors explain misconfigurations
|
||||
|
||||
### Neutral
|
||||
|
||||
⚪ **Dependency Trade-off**:
|
||||
- `fastembed` adds ~5MB to binary
|
||||
- `rig-core` adds ~2MB
|
||||
- Total: ~7MB overhead
|
||||
|
||||
Not a concern for CLI/MCP server use case.
|
||||
|
||||
---
|
||||
|
||||
## Provider Comparison
|
||||
|
||||
| Provider | Dimensions | Quality | Cost | Privacy | Offline |
|
||||
| -------------- | ---------- | --------- | ------------- | --------- | ------- |
|
||||
| **fastembed** | 384 | Good | Free | ✅ Local | ✅ Yes |
|
||||
| **OpenAI** | 1536 | Excellent | $0.0001/1K | ❌ Cloud | ❌ No |
|
||||
| **Claude** | 1024 | Excellent | $0.00025/1K | ❌ Cloud | ❌ No |
|
||||
| **Ollama** | 768 | Very Good | Free | ✅ Local | ✅ Yes |
|
||||
|
||||
**Recommendation by Use Case**:
|
||||
|
||||
- **Development**: fastembed (fast, free, offline)
|
||||
- **Small Teams**: fastembed or Ollama (privacy, no costs)
|
||||
- **Enterprise**: OpenAI or Claude (best quality, scalable)
|
||||
- **Self-Hosted**: Ollama (good quality, local control)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Timeline
|
||||
|
||||
1. ✅ Define `EmbeddingProvider` trait
|
||||
2. ✅ Implement FastEmbedProvider (stub, feature-gated)
|
||||
3. ✅ Implement RigEmbeddingProvider (stub, feature-gated)
|
||||
4. ⏳ Complete FastEmbed integration with model download
|
||||
5. ⏳ Complete rig-core integration (OpenAI, Claude, Ollama)
|
||||
6. ⏳ Add query engine with similarity search
|
||||
7. ⏳ Document provider selection and trade-offs
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
**Success Criteria**:
|
||||
- Users can switch providers via config change
|
||||
- Local embeddings work without API keys
|
||||
- Production deployments use cloud providers successfully
|
||||
- Query quality acceptable for both local and cloud embeddings
|
||||
|
||||
**Metrics**:
|
||||
- Embedding generation latency (local vs cloud)
|
||||
- Query accuracy (precision@10 for semantic search)
|
||||
- API costs (cloud providers)
|
||||
- User satisfaction (feedback on search quality)
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [fastembed Documentation](https://github.com/Anush008/fastembed-rs)
|
||||
- [rig-core Documentation](https://github.com/0xPlaygrounds/rig)
|
||||
- [OpenAI Embeddings API](https://platform.openai.com/docs/guides/embeddings)
|
||||
- [BAAI/bge Models](https://huggingface.co/BAAI/bge-small-en-v1.5)
|
||||
- [Ollama Embeddings](https://ollama.com/blog/embedding-models)
|
||||
|
||||
---
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change |
|
||||
| ---------- | ------------------ | ---------------- |
|
||||
| 2026-01-17 | Architecture Team | Initial decision |
|
||||
|
||||
---
|
||||
|
||||
**Previous ADR**: [ADR-001: Nickel vs TOML](001-nickel-vs-toml.md)
|
||||
**Next ADR**: [ADR-003: Hybrid Storage Strategy](003-hybrid-storage.md)
|
||||
449
docs/architecture/adrs/003-hybrid-storage.md
Normal file
@ -0,0 +1,449 @@
|
||||
# ADR-003: Hybrid Storage Strategy
|
||||
|
||||
**Status**: Accepted
|
||||
|
||||
**Date**: 2026-01-17
|
||||
|
||||
**Deciders**: Architecture Team
|
||||
|
||||
**Context**: Storage Backend Strategy for Knowledge Base
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The KOGRAL needs to store knowledge graphs with these requirements:
|
||||
|
||||
1. **Git-Friendly**: Knowledge should version alongside code
|
||||
2. **Scalable**: Support small projects (10s of nodes) to large organizations (10,000+ nodes)
|
||||
3. **Queryable**: Efficient graph queries and relationship traversal
|
||||
4. **Offline-Capable**: Work without network access
|
||||
5. **Collaborative**: Support shared organizational knowledge
|
||||
6. **Cost-Effective**: Free for small projects, reasonable cost at scale
|
||||
|
||||
**Constraints**:
|
||||
|
||||
- Developers want to edit knowledge in text editors
|
||||
- Organizations want centralized guideline management
|
||||
- Git workflows essential for code-adjacent knowledge
|
||||
- Large graphs need database performance
|
||||
|
||||
### Option 1: Filesystem Only
|
||||
|
||||
**Approach**: Store everything as markdown files
|
||||
|
||||
**Pros**:
|
||||
- ✅ Git-native (perfect for versioning)
|
||||
- ✅ Text editor friendly
|
||||
- ✅ No dependencies
|
||||
- ✅ Works offline
|
||||
- ✅ Free
|
||||
|
||||
**Cons**:
|
||||
- ❌ Poor performance for large graphs (100 0+ nodes)
|
||||
- ❌ No efficient graph queries
|
||||
- ❌ Difficult to share across projects
|
||||
- ❌ Manual sync for collaboration
|
||||
|
||||
**Scalability**: Good for < 100 nodes, poor beyond
|
||||
|
||||
### Option 2: Database Only (SurrealDB)
|
||||
|
||||
**Approach**: Store all knowledge in SurrealDB graph database
|
||||
|
||||
**Pros**:
|
||||
- ✅ Excellent query performance
|
||||
- ✅ Native graph relationships
|
||||
- ✅ Scalable to millions of nodes
|
||||
- ✅ Centralized for collaboration
|
||||
|
||||
**Cons**:
|
||||
- ❌ Not git-trackable
|
||||
- ❌ Requires running database server
|
||||
- ❌ Can't edit with text editor
|
||||
- ❌ Network dependency
|
||||
- ❌ Infrastructure cost
|
||||
|
||||
**Scalability**: Excellent, but loses developer workflow benefits
|
||||
|
||||
### Option 3: Hybrid (Filesystem + SurrealDB)
|
||||
|
||||
**Approach**: Filesystem for local project knowledge, SurrealDB for shared organizational knowledge
|
||||
|
||||
**Pros**:
|
||||
- ✅ Git-friendly for project knowledge
|
||||
- ✅ Text editor friendly
|
||||
- ✅ Scalable for shared knowledge
|
||||
- ✅ Works offline (local graph)
|
||||
- ✅ Collaborative (shared graph)
|
||||
- ✅ Cost-effective (DB only for shared)
|
||||
|
||||
**Cons**:
|
||||
- ❌ More complex implementation
|
||||
- ❌ Sync mechanism needed
|
||||
- ❌ Two storage systems to manage
|
||||
|
||||
**Scalability**: Excellent - best of both worlds
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
**We will use a hybrid storage strategy: Filesystem (local) + SurrealDB (shared).**
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Project A (.kogral/) │
|
||||
│ Storage: Filesystem (git-tracked) │
|
||||
│ Scope: Project-specific notes, decisions, patterns │
|
||||
│ Access: Local only │
|
||||
└──────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
│ [inherits]
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Shared KB (SurrealDB or synced filesystem) │
|
||||
│ Storage: SurrealDB (scalable) or filesystem (synced) │
|
||||
│ Scope: Organization-wide guidelines, patterns │
|
||||
│ Access: All projects │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Implementation**:
|
||||
|
||||
```nickel
|
||||
# Project config
|
||||
{
|
||||
storage = {
|
||||
primary = 'filesystem, # Local project knowledge
|
||||
secondary = {
|
||||
enabled = true,
|
||||
type = 'surrealdb, # Shared knowledge
|
||||
url = "ws://kb-central.company.com:8000",
|
||||
namespace = "organization",
|
||||
database = "shared-kb",
|
||||
},
|
||||
},
|
||||
|
||||
inheritance = {
|
||||
base = "surrealdb://organization/shared-kb", # Inherit from shared
|
||||
priority = 100, # Project overrides shared
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Sync Strategy**:
|
||||
|
||||
```text
|
||||
.kogral/ (Filesystem)
|
||||
↓ [on save]
|
||||
Watch for changes
|
||||
↓ [debounced]
|
||||
Sync to SurrealDB
|
||||
↓
|
||||
Shared graph updated
|
||||
↓ [on query]
|
||||
Merge local + shared results
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
✅ **Developer Workflow Preserved**:
|
||||
|
||||
```bash
|
||||
# Local knowledge workflow (unchanged)
|
||||
vim .kogral/notes/my-note.md
|
||||
git add .kogral/notes/my-note.md
|
||||
git commit -m "Add implementation note"
|
||||
git push
|
||||
```
|
||||
|
||||
✅ **Git Integration**:
|
||||
- Project knowledge versioned with code
|
||||
- Branches include relevant knowledge
|
||||
- Merges resolve knowledge conflicts
|
||||
- PR reviews include knowledge changes
|
||||
|
||||
✅ **Offline Development**:
|
||||
- Full functionality without network
|
||||
- Shared guidelines cached locally
|
||||
- Sync when reconnected
|
||||
|
||||
✅ **Scalability**:
|
||||
- Projects: filesystem (100s of nodes, fine performance)
|
||||
- Organization: SurrealDB (10,000+ nodes, excellent performance)
|
||||
|
||||
✅ **Collaboration**:
|
||||
- Shared guidelines accessible to all projects
|
||||
- Updates to shared knowledge propagate automatically
|
||||
- Consistent practices across organization
|
||||
|
||||
✅ **Cost-Effective**:
|
||||
- Small projects: free (filesystem only)
|
||||
- Organizations: SurrealDB for shared only (not all project knowledge)
|
||||
|
||||
✅ **Gradual Adoption**:
|
||||
- Start with filesystem only
|
||||
- Add SurrealDB when needed
|
||||
- Feature-gated (`--features surrealdb`)
|
||||
|
||||
### Negative
|
||||
|
||||
❌ **Complexity**:
|
||||
- Two storage implementations
|
||||
- Sync mechanism required
|
||||
- Conflict resolution needed
|
||||
|
||||
**Mitigation**:
|
||||
- Storage trait abstracts differences
|
||||
- Sync is optional (can disable)
|
||||
- Conflicts rare (guidelines change infrequently)
|
||||
|
||||
❌ **Sync Latency**:
|
||||
- Changes to shared KOGRAL not instant in all projects
|
||||
|
||||
**Mitigation**:
|
||||
- Acceptable latency (guidelines don't change rapidly)
|
||||
- Manual sync command available (`kogral sync`)
|
||||
- Auto-sync on query (fetch latest)
|
||||
|
||||
❌ **Infrastructure Requirement**:
|
||||
- SurrealDB server needed for shared KOGRAL
|
||||
|
||||
**Mitigation**:
|
||||
- Optional (can use synced filesystem instead)
|
||||
- Docker Compose for easy setup
|
||||
- Managed SurrealDB Cloud option
|
||||
|
||||
### Neutral
|
||||
|
||||
⚪ **Storage Trait Implementation**:
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait Storage {
|
||||
async fn save_graph(&self, graph: &Graph) -> Result<()>;
|
||||
async fn load_graph(&self, name: &str) -> Result<Graph>;
|
||||
async fn list_graphs(&self) -> Result<Vec<String>>;
|
||||
}
|
||||
|
||||
// Three implementations
|
||||
impl Storage for FilesystemStorage { /* ... */ }
|
||||
impl Storage for SurrealDbStorage { /* ... */ }
|
||||
impl Storage for MemoryStorage { /* ... */ }
|
||||
```
|
||||
|
||||
Abstraction makes multi-backend manageable.
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Small Project (Solo Developer)
|
||||
|
||||
**Config**:
|
||||
|
||||
```nickel
|
||||
{ storage = { primary = 'filesystem } }
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
- All knowledge in `.kogral/` directory
|
||||
- Git-tracked with code
|
||||
- No database required
|
||||
- Works offline
|
||||
|
||||
### Medium Project (Team)
|
||||
|
||||
**Config**:
|
||||
|
||||
```nickel
|
||||
{
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = {
|
||||
enabled = true,
|
||||
type = 'surrealdb,
|
||||
url = "ws://team-kb.local:8000",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
- Project knowledge in `.kogral/` (git-tracked)
|
||||
- Shared team patterns in SurrealDB
|
||||
- Automatic sync
|
||||
- Offline fallback to cached
|
||||
|
||||
### Large Organization
|
||||
|
||||
**Config**:
|
||||
|
||||
```nickel
|
||||
{
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = {
|
||||
enabled = true,
|
||||
type = 'surrealdb,
|
||||
url = "ws://kb.company.com:8000",
|
||||
namespace = "engineering",
|
||||
database = "shared-kb",
|
||||
},
|
||||
},
|
||||
|
||||
inheritance = {
|
||||
base = "surrealdb://engineering/shared-kb",
|
||||
guidelines = [
|
||||
"surrealdb://engineering/rust-guidelines",
|
||||
"surrealdb://engineering/security-policies",
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
- Project-specific in `.kogral/`
|
||||
- Organization guidelines in SurrealDB
|
||||
- Security policies enforced
|
||||
- Automatic guideline updates
|
||||
|
||||
---
|
||||
|
||||
## Sync Mechanism
|
||||
|
||||
### Filesystem → SurrealDB
|
||||
|
||||
**Trigger**: File changes detected (via `notify` crate)
|
||||
|
||||
**Process**:
|
||||
1. Parse changed markdown file
|
||||
2. Convert to Node struct
|
||||
3. Upsert to SurrealDB
|
||||
4. Update relationships
|
||||
|
||||
**Debouncing**: 500ms (configurable)
|
||||
|
||||
### SurrealDB → Filesystem
|
||||
|
||||
**Trigger**: Query for shared knowledge
|
||||
|
||||
**Process**:
|
||||
1. Query SurrealDB for shared nodes
|
||||
2. Cache locally (in-memory or filesystem)
|
||||
3. Merge with local results
|
||||
4. Return combined
|
||||
|
||||
**Caching**: TTL-based (5 minutes default)
|
||||
|
||||
### Conflict Resolution
|
||||
|
||||
**Strategy**: Last-write-wins with version tracking
|
||||
|
||||
**Example**:
|
||||
|
||||
```text
|
||||
Project A: Updates shared guideline (v1 → v2)
|
||||
Project B: Has cached v1
|
||||
|
||||
On Project B query:
|
||||
- Detects v2 available
|
||||
- Fetches v2
|
||||
- Updates cache
|
||||
- Uses v2 going forward
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Git Submodules for Shared Knowledge
|
||||
|
||||
**Rejected**: Cumbersome workflow
|
||||
- Requires manual submodule update
|
||||
- Merge conflicts in shared submodule
|
||||
- Not discoverable (need to know submodule exists)
|
||||
|
||||
### Syncthing for Filesystem Sync
|
||||
|
||||
**Rejected**: Not designed for this use case
|
||||
- No query optimization
|
||||
- No relationship indexes
|
||||
- Sync conflicts difficult to resolve
|
||||
|
||||
### PostgreSQL with JSON
|
||||
|
||||
**Rejected**: Not a graph database
|
||||
- Poor graph query performance
|
||||
- Relationship traversal requires complex SQL joins
|
||||
- No native graph features
|
||||
|
||||
---
|
||||
|
||||
## Migration Path
|
||||
|
||||
### Phase 1: Filesystem Only (Current)
|
||||
- All storage via filesystem
|
||||
- Git-tracked
|
||||
- No database required
|
||||
|
||||
### Phase 2: Optional SurrealDB
|
||||
- Add SurrealDB support (feature-gated)
|
||||
- Manual sync command
|
||||
- Shared KB opt-in
|
||||
|
||||
### Phase 3: Automatic Sync
|
||||
- File watching
|
||||
- Auto-sync on changes
|
||||
- Background sync
|
||||
|
||||
### Phase 4: Multi-Tenant SurrealDB
|
||||
- Organization namespaces
|
||||
- Access control
|
||||
- Audit logs
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
**Success Criteria**:
|
||||
- Developers don't notice hybrid complexity
|
||||
- Sync completes < 1 second for typical changes
|
||||
- Shared guidelines accessible in < 100ms
|
||||
- Zero data loss in sync
|
||||
|
||||
**Metrics**:
|
||||
- Sync latency (P50, P95, P99)
|
||||
- Cache hit rate (shared knowledge)
|
||||
- Conflict rate (expect < 0.1%)
|
||||
- User satisfaction
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [SurrealDB Documentation](https://surrealdb.com/docs)
|
||||
- [Storage Trait Implementation](../../crates/kogral-core/src/storage/mod.rs)
|
||||
- [FilesystemStorage](../../crates/kogral-core/src/storage/filesystem.rs)
|
||||
- [SurrealDbStorage](../../crates/kogral-core/src/storage/surrealdb.rs)
|
||||
- [Sync Mechanism](../../scripts/kogral-sync.nu)
|
||||
|
||||
---
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change |
|
||||
| ---------- | ------------------ | ---------------- |
|
||||
| 2026-01-17 | Architecture Team | Initial decision |
|
||||
|
||||
---
|
||||
|
||||
**Previous ADR**: [ADR-002: FastEmbed via AI Providers](002-fastembed-ai-providers.md)
|
||||
**Next ADR**: [ADR-004: Logseq Compatibility](004-logseq-compatibility.md)
|
||||
196
docs/architecture/adrs/004-logseq-blocks-support.md
Normal file
@ -0,0 +1,196 @@
|
||||
# ADR-004: Logseq Blocks Support
|
||||
|
||||
## Status
|
||||
|
||||
**Proposed** (Design phase)
|
||||
|
||||
## Context
|
||||
|
||||
Logseq uses **content blocks** as the fundamental unit of information, not full documents. KB currently treats `Node.content` as flat markdown string, which loses block-level features on import/export:
|
||||
|
||||
**Lost features**:
|
||||
- Block properties (`#card`, `TODO`, custom properties)
|
||||
- Block hierarchy (outliner nesting)
|
||||
- Block references (`((block-uuid))`)
|
||||
- Block-level queries (find all flashcards, TODOs)
|
||||
|
||||
**User requirement**: Round-trip Logseq import/export with full fidelity:
|
||||
|
||||
```text
|
||||
Logseq → KOGRAL Import → KOGRAL Storage → KOGRAL Export → Logseq
|
||||
(blocks preserved at every step)
|
||||
```
|
||||
|
||||
## Decision
|
||||
|
||||
Implement **Hybrid Block Support** (structured + markdown):
|
||||
|
||||
### 1. Add Block Data Structure
|
||||
|
||||
```rust
|
||||
pub struct Block {
|
||||
pub id: String, // UUID
|
||||
pub content: String, // Block text
|
||||
pub properties: BlockProperties, // Tags, status, custom
|
||||
pub children: Vec<Block>, // Nested blocks
|
||||
// ... timestamps ...
|
||||
}
|
||||
|
||||
pub struct BlockProperties {
|
||||
pub tags: Vec<String>, // #card, #important
|
||||
pub status: Option<TaskStatus>, // TODO, DONE, etc.
|
||||
pub custom: HashMap<String, String>, // property:: value
|
||||
pub block_refs: Vec<String>, // ((uuid))
|
||||
pub page_refs: Vec<String>, // [[page]]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Extend Node Model
|
||||
|
||||
```rust
|
||||
pub struct Node {
|
||||
// ... existing fields ...
|
||||
pub content: String, // Source of truth (markdown)
|
||||
pub blocks: Option<Vec<Block>>, // Cached structure (optional)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Bidirectional Parser
|
||||
|
||||
- **Parse**: Markdown → `Vec<Block>` (lazy, on-demand)
|
||||
- **Serialize**: `Vec<Block>` → Markdown (for export)
|
||||
|
||||
### 4. Storage Strategy
|
||||
|
||||
**Filesystem** (git-friendly, Logseq-compatible):
|
||||
|
||||
```markdown
|
||||
- Block 1 #card
|
||||
- Nested answer
|
||||
- TODO Block 2
|
||||
priority:: high
|
||||
```
|
||||
|
||||
**SurrealDB** (queryable):
|
||||
|
||||
```sql
|
||||
DEFINE TABLE block;
|
||||
DEFINE FIELD node_id ON block TYPE record(node);
|
||||
DEFINE FIELD block_id ON block TYPE string;
|
||||
DEFINE FIELD properties ON block TYPE object;
|
||||
DEFINE INDEX block_tags ON block COLUMNS properties.tags;
|
||||
```
|
||||
|
||||
### 5. Query Extensions
|
||||
|
||||
```rust
|
||||
// Find all flashcards
|
||||
graph.find_blocks_by_tag("card")
|
||||
|
||||
// Find all TODOs
|
||||
graph.find_all_todos()
|
||||
|
||||
// Find blocks with custom property
|
||||
node.find_blocks_by_property("priority", "high")
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
✅ **Full Logseq Compatibility** - Import/export preserves all block features
|
||||
✅ **Queryable Blocks** - Find #card, TODO, custom properties across KOGRAL
|
||||
✅ **Backward Compatible** - Existing nodes without blocks still work
|
||||
✅ **Type-Safe** - Structured data instead of regex parsing everywhere
|
||||
✅ **Extensible** - Custom block properties supported
|
||||
✅ **Hierarchy Preserved** - Nested blocks maintain parent-child relationships
|
||||
|
||||
### Negative
|
||||
|
||||
⚠️ **Added Complexity** - New data structures, parser, sync logic
|
||||
⚠️ **Dual Representation** - Must keep `content` and `blocks` in sync
|
||||
⚠️ **Storage Overhead** - SurrealDB stores both markdown and structure
|
||||
⚠️ **Migration Required** - Existing data needs parsing to populate blocks
|
||||
|
||||
### Neutral
|
||||
|
||||
⚙️ **Lazy Parsing** - Blocks parsed on-demand (not stored by default)
|
||||
⚙️ **Opt-In** - Config flag `blocks.enabled` to activate features
|
||||
⚙️ **Gradual Adoption** - Can implement in phases
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
**Phase 1: Foundation** (No behavior change)
|
||||
- Add `Block` struct to `models/block.rs`
|
||||
- Add optional `blocks` field to `Node`
|
||||
- Add config: `blocks.enabled = false` (default off)
|
||||
|
||||
**Phase 2: Parser**
|
||||
- Implement `BlockParser::parse()` (markdown → blocks)
|
||||
- Implement `BlockParser::serialize()` (blocks → markdown)
|
||||
- Add `Node::get_blocks()` method (lazy parsing)
|
||||
|
||||
**Phase 3: Logseq Integration**
|
||||
- Update `LogseqImporter` to parse blocks
|
||||
- Update `LogseqExporter` to serialize blocks
|
||||
- Test round-trip (Logseq → KB → Logseq)
|
||||
|
||||
**Phase 4: Query API**
|
||||
- Add `Graph::find_blocks_by_tag()`
|
||||
- Add `Graph::find_all_todos()`
|
||||
- Add `Node::find_blocks_by_property()`
|
||||
|
||||
**Phase 5: MCP/CLI Integration**
|
||||
- Add `kb/find_blocks` MCP tool
|
||||
- Add `kogral find-cards` CLI command
|
||||
- Add `kogral find-todos` CLI command
|
||||
|
||||
**Phase 6: SurrealDB Backend**
|
||||
- Create `block` table schema
|
||||
- Index on tags, status, properties
|
||||
- Store blocks alongside nodes
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Blocks as First-Class Nodes
|
||||
|
||||
Convert each Logseq block to a separate KOGRAL Node.
|
||||
|
||||
**Rejected**: Too granular, explosion of nodes, loses document context.
|
||||
|
||||
### Alternative 2: Parser-Only (No Storage)
|
||||
|
||||
Keep `content: String`, parse blocks on every access.
|
||||
|
||||
**Rejected**: Can't query blocks in database, parse overhead, can't index.
|
||||
|
||||
### Alternative 3: Metadata Field
|
||||
|
||||
Store blocks in `metadata: HashMap<String, Value>`.
|
||||
|
||||
**Rejected**: Not type-safe, harder to query, no schema validation.
|
||||
|
||||
## References
|
||||
|
||||
- [Logseq Block Format](https://docs.logseq.com/#/page/blocks)
|
||||
- [Full Design Document](../logseq-blocks-design.md)
|
||||
- [Implementation Tracking](https://github.com/.../issues/XXX)
|
||||
|
||||
## Notes
|
||||
|
||||
**Backward Compatibility Strategy**:
|
||||
- `content` remains source of truth
|
||||
- `blocks` is optional enhancement
|
||||
- Old code works unchanged
|
||||
- New features opt-in via config
|
||||
|
||||
**Migration Path**:
|
||||
- Existing users: blocks disabled by default
|
||||
- New users: blocks enabled, parsed on import
|
||||
- Manual: `kogral reindex --parse-blocks` to populate
|
||||
|
||||
---
|
||||
|
||||
**Decision Date**: 2026-01-17
|
||||
**Approvers**: TBD
|
||||
**Review Date**: After Phase 2 implementation
|
||||
1
docs/architecture/adrs/005-mcp-protocol.md
Normal file
@ -0,0 +1 @@
|
||||
# ADR-005: MCP Protocol for AI Integration
|
||||
539
docs/architecture/config-driven.md
Normal file
@ -0,0 +1,539 @@
|
||||
# Config-Driven Architecture
|
||||
|
||||
The KOGRAL follows a **config-driven architecture** where all behavior is defined through Nickel configuration files rather than hardcoded in Rust.
|
||||
|
||||
## Philosophy
|
||||
|
||||
**"Configuration, not code, defines behavior"**
|
||||
|
||||
Instead of hardcoding storage backends, embedding providers, or query parameters, KOGRAL uses a layered configuration system that composes settings from multiple sources:
|
||||
|
||||
1. **Schema contracts** (type definitions)
|
||||
2. **Defaults** (base values)
|
||||
3. **Mode overlays** (dev/prod/test optimizations)
|
||||
4. **User customizations** (project-specific overrides)
|
||||
|
||||
This approach provides:
|
||||
|
||||
- ✅ **Type safety** - Nickel contracts validate configuration before runtime
|
||||
- ✅ **Composability** - Mix and match configurations for different environments
|
||||
- ✅ **Discoverability** - Self-documenting schemas with inline documentation
|
||||
- ✅ **Hot-reload** - Change behavior without recompiling Rust code
|
||||
- ✅ **Double validation** - Nickel contracts + serde ensure correctness
|
||||
|
||||
## Configuration Composition Flow
|
||||
|
||||

|
||||
|
||||
The configuration system uses a **four-layer composition pattern**:
|
||||
|
||||
### Layer 1: Schema Contracts
|
||||
|
||||
**Location**: `schemas/contracts.ncl`
|
||||
|
||||
**Purpose**: Define types and validation rules using Nickel contracts.
|
||||
|
||||
**Example**:
|
||||
|
||||
```nickel
|
||||
{
|
||||
StorageType = [| 'filesystem, 'memory, 'surrealdb |],
|
||||
|
||||
StorageConfig = {
|
||||
primary | StorageType
|
||||
| doc "Primary storage backend"
|
||||
| default = 'filesystem,
|
||||
|
||||
secondary | SecondaryStorageConfig
|
||||
| doc "Optional secondary storage"
|
||||
| default = { enabled = false },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Enum validation (only valid storage types accepted)
|
||||
- Required vs optional fields
|
||||
- Default values for optional fields
|
||||
- Documentation attached to types
|
||||
|
||||
### Layer 2: Defaults
|
||||
|
||||
**Location**: `schemas/defaults.ncl`
|
||||
|
||||
**Purpose**: Provide sensible base values for all configuration options.
|
||||
|
||||
**Example**:
|
||||
|
||||
```nickel
|
||||
{
|
||||
base = {
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = {
|
||||
enabled = false,
|
||||
type = 'surrealdb,
|
||||
url = "ws://localhost:8000",
|
||||
},
|
||||
},
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'fastembed,
|
||||
model = "BAAI/bge-small-en-v1.5",
|
||||
dimensions = 384,
|
||||
},
|
||||
} | contracts.KbConfig,
|
||||
}
|
||||
```
|
||||
|
||||
**Validated by**: `contracts.KbConfig` contract ensures defaults are valid.
|
||||
|
||||
### Layer 3: Mode Overlays
|
||||
|
||||
**Location**: `schemas/modes/{dev,prod,test}.ncl`
|
||||
|
||||
**Purpose**: Environment-specific optimizations and tuning.
|
||||
|
||||
#### Development Mode (`dev.ncl`)
|
||||
|
||||
Optimized for: Fast iteration, local development, debugging
|
||||
|
||||
```nickel
|
||||
{
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = { enabled = false }, # No database overhead
|
||||
},
|
||||
embeddings = {
|
||||
provider = 'fastembed, # Local, no API costs
|
||||
},
|
||||
sync = {
|
||||
auto_index = false, # Manual control
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Production Mode (`prod.ncl`)
|
||||
|
||||
Optimized for: Performance, reliability, scalability
|
||||
|
||||
```nickel
|
||||
{
|
||||
storage = {
|
||||
secondary = { enabled = true }, # SurrealDB for scale
|
||||
},
|
||||
embeddings = {
|
||||
provider = 'openai, # High-quality cloud embeddings
|
||||
model = "text-embedding-3-small",
|
||||
dimensions = 1536,
|
||||
},
|
||||
sync = {
|
||||
auto_index = true,
|
||||
debounce_ms = 300, # Fast response
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Test Mode (`test.ncl`)
|
||||
|
||||
Optimized for: Fast tests, isolation, determinism
|
||||
|
||||
```nickel
|
||||
{
|
||||
storage = {
|
||||
primary = 'memory, # Ephemeral, no disk I/O
|
||||
},
|
||||
embeddings = {
|
||||
enabled = false, # Disable for speed
|
||||
},
|
||||
sync = {
|
||||
auto_index = false,
|
||||
debounce_ms = 0, # No delays in tests
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Layer 4: User Customizations
|
||||
|
||||
**Location**: `.kogral-config/core/kogral.ncl` or `.kogral-config/platform/{dev,prod,test}.ncl`
|
||||
|
||||
**Purpose**: Project-specific or deployment-specific overrides.
|
||||
|
||||
**Example** (user project config):
|
||||
|
||||
```nickel
|
||||
let mode = import "../../schemas/modes/dev.ncl" in
|
||||
|
||||
let user_custom = {
|
||||
graph = {
|
||||
name = "my-project",
|
||||
},
|
||||
embeddings = {
|
||||
provider = 'claude, # Override to use Claude
|
||||
model = "claude-3-haiku-20240307",
|
||||
},
|
||||
query = {
|
||||
similarity_threshold = 0.7, # Stricter threshold
|
||||
},
|
||||
} in
|
||||
|
||||
helpers.compose_config defaults.base mode user_custom
|
||||
| contracts.KbConfig
|
||||
```
|
||||
|
||||
## Composition Mechanism
|
||||
|
||||
The `helpers.ncl` module provides the composition function:
|
||||
|
||||
```nickel
|
||||
{
|
||||
# Recursively merge with override precedence
|
||||
merge_with_override = fun base override => /* ... */,
|
||||
|
||||
# Compose three layers
|
||||
compose_config = fun defaults mode_config user_custom =>
|
||||
let with_mode = merge_with_override defaults mode_config in
|
||||
merge_with_override with_mode user_custom,
|
||||
}
|
||||
```
|
||||
|
||||
**Merge behavior**:
|
||||
- Records are merged recursively
|
||||
- Override values take precedence over base values
|
||||
- Arrays are not merged, override replaces base
|
||||
- Null in override keeps base value
|
||||
|
||||
**Example merge**:
|
||||
|
||||
```nickel
|
||||
base = { storage = { primary = 'filesystem }, embeddings = { enabled = true } }
|
||||
override = { storage = { primary = 'memory } }
|
||||
# Result: { storage = { primary = 'memory }, embeddings = { enabled = true } }
|
||||
```
|
||||
|
||||
## Export to JSON
|
||||
|
||||
Once composed, the Nickel configuration is exported to JSON for Rust consumption:
|
||||
|
||||
```bash
|
||||
nickel export --format json .kogral-config/core/kogral.ncl > .kogral-config/targets/kogral-core.json
|
||||
```
|
||||
|
||||
**Output** (`.kogral-config/targets/kogral-core.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"graph": {
|
||||
"name": "my-project",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"storage": {
|
||||
"primary": "memory",
|
||||
"secondary": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"embeddings": {
|
||||
"enabled": true,
|
||||
"provider": "claude",
|
||||
"model": "claude-3-haiku-20240307",
|
||||
"dimensions": 768
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rust Integration
|
||||
|
||||
The Rust code deserializes the JSON into typed structs using serde:
|
||||
|
||||
```rust
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct KbConfig {
|
||||
pub graph: GraphConfig,
|
||||
pub storage: StorageConfig,
|
||||
pub embeddings: EmbeddingConfig,
|
||||
pub templates: TemplateConfig,
|
||||
pub query: QueryConfig,
|
||||
pub mcp: McpConfig,
|
||||
pub sync: SyncConfig,
|
||||
}
|
||||
|
||||
impl KbConfig {
|
||||
pub fn from_file(path: &Path) -> Result<Self> {
|
||||
let json = std::fs::read_to_string(path)?;
|
||||
let config: KbConfig = serde_json::from_str(&json)?;
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in kogral-core**:
|
||||
|
||||
```rust
|
||||
let config = KbConfig::from_file(".kogral-config/targets/kogral-core.json")?;
|
||||
|
||||
// Config drives behavior
|
||||
let storage: Box<dyn Storage> = match config.storage.primary {
|
||||
StorageType::Filesystem => Box::new(FilesystemStorage::new(&config)?),
|
||||
StorageType::Memory => Box::new(MemoryStorage::new()),
|
||||
StorageType::SurrealDb => Box::new(SurrealDbStorage::new(&config).await?),
|
||||
};
|
||||
|
||||
let embeddings: Box<dyn EmbeddingProvider> = match config.embeddings.provider {
|
||||
EmbeddingProviderType::FastEmbed => Box::new(FastEmbedProvider::new()?),
|
||||
EmbeddingProviderType::OpenAI => Box::new(RigEmbeddingProvider::openai(&config)?),
|
||||
EmbeddingProviderType::Claude => Box::new(RigEmbeddingProvider::claude(&config)?),
|
||||
EmbeddingProviderType::Ollama => Box::new(RigEmbeddingProvider::ollama(&config)?),
|
||||
};
|
||||
```
|
||||
|
||||
## Double Validation
|
||||
|
||||
Configuration is validated **twice**:
|
||||
|
||||
### 1. Nickel Contract Validation
|
||||
|
||||
At export time, Nickel validates:
|
||||
- ✅ Types match contracts (e.g., `primary | StorageType`)
|
||||
- ✅ Required fields are present
|
||||
- ✅ Enums have valid values
|
||||
- ✅ Nested structure is correct
|
||||
|
||||
**Error example**:
|
||||
|
||||
```text
|
||||
error: contract broken by a value
|
||||
┌─ .kogral-config/core/kogral.ncl:15:5
|
||||
│
|
||||
15│ primary = 'invalid,
|
||||
│ ^^^^^^^^^^^^^^^^^^^ applied to this expression
|
||||
│
|
||||
= This value is not in the enum ['filesystem, 'memory, 'surrealdb]
|
||||
```
|
||||
|
||||
### 2. Serde Deserialization Validation
|
||||
|
||||
At runtime, serde validates:
|
||||
- ✅ JSON structure matches Rust types
|
||||
- ✅ Field names match (with rename support)
|
||||
- ✅ Values can be converted to Rust types
|
||||
- ✅ Required fields are not null
|
||||
|
||||
**Error example**:
|
||||
|
||||
```rust
|
||||
Error: missing field `graph` at line 1 column 123
|
||||
```
|
||||
|
||||
## Benefits of Config-Driven Architecture
|
||||
|
||||
### 1. Zero Hardcoding
|
||||
|
||||
**Bad** (hardcoded):
|
||||
|
||||
```rust
|
||||
// Hardcoded - requires recompilation to change
|
||||
let storage = FilesystemStorage::new("/fixed/path");
|
||||
let threshold = 0.6;
|
||||
```
|
||||
|
||||
**Good** (config-driven):
|
||||
|
||||
```rust
|
||||
// Config-driven - change via .ncl file
|
||||
let storage = create_storage(&config)?;
|
||||
let threshold = config.query.similarity_threshold;
|
||||
```
|
||||
|
||||
### 2. Environment Flexibility
|
||||
|
||||
Same codebase, different behavior:
|
||||
|
||||
```bash
|
||||
# Development
|
||||
nickel export .kogral-config/platform/dev.ncl > targets/kogral-core.json
|
||||
# → Filesystem storage, fastembed, no auto-sync
|
||||
|
||||
# Production
|
||||
nickel export .kogral-config/platform/prod.ncl > targets/kogral-core.json
|
||||
# → SurrealDB enabled, OpenAI embeddings, auto-sync
|
||||
|
||||
# Testing
|
||||
nickel export .kogral-config/platform/test.ncl > targets/kogral-core.json
|
||||
# → In-memory storage, no embeddings, isolated
|
||||
```
|
||||
|
||||
### 3. Self-Documenting
|
||||
|
||||
Nickel contracts include inline documentation:
|
||||
|
||||
```nickel
|
||||
StorageType = [| 'filesystem, 'memory, 'surrealdb |]
|
||||
| doc "Storage backend type: filesystem (git-tracked), memory (ephemeral), surrealdb (scalable)",
|
||||
```
|
||||
|
||||
IDEs can show this documentation when editing `.ncl` files.
|
||||
|
||||
### 4. Type-Safe Evolution
|
||||
|
||||
When adding new features:
|
||||
|
||||
1. Update contract in `contracts.ncl`
|
||||
2. Add default in `defaults.ncl`
|
||||
3. Export validates existing configs
|
||||
4. Rust compilation validates deserialization
|
||||
|
||||
Breaking changes are caught **before runtime**.
|
||||
|
||||
### 5. Testability
|
||||
|
||||
Different test scenarios without code changes:
|
||||
|
||||
```nickel
|
||||
# test-semantic-search.ncl
|
||||
let test_config = defaults.base & {
|
||||
embeddings = { enabled = true, provider = 'fastembed },
|
||||
query = { similarity_threshold = 0.3 },
|
||||
} in test_config
|
||||
```
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn test_semantic_search() {
|
||||
let config = KbConfig::from_file("test-semantic-search.json")?;
|
||||
// Config drives test behavior
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Discovery
|
||||
|
||||
KOGRAL tools automatically discover configuration:
|
||||
|
||||
1. **Check `.kogral-config/targets/kogral-core.json`** (pre-exported)
|
||||
2. **Check `.kogral-config/core/kogral.ncl`** (export on-demand)
|
||||
3. **Check environment variable `KOGRAL_CONFIG`**
|
||||
4. **Fall back to embedded defaults**
|
||||
|
||||
```rust
|
||||
impl KbConfig {
|
||||
pub fn discover() -> Result<Self> {
|
||||
if let Ok(config) = Self::from_file(".kogral-config/targets/kogral-core.json") {
|
||||
return Ok(config);
|
||||
}
|
||||
|
||||
if Path::new(".kogral-config/core/kogral.ncl").exists() {
|
||||
// Export and load
|
||||
let output = Command::new("nickel")
|
||||
.args(["export", "--format", "json", ".kogral-config/core/kogral.ncl"])
|
||||
.output()?;
|
||||
return serde_json::from_slice(&output.stdout)?;
|
||||
}
|
||||
|
||||
if let Ok(path) = std::env::var("KOGRAL_CONFIG") {
|
||||
return Self::from_file(&path);
|
||||
}
|
||||
|
||||
Ok(Self::default()) // Embedded defaults
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with justfile
|
||||
|
||||
The `justfile` integrates configuration validation:
|
||||
|
||||
```just
|
||||
# Validate all Nickel configs
|
||||
nickel-validate-all:
|
||||
@echo "Validating Nickel schemas..."
|
||||
nickel typecheck schemas/contracts.ncl
|
||||
nickel typecheck schemas/defaults.ncl
|
||||
nickel typecheck schemas/helpers.ncl
|
||||
nickel typecheck schemas/modes/dev.ncl
|
||||
nickel typecheck schemas/modes/prod.ncl
|
||||
nickel typecheck schemas/modes/test.ncl
|
||||
nickel typecheck .kogral-config/core/kogral.ncl
|
||||
|
||||
# Export all platform configs
|
||||
nickel-export-all:
|
||||
@echo "Exporting platform configs to JSON..."
|
||||
@mkdir -p .kogral-config/targets
|
||||
nickel export --format json .kogral-config/platform/dev.ncl > .kogral-config/targets/kogral-dev.json
|
||||
nickel export --format json .kogral-config/platform/prod.ncl > .kogral-config/targets/kogral-prod.json
|
||||
nickel export --format json .kogral-config/platform/test.ncl > .kogral-config/targets/kogral-test.json
|
||||
@echo "Exported 3 configurations to .kogral-config/targets/"
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
just nickel::validate-all # Check configs are valid
|
||||
just nickel::export-all # Generate JSON for all environments
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Never Hardcode
|
||||
|
||||
If it's behavior, it's config:
|
||||
- Storage paths
|
||||
- API endpoints
|
||||
- Thresholds
|
||||
- Timeouts
|
||||
- Feature flags
|
||||
- Provider selection
|
||||
|
||||
### 2. Use Modes for Environment Differences
|
||||
|
||||
Don't put environment-specific values in user config:
|
||||
|
||||
**Bad**:
|
||||
|
||||
```nickel
|
||||
# user config with env-specific values
|
||||
{
|
||||
storage = {
|
||||
url = if env == "prod" then "prod-url" else "dev-url" # Don't do this
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Good**:
|
||||
|
||||
```nickel
|
||||
# modes/prod.ncl
|
||||
{ storage = { url = "prod-url" } }
|
||||
|
||||
# modes/dev.ncl
|
||||
{ storage = { url = "dev-url" } }
|
||||
|
||||
# user config is environment-agnostic
|
||||
{ graph = { name = "my-project" } }
|
||||
```
|
||||
|
||||
### 3. Document Complex Fields
|
||||
|
||||
Use Nickel's `doc` metadata:
|
||||
|
||||
```nickel
|
||||
similarity_threshold | Number
|
||||
| doc "Minimum cosine similarity (0-1) for semantic search matches. Higher = stricter."
|
||||
| default = 0.6,
|
||||
```
|
||||
|
||||
### 4. Validate Early
|
||||
|
||||
Run `nickel typecheck` in CI/CD before building Rust code.
|
||||
|
||||
### 5. Version Configs
|
||||
|
||||
Track `.ncl` files in git, ignore `.kogral-config/targets/*.json` (generated).
|
||||
|
||||
## See Also
|
||||
|
||||
- **Schema Reference**: [Configuration Schema](../config/schema.md)
|
||||
- **User Guide**: [Configuration Guide](../config/overview.md)
|
||||
- **ADR**: [Why Nickel vs TOML](adrs/001-nickel-vs-toml.md)
|
||||
- **Examples**: `.kogral-config/core/kogral.ncl`, `.kogral-config/platform/*.ncl`
|
||||
1
docs/architecture/graph-model.md
Normal file
@ -0,0 +1 @@
|
||||
# Graph Model
|
||||
935
docs/architecture/logseq-blocks-design.md
Normal file
@ -0,0 +1,935 @@
|
||||
# Logseq Blocks Support - Architecture Design
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Logseq uses **content blocks** as the fundamental unit of information, not full documents. Each block can have:
|
||||
|
||||
- **Properties**: `#card`, `TODO`, `DONE`, custom properties
|
||||
- **Tags**: Inline tags like `#flashcard`, `#important`
|
||||
- **References**: Block references `((block-id))`, page references `[[page]]`
|
||||
- **Nesting**: Outliner-style hierarchy (parent-child blocks)
|
||||
- **Metadata**: Block-level properties (unlike page-level frontmatter)
|
||||
|
||||
**Current KB limitation**: Nodes only have `content: String` (flat markdown). Importing from Logseq loses block structure and properties.
|
||||
|
||||
**Requirement**: Support round-trip import/export with full block fidelity:
|
||||
|
||||
```text
|
||||
Logseq Graph → KOGRAL Import → KOGRAL Storage → KOGRAL Export → Logseq Graph
|
||||
(blocks preserved) (blocks preserved)
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### 1. Flashcards (`#card`)
|
||||
|
||||
**Logseq**:
|
||||
|
||||
```markdown
|
||||
- What is Rust's ownership model? #card
|
||||
- Rust uses ownership, borrowing, and lifetimes
|
||||
- Three rules: one owner, many borrows XOR one mutable
|
||||
```
|
||||
|
||||
**KB needs to preserve**:
|
||||
- Block with `#card` property
|
||||
- Nested answer blocks
|
||||
- Ability to query all cards
|
||||
|
||||
### 2. Task Tracking (`TODO`/`DONE`)
|
||||
|
||||
**Logseq**:
|
||||
|
||||
```markdown
|
||||
- TODO Implement block parser #rust
|
||||
- DONE Research block structure
|
||||
- TODO Write parser tests
|
||||
```
|
||||
|
||||
**KB needs to preserve**:
|
||||
- Task status per block
|
||||
- Hierarchical task breakdown
|
||||
- Tags on tasks
|
||||
|
||||
### 3. Block References
|
||||
|
||||
**Logseq**:
|
||||
|
||||
```markdown
|
||||
- Core concept: ((block-uuid-123))
|
||||
- See also: [[Related Page]]
|
||||
```
|
||||
|
||||
**KB needs to preserve**:
|
||||
- Block-to-block links (not just page-to-page)
|
||||
- UUID references
|
||||
|
||||
### 4. Block Properties
|
||||
|
||||
**Logseq**:
|
||||
|
||||
```markdown
|
||||
- This is a block with properties
|
||||
property1:: value1
|
||||
property2:: value2
|
||||
```
|
||||
|
||||
**KB needs to preserve**:
|
||||
- Custom key-value properties per block
|
||||
- Property inheritance/override
|
||||
|
||||
## Design Options
|
||||
|
||||
### Option A: Blocks as First-Class Data Structure
|
||||
|
||||
**Add `blocks` field to Node**:
|
||||
|
||||
```rust
|
||||
pub struct Node {
|
||||
// ... existing fields ...
|
||||
pub content: String, // Backward compat: flat markdown
|
||||
pub blocks: Option<Vec<Block>>, // NEW: Structured blocks
|
||||
}
|
||||
|
||||
pub struct Block {
|
||||
pub id: String, // UUID or auto-generated
|
||||
pub content: String, // Block text
|
||||
pub properties: BlockProperties, // Tags, status, custom props
|
||||
pub children: Vec<Block>, // Nested blocks
|
||||
pub created: DateTime<Utc>,
|
||||
pub modified: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub struct BlockProperties {
|
||||
pub tags: Vec<String>, // #card, #important
|
||||
pub status: Option<TaskStatus>, // TODO, DONE, WAITING
|
||||
pub custom: HashMap<String, String>, // property:: value
|
||||
}
|
||||
|
||||
pub enum TaskStatus {
|
||||
Todo,
|
||||
Doing,
|
||||
Done,
|
||||
Waiting,
|
||||
Cancelled,
|
||||
}
|
||||
```
|
||||
|
||||
**Pros**:
|
||||
- ✅ Type-safe, explicit structure
|
||||
- ✅ Queryable (find all #card blocks)
|
||||
- ✅ Preserves hierarchy
|
||||
- ✅ Supports block-level operations
|
||||
|
||||
**Cons**:
|
||||
- ❌ Adds complexity to Node
|
||||
- ❌ Dual representation (content + blocks)
|
||||
- ❌ Requires migration of existing data
|
||||
|
||||
### Option B: Parser-Only Approach
|
||||
|
||||
**Keep `content: String`, parse blocks on-demand**:
|
||||
|
||||
```rust
|
||||
pub struct BlockParser;
|
||||
|
||||
impl BlockParser {
|
||||
// Parse markdown content into block structure
|
||||
fn parse(content: &str) -> Vec<Block>;
|
||||
|
||||
// Serialize blocks back to markdown
|
||||
fn serialize(blocks: &[Block]) -> String;
|
||||
}
|
||||
|
||||
// Usage
|
||||
let blocks = BlockParser::parse(&node.content);
|
||||
let filtered = blocks.iter().filter(|b| b.properties.tags.contains("card"));
|
||||
```
|
||||
|
||||
**Pros**:
|
||||
- ✅ No schema changes
|
||||
- ✅ Backward compatible
|
||||
- ✅ Simple storage (still just String)
|
||||
|
||||
**Cons**:
|
||||
- ❌ Parse overhead on every access
|
||||
- ❌ Can't query blocks in database (SurrealDB)
|
||||
- ❌ Harder to index/search blocks
|
||||
|
||||
### Option C: Hybrid Approach (RECOMMENDED)
|
||||
|
||||
**Combine both: structured storage + lazy parsing**:
|
||||
|
||||
```rust
|
||||
pub struct Node {
|
||||
// ... existing fields ...
|
||||
pub content: String, // Source of truth (markdown)
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub blocks: Option<Vec<Block>>, // Cached structure (parsed)
|
||||
}
|
||||
|
||||
impl Node {
|
||||
// Parse blocks from content if not already cached
|
||||
pub fn get_blocks(&mut self) -> &Vec<Block> {
|
||||
if self.blocks.is_none() {
|
||||
self.blocks = Some(BlockParser::parse(&self.content));
|
||||
}
|
||||
self.blocks.as_ref().unwrap()
|
||||
}
|
||||
|
||||
// Update content from blocks (when blocks modified)
|
||||
pub fn sync_blocks_to_content(&mut self) {
|
||||
if let Some(ref blocks) = self.blocks {
|
||||
self.content = BlockParser::serialize(blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Storage Strategy**:
|
||||
|
||||
1. **Filesystem** - Store as markdown (Logseq compatible):
|
||||
|
||||
```markdown
|
||||
- Block 1 #card
|
||||
- Nested block
|
||||
- Block 2 TODO
|
||||
```
|
||||
|
||||
2. **SurrealDB** - Store both:
|
||||
|
||||
```sql
|
||||
DEFINE TABLE block SCHEMAFULL;
|
||||
DEFINE FIELD node_id ON block TYPE record(node);
|
||||
DEFINE FIELD block_id ON block TYPE string;
|
||||
DEFINE FIELD content ON block TYPE string;
|
||||
DEFINE FIELD properties ON block TYPE object;
|
||||
DEFINE FIELD parent_id ON block TYPE option<string>;
|
||||
|
||||
-- Index for queries
|
||||
DEFINE INDEX block_tags ON block COLUMNS properties.tags;
|
||||
DEFINE INDEX block_status ON block COLUMNS properties.status;
|
||||
```
|
||||
|
||||
**Pros**:
|
||||
- ✅ Best of both worlds
|
||||
- ✅ Filesystem stays Logseq-compatible
|
||||
- ✅ SurrealDB can query blocks
|
||||
- ✅ Lazy parsing (only when needed)
|
||||
- ✅ Backward compatible
|
||||
|
||||
**Cons**:
|
||||
- ⚠️ Need to keep content/blocks in sync
|
||||
- ⚠️ More complex implementation
|
||||
|
||||
## Recommended Implementation
|
||||
|
||||
**Phase 1: Data Model**
|
||||
|
||||
```rust
|
||||
// crates/kogral-core/src/models/block.rs
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A content block (Logseq-style)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Block {
|
||||
/// Unique block identifier (UUID)
|
||||
pub id: String,
|
||||
|
||||
/// Block content (markdown text, excluding nested blocks)
|
||||
pub content: String,
|
||||
|
||||
/// Block properties (tags, status, custom)
|
||||
pub properties: BlockProperties,
|
||||
|
||||
/// Child blocks (nested hierarchy)
|
||||
#[serde(default)]
|
||||
pub children: Vec<Block>,
|
||||
|
||||
/// Creation timestamp
|
||||
pub created: DateTime<Utc>,
|
||||
|
||||
/// Last modification timestamp
|
||||
pub modified: DateTime<Utc>,
|
||||
|
||||
/// Parent block ID (if nested)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub parent_id: Option<String>,
|
||||
}
|
||||
|
||||
/// Block-level properties
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct BlockProperties {
|
||||
/// Tags (e.g., #card, #important)
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
|
||||
/// Task status (TODO, DONE, etc.)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<TaskStatus>,
|
||||
|
||||
/// Custom properties (property:: value)
|
||||
#[serde(default)]
|
||||
pub custom: HashMap<String, String>,
|
||||
|
||||
/// Block references ((uuid))
|
||||
#[serde(default)]
|
||||
pub block_refs: Vec<String>,
|
||||
|
||||
/// Page references ([[page]])
|
||||
#[serde(default)]
|
||||
pub page_refs: Vec<String>,
|
||||
}
|
||||
|
||||
/// Task status for TODO blocks
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum TaskStatus {
|
||||
Todo,
|
||||
Doing,
|
||||
Done,
|
||||
Later,
|
||||
Now,
|
||||
Waiting,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
/// Create a new block with content
|
||||
pub fn new(content: String) -> Self {
|
||||
use uuid::Uuid;
|
||||
Self {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
content,
|
||||
properties: BlockProperties::default(),
|
||||
children: Vec::new(),
|
||||
created: Utc::now(),
|
||||
modified: Utc::now(),
|
||||
parent_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a child block
|
||||
pub fn add_child(&mut self, mut child: Block) {
|
||||
child.parent_id = Some(self.id.clone());
|
||||
self.children.push(child);
|
||||
self.modified = Utc::now();
|
||||
}
|
||||
|
||||
/// Add a tag to this block
|
||||
pub fn add_tag(&mut self, tag: String) {
|
||||
if !self.properties.tags.contains(&tag) {
|
||||
self.properties.tags.push(tag);
|
||||
self.modified = Utc::now();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set task status
|
||||
pub fn set_status(&mut self, status: TaskStatus) {
|
||||
self.properties.status = Some(status);
|
||||
self.modified = Utc::now();
|
||||
}
|
||||
|
||||
/// Get all blocks (self + descendants) as flat list
|
||||
pub fn flatten(&self) -> Vec<&Block> {
|
||||
let mut result = vec![self];
|
||||
for child in &self.children {
|
||||
result.extend(child.flatten());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Find block by ID in tree
|
||||
pub fn find(&self, id: &str) -> Option<&Block> {
|
||||
if self.id == id {
|
||||
return Some(self);
|
||||
}
|
||||
for child in &self.children {
|
||||
if let Some(found) = child.find(id) {
|
||||
return Some(found);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Phase 2: Update Node Model**
|
||||
|
||||
```rust
|
||||
// crates/kogral-core/src/models.rs (modifications)
|
||||
|
||||
use crate::models::block::Block;
|
||||
|
||||
pub struct Node {
|
||||
// ... existing fields ...
|
||||
pub content: String,
|
||||
|
||||
/// Structured blocks (optional, parsed from content)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub blocks: Option<Vec<Block>>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
/// Get blocks, parsing from content if needed
|
||||
pub fn get_blocks(&mut self) -> Result<&Vec<Block>> {
|
||||
if self.blocks.is_none() {
|
||||
self.blocks = Some(crate::parser::BlockParser::parse(&self.content)?);
|
||||
}
|
||||
Ok(self.blocks.as_ref().unwrap())
|
||||
}
|
||||
|
||||
/// Update content from blocks
|
||||
pub fn sync_blocks_to_content(&mut self) {
|
||||
if let Some(ref blocks) = self.blocks {
|
||||
self.content = crate::parser::BlockParser::serialize(blocks);
|
||||
}
|
||||
}
|
||||
|
||||
/// Find all blocks with a specific tag
|
||||
pub fn find_blocks_by_tag(&mut self, tag: &str) -> Result<Vec<&Block>> {
|
||||
let blocks = self.get_blocks()?;
|
||||
let mut result = Vec::new();
|
||||
for block in blocks {
|
||||
for b in block.flatten() {
|
||||
if b.properties.tags.iter().any(|t| t == tag) {
|
||||
result.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Find all TODO blocks
|
||||
pub fn find_todos(&mut self) -> Result<Vec<&Block>> {
|
||||
let blocks = self.get_blocks()?;
|
||||
let mut result = Vec::new();
|
||||
for block in blocks {
|
||||
for b in block.flatten() {
|
||||
if matches!(b.properties.status, Some(TaskStatus::Todo)) {
|
||||
result.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Phase 3: Block Parser**
|
||||
|
||||
```rust
|
||||
// crates/kogral-core/src/parser/block_parser.rs
|
||||
|
||||
use crate::models::block::{Block, BlockProperties, TaskStatus};
|
||||
use regex::Regex;
|
||||
|
||||
pub struct BlockParser;
|
||||
|
||||
impl BlockParser {
|
||||
/// Parse markdown content into block structure
|
||||
///
|
||||
/// Handles:
|
||||
/// - Outliner format (- prefix with indentation)
|
||||
/// - Tags (#card, #important)
|
||||
/// - Task status (TODO, DONE)
|
||||
/// - Properties (property:: value)
|
||||
/// - Block references (((uuid)))
|
||||
/// - Page references ([[page]])
|
||||
pub fn parse(content: &str) -> Result<Vec<Block>> {
|
||||
let mut blocks = Vec::new();
|
||||
let mut stack: Vec<(usize, Block)> = Vec::new(); // (indent_level, block)
|
||||
|
||||
for line in content.lines() {
|
||||
// Detect indentation level
|
||||
let indent = count_indent(line);
|
||||
let trimmed = line.trim_start();
|
||||
|
||||
// Skip empty lines
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse block line
|
||||
if let Some(block_content) = trimmed.strip_prefix("- ") {
|
||||
let mut block = Self::parse_block_line(block_content)?;
|
||||
|
||||
// Pop stack until we find parent level
|
||||
while let Some((level, _)) = stack.last() {
|
||||
if *level < indent {
|
||||
break;
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
// Add as child to parent or as root
|
||||
if let Some((_, parent)) = stack.last_mut() {
|
||||
parent.add_child(block.clone());
|
||||
} else {
|
||||
blocks.push(block.clone());
|
||||
}
|
||||
|
||||
stack.push((indent, block));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(blocks)
|
||||
}
|
||||
|
||||
/// Parse a single block line (after "- " prefix)
|
||||
fn parse_block_line(line: &str) -> Result<Block> {
|
||||
let mut block = Block::new(String::new());
|
||||
let mut properties = BlockProperties::default();
|
||||
|
||||
// Extract task status (TODO, DONE, etc.)
|
||||
let (status, remaining) = Self::extract_task_status(line);
|
||||
properties.status = status;
|
||||
|
||||
// Extract tags (#card, #important)
|
||||
let (tags, remaining) = Self::extract_tags(remaining);
|
||||
properties.tags = tags;
|
||||
|
||||
// Extract properties (property:: value)
|
||||
let (custom_props, remaining) = Self::extract_properties(remaining);
|
||||
properties.custom = custom_props;
|
||||
|
||||
// Extract block references (((uuid)))
|
||||
let (block_refs, remaining) = Self::extract_block_refs(remaining);
|
||||
properties.block_refs = block_refs;
|
||||
|
||||
// Extract page references ([[page]])
|
||||
let (page_refs, content) = Self::extract_page_refs(remaining);
|
||||
properties.page_refs = page_refs;
|
||||
|
||||
block.content = content.trim().to_string();
|
||||
block.properties = properties;
|
||||
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
/// Serialize blocks back to markdown
|
||||
pub fn serialize(blocks: &[Block]) -> String {
|
||||
let mut result = String::new();
|
||||
for block in blocks {
|
||||
Self::serialize_block(&mut result, block, 0);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn serialize_block(output: &mut String, block: &Block, indent: usize) {
|
||||
// Write indent
|
||||
for _ in 0..indent {
|
||||
output.push_str(" ");
|
||||
}
|
||||
|
||||
// Write prefix
|
||||
output.push_str("- ");
|
||||
|
||||
// Write task status
|
||||
if let Some(status) = block.properties.status {
|
||||
output.push_str(&format!("{:?} ", status).to_uppercase());
|
||||
}
|
||||
|
||||
// Write content
|
||||
output.push_str(&block.content);
|
||||
|
||||
// Write tags
|
||||
for tag in &block.properties.tags {
|
||||
output.push_str(&format!(" #{}", tag));
|
||||
}
|
||||
|
||||
// Write properties
|
||||
if !block.properties.custom.is_empty() {
|
||||
output.push('\n');
|
||||
for (key, value) in &block.properties.custom {
|
||||
for _ in 0..=indent {
|
||||
output.push_str(" ");
|
||||
}
|
||||
output.push_str(&format!("{}:: {}\n", key, value));
|
||||
}
|
||||
}
|
||||
|
||||
output.push('\n');
|
||||
|
||||
// Write children recursively
|
||||
for child in &block.children {
|
||||
Self::serialize_block(output, child, indent + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods for extraction
|
||||
fn extract_task_status(line: &str) -> (Option<TaskStatus>, &str) {
|
||||
let line = line.trim_start();
|
||||
if let Some(rest) = line.strip_prefix("TODO ") {
|
||||
(Some(TaskStatus::Todo), rest)
|
||||
} else if let Some(rest) = line.strip_prefix("DONE ") {
|
||||
(Some(TaskStatus::Done), rest)
|
||||
} else if let Some(rest) = line.strip_prefix("DOING ") {
|
||||
(Some(TaskStatus::Doing), rest)
|
||||
} else if let Some(rest) = line.strip_prefix("LATER ") {
|
||||
(Some(TaskStatus::Later), rest)
|
||||
} else if let Some(rest) = line.strip_prefix("NOW ") {
|
||||
(Some(TaskStatus::Now), rest)
|
||||
} else if let Some(rest) = line.strip_prefix("WAITING ") {
|
||||
(Some(TaskStatus::Waiting), rest)
|
||||
} else if let Some(rest) = line.strip_prefix("CANCELLED ") {
|
||||
(Some(TaskStatus::Cancelled), rest)
|
||||
} else {
|
||||
(None, line)
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_tags(line: &str) -> (Vec<String>, String) {
|
||||
let tag_regex = Regex::new(r"#(\w+)").unwrap();
|
||||
let mut tags = Vec::new();
|
||||
let mut result = line.to_string();
|
||||
|
||||
for cap in tag_regex.captures_iter(line) {
|
||||
if let Some(tag) = cap.get(1) {
|
||||
tags.push(tag.as_str().to_string());
|
||||
result = result.replace(&format!("#{}", tag.as_str()), "");
|
||||
}
|
||||
}
|
||||
|
||||
(tags, result.trim().to_string())
|
||||
}
|
||||
|
||||
fn extract_properties(line: &str) -> (HashMap<String, String>, String) {
|
||||
let prop_regex = Regex::new(r"(\w+)::\s*([^\n]+)").unwrap();
|
||||
let mut props = HashMap::new();
|
||||
let mut result = line.to_string();
|
||||
|
||||
for cap in prop_regex.captures_iter(line) {
|
||||
if let (Some(key), Some(value)) = (cap.get(1), cap.get(2)) {
|
||||
props.insert(key.as_str().to_string(), value.as_str().trim().to_string());
|
||||
result = result.replace(&cap[0], "");
|
||||
}
|
||||
}
|
||||
|
||||
(props, result.trim().to_string())
|
||||
}
|
||||
|
||||
fn extract_block_refs(line: &str) -> (Vec<String>, String) {
|
||||
let ref_regex = Regex::new(r"\(\(([^)]+)\)\)").unwrap();
|
||||
let mut refs = Vec::new();
|
||||
let mut result = line.to_string();
|
||||
|
||||
for cap in ref_regex.captures_iter(line) {
|
||||
if let Some(uuid) = cap.get(1) {
|
||||
refs.push(uuid.as_str().to_string());
|
||||
result = result.replace(&cap[0], "");
|
||||
}
|
||||
}
|
||||
|
||||
(refs, result.trim().to_string())
|
||||
}
|
||||
|
||||
fn extract_page_refs(line: &str) -> (Vec<String>, String) {
|
||||
let page_regex = Regex::new(r"\[\[([^\]]+)\]\]").unwrap();
|
||||
let mut pages = Vec::new();
|
||||
let result = line.to_string();
|
||||
|
||||
for cap in page_regex.captures_iter(line) {
|
||||
if let Some(page) = cap.get(1) {
|
||||
pages.push(page.as_str().to_string());
|
||||
// Keep [[page]] in content for now (backward compat)
|
||||
}
|
||||
}
|
||||
|
||||
(pages, result)
|
||||
}
|
||||
}
|
||||
|
||||
fn count_indent(line: &str) -> usize {
|
||||
line.chars().take_while(|c| c.is_whitespace()).count() / 2
|
||||
}
|
||||
```
|
||||
|
||||
**Phase 4: Logseq Import/Export**
|
||||
|
||||
```rust
|
||||
// crates/kogral-core/src/logseq.rs
|
||||
|
||||
use crate::models::{Node, NodeType};
|
||||
use crate::models::block::Block;
|
||||
use crate::parser::BlockParser;
|
||||
|
||||
pub struct LogseqImporter;
|
||||
|
||||
impl LogseqImporter {
|
||||
/// Import a Logseq page (markdown file) as a Node
|
||||
pub fn import_page(path: &Path) -> Result<Node> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
|
||||
// Extract frontmatter if present
|
||||
let (frontmatter, body) = Self::split_frontmatter(&content);
|
||||
|
||||
// Parse blocks from body
|
||||
let blocks = BlockParser::parse(&body)?;
|
||||
|
||||
// Create node with blocks
|
||||
let mut node = Node::new(NodeType::Note, Self::extract_title(path));
|
||||
node.content = body;
|
||||
node.blocks = Some(blocks);
|
||||
|
||||
// Apply frontmatter properties
|
||||
if let Some(fm) = frontmatter {
|
||||
Self::apply_frontmatter(&mut node, &fm)?;
|
||||
}
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
fn split_frontmatter(content: &str) -> (Option<String>, String) {
|
||||
if content.starts_with("---\n") {
|
||||
if let Some(end) = content[4..].find("\n---\n") {
|
||||
let frontmatter = content[4..4 + end].to_string();
|
||||
let body = content[4 + end + 5..].to_string();
|
||||
return (Some(frontmatter), body);
|
||||
}
|
||||
}
|
||||
(None, content.to_string())
|
||||
}
|
||||
|
||||
fn extract_title(path: &Path) -> String {
|
||||
path.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("Untitled")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn apply_frontmatter(node: &mut Node, frontmatter: &str) -> Result<()> {
|
||||
// Parse YAML frontmatter and apply to node
|
||||
// ... implementation ...
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LogseqExporter;
|
||||
|
||||
impl LogseqExporter {
|
||||
/// Export a Node to Logseq page format
|
||||
pub fn export_page(node: &Node, path: &Path) -> Result<()> {
|
||||
let mut output = String::new();
|
||||
|
||||
// Generate frontmatter
|
||||
output.push_str("---\n");
|
||||
output.push_str(&Self::generate_frontmatter(node)?);
|
||||
output.push_str("---\n\n");
|
||||
|
||||
// Serialize blocks or use content
|
||||
if let Some(ref blocks) = node.blocks {
|
||||
output.push_str(&BlockParser::serialize(blocks));
|
||||
} else {
|
||||
output.push_str(&node.content);
|
||||
}
|
||||
|
||||
std::fs::write(path, output)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_frontmatter(node: &Node) -> Result<String> {
|
||||
let mut fm = String::new();
|
||||
fm.push_str(&format!("title: {}\n", node.title));
|
||||
fm.push_str(&format!("tags: {}\n", node.tags.join(", ")));
|
||||
// ... more frontmatter fields ...
|
||||
Ok(fm)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Query API Extensions
|
||||
|
||||
```rust
|
||||
// New methods in Graph or Query module
|
||||
|
||||
impl Graph {
|
||||
/// Find all blocks with a specific tag across all nodes
|
||||
pub fn find_blocks_by_tag(&mut self, tag: &str) -> Vec<(&Node, &Block)> {
|
||||
let mut results = Vec::new();
|
||||
for node in self.nodes.values_mut() {
|
||||
if let Ok(blocks) = node.find_blocks_by_tag(tag) {
|
||||
for block in blocks {
|
||||
results.push((node as &Node, block));
|
||||
}
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
/// Find all flashcards (#card blocks)
|
||||
pub fn find_flashcards(&mut self) -> Vec<(&Node, &Block)> {
|
||||
self.find_blocks_by_tag("card")
|
||||
}
|
||||
|
||||
/// Find all TODO items across knowledge base
|
||||
pub fn find_all_todos(&mut self) -> Vec<(&Node, &Block)> {
|
||||
let mut results = Vec::new();
|
||||
for node in self.nodes.values_mut() {
|
||||
if let Ok(todos) = node.find_todos() {
|
||||
for block in todos {
|
||||
results.push((node as &Node, block));
|
||||
}
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MCP Tool Extensions
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "kogral/find_blocks",
|
||||
"description": "Find blocks by tag, status, or properties",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": { "type": "string", "description": "Filter by tag (e.g., 'card')" },
|
||||
"status": { "type": "string", "enum": ["TODO", "DONE", "DOING"] },
|
||||
"property": { "type": "string", "description": "Custom property key" },
|
||||
"value": { "type": "string", "description": "Property value to match" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```nickel
|
||||
# schemas/contracts.ncl (additions)
|
||||
|
||||
BlockConfig = {
|
||||
enabled | Bool
|
||||
| doc "Enable block-level parsing and storage"
|
||||
| default = true,
|
||||
|
||||
preserve_hierarchy | Bool
|
||||
| doc "Preserve block nesting on import/export"
|
||||
| default = true,
|
||||
|
||||
parse_on_load | Bool
|
||||
| doc "Automatically parse blocks when loading nodes"
|
||||
| default = false, # Lazy parsing by default
|
||||
|
||||
supported_statuses | Array String
|
||||
| doc "Supported task statuses"
|
||||
| default = ["TODO", "DONE", "DOING", "LATER", "NOW", "WAITING", "CANCELLED"],
|
||||
}
|
||||
|
||||
KbConfig = {
|
||||
# ... existing fields ...
|
||||
|
||||
blocks | BlockConfig
|
||||
| doc "Block-level features configuration"
|
||||
| default = {},
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Path
|
||||
|
||||
**Phase 1**: Add Block models (no behavior change)
|
||||
**Phase 2**: Add BlockParser (opt-in via config)
|
||||
**Phase 3**: Update Logseq import/export
|
||||
**Phase 4**: Add block queries to CLI/MCP
|
||||
**Phase 5**: SurrealDB block indexing
|
||||
|
||||
**Backward Compatibility**:
|
||||
- Existing nodes without `blocks` field work as before
|
||||
- `content` remains source of truth
|
||||
- `blocks` is optional cache/structure
|
||||
- Config flag `blocks.enabled` to opt-in
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_simple_block() {
|
||||
let content = "- This is a block #card";
|
||||
let blocks = BlockParser::parse(content).unwrap();
|
||||
|
||||
assert_eq!(blocks.len(), 1);
|
||||
assert_eq!(blocks[0].content, "This is a block");
|
||||
assert_eq!(blocks[0].properties.tags, vec!["card"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_nested_blocks() {
|
||||
let content = r#"
|
||||
- Parent block
|
||||
- Child block 1
|
||||
- Child block 2
|
||||
- Grandchild
|
||||
"#;
|
||||
let blocks = BlockParser::parse(content).unwrap();
|
||||
|
||||
assert_eq!(blocks.len(), 1);
|
||||
assert_eq!(blocks[0].children.len(), 2);
|
||||
assert_eq!(blocks[0].children[1].children.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_todo() {
|
||||
let content = "- TODO Implement feature #rust";
|
||||
let blocks = BlockParser::parse(content).unwrap();
|
||||
|
||||
assert_eq!(blocks[0].properties.status, Some(TaskStatus::Todo));
|
||||
assert_eq!(blocks[0].content, "Implement feature");
|
||||
assert_eq!(blocks[0].properties.tags, vec!["rust"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
let original = r#"- Block 1 #card
|
||||
- Nested
|
||||
- TODO Block 2
|
||||
priority:: high
|
||||
"#;
|
||||
let blocks = BlockParser::parse(original).unwrap();
|
||||
let serialized = BlockParser::serialize(&blocks);
|
||||
let reparsed = BlockParser::parse(&serialized).unwrap();
|
||||
|
||||
assert_eq!(blocks.len(), reparsed.len());
|
||||
assert_eq!(blocks[0].properties, reparsed[0].properties);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Recommended Approach**: Hybrid (Option C)
|
||||
|
||||
- **Add** `Block` struct with properties, hierarchy
|
||||
- **Extend** `Node` with optional `blocks: Option<Vec<Block>>`
|
||||
- **Implement** bidirectional parser (markdown ↔ blocks)
|
||||
- **Preserve** `content` as source of truth (backward compat)
|
||||
- **Enable** block queries in CLI/MCP
|
||||
- **Support** round-trip Logseq import/export
|
||||
|
||||
**Benefits**:
|
||||
- ✅ Full Logseq compatibility
|
||||
- ✅ Queryable blocks (find #card, TODO, etc.)
|
||||
- ✅ Backward compatible
|
||||
- ✅ Extensible (custom properties)
|
||||
- ✅ Type-safe structure
|
||||
|
||||
**Trade-offs**:
|
||||
- ⚠️ Added complexity
|
||||
- ⚠️ Need to sync content ↔ blocks
|
||||
- ⚠️ More storage for SurrealDB backend
|
||||
|
||||
**Next Steps**:
|
||||
1. Review and approve design
|
||||
2. Implement Phase 1 (Block models)
|
||||
3. Implement Phase 2 (BlockParser)
|
||||
4. Update Logseq import/export
|
||||
5. Add block queries to MCP/CLI
|
||||
486
docs/architecture/overview.md
Normal file
@ -0,0 +1,486 @@
|
||||
# System Architecture
|
||||
|
||||
Comprehensive overview of the KOGRAL architecture.
|
||||
|
||||
## High-Level Architecture
|
||||
|
||||

|
||||
|
||||
The KOGRAL consists of three main layers:
|
||||
|
||||
1. **User Interfaces**: kogral-cli (terminal), kogral-mcp (AI integration), NuShell scripts (automation)
|
||||
2. **Core Library (kogral-core)**: Rust library with graph engine, storage abstraction, embeddings, query engine
|
||||
3. **Storage Backends**: Filesystem (git-friendly), SurrealDB (scalable), In-Memory (cache/testing)
|
||||
|
||||
## Component Details
|
||||
|
||||
### kogral-cli (Command-Line Interface)
|
||||
|
||||
**Purpose**: Primary user interface for local knowledge management.
|
||||
|
||||
**Commands** (13 total):
|
||||
- `init`: Initialize `.kogral/` directory
|
||||
- `add`: Create nodes (note, decision, guideline, pattern, journal)
|
||||
- `search`: Text and semantic search
|
||||
- `link`: Create relationships between nodes
|
||||
- `list`: List all nodes
|
||||
- `show`: Display node details
|
||||
- `delete`: Remove nodes
|
||||
- `graph`: Visualize knowledge graph
|
||||
- `sync`: Sync filesystem ↔ SurrealDB
|
||||
- `serve`: Start MCP server
|
||||
- `import`: Import from Logseq
|
||||
- `export`: Export to Logseq/JSON
|
||||
- `config`: Manage configuration
|
||||
|
||||
**Technology**: Rust + clap (derive API)
|
||||
|
||||
**Features**:
|
||||
- Colored terminal output
|
||||
- Interactive prompts
|
||||
- Dry-run modes
|
||||
- Validation before operations
|
||||
|
||||
### kogral-mcp (MCP Server)
|
||||
|
||||
**Purpose**: AI integration via Model Context Protocol.
|
||||
|
||||
**Protocol**: JSON-RPC 2.0 over stdio
|
||||
|
||||
**Components**:
|
||||
|
||||
1. **Tools** (7):
|
||||
- `kogral/search`: Query knowledge base
|
||||
- `kogral/add_note`: Create notes
|
||||
- `kogral/add_decision`: Create ADRs
|
||||
- `kogral/link`: Create relationships
|
||||
- `kogral/get_guidelines`: Retrieve guidelines with inheritance
|
||||
- `kb/list_graphs`: List available graphs
|
||||
- `kogral/export`: Export to formats
|
||||
|
||||
2. **Resources** (6 URIs):
|
||||
- `kogral://project/notes`
|
||||
- `kogral://project/decisions`
|
||||
- `kogral://project/guidelines`
|
||||
- `kogral://project/patterns`
|
||||
- `kogral://shared/guidelines`
|
||||
- `kogral://shared/patterns`
|
||||
|
||||
3. **Prompts** (2):
|
||||
- `kogral/summarize_project`: Generate project summary
|
||||
- `kogral/find_related`: Find related nodes
|
||||
|
||||
**Integration**: Claude Code via `~/.config/claude/config.json`
|
||||
|
||||
### NuShell Scripts
|
||||
|
||||
**Purpose**: Automation and maintenance tasks.
|
||||
|
||||
**Scripts** (6):
|
||||
- `kogral-sync.nu`: Filesystem ↔ SurrealDB sync
|
||||
- `kogral-backup.nu`: Archive knowledge base
|
||||
- `kogral-reindex.nu`: Rebuild embeddings
|
||||
- `kogral-import-logseq.nu`: Import from Logseq
|
||||
- `kogral-export-logseq.nu`: Export to Logseq
|
||||
- `kogral-stats.nu`: Graph statistics
|
||||
|
||||
**Features**:
|
||||
- Colored output
|
||||
- Dry-run modes
|
||||
- Progress indicators
|
||||
- Error handling
|
||||
|
||||
## Core Library (kogral-core)
|
||||
|
||||
### Models
|
||||
|
||||
**Graph**:
|
||||
|
||||
```rust
|
||||
pub struct Graph {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub nodes: HashMap<String, Node>, // ID → Node
|
||||
pub edges: Vec<Edge>,
|
||||
pub metadata: HashMap<String, Value>,
|
||||
}
|
||||
```
|
||||
|
||||
**Node**:
|
||||
|
||||
```rust
|
||||
pub struct Node {
|
||||
pub id: String,
|
||||
pub node_type: NodeType,
|
||||
pub title: String,
|
||||
pub content: String,
|
||||
pub tags: Vec<String>,
|
||||
pub status: NodeStatus,
|
||||
pub created: DateTime<Utc>,
|
||||
pub modified: DateTime<Utc>,
|
||||
// ... relationships, metadata
|
||||
}
|
||||
```
|
||||
|
||||
**Edge**:
|
||||
|
||||
```rust
|
||||
pub struct Edge {
|
||||
pub from: String,
|
||||
pub to: String,
|
||||
pub relation: EdgeType,
|
||||
pub strength: f32,
|
||||
pub created: DateTime<Utc>,
|
||||
}
|
||||
```
|
||||
|
||||
### Storage Trait
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
/// Save a complete graph to storage
|
||||
async fn save_graph(&mut self, graph: &Graph) -> Result<()>;
|
||||
|
||||
/// Load a graph from storage
|
||||
async fn load_graph(&self, name: &str) -> Result<Graph>;
|
||||
|
||||
/// Save a single node to storage
|
||||
async fn save_node(&mut self, node: &Node) -> Result<()>;
|
||||
|
||||
/// Load a node by ID
|
||||
async fn load_node(&self, graph_name: &str, node_id: &str) -> Result<Node>;
|
||||
|
||||
/// Delete a node
|
||||
async fn delete_node(&mut self, graph_name: &str, node_id: &str) -> Result<()>;
|
||||
|
||||
/// List all graphs
|
||||
async fn list_graphs(&self) -> Result<Vec<String>>;
|
||||
|
||||
/// List nodes in a graph, optionally filtered by type
|
||||
async fn list_nodes(&self, graph_name: &str, node_type: Option<&str>) -> Result<Vec<Node>>;
|
||||
}
|
||||
```
|
||||
|
||||
**Implementations**:
|
||||
1. `FilesystemStorage`: Git-friendly markdown files
|
||||
2. `MemoryStorage`: In-memory with DashMap
|
||||
3. `SurrealDbStorage`: Scalable graph database
|
||||
|
||||
### Embedding Provider Trait
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait EmbeddingProvider: Send + Sync {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
|
||||
fn dimensions(&self) -> usize;
|
||||
fn model_name(&self) -> &str;
|
||||
}
|
||||
```
|
||||
|
||||
**Implementations**:
|
||||
1. `FastEmbedProvider`: Local fastembed
|
||||
2. `RigEmbeddingProvider`: OpenAI, Claude, Ollama (via rig-core)
|
||||
|
||||
### Parser
|
||||
|
||||
**Input**: Markdown file with YAML frontmatter
|
||||
|
||||
**Output**: `Node` struct
|
||||
|
||||
**Features**:
|
||||
- YAML frontmatter extraction
|
||||
- Markdown body parsing
|
||||
- Wikilink detection (`[[linked-note]]`)
|
||||
- Code reference parsing (`@file.rs:42`)
|
||||
|
||||
**Example**:
|
||||
|
||||
```markdown
|
||||
---
|
||||
id: note-123
|
||||
type: note
|
||||
title: My Note
|
||||
tags: [rust, async]
|
||||
---
|
||||
|
||||
# My Note
|
||||
|
||||
Content with [[other-note]] and @src/main.rs:10
|
||||
```
|
||||
|
||||
→
|
||||
|
||||
```rust
|
||||
Node {
|
||||
id: "note-123",
|
||||
node_type: NodeType::Note,
|
||||
title: "My Note",
|
||||
content: "Content with [[other-note]] and @src/main.rs:10",
|
||||
tags: vec!["rust", "async"],
|
||||
// ... parsed wikilinks, code refs
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration System
|
||||
|
||||
### Nickel Schema
|
||||
|
||||
```nickel
|
||||
# schemas/kogral-config.ncl
|
||||
{
|
||||
KbConfig = {
|
||||
graph | GraphConfig,
|
||||
storage | StorageConfig,
|
||||
embeddings | EmbeddingConfig,
|
||||
templates | TemplateConfig,
|
||||
query | QueryConfig,
|
||||
mcp | McpConfig,
|
||||
sync | SyncConfig,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Loading Process
|
||||
|
||||
```text
|
||||
User writes: .kogral/config.ncl
|
||||
↓ [nickel export --format json]
|
||||
JSON intermediate
|
||||
↓ [serde_json::from_str]
|
||||
KbConfig struct (Rust)
|
||||
↓
|
||||
Runtime behavior
|
||||
```
|
||||
|
||||
**Double Validation**:
|
||||
1. Nickel contracts: Type-safe, enum validation
|
||||
2. Serde deserialization: Rust type checking
|
||||
|
||||
**Benefits**:
|
||||
- Errors caught at export time
|
||||
- Runtime guaranteed valid config
|
||||
- Self-documenting schemas
|
||||
|
||||
## Storage Architecture
|
||||
|
||||
### Hybrid Strategy
|
||||
|
||||
**Local Graph** (per project):
|
||||
- Storage: Filesystem (`.kogral/` directory)
|
||||
- Format: Markdown + YAML frontmatter
|
||||
- Version control: Git
|
||||
- Scope: Project-specific knowledge
|
||||
|
||||
**Shared Graph** (organization):
|
||||
- Storage: SurrealDB (or synced filesystem)
|
||||
- Format: Same markdown (for compatibility)
|
||||
- Version control: Optional
|
||||
- Scope: Organization-wide guidelines
|
||||
|
||||
**Sync**:
|
||||
|
||||
```text
|
||||
Filesystem (.kogral/)
|
||||
↕ [bidirectional sync]
|
||||
SurrealDB (central)
|
||||
```
|
||||
|
||||
### File Layout
|
||||
|
||||
```text
|
||||
.kogral/
|
||||
├── config.toml # Graph metadata
|
||||
├── notes/
|
||||
│ ├── async-patterns.md # Individual note
|
||||
│ └── error-handling.md
|
||||
├── decisions/
|
||||
│ ├── 0001-use-rust.md # ADR format
|
||||
│ └── 0002-surrealdb.md
|
||||
├── guidelines/
|
||||
│ ├── rust-errors.md # Project guideline
|
||||
│ └── testing.md
|
||||
├── patterns/
|
||||
│ └── repository.md
|
||||
└── journal/
|
||||
├── 2026-01-17.md # Daily journal
|
||||
└── 2026-01-18.md
|
||||
```
|
||||
|
||||
## Query Engine
|
||||
|
||||
### Text Search
|
||||
|
||||
```rust
|
||||
let results = graph.nodes.values()
|
||||
.filter(|node| {
|
||||
node.title.contains(&query) ||
|
||||
node.content.contains(&query) ||
|
||||
node.tags.iter().any(|tag| tag.contains(&query))
|
||||
})
|
||||
.collect();
|
||||
```
|
||||
|
||||
### Semantic Search
|
||||
|
||||
```rust
|
||||
let query_embedding = embeddings.embed(vec![query]).await?;
|
||||
let mut scored: Vec<_> = graph.nodes.values()
|
||||
.filter_map(|node| {
|
||||
let node_embedding = node.embedding.as_ref()?;
|
||||
let similarity = cosine_similarity(&query_embedding[0], node_embedding);
|
||||
(similarity >= threshold).then_some((node, similarity))
|
||||
})
|
||||
.collect();
|
||||
scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
```
|
||||
|
||||
### Cross-Graph Query
|
||||
|
||||
```rust
|
||||
// Query both project and shared graphs
|
||||
let project_results = project_graph.search(&query).await?;
|
||||
let shared_results = shared_graph.search(&query).await?;
|
||||
|
||||
// Merge with deduplication
|
||||
let combined = merge_results(project_results, shared_results);
|
||||
```
|
||||
|
||||
## MCP Protocol Flow
|
||||
|
||||
```text
|
||||
Claude Code kogral-mcp kogral-core
|
||||
│ │ │
|
||||
├─ JSON-RPC request ───→ │ │
|
||||
│ kogral/search │ │
|
||||
│ {"query": "rust"} │ │
|
||||
│ ├─ search() ──────────→ │
|
||||
│ │ │
|
||||
│ │ Query engine
|
||||
│ │ Text + semantic
|
||||
│ │ │
|
||||
│ │ ←──── results ─────────┤
|
||||
│ │ │
|
||||
│ ←─ JSON-RPC response ──┤ │
|
||||
│ {"results": [...]} │ │
|
||||
```
|
||||
|
||||
## Template System
|
||||
|
||||
**Engine**: Tera (Jinja2-like)
|
||||
|
||||
**Templates**:
|
||||
|
||||
1. **Document Templates** (6):
|
||||
- `note.md.tera`
|
||||
- `decision.md.tera`
|
||||
- `guideline.md.tera`
|
||||
- `pattern.md.tera`
|
||||
- `journal.md.tera`
|
||||
- `execution.md.tera`
|
||||
|
||||
2. **Export Templates** (4):
|
||||
- `logseq-page.md.tera`
|
||||
- `logseq-journal.md.tera`
|
||||
- `summary.md.tera`
|
||||
- `graph.json.tera`
|
||||
|
||||
**Usage**:
|
||||
|
||||
```rust
|
||||
let mut tera = Tera::new("templates/**/*.tera")?;
|
||||
let rendered = tera.render("note.md.tera", &context)?;
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Strategy**: `thiserror` for structured errors
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum KbError {
|
||||
#[error("Storage error: {0}")]
|
||||
Storage(String),
|
||||
|
||||
#[error("Node not found: {0}")]
|
||||
NodeNotFound(String),
|
||||
|
||||
#[error("Configuration error: {0}")]
|
||||
Config(String),
|
||||
|
||||
#[error("Parse error: {0}")]
|
||||
Parse(String),
|
||||
|
||||
#[error("Embedding error: {0}")]
|
||||
Embedding(String),
|
||||
}
|
||||
```
|
||||
|
||||
**Propagation**: `?` operator throughout
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
**Unit Tests**: Per module (models, parser, storage)
|
||||
|
||||
**Integration Tests**: Full workflow (add → save → load → query)
|
||||
|
||||
**Test Coverage**:
|
||||
- kogral-core: 48 tests
|
||||
- kogral-mcp: 5 tests
|
||||
- Total: 56 tests
|
||||
|
||||
**Test Data**: Fixtures in `tests/fixtures/`
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
**Node Lookup**: O(1) via HashMap
|
||||
|
||||
**Semantic Search**: O(n) with early termination (threshold filter)
|
||||
|
||||
**Storage**:
|
||||
- Filesystem: Lazy loading (load on demand)
|
||||
- Memory: Full graph in RAM
|
||||
- SurrealDB: Query optimization (indexes)
|
||||
|
||||
**Embeddings**:
|
||||
- Cache embeddings in node metadata
|
||||
- Batch processing (configurable batch size)
|
||||
- Async generation (non-blocking)
|
||||
|
||||
## Security
|
||||
|
||||
**No unsafe code**: `#![forbid(unsafe_code)]`
|
||||
|
||||
**Input validation**:
|
||||
- Nickel contracts validate config
|
||||
- serde validates JSON
|
||||
- Custom validation for user input
|
||||
|
||||
**File operations**:
|
||||
- Path sanitization (no `../` traversal)
|
||||
- Permissions checking
|
||||
- Atomic writes (temp file + rename)
|
||||
|
||||
## Scalability
|
||||
|
||||
**Small Projects** (< 1000 nodes):
|
||||
- Filesystem storage
|
||||
- In-memory search
|
||||
- Local embeddings (fastembed)
|
||||
|
||||
**Medium Projects** (1000-10,000 nodes):
|
||||
- Filesystem + SurrealDB sync
|
||||
- Semantic search with caching
|
||||
- Cloud embeddings (OpenAI/Claude)
|
||||
|
||||
**Large Organizations** (> 10,000 nodes):
|
||||
- SurrealDB primary
|
||||
- Distributed embeddings
|
||||
- Multi-graph federation
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **Graph Model Details**: [Graph Model](graph-model.md)
|
||||
- **Storage Deep Dive**: [Storage Architecture](storage-architecture.md)
|
||||
- **ADRs**: [Architectural Decisions](adrs/001-nickel-vs-toml.md)
|
||||
- **Implementation**: [Development Guide](../contributing/development.md)
|
||||
1
docs/architecture/storage-architecture.md
Normal file
@ -0,0 +1 @@
|
||||
# Storage Architecture
|
||||
38
docs/book.toml
Normal file
@ -0,0 +1,38 @@
|
||||
[book]
|
||||
title = "KOGRAL Documentation"
|
||||
authors = ["KOGRAL Team"]
|
||||
description = "Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams"
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "."
|
||||
|
||||
[build]
|
||||
build-dir = "book"
|
||||
create-missing = true
|
||||
|
||||
[output.html]
|
||||
default-theme = "rust"
|
||||
preferred-dark-theme = "navy"
|
||||
git-repository-url = "https://github.com/your-org/knowledge-base"
|
||||
edit-url-template = "https://github.com/your-org/knowledge-base/edit/main/docs/{path}"
|
||||
|
||||
[output.html.print]
|
||||
enable = true
|
||||
|
||||
[output.html.fold]
|
||||
enable = true
|
||||
level = 1
|
||||
|
||||
[output.html.search]
|
||||
enable = true
|
||||
limit-results = 30
|
||||
teaser-word-count = 30
|
||||
use-boolean-and = true
|
||||
boost-title = 2
|
||||
boost-hierarchy = 1
|
||||
boost-paragraph = 1
|
||||
expand = true
|
||||
heading-split-level = 3
|
||||
|
||||
[preprocessor.links]
|
||||
[preprocessor.index]
|
||||
1
docs/book/.nojekyll
Normal file
@ -0,0 +1 @@
|
||||
This file makes sure that Github Pages doesn't process mdBook's output.
|
||||
214
docs/book/404.html
Normal file
@ -0,0 +1,214 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Page not found - KOGRAL Documentation</title>
|
||||
<base href="/">
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="favicon.svg">
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
<link rel="stylesheet" href="css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="document-not-found-404"><a class="header" href="#document-not-found-404">Document not found (404)</a></h1>
|
||||
<p>This URL is invalid, sorry. Please use the navigation bar or search to continue.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="elasticlunr.min.js"></script>
|
||||
<script src="mark.min.js"></script>
|
||||
<script src="searcher.js"></script>
|
||||
|
||||
<script src="clipboard.min.js"></script>
|
||||
<script src="highlight.js"></script>
|
||||
<script src="book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
4
docs/book/FontAwesome/css/font-awesome.css
vendored
Normal file
BIN
docs/book/FontAwesome/fonts/FontAwesome.ttf
Normal file
BIN
docs/book/FontAwesome/fonts/fontawesome-webfont.eot
Normal file
2671
docs/book/FontAwesome/fonts/fontawesome-webfont.svg
Normal file
|
After Width: | Height: | Size: 434 KiB |
BIN
docs/book/FontAwesome/fonts/fontawesome-webfont.ttf
Normal file
BIN
docs/book/FontAwesome/fonts/fontawesome-webfont.woff
Normal file
BIN
docs/book/FontAwesome/fonts/fontawesome-webfont.woff2
Normal file
227
docs/book/ai/claude.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Claude Integration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./ai/claude.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="claude-integration"><a class="header" href="#claude-integration">Claude Integration</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../ai/openai.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/ollama.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../ai/openai.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/ollama.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/ai/embeddings-overview.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Embeddings Overview - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./ai/embeddings-overview.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="embeddings-overview"><a class="header" href="#embeddings-overview">Embeddings Overview</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../storage/sync.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/providers.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../storage/sync.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/providers.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/ai/fastembed.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>FastEmbed Local - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./ai/fastembed.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="fastembed-local"><a class="header" href="#fastembed-local">FastEmbed Local</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../ai/providers.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/openai.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../ai/providers.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/openai.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/ai/ollama.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Ollama Integration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./ai/ollama.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="ollama-integration"><a class="header" href="#ollama-integration">Ollama Integration</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../ai/claude.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/semantic-search.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../ai/claude.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/semantic-search.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/ai/openai.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>OpenAI Integration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./ai/openai.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="openai-integration"><a class="header" href="#openai-integration">OpenAI Integration</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../ai/fastembed.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/claude.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../ai/fastembed.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/claude.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/ai/providers.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Provider Configuration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./ai/providers.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="provider-configuration"><a class="header" href="#provider-configuration">Provider Configuration</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../ai/embeddings-overview.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/fastembed.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../ai/embeddings-overview.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../ai/fastembed.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/ai/semantic-search.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Semantic Search - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./ai/semantic-search.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="semantic-search"><a class="header" href="#semantic-search">Semantic Search</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../ai/ollama.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../templates/overview.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../ai/ollama.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../templates/overview.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/api/mcp-protocol.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>MCP Protocol - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./api/mcp-protocol.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="mcp-protocol"><a class="header" href="#mcp-protocol">MCP Protocol</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../apps/git.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/mcp-tools.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../apps/git.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/mcp-tools.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/api/mcp-resources.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Resources - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./api/mcp-resources.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="resources"><a class="header" href="#resources">Resources</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../api/mcp-tools.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/rust-api.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../api/mcp-tools.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/rust-api.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
942
docs/book/api/mcp-tools.html
Normal file
@ -0,0 +1,942 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Tools Reference - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./api/mcp-tools.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="mcp-tools-api-reference"><a class="header" href="#mcp-tools-api-reference">MCP Tools API Reference</a></h1>
|
||||
<p>Complete reference for the Model Context Protocol (MCP) server tools and resources.</p>
|
||||
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
|
||||
<p>The kogral-mcp server implements the MCP protocol (JSON-RPC 2.0) for Claude Code integration. It provides:</p>
|
||||
<ul>
|
||||
<li><strong>10 Tools</strong>: Operations for querying and modifying knowledge base (7 core + 3 block tools)</li>
|
||||
<li><strong>6 Resources</strong>: Access to knowledge graph content via URIs</li>
|
||||
<li><strong>2 Prompts</strong>: Guided workflows for common tasks</li>
|
||||
</ul>
|
||||
<h2 id="server-configuration"><a class="header" href="#server-configuration">Server Configuration</a></h2>
|
||||
<h3 id="start-mcp-server"><a class="header" href="#start-mcp-server">Start MCP Server</a></h3>
|
||||
<pre><code class="language-bash"># Stdio transport (local use)
|
||||
kb serve
|
||||
|
||||
# Or run directly
|
||||
kb-mcp serve
|
||||
</code></pre>
|
||||
<h3 id="claude-code-configuration"><a class="header" href="#claude-code-configuration">Claude Code Configuration</a></h3>
|
||||
<p>Add to <code>~/.config/claude/config.json</code>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"mcpServers": {
|
||||
"kogral-mcp": {
|
||||
"command": "/path/to/kb-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"KOGRAL_DIR": "/path/to/project/.kogral"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="tools"><a class="header" href="#tools">Tools</a></h2>
|
||||
<h3 id="kbsearch"><a class="header" href="#kbsearch">kb/search</a></h3>
|
||||
<p>Search the knowledge base using text and/or semantic similarity.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Search query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["note", "decision", "guideline", "pattern", "journal", "execution", "all"],
|
||||
"description": "Filter by node type",
|
||||
"default": "all"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Limit search to specific project graph"
|
||||
},
|
||||
"semantic": {
|
||||
"type": "boolean",
|
||||
"description": "Enable semantic similarity search",
|
||||
"default": true
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number",
|
||||
"description": "Minimum similarity threshold (0-1)",
|
||||
"default": 0.4
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of results",
|
||||
"default": 10
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "kogral/search",
|
||||
"params": {
|
||||
"query": "error handling patterns in Rust",
|
||||
"type": "pattern",
|
||||
"semantic": true,
|
||||
"threshold": 0.6,
|
||||
"limit": 5
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 3 result(s):\n\n1. Error Handling with thiserror (pattern, score: 0.85)\n Tags: rust, error-handling\n Created: 2026-01-15\n \n2. Result Type Best Practices (guideline, score: 0.72)\n Tags: rust, error-handling, best-practices\n Created: 2026-01-10\n\n3. Custom Error Types (note, score: 0.65)\n Tags: rust, error-handling\n Created: 2026-01-08"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="kbadd_note"><a class="header" href="#kbadd_note">kb/add_note</a></h3>
|
||||
<p>Add a new note to the knowledge base.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Note title"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Note content (markdown)"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Tags for categorization",
|
||||
"default": []
|
||||
},
|
||||
"relates_to": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Related node IDs",
|
||||
"default": []
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Project graph name",
|
||||
"default": "default"
|
||||
}
|
||||
},
|
||||
"required": ["title", "content"]
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "kogral/add_note",
|
||||
"params": {
|
||||
"title": "Async Trait Patterns",
|
||||
"content": "Common patterns for using async traits in Rust:\n\n1. Use `async-trait` crate\n2. Box return types for flexibility\n3. Consider Send + Sync bounds",
|
||||
"tags": ["rust", "async", "patterns"],
|
||||
"relates_to": ["pattern-error-handling"]
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Note added successfully: note-async-trait-patterns (ID: note-abc123)"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="kbadd_decision"><a class="header" href="#kbadd_decision">kb/add_decision</a></h3>
|
||||
<p>Create an Architectural Decision Record (ADR).</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Decision title"
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"description": "Decision context and background"
|
||||
},
|
||||
"decision": {
|
||||
"type": "string",
|
||||
"description": "The decision made"
|
||||
},
|
||||
"consequences": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "List of consequences",
|
||||
"default": []
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["proposed", "accepted", "rejected", "deprecated", "superseded"],
|
||||
"default": "proposed"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"required": ["title", "context", "decision"]
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "kogral/add_decision",
|
||||
"params": {
|
||||
"title": "Use SurrealDB for Knowledge Graph Storage",
|
||||
"context": "Need a scalable storage solution that supports graph queries and can handle growing knowledge base",
|
||||
"decision": "Adopt SurrealDB as the primary storage backend for production deployments",
|
||||
"consequences": [
|
||||
"Better query performance for graph traversal",
|
||||
"Native support for relationships",
|
||||
"Additional infrastructure dependency",
|
||||
"Team needs to learn SurrealDB query language"
|
||||
],
|
||||
"status": "accepted",
|
||||
"tags": ["architecture", "storage", "surrealdb"]
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Decision added: decision-use-surrealdb (ID: decision-xyz789)\nStatus: accepted"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="kblink"><a class="header" href="#kblink">kb/link</a></h3>
|
||||
<p>Create a relationship between two nodes.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"from": {
|
||||
"type": "string",
|
||||
"description": "Source node ID"
|
||||
},
|
||||
"to": {
|
||||
"type": "string",
|
||||
"description": "Target node ID"
|
||||
},
|
||||
"relation": {
|
||||
"type": "string",
|
||||
"enum": ["relates_to", "depends_on", "implements", "extends", "supersedes", "explains"],
|
||||
"description": "Relationship type"
|
||||
},
|
||||
"strength": {
|
||||
"type": "number",
|
||||
"description": "Relationship strength (0-1)",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"default": 1.0
|
||||
}
|
||||
},
|
||||
"required": ["from", "to", "relation"]
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"method": "kogral/link",
|
||||
"params": {
|
||||
"from": "note-async-trait-patterns",
|
||||
"to": "pattern-error-handling",
|
||||
"relation": "relates_to",
|
||||
"strength": 0.8
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Link created: note-async-trait-patterns --[relates_to]--> pattern-error-handling (strength: 0.8)"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Relationship Types</strong>:</p>
|
||||
<ul>
|
||||
<li><code>relates_to</code>: General conceptual relationship</li>
|
||||
<li><code>depends_on</code>: Dependency (from depends on to)</li>
|
||||
<li><code>implements</code>: Implementation of concept/pattern</li>
|
||||
<li><code>extends</code>: Inherits or extends another node</li>
|
||||
<li><code>supersedes</code>: Replaces an older version</li>
|
||||
<li><code>explains</code>: Provides documentation/clarification</li>
|
||||
</ul>
|
||||
<h3 id="kbget_guidelines"><a class="header" href="#kbget_guidelines">kb/get_guidelines</a></h3>
|
||||
<p>Retrieve guidelines for current project with inheritance resolution.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "Programming language (e.g., rust, nushell)"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Guideline category (e.g., error-handling, testing)"
|
||||
},
|
||||
"include_base": {
|
||||
"type": "boolean",
|
||||
"description": "Include shared/base guidelines",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "kogral/get_guidelines",
|
||||
"params": {
|
||||
"language": "rust",
|
||||
"category": "error-handling",
|
||||
"include_base": true
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Guidelines for rust/error-handling:\n\n## Project Guidelines (priority: 150)\n\n1. Custom Error Types (guideline-custom-errors)\n - Use thiserror for error definitions\n - Implement From traits for conversions\n - Source: .kogral/guidelines/rust-errors.md\n\n## Shared Guidelines (priority: 50)\n\n1. Result Type Best Practices (guideline-result-best-practices)\n - Always use Result<T> for fallible operations\n - Never use unwrap() in production\n - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md\n\n2. Error Propagation (guideline-error-propagation)\n - Use ? operator for error propagation\n - Add context with .context()\n - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="kblist_graphs"><a class="header" href="#kblist_graphs">kb/list_graphs</a></h3>
|
||||
<p>List available knowledge graphs.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"method": "kb/list_graphs"
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Available graphs:\n\n- default (Current project)\n Path: /path/to/project/.kogral\n Nodes: 42\n Last modified: 2026-01-17T10:30:00Z\n\n- shared (Shared guidelines)\n Path: ~/Tools/.kogral-shared\n Nodes: 156\n Last modified: 2026-01-15T14:20:00Z"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="kbexport"><a class="header" href="#kbexport">kb/export</a></h3>
|
||||
<p>Export knowledge base to various formats.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["logseq", "json", "markdown"],
|
||||
"description": "Export format"
|
||||
},
|
||||
"output_path": {
|
||||
"type": "string",
|
||||
"description": "Output file or directory path"
|
||||
},
|
||||
"include_types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["note", "decision", "guideline", "pattern", "journal", "execution"]
|
||||
},
|
||||
"description": "Node types to include",
|
||||
"default": ["note", "decision", "guideline", "pattern"]
|
||||
},
|
||||
"skip_journals": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"required": ["format", "output_path"]
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"method": "kogral/export",
|
||||
"params": {
|
||||
"format": "logseq",
|
||||
"output_path": "/Users/akasha/logseq-graph",
|
||||
"include_types": ["note", "decision", "guideline"],
|
||||
"skip_journals": true
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Export completed:\n\nFormat: Logseq\nOutput: /Users/akasha/logseq-graph\nExported: 42 nodes\n - 23 notes\n - 12 decisions\n - 7 guidelines\n\nLogseq graph ready to open."
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="block-tools"><a class="header" href="#block-tools">Block Tools</a></h2>
|
||||
<p>Tools for querying Logseq content blocks. Requires <code>blocks.enable_mcp_tools = true</code> in configuration.</p>
|
||||
<h3 id="kbfind_blocks"><a class="header" href="#kbfind_blocks">kb/find_blocks</a></h3>
|
||||
<p>Find blocks by tag, task status, or custom property across the knowledge base.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "Find blocks with this tag (e.g., 'card', 'important')"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["TODO", "DOING", "DONE", "LATER", "NOW", "WAITING", "CANCELLED"],
|
||||
"description": "Find blocks with this task status"
|
||||
},
|
||||
"property_key": {
|
||||
"type": "string",
|
||||
"description": "Custom property key to search for"
|
||||
},
|
||||
"property_value": {
|
||||
"type": "string",
|
||||
"description": "Custom property value to match"
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of results",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Note</strong>: Provide one of: <code>tag</code>, <code>status</code>, or both <code>property_key</code> and <code>property_value</code>.</p>
|
||||
<p><strong>Example Request</strong> (find blocks by tag):</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"method": "kogral/find_blocks",
|
||||
"params": {
|
||||
"tag": "high-priority",
|
||||
"limit": 10
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 3 blocks with tag '#high-priority':\n\n**Project Tasks** (project-tasks-123)\n - TODO Implement authentication #high-priority\n - TODO Fix security vulnerability #high-priority\n\n**Sprint Planning** (sprint-planning-456)\n - DOING Refactor database layer #high-priority"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong> (find blocks by property):</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 9,
|
||||
"method": "kogral/find_blocks",
|
||||
"params": {
|
||||
"property_key": "priority",
|
||||
"property_value": "high",
|
||||
"limit": 15
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="kbfind_todos"><a class="header" href="#kbfind_todos">kb/find_todos</a></h3>
|
||||
<p>Find all TODO blocks across the knowledge base.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of results",
|
||||
"default": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 10,
|
||||
"method": "kogral/find_todos",
|
||||
"params": {
|
||||
"limit": 25
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 10,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 8 TODO blocks:\n\n**Project Tasks** (project-tasks-123)\n - TODO Implement authentication\n - TODO Write integration tests\n - TODO Update documentation\n\n**Bug Fixes** (bug-fixes-456)\n - TODO Fix race condition in cache\n - TODO Address memory leak\n\n**Research** (research-789)\n - TODO Evaluate GraphQL alternatives\n - TODO Benchmark new approach\n - TODO Document findings"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="kbfind_cards"><a class="header" href="#kbfind_cards">kb/find_cards</a></h3>
|
||||
<p>Find all flashcard blocks (blocks tagged with #card) for spaced repetition learning.</p>
|
||||
<p><strong>Input Schema</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of flashcards",
|
||||
"default": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 11,
|
||||
"method": "kogral/find_cards",
|
||||
"params": {
|
||||
"limit": 5
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Response</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 11,
|
||||
"result": {
|
||||
"type": "text",
|
||||
"text": "Found 3 flashcards:\n\n**Rust Learning** (rust-learning-123)\n - What is Rust's ownership model? #card #rust\n - Ownership prevents data races at compile time\n - Each value has a single owner\n\n**System Design** (system-design-456)\n - What is the CAP theorem? #card #distributed-systems\n - Consistency, Availability, Partition tolerance\n - Can only guarantee 2 of 3\n\n**Algorithms** (algorithms-789)\n - What is the time complexity of quicksort? #card #algorithms\n - Average: O(n log n)\n - Worst case: O(n²)"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Use Cases</strong>:</p>
|
||||
<ul>
|
||||
<li><strong>kb/find_blocks</strong>: General block search by metadata</li>
|
||||
<li><strong>kb/find_todos</strong>: Task management and tracking</li>
|
||||
<li><strong>kb/find_cards</strong>: Spaced repetition learning system</li>
|
||||
</ul>
|
||||
<p><strong>See Also</strong>:</p>
|
||||
<ul>
|
||||
<li><a href="../kb/core-concepts.html#logseq-content-blocks">Logseq Blocks Support</a></li>
|
||||
<li><a href="../architecture/adrs/004-logseq-blocks-support.html">ADR-004: Logseq Blocks Support</a></li>
|
||||
</ul>
|
||||
<h2 id="resources"><a class="header" href="#resources">Resources</a></h2>
|
||||
<p>Resources provide read-only access to knowledge graph content via URIs.</p>
|
||||
<h3 id="kogralprojectnotes"><a class="header" href="#kogralprojectnotes">kogral://project/notes</a></h3>
|
||||
<p>Access project notes.</p>
|
||||
<p><strong>URI</strong>: <code>kogral://project/notes</code> or <code>kogral://project/notes/{note-id}</code></p>
|
||||
<p><strong>Example</strong>: Read all project notes</p>
|
||||
<pre><code>kogral://project/notes
|
||||
</code></pre>
|
||||
<p><strong>Example</strong>: Read specific note</p>
|
||||
<pre><code>kogral://project/notes/async-trait-patterns
|
||||
</code></pre>
|
||||
<h3 id="kogralprojectdecisions"><a class="header" href="#kogralprojectdecisions">kogral://project/decisions</a></h3>
|
||||
<p>Access project decisions (ADRs).</p>
|
||||
<p><strong>URI</strong>: <code>kogral://project/decisions</code> or <code>kogral://project/decisions/{decision-id}</code></p>
|
||||
<h3 id="kogralprojectguidelines"><a class="header" href="#kogralprojectguidelines">kogral://project/guidelines</a></h3>
|
||||
<p>Access project-specific guidelines.</p>
|
||||
<p><strong>URI</strong>: <code>kogral://project/guidelines</code> or <code>kogral://project/guidelines/{guideline-id}</code></p>
|
||||
<h3 id="kogralprojectpatterns"><a class="header" href="#kogralprojectpatterns">kogral://project/patterns</a></h3>
|
||||
<p>Access project patterns.</p>
|
||||
<p><strong>URI</strong>: <code>kogral://project/patterns</code> or <code>kogral://project/patterns/{pattern-id}</code></p>
|
||||
<h3 id="kogralsharedguidelines"><a class="header" href="#kogralsharedguidelines">kogral://shared/guidelines</a></h3>
|
||||
<p>Access shared guidelines (inherited).</p>
|
||||
<p><strong>URI</strong>: <code>kogral://shared/guidelines</code> or <code>kogral://shared/guidelines/{guideline-id}</code></p>
|
||||
<h3 id="kogralsharedpatterns"><a class="header" href="#kogralsharedpatterns">kogral://shared/patterns</a></h3>
|
||||
<p>Access shared patterns (inherited).</p>
|
||||
<p><strong>URI</strong>: <code>kogral://shared/patterns</code> or <code>kogral://shared/patterns/{pattern-id}</code></p>
|
||||
<h2 id="prompts"><a class="header" href="#prompts">Prompts</a></h2>
|
||||
<p>Prompts provide guided workflows for common tasks.</p>
|
||||
<h3 id="kbsummarize_project"><a class="header" href="#kbsummarize_project">kb/summarize_project</a></h3>
|
||||
<p>Generate a comprehensive project knowledge summary.</p>
|
||||
<p><strong>Arguments</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Project graph name",
|
||||
"default": "default"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"method": "kogral/summarize_project",
|
||||
"params": {
|
||||
"project": "default"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Returns</strong>: Prompt messages with project summary including:</p>
|
||||
<ul>
|
||||
<li>Total node counts by type</li>
|
||||
<li>Recent additions</li>
|
||||
<li>Top tags</li>
|
||||
<li>Key decisions</li>
|
||||
<li>Active patterns</li>
|
||||
</ul>
|
||||
<h3 id="kbfind_related"><a class="header" href="#kbfind_related">kb/find_related</a></h3>
|
||||
<p>Find nodes related to a specific topic or node.</p>
|
||||
<p><strong>Arguments</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"node_id": {
|
||||
"type": "string",
|
||||
"description": "Node ID to find relations for"
|
||||
},
|
||||
"depth": {
|
||||
"type": "integer",
|
||||
"description": "Maximum traversal depth",
|
||||
"default": 2
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Example Request</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 9,
|
||||
"method": "kb/find_related",
|
||||
"params": {
|
||||
"node_id": "pattern-error-handling",
|
||||
"depth": 2
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Returns</strong>: Prompt messages with:</p>
|
||||
<ul>
|
||||
<li>Direct relationships</li>
|
||||
<li>Indirect relationships (depth 2+)</li>
|
||||
<li>Common tags</li>
|
||||
<li>Related guidelines</li>
|
||||
</ul>
|
||||
<h2 id="error-handling"><a class="header" href="#error-handling">Error Handling</a></h2>
|
||||
<h3 id="error-codes"><a class="header" href="#error-codes">Error Codes</a></h3>
|
||||
<p>Standard JSON-RPC 2.0 error codes:</p>
|
||||
<div class="table-wrapper"><table><thead><tr><th>Code</th><th>Meaning</th><th>Description</th></tr></thead><tbody>
|
||||
<tr><td>-32700</td><td>Parse error</td><td>Invalid JSON</td></tr>
|
||||
<tr><td>-32600</td><td>Invalid Request</td><td>Missing required fields</td></tr>
|
||||
<tr><td>-32601</td><td>Method not found</td><td>Unknown tool/resource</td></tr>
|
||||
<tr><td>-32602</td><td>Invalid params</td><td>Parameter validation failed</td></tr>
|
||||
<tr><td>-32603</td><td>Internal error</td><td>Server-side error</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<h3 id="example-error-response"><a class="header" href="#example-error-response">Example Error Response</a></h3>
|
||||
<pre><code class="language-json">{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"error": {
|
||||
"code": -32602,
|
||||
"message": "Invalid params",
|
||||
"data": {
|
||||
"details": "Field 'query' is required but missing",
|
||||
"field": "query"
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
|
||||
<h3 id="1-use-semantic-search-for-discovery"><a class="header" href="#1-use-semantic-search-for-discovery">1. Use Semantic Search for Discovery</a></h3>
|
||||
<pre><code class="language-json">{
|
||||
"method": "kogral/search",
|
||||
"params": {
|
||||
"query": "how to handle database connection errors",
|
||||
"semantic": true,
|
||||
"threshold": 0.5
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="2-link-related-concepts"><a class="header" href="#2-link-related-concepts">2. Link Related Concepts</a></h3>
|
||||
<pre><code class="language-json">{
|
||||
"method": "kogral/link",
|
||||
"params": {
|
||||
"from": "note-new-discovery",
|
||||
"to": "pattern-related-pattern",
|
||||
"relation": "implements"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="3-query-guidelines-before-implementation"><a class="header" href="#3-query-guidelines-before-implementation">3. Query Guidelines Before Implementation</a></h3>
|
||||
<pre><code class="language-json">{
|
||||
"method": "kogral/get_guidelines",
|
||||
"params": {
|
||||
"language": "rust",
|
||||
"category": "testing"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="4-document-decisions-with-adrs"><a class="header" href="#4-document-decisions-with-adrs">4. Document Decisions with ADRs</a></h3>
|
||||
<pre><code class="language-json">{
|
||||
"method": "kogral/add_decision",
|
||||
"params": {
|
||||
"title": "Use X for Y",
|
||||
"context": "Background...",
|
||||
"decision": "We will...",
|
||||
"consequences": ["Pro 1", "Con 1"]
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="see-also"><a class="header" href="#see-also">See Also</a></h2>
|
||||
<ul>
|
||||
<li><a href="https://modelcontextprotocol.io/docs">MCP Specification</a></li>
|
||||
<li><a href="../user-guide/quickstart.html">Quick Start Guide</a></li>
|
||||
<li><a href="../user-guide/configuration.html">Configuration Reference</a></li>
|
||||
<li><a href="../../crates/kb-mcp/src/">kb-mcp Source Code</a></li>
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../api/mcp-protocol.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/mcp-resources.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../api/mcp-protocol.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/mcp-resources.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/api/rust-api.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Rust API - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./api/rust-api.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="rust-api"><a class="header" href="#rust-api">Rust API</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../api/mcp-resources.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../contributing/development.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../api/mcp-resources.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../contributing/development.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/apps/claude-code.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Claude Code Integration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./apps/claude-code.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="claude-code-integration"><a class="header" href="#claude-code-integration">Claude Code Integration</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../apps/mcp-quickguide.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/logseq.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../apps/mcp-quickguide.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/logseq.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/apps/git.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Git Workflows - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./apps/git.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="git-workflows"><a class="header" href="#git-workflows">Git Workflows</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../apps/obsidian.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/mcp-protocol.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../apps/obsidian.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../api/mcp-protocol.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/apps/logseq.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Logseq Integration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./apps/logseq.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="logseq-integration"><a class="header" href="#logseq-integration">Logseq Integration</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../apps/claude-code.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/obsidian.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../apps/claude-code.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/obsidian.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
561
docs/book/apps/mcp-quickguide.html
Normal file
@ -0,0 +1,561 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>MCP Quick Guide - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./apps/mcp-quickguide.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="mcp-quick-guide"><a class="header" href="#mcp-quick-guide">MCP Quick Guide</a></h1>
|
||||
<p>Fast-track guide to integrating KOGRAL with Claude Code via the Model Context Protocol (MCP).</p>
|
||||
<h2 id="what-is-mcp"><a class="header" href="#what-is-mcp">What is MCP?</a></h2>
|
||||
<p>MCP (Model Context Protocol) is a protocol that allows Claude Code to interact with external tools and data sources. The kogral-mcp server exposes your KOGRAL knowledge base to Claude Code for AI-assisted knowledge management.</p>
|
||||
<h2 id="quick-setup-5-minutes"><a class="header" href="#quick-setup-5-minutes">Quick Setup (5 Minutes)</a></h2>
|
||||
<h3 id="step-1-build-mcp-server"><a class="header" href="#step-1-build-mcp-server">Step 1: Build MCP Server</a></h3>
|
||||
<pre><code class="language-bash"># Build kogral-mcp server
|
||||
cargo build --package kb-mcp --release
|
||||
|
||||
# Verify binary
|
||||
ls -lh target/release/kb-mcp
|
||||
</code></pre>
|
||||
<h3 id="step-2-configure-claude-code"><a class="header" href="#step-2-configure-claude-code">Step 2: Configure Claude Code</a></h3>
|
||||
<p>Add to <code>~/.config/claude/config.json</code>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"mcpServers": {
|
||||
"kogral-mcp": {
|
||||
"command": "/path/to/knowledge-base/target/release/kb-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"KOGRAL_DIR": "/path/to/your/project/.kogral"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Replace <code>/path/to/knowledge-base</code> and <code>/path/to/your/project/.kogral</code> with your actual paths.</p>
|
||||
<h3 id="step-3-initialize-kogral"><a class="header" href="#step-3-initialize-kogral">Step 3: Initialize KOGRAL</a></h3>
|
||||
<pre><code class="language-bash"># Navigate to your project
|
||||
cd /path/to/your/project
|
||||
|
||||
# Initialize .kb directory
|
||||
kb init
|
||||
</code></pre>
|
||||
<h3 id="step-4-start-claude-code"><a class="header" href="#step-4-start-claude-code">Step 4: Start Claude Code</a></h3>
|
||||
<pre><code class="language-bash"># Start Claude Code (will auto-connect to kb-mcp)
|
||||
claude-code
|
||||
</code></pre>
|
||||
<h3 id="step-5-test-connection"><a class="header" href="#step-5-test-connection">Step 5: Test Connection</a></h3>
|
||||
<p>In Claude Code, try:</p>
|
||||
<pre><code>Search for "error handling"
|
||||
</code></pre>
|
||||
<p>Claude will use the <code>kogral/search</code> tool to query your knowledge base.</p>
|
||||
<hr />
|
||||
<h2 id="essential-commands"><a class="header" href="#essential-commands">Essential Commands</a></h2>
|
||||
<h3 id="search-knowledge-base"><a class="header" href="#search-knowledge-base">Search Knowledge Base</a></h3>
|
||||
<p><strong>Natural language</strong>:</p>
|
||||
<pre><code>Find notes about Rust error handling
|
||||
</code></pre>
|
||||
<p><strong>Claude translates to</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"tool": "kogral/search",
|
||||
"params": {
|
||||
"query": "error handling",
|
||||
"type": "note",
|
||||
"semantic": true
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="add-note"><a class="header" href="#add-note">Add Note</a></h3>
|
||||
<p><strong>Natural language</strong>:</p>
|
||||
<pre><code>Add a note about async Rust patterns with tags rust, async, patterns
|
||||
</code></pre>
|
||||
<p><strong>Claude translates to</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"tool": "kogral/add_note",
|
||||
"params": {
|
||||
"title": "Async Rust Patterns",
|
||||
"content": "...",
|
||||
"tags": ["rust", "async", "patterns"]
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="add-decision"><a class="header" href="#add-decision">Add Decision</a></h3>
|
||||
<p><strong>Natural language</strong>:</p>
|
||||
<pre><code>Document decision to use SurrealDB for storage
|
||||
</code></pre>
|
||||
<p><strong>Claude translates to</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"tool": "kogral/add_decision",
|
||||
"params": {
|
||||
"title": "Use SurrealDB for Storage",
|
||||
"context": "Need scalable graph database",
|
||||
"decision": "Adopt SurrealDB as primary storage backend",
|
||||
"consequences": ["Better query performance", "Additional infrastructure"]
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="get-guidelines"><a class="header" href="#get-guidelines">Get Guidelines</a></h3>
|
||||
<p><strong>Natural language</strong>:</p>
|
||||
<pre><code>What are our Rust error handling guidelines?
|
||||
</code></pre>
|
||||
<p><strong>Claude translates to</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"tool": "kogral/get_guidelines",
|
||||
"params": {
|
||||
"language": "rust",
|
||||
"category": "error-handling"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="link-concepts"><a class="header" href="#link-concepts">Link Concepts</a></h3>
|
||||
<p><strong>Natural language</strong>:</p>
|
||||
<pre><code>Link note-async-patterns to decision-use-tokio as implements
|
||||
</code></pre>
|
||||
<p><strong>Claude translates to</strong>:</p>
|
||||
<pre><code class="language-json">{
|
||||
"tool": "kogral/link",
|
||||
"params": {
|
||||
"from": "note-async-patterns",
|
||||
"to": "decision-use-tokio",
|
||||
"relation": "implements"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h2 id="common-workflows"><a class="header" href="#common-workflows">Common Workflows</a></h2>
|
||||
<h3 id="capture-meeting-notes"><a class="header" href="#capture-meeting-notes">Capture Meeting Notes</a></h3>
|
||||
<p><strong>You</strong>:</p>
|
||||
<pre><code>We just decided in our meeting to migrate from REST to GraphQL.
|
||||
Context: current REST API is too chatty, requires multiple round trips.
|
||||
Consequences: better client performance, but team needs to learn GraphQL.
|
||||
Create a decision record.
|
||||
</code></pre>
|
||||
<p><strong>Claude</strong> (uses <code>kb/add_decision</code>):</p>
|
||||
<pre><code>Created decision: decision-migrate-to-graphql
|
||||
|
||||
Status: proposed
|
||||
Context: Current REST API requires multiple round trips (chatty)
|
||||
Decision: Migrate from REST to GraphQL API
|
||||
Consequences:
|
||||
- ✅ Better client performance
|
||||
- ❌ Team learning curve for GraphQL
|
||||
</code></pre>
|
||||
<h3 id="find-related-knowledge"><a class="header" href="#find-related-knowledge">Find Related Knowledge</a></h3>
|
||||
<p><strong>You</strong>:</p>
|
||||
<pre><code>I'm working on error handling. What related notes and patterns do we have?
|
||||
</code></pre>
|
||||
<p><strong>Claude</strong> (uses <code>kogral/search</code> with semantic=true):</p>
|
||||
<pre><code>Found 5 related items:
|
||||
|
||||
1. **Error Handling with thiserror** (pattern)
|
||||
Tags: rust, error-handling
|
||||
Created: 2026-01-15
|
||||
|
||||
2. **Result Type Best Practices** (guideline)
|
||||
Tags: rust, error-handling, best-practices
|
||||
Created: 2026-01-10
|
||||
|
||||
3. **Decision: Use anyhow for Application Errors** (decision)
|
||||
Status: accepted
|
||||
Created: 2026-01-08
|
||||
|
||||
4. **Custom Error Types** (note)
|
||||
Tags: rust, error-handling
|
||||
Created: 2026-01-05
|
||||
|
||||
5. **Error Propagation Patterns** (pattern)
|
||||
Tags: rust, error-handling
|
||||
Created: 2026-01-03
|
||||
|
||||
Would you like me to link your current work to any of these?
|
||||
</code></pre>
|
||||
<h3 id="document-implementation"><a class="header" href="#document-implementation">Document Implementation</a></h3>
|
||||
<p><strong>You</strong>:</p>
|
||||
<pre><code>I just implemented the user authentication module following our security guidelines.
|
||||
Add a note and link it to our security guideline.
|
||||
</code></pre>
|
||||
<p><strong>Claude</strong> (uses <code>kb/add_note</code> + <code>kb/link</code>):</p>
|
||||
<pre><code>Created note: note-user-auth-implementation
|
||||
Tags: security, authentication, implementation
|
||||
|
||||
Linked:
|
||||
note-user-auth-implementation --[implements]--> guideline-security-practices
|
||||
|
||||
Done! Your implementation is now documented and connected to our guidelines.
|
||||
</code></pre>
|
||||
<h3 id="review-project-knowledge"><a class="header" href="#review-project-knowledge">Review Project Knowledge</a></h3>
|
||||
<p><strong>You</strong>:</p>
|
||||
<pre><code>Summarize our project knowledge: what decisions have we made, what patterns are we using?
|
||||
</code></pre>
|
||||
<p><strong>Claude</strong> (uses <code>kb/summarize_project</code> prompt):</p>
|
||||
<pre><code># Project Knowledge Summary
|
||||
|
||||
**Total Nodes**: 42
|
||||
|
||||
## Recent Decisions (5)
|
||||
- Use SurrealDB for Storage (accepted, 2 days ago)
|
||||
- Adopt MCP for AI Integration (accepted, 5 days ago)
|
||||
- Nickel for Configuration (accepted, 1 week ago)
|
||||
|
||||
## Active Patterns (8)
|
||||
- Error Handling with thiserror
|
||||
- Repository Pattern for DB
|
||||
- Builder Pattern for Complex Objects
|
||||
- ...
|
||||
|
||||
## Top Tags
|
||||
1. rust (23 nodes)
|
||||
2. architecture (12 nodes)
|
||||
3. error-handling (8 nodes)
|
||||
4. patterns (7 nodes)
|
||||
|
||||
## Guideline Coverage
|
||||
- Rust: 5 guidelines
|
||||
- Testing: 3 guidelines
|
||||
- Security: 2 guidelines
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h2 id="advanced-usage"><a class="header" href="#advanced-usage">Advanced Usage</a></h2>
|
||||
<h3 id="conditional-queries"><a class="header" href="#conditional-queries">Conditional Queries</a></h3>
|
||||
<p><strong>You</strong>:</p>
|
||||
<pre><code>Find all decisions made in the last week that are still proposed (not accepted)
|
||||
</code></pre>
|
||||
<p><strong>Claude</strong> (combines <code>kogral/search</code> with filtering):</p>
|
||||
<pre><code>Found 2 proposed decisions from last week:
|
||||
|
||||
1. **Migrate to GraphQL** (proposed)
|
||||
Created: 3 days ago
|
||||
Context: REST API is too chatty...
|
||||
|
||||
2. **Add Redis Caching** (proposed)
|
||||
Created: 5 days ago
|
||||
Context: Database queries are slow...
|
||||
|
||||
Would you like to review these and update their status?
|
||||
</code></pre>
|
||||
<h3 id="cross-reference-check"><a class="header" href="#cross-reference-check">Cross-Reference Check</a></h3>
|
||||
<p><strong>You</strong>:</p>
|
||||
<pre><code>Before I implement X, check if we have any decisions or guidelines about it
|
||||
</code></pre>
|
||||
<p><strong>Claude</strong> (uses <code>kogral/search</code> + <code>kogral/get_guidelines</code>):</p>
|
||||
<pre><code>Checking existing knowledge about X...
|
||||
|
||||
**Decisions**: None found
|
||||
**Guidelines**: 1 relevant guideline found
|
||||
- guideline-module-structure: "Keep modules focused and small"
|
||||
|
||||
**Related Notes**: 2 notes mention similar topics
|
||||
- note-module-design-patterns
|
||||
- note-separation-of-concerns
|
||||
|
||||
Recommendation: Your implementation of X looks aligned with our guidelines.
|
||||
Consider linking it to guideline-module-structure when done.
|
||||
</code></pre>
|
||||
<h3 id="export-for-sharing"><a class="header" href="#export-for-sharing">Export for Sharing</a></h3>
|
||||
<p><strong>You</strong>:</p>
|
||||
<pre><code>Export our architectural decisions to Logseq so I can review them visually
|
||||
</code></pre>
|
||||
<p><strong>Claude</strong> (uses <code>kb/export</code>):</p>
|
||||
<pre><code>Exporting to Logseq format...
|
||||
|
||||
✓ Exported 12 decisions to /Users/akasha/logseq-export/
|
||||
- 12 decision records
|
||||
- Preserved wikilinks
|
||||
- Added Logseq properties
|
||||
|
||||
You can now open /Users/akasha/logseq-export/ in Logseq to visualize the decision graph.
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h2 id="tips-and-tricks"><a class="header" href="#tips-and-tricks">Tips and Tricks</a></h2>
|
||||
<h3 id="1-use-natural-language"><a class="header" href="#1-use-natural-language">1. Use Natural Language</a></h3>
|
||||
<p>Don't worry about exact tool syntax. Claude understands:</p>
|
||||
<p>❌ <strong>Don't say</strong>: "Use kb/search with query='rust' and type='pattern'"
|
||||
✅ <strong>Do say</strong>: "Find Rust patterns in KOGRAL"</p>
|
||||
<h3 id="2-be-specific-with-tags"><a class="header" href="#2-be-specific-with-tags">2. Be Specific with Tags</a></h3>
|
||||
<p>When adding notes, use consistent tags:</p>
|
||||
<p>✅ <strong>Good</strong>: <code>tags: rust, error-handling, pattern</code>
|
||||
❌ <strong>Bad</strong>: <code>tags: Rust, ErrorHandling, patterns</code></p>
|
||||
<h3 id="3-link-as-you-go"><a class="header" href="#3-link-as-you-go">3. Link as You Go</a></h3>
|
||||
<p>After creating notes, ask Claude to link them:</p>
|
||||
<pre><code>Link this note to our error handling guideline as 'implements'
|
||||
</code></pre>
|
||||
<h3 id="4-review-regularly"><a class="header" href="#4-review-regularly">4. Review Regularly</a></h3>
|
||||
<p>Ask Claude for summaries:</p>
|
||||
<pre><code>What have we documented this week?
|
||||
</code></pre>
|
||||
<h3 id="5-use-semantic-search"><a class="header" href="#5-use-semantic-search">5. Use Semantic Search</a></h3>
|
||||
<p>For conceptual queries:</p>
|
||||
<pre><code>Find anything related to "making code maintainable"
|
||||
</code></pre>
|
||||
<p>Not just keyword "maintainable", but concepts like refactoring, clean code, patterns, etc.</p>
|
||||
<hr />
|
||||
<h2 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h2>
|
||||
<h3 id="mcp-server-not-responding"><a class="header" href="#mcp-server-not-responding">"MCP server not responding"</a></h3>
|
||||
<pre><code class="language-bash"># Check kb-mcp is built
|
||||
ls target/release/kb-mcp
|
||||
|
||||
# Test manually
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"kogral/search","params":{"query":"test"}}' \
|
||||
| target/release/kb-mcp serve
|
||||
</code></pre>
|
||||
<h3 id="kb-directory-not-found"><a class="header" href="#kb-directory-not-found">"KB directory not found"</a></h3>
|
||||
<pre><code class="language-bash"># Verify .kb exists
|
||||
ls -la /path/to/project/.kogral
|
||||
|
||||
# Initialize if missing
|
||||
cd /path/to/project
|
||||
kb init
|
||||
</code></pre>
|
||||
<h3 id="permission-denied"><a class="header" href="#permission-denied">"Permission denied"</a></h3>
|
||||
<pre><code class="language-bash"># Make binary executable
|
||||
chmod +x target/release/kb-mcp
|
||||
|
||||
# Check environment variable
|
||||
echo $KOGRAL_DIR
|
||||
</code></pre>
|
||||
<h3 id="empty-search-results"><a class="header" href="#empty-search-results">"Empty search results"</a></h3>
|
||||
<pre><code class="language-bash"># Add some test content
|
||||
kb add note "Test Note" --content "Test content"
|
||||
|
||||
# Try search again in Claude Code
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h2 id="next-steps"><a class="header" href="#next-steps">Next Steps</a></h2>
|
||||
<ul>
|
||||
<li><strong>Read</strong>: <a href="../api/mcp-tools.html">MCP Tools API Reference</a> for all available tools</li>
|
||||
<li><strong>Explore</strong>: <a href="../guides/use-cases.html">Use Cases</a> for more examples</li>
|
||||
<li><strong>Configure</strong>: <a href="../config/overview.html">Configuration Reference</a> to customize behavior</li>
|
||||
<li><strong>Integrate</strong>: <a href="claude-code.html">Claude Code Integration</a> for advanced setup</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="quick-reference-card"><a class="header" href="#quick-reference-card">Quick Reference Card</a></h2>
|
||||
<div class="table-wrapper"><table><thead><tr><th>Task</th><th>Say to Claude</th></tr></thead><tbody>
|
||||
<tr><td>Search</td><td>"Find notes about X"</td></tr>
|
||||
<tr><td>Add note</td><td>"Add a note about X with tags Y, Z"</td></tr>
|
||||
<tr><td>Add decision</td><td>"Document decision to use X for Y"</td></tr>
|
||||
<tr><td>Get guidelines</td><td>"What are our X guidelines?"</td></tr>
|
||||
<tr><td>Link nodes</td><td>"Link A to B as implements"</td></tr>
|
||||
<tr><td>Summarize</td><td>"Summarize project knowledge"</td></tr>
|
||||
<tr><td>Export</td><td>"Export to Logseq format"</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<hr />
|
||||
<p><strong>Remember</strong>: Claude Code with MCP turns KOGRAL into an active participant in your development workflow. Ask questions, capture decisions, and let AI help you maintain your project's knowledge graph.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../cli/nushell-scripts.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/claude-code.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../cli/nushell-scripts.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/claude-code.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/apps/obsidian.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Obsidian Compatibility - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./apps/obsidian.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="obsidian-compatibility"><a class="header" href="#obsidian-compatibility">Obsidian Compatibility</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../apps/logseq.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/git.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../apps/logseq.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../apps/git.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
508
docs/book/architecture/adrs/001-nickel-vs-toml.html
Normal file
@ -0,0 +1,508 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>ADR-001: Nickel vs TOML for Configuration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../../favicon.svg">
|
||||
<link rel="shortcut icon" href="../../favicon.png">
|
||||
<link rel="stylesheet" href="../../css/variables.css">
|
||||
<link rel="stylesheet" href="../../css/general.css">
|
||||
<link rel="stylesheet" href="../../css/chrome.css">
|
||||
<link rel="stylesheet" href="../../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/adrs/001-nickel-vs-toml.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="adr-001-nickel-vs-toml-for-configuration"><a class="header" href="#adr-001-nickel-vs-toml-for-configuration">ADR-001: Nickel vs TOML for Configuration</a></h1>
|
||||
<p><strong>Status</strong>: Accepted</p>
|
||||
<p><strong>Date</strong>: 2026-01-17</p>
|
||||
<p><strong>Deciders</strong>: Architecture Team</p>
|
||||
<p><strong>Context</strong>: Configuration Strategy for Knowledge Base System</p>
|
||||
<hr />
|
||||
<h2 id="context"><a class="header" href="#context">Context</a></h2>
|
||||
<p>The KOGRAL requires a flexible, type-safe configuration format that supports:</p>
|
||||
<ol>
|
||||
<li><strong>Complex nested structures</strong> (graph settings, storage configs, embedding providers)</li>
|
||||
<li><strong>Type validation</strong> (prevent runtime errors from config mistakes)</li>
|
||||
<li><strong>Composition and inheritance</strong> (shared configs, environment-specific overrides)</li>
|
||||
<li><strong>Documentation</strong> (self-documenting schemas)</li>
|
||||
<li><strong>Validation before runtime</strong> (catch errors early)</li>
|
||||
</ol>
|
||||
<p>We evaluated two primary options:</p>
|
||||
<h3 id="option-1-toml-traditional-config-format"><a class="header" href="#option-1-toml-traditional-config-format">Option 1: TOML (Traditional Config Format)</a></h3>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li>Widely adopted in Rust ecosystem (<code>Cargo.toml</code>)</li>
|
||||
<li>Simple, human-readable syntax</li>
|
||||
<li>Native <code>serde</code> support</li>
|
||||
<li>IDE support (syntax highlighting, completion)</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>No type system (validation only at runtime)</li>
|
||||
<li>Limited composition (no imports, no functions)</li>
|
||||
<li>No schema validation (errors discovered during execution)</li>
|
||||
<li>Verbose for complex nested structures</li>
|
||||
<li>No documentation in config files</li>
|
||||
</ul>
|
||||
<p><strong>Example TOML</strong>:</p>
|
||||
<pre><code class="language-toml">[graph]
|
||||
name = "my-project"
|
||||
version = "1.0.0"
|
||||
|
||||
[storage]
|
||||
primary = "filesystem" # String, not validated as enum
|
||||
|
||||
[storage.secondary]
|
||||
enabled = true
|
||||
type = "surrealdb" # Typo would fail at runtime
|
||||
url = "ws://localhost:8000"
|
||||
|
||||
[embeddings]
|
||||
enabled = true
|
||||
provider = "openai" # No validation of valid providers
|
||||
model = "text-embedding-3-small"
|
||||
</code></pre>
|
||||
<p><strong>Problems</strong>:</p>
|
||||
<ul>
|
||||
<li>Typos in enum values (<code>"surrealdb"</code> vs <code>"surealdb"</code>) fail at runtime</li>
|
||||
<li>No validation that <code>provider = "openai"</code> requires <code>api_key_env</code></li>
|
||||
<li>No documentation of valid options</li>
|
||||
<li>No way to compose configs (e.g., base config + environment override)</li>
|
||||
</ul>
|
||||
<h3 id="option-2-nickel-functional-configuration-language"><a class="header" href="#option-2-nickel-functional-configuration-language">Option 2: Nickel (Functional Configuration Language)</a></h3>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li><strong>Type system</strong> with contracts (validate before runtime)</li>
|
||||
<li><strong>Composition</strong> via imports and merging</li>
|
||||
<li><strong>Documentation</strong> in schemas (self-documenting)</li>
|
||||
<li><strong>Validation</strong> at export time (catch errors early)</li>
|
||||
<li><strong>Functions</strong> for conditional logic</li>
|
||||
<li><strong>Default values</strong> in schema definitions</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>Less familiar to Rust developers</li>
|
||||
<li>Requires separate <code>nickel</code> CLI tool</li>
|
||||
<li>Smaller ecosystem</li>
|
||||
<li>Steeper learning curve</li>
|
||||
</ul>
|
||||
<p><strong>Example Nickel</strong>:</p>
|
||||
<pre><code class="language-nickel"># schemas/kb-config.ncl
|
||||
{
|
||||
KbConfig = {
|
||||
graph | GraphConfig,
|
||||
storage | StorageConfig,
|
||||
embeddings | EmbeddingConfig,
|
||||
},
|
||||
|
||||
StorageConfig = {
|
||||
primary | [| 'filesystem, 'memory |], # Enum validated at export
|
||||
|
||||
secondary | {
|
||||
enabled | Bool,
|
||||
type | [| 'surrealdb, 'sqlite |], # Typos caught immediately
|
||||
url | String,
|
||||
} | optional,
|
||||
},
|
||||
|
||||
EmbeddingConfig = {
|
||||
enabled | Bool,
|
||||
provider | [| 'openai, 'claude, 'fastembed |], # Valid providers enforced
|
||||
model | String,
|
||||
api_key_env | String | doc "Environment variable for API key",
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Benefits</strong>:</p>
|
||||
<ul>
|
||||
<li>Typos in enum values caught at <code>nickel export</code> time</li>
|
||||
<li>Schema enforces required fields based on provider</li>
|
||||
<li>Documentation embedded in schema</li>
|
||||
<li>Config can be composed: <code>import "base.ncl" & { /* overrides */ }</code></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="decision"><a class="header" href="#decision">Decision</a></h2>
|
||||
<p><strong>We will use Nickel for configuration.</strong></p>
|
||||
<p><strong>Implementation</strong>:</p>
|
||||
<ol>
|
||||
<li>Define schemas in <code>schemas/*.ncl</code> with type contracts</li>
|
||||
<li>Users write configs in <code>.kogral/config.ncl</code></li>
|
||||
<li>Export to JSON via CLI: <code>nickel export --format json config.ncl</code></li>
|
||||
<li>Load JSON in Rust via <code>serde_json</code> into typed structs</li>
|
||||
</ol>
|
||||
<p><strong>Pattern</strong> (double validation):</p>
|
||||
<pre><code>Nickel Config (.ncl)
|
||||
↓ [nickel export]
|
||||
JSON (validated by Nickel contracts)
|
||||
↓ [serde_json::from_str]
|
||||
Rust Struct (validated by serde)
|
||||
↓
|
||||
Runtime (guaranteed valid config)
|
||||
</code></pre>
|
||||
<p><strong>Bridge Code</strong> (<code>kb-core/src/config/nickel.rs</code>):</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub fn load_config<P: AsRef<Path>>(path: P) -> Result<KbConfig> {
|
||||
// Export Nickel to JSON
|
||||
let json = export_nickel_to_json(path)?;
|
||||
|
||||
// Deserialize to Rust struct
|
||||
let config: KbConfig = serde_json::from_str(&json)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn export_nickel_to_json<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||
let output = Command::new("nickel")
|
||||
.arg("export")
|
||||
.arg("--format").arg("json")
|
||||
.arg(path.as_ref())
|
||||
.output()?;
|
||||
|
||||
Ok(String::from_utf8(output.stdout)?)
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<hr />
|
||||
<h2 id="consequences"><a class="header" href="#consequences">Consequences</a></h2>
|
||||
<h3 id="positive"><a class="header" href="#positive">Positive</a></h3>
|
||||
<p>✅ <strong>Type Safety</strong>: Config errors caught before runtime</p>
|
||||
<ul>
|
||||
<li>Invalid enum values fail at export: <code>'filesystm</code> → error</li>
|
||||
<li>Missing required fields detected: no <code>graph.name</code> → error</li>
|
||||
<li>Type mismatches prevented: <code>enabled = "yes"</code> → error (expects Bool)</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Self-Documenting</strong>: Schemas serve as documentation</p>
|
||||
<ul>
|
||||
<li><code>| doc "Environment variable for API key"</code> describes fields</li>
|
||||
<li>Enum options visible in schema: <code>[| 'openai, 'claude, 'fastembed |]</code></li>
|
||||
<li>Default values explicit: <code>| default = 'filesystem</code></li>
|
||||
</ul>
|
||||
<p>✅ <strong>Composition</strong>: Config reuse and overrides</p>
|
||||
<pre><code class="language-nickel"># base.ncl
|
||||
{ graph = { version = "1.0.0" } }
|
||||
|
||||
# project.ncl
|
||||
import "base.ncl" & { graph = { name = "my-project" } }
|
||||
</code></pre>
|
||||
<p>✅ <strong>Validation Before Deployment</strong>: Catch errors in CI</p>
|
||||
<pre><code class="language-bash"># CI pipeline
|
||||
nickel typecheck config.ncl
|
||||
nickel export --format json config.ncl > /dev/null
|
||||
</code></pre>
|
||||
<p>✅ <strong>Conditional Logic</strong>: Environment-specific configs</p>
|
||||
<pre><code class="language-nickel">let is_prod = std.string.is_match "prod" (std.env.get "ENV") in
|
||||
{
|
||||
embeddings = {
|
||||
provider = if is_prod then 'openai else 'fastembed,
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="negative"><a class="header" href="#negative">Negative</a></h3>
|
||||
<p>❌ <strong>Learning Curve</strong>: Team must learn Nickel syntax</p>
|
||||
<ul>
|
||||
<li><strong>Mitigation</strong>: Provide comprehensive examples in <code>config/</code> directory</li>
|
||||
<li><strong>Mitigation</strong>: Document common patterns in <code>docs/config/</code></li>
|
||||
</ul>
|
||||
<p>❌ <strong>Tool Dependency</strong>: Requires <code>nickel</code> CLI installed</p>
|
||||
<ul>
|
||||
<li><strong>Mitigation</strong>: Document installation in setup guide</li>
|
||||
<li><strong>Mitigation</strong>: Check <code>nickel</code> availability in <code>kb init</code> command</li>
|
||||
</ul>
|
||||
<p>❌ <strong>IDE Support</strong>: Limited compared to TOML</p>
|
||||
<ul>
|
||||
<li><strong>Mitigation</strong>: Use LSP (nickel-lang-lsp) for VSCode/Neovim</li>
|
||||
<li><strong>Mitigation</strong>: Syntax highlighting available for major editors</li>
|
||||
</ul>
|
||||
<p>❌ <strong>Ecosystem Size</strong>: Smaller than TOML</p>
|
||||
<ul>
|
||||
<li><strong>Mitigation</strong>: Nickel actively developed by Tweag</li>
|
||||
<li><strong>Mitigation</strong>: Stable language specification (v1.0+)</li>
|
||||
</ul>
|
||||
<h3 id="neutral"><a class="header" href="#neutral">Neutral</a></h3>
|
||||
<p>⚪ <strong>Two-Stage Loading</strong>: Nickel → JSON → Rust</p>
|
||||
<ul>
|
||||
<li>Not a performance concern (config loaded once at startup)</li>
|
||||
<li>Adds resilience (double validation)</li>
|
||||
<li>Allows runtime config inspection (read JSON directly)</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="alternatives-considered"><a class="header" href="#alternatives-considered">Alternatives Considered</a></h2>
|
||||
<h3 id="json-schema"><a class="header" href="#json-schema">JSON Schema</a></h3>
|
||||
<p><strong>Rejected</strong>: Not ergonomic for humans to write</p>
|
||||
<ul>
|
||||
<li>No comments</li>
|
||||
<li>Verbose syntax (<code>{"key": "value"}</code> vs <code>key = value</code>)</li>
|
||||
<li>JSON Schema separate from config (duplication)</li>
|
||||
</ul>
|
||||
<h3 id="yaml"><a class="header" href="#yaml">YAML</a></h3>
|
||||
<p><strong>Rejected</strong>: No type system, ambiguous parsing</p>
|
||||
<ul>
|
||||
<li>Boolean confusion: <code>yes</code>/<code>no</code>/<code>on</code>/<code>off</code>/<code>true</code>/<code>false</code></li>
|
||||
<li>Indentation-sensitive (error-prone)</li>
|
||||
<li>No validation without external tools</li>
|
||||
</ul>
|
||||
<h3 id="dhall"><a class="header" href="#dhall">Dhall</a></h3>
|
||||
<p><strong>Rejected</strong>: More complex than needed</p>
|
||||
<ul>
|
||||
<li>Turing-incomplete by design (limits use cases)</li>
|
||||
<li>Smaller ecosystem than Nickel</li>
|
||||
<li>Steeper learning curve</li>
|
||||
</ul>
|
||||
<h3 id="kcl-kusionstack-configuration-language"><a class="header" href="#kcl-kusionstack-configuration-language">KCL (KusionStack Configuration Language)</a></h3>
|
||||
<p><strong>Rejected</strong>: Kubernetes-focused, less general-purpose</p>
|
||||
<ul>
|
||||
<li>Designed for K8s manifests</li>
|
||||
<li>Less mature than Nickel for general config</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="implementation-timeline"><a class="header" href="#implementation-timeline">Implementation Timeline</a></h2>
|
||||
<ol>
|
||||
<li>✅ Define base schemas (<code>schemas/kb-config.ncl</code>)</li>
|
||||
<li>✅ Implement Nickel loader (<code>kb-core/src/config/nickel.rs</code>)</li>
|
||||
<li>✅ Create example configs (<code>config/defaults.ncl</code>, <code>config/production.ncl</code>)</li>
|
||||
<li>✅ Document Nickel usage (<code>docs/config/nickel-schemas.md</code>)</li>
|
||||
<li>⏳ Add LSP recommendations to setup guide</li>
|
||||
<li>⏳ Create Nickel → TOML migration tool (for existing users)</li>
|
||||
</ol>
|
||||
<hr />
|
||||
<h2 id="monitoring"><a class="header" href="#monitoring">Monitoring</a></h2>
|
||||
<p><strong>Success Criteria</strong>:</p>
|
||||
<ul>
|
||||
<li>Config errors caught at export time (not runtime)</li>
|
||||
<li>Users can compose configs for different environments</li>
|
||||
<li>Team comfortable with Nickel syntax within 2 weeks</li>
|
||||
</ul>
|
||||
<p><strong>Metrics</strong>:</p>
|
||||
<ul>
|
||||
<li>Number of config validation errors caught before runtime</li>
|
||||
<li>Time to diagnose config issues (should decrease)</li>
|
||||
<li>User feedback on config complexity</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="references"><a class="header" href="#references">References</a></h2>
|
||||
<ul>
|
||||
<li><a href="https://nickel-lang.org/">Nickel Language</a></li>
|
||||
<li><a href="https://nickel-lang.org/user-manual/introduction">Nickel User Manual</a></li>
|
||||
<li><a href="../../crates/kb-core/src/config/README.html">platform-config pattern</a> (reference implementation)</li>
|
||||
<li><a href="https://toml.io/">TOML Specification</a></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="revision-history"><a class="header" href="#revision-history">Revision History</a></h2>
|
||||
<div class="table-wrapper"><table><thead><tr><th>Date</th><th>Author</th><th>Change</th></tr></thead><tbody>
|
||||
<tr><td>2026-01-17</td><td>Architecture Team</td><td>Initial decision</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<hr />
|
||||
<p><strong>Next ADR</strong>: <a href="002-fastembed-ai-providers.html">ADR-002: FastEmbed via AI Providers</a></p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../../architecture/logseq-blocks-design.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/002-fastembed-ai-providers.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../../architecture/logseq-blocks-design.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/002-fastembed-ai-providers.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../../elasticlunr.min.js"></script>
|
||||
<script src="../../mark.min.js"></script>
|
||||
<script src="../../searcher.js"></script>
|
||||
|
||||
<script src="../../clipboard.min.js"></script>
|
||||
<script src="../../highlight.js"></script>
|
||||
<script src="../../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
566
docs/book/architecture/adrs/002-fastembed-ai-providers.html
Normal file
@ -0,0 +1,566 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>ADR-002: FastEmbed via AI Providers - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../../favicon.svg">
|
||||
<link rel="shortcut icon" href="../../favicon.png">
|
||||
<link rel="stylesheet" href="../../css/variables.css">
|
||||
<link rel="stylesheet" href="../../css/general.css">
|
||||
<link rel="stylesheet" href="../../css/chrome.css">
|
||||
<link rel="stylesheet" href="../../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/adrs/002-fastembed-ai-providers.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="adr-002-fastembed-via-ai-providers-for-embeddings"><a class="header" href="#adr-002-fastembed-via-ai-providers-for-embeddings">ADR-002: FastEmbed via AI Providers for Embeddings</a></h1>
|
||||
<p><strong>Status</strong>: Accepted</p>
|
||||
<p><strong>Date</strong>: 2026-01-17</p>
|
||||
<p><strong>Deciders</strong>: Architecture Team</p>
|
||||
<p><strong>Context</strong>: Embedding Strategy for Semantic Search</p>
|
||||
<hr />
|
||||
<h2 id="context"><a class="header" href="#context">Context</a></h2>
|
||||
<p>The KOGRAL requires embedding generation for semantic search capabilities. Embeddings convert text into numerical vectors that capture semantic meaning, enabling "find concepts" rather than just "find keywords".</p>
|
||||
<p><strong>Requirements</strong>:</p>
|
||||
<ol>
|
||||
<li><strong>Local-First Option</strong>: Must work offline without external API dependencies</li>
|
||||
<li><strong>Production Scalability</strong>: Support cloud AI providers for large-scale deployments</li>
|
||||
<li><strong>Multiple Providers</strong>: Flexibility to choose based on cost, quality, privacy</li>
|
||||
<li><strong>Cost-Effective Development</strong>: Free local embeddings for development and testing</li>
|
||||
<li><strong>Quality</strong>: Good enough embeddings for finding related concepts</li>
|
||||
</ol>
|
||||
<p><strong>Options Evaluated</strong>:</p>
|
||||
<h3 id="option-1-only-local-embeddings-fastembed"><a class="header" href="#option-1-only-local-embeddings-fastembed">Option 1: Only Local Embeddings (fastembed)</a></h3>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li>No API costs</li>
|
||||
<li>Works offline</li>
|
||||
<li>Privacy-preserving (no data leaves machine)</li>
|
||||
<li>Fast (local GPU acceleration possible)</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>Limited model quality compared to cloud providers</li>
|
||||
<li>Resource-intensive (requires download ~100MB models)</li>
|
||||
<li>Single provider lock-in (fastembed library)</li>
|
||||
</ul>
|
||||
<p><strong>Example</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>use fastembed::{TextEmbedding, InitOptions};
|
||||
|
||||
let model = TextEmbedding::try_new(InitOptions {
|
||||
model_name: "BAAI/bge-small-en-v1.5",
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
let embeddings = model.embed(vec!["Hello world"], None)?;
|
||||
// Output: Vec<Vec<f32>> with 384 dimensions
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="option-2-only-cloud-ai-providers-openai-claude-etc"><a class="header" href="#option-2-only-cloud-ai-providers-openai-claude-etc">Option 2: Only Cloud AI Providers (OpenAI, Claude, etc.)</a></h3>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li>State-of-the-art embedding quality</li>
|
||||
<li>No local resource usage</li>
|
||||
<li>Latest models available</li>
|
||||
<li>Scalable to millions of documents</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>Requires API keys (cost per embedding)</li>
|
||||
<li>Network dependency (no offline mode)</li>
|
||||
<li>Privacy concerns (data sent to third parties)</li>
|
||||
<li>Vendor lock-in risk</li>
|
||||
</ul>
|
||||
<p><strong>Example</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>use rig::providers::openai;
|
||||
|
||||
let client = openai::Client::new("sk-...");
|
||||
let embeddings = client.embeddings("text-embedding-3-small")
|
||||
.embed_documents(vec!["Hello world"]).await?;
|
||||
// Output: Vec<Vec<f32>> with 1536 dimensions
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="option-3-hybrid-strategy-fastembed--ai-providers-via-rig-core"><a class="header" href="#option-3-hybrid-strategy-fastembed--ai-providers-via-rig-core">Option 3: Hybrid Strategy (fastembed + AI providers via rig-core)</a></h3>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li>✅ Best of both worlds: local dev, cloud production</li>
|
||||
<li>✅ User choice: privacy-first or quality-first</li>
|
||||
<li>✅ Cost flexibility: free for small projects, paid for scale</li>
|
||||
<li>✅ Unified interface via <code>rig-core</code> library</li>
|
||||
<li>✅ Easy provider switching (config-driven)</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>❌ More complex implementation (multiple providers)</li>
|
||||
<li>❌ Dimension mismatch between providers (384 vs 1536)</li>
|
||||
<li>❌ Additional dependencies (<code>rig-core</code>, <code>fastembed</code>)</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="decision"><a class="header" href="#decision">Decision</a></h2>
|
||||
<p><strong>We will use a hybrid strategy: fastembed (local) + AI providers (via rig-core).</strong></p>
|
||||
<p><strong>Implementation</strong>:</p>
|
||||
<ol>
|
||||
<li><strong>Default</strong>: <code>fastembed</code> with <code>BAAI/bge-small-en-v1.5</code> (384 dimensions)</li>
|
||||
<li><strong>Optional</strong>: OpenAI, Claude, Ollama via <code>rig-core</code> (configurable)</li>
|
||||
<li><strong>Interface</strong>: <code>EmbeddingProvider</code> trait abstracts provider details</li>
|
||||
<li><strong>Config-Driven</strong>: Provider selection via Nickel configuration</li>
|
||||
</ol>
|
||||
<p><strong>Architecture</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[async_trait]
|
||||
pub trait EmbeddingProvider: Send + Sync {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
|
||||
fn dimensions(&self) -> usize;
|
||||
fn model_name(&self) -> &str;
|
||||
}
|
||||
|
||||
// Local implementation
|
||||
pub struct FastEmbedProvider {
|
||||
model: TextEmbedding,
|
||||
}
|
||||
|
||||
impl FastEmbedProvider {
|
||||
pub fn new(model_name: &str) -> Result<Self> {
|
||||
let model = TextEmbedding::try_new(InitOptions {
|
||||
model_name: model_name.into(),
|
||||
..Default::default()
|
||||
})?;
|
||||
Ok(Self { model })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EmbeddingProvider for FastEmbedProvider {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
|
||||
Ok(self.model.embed(texts, None)?)
|
||||
}
|
||||
|
||||
fn dimensions(&self) -> usize { 384 }
|
||||
fn model_name(&self) -> &str { "BAAI/bge-small-en-v1.5" }
|
||||
}
|
||||
|
||||
// Cloud provider implementation (via rig-core)
|
||||
pub struct RigEmbeddingProvider {
|
||||
client: rig::Client,
|
||||
model: String,
|
||||
dimensions: usize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EmbeddingProvider for RigEmbeddingProvider {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
|
||||
let embeddings = self.client
|
||||
.embeddings(&self.model)
|
||||
.embed_documents(texts)
|
||||
.await?;
|
||||
Ok(embeddings)
|
||||
}
|
||||
|
||||
fn dimensions(&self) -> usize { self.dimensions }
|
||||
fn model_name(&self) -> &str { &self.model }
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Configuration</strong> (Nickel):</p>
|
||||
<pre><code class="language-nickel"># Local development (default)
|
||||
{
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'fastembed,
|
||||
model = "BAAI/bge-small-en-v1.5",
|
||||
dimensions = 384,
|
||||
},
|
||||
}
|
||||
|
||||
# Production with OpenAI
|
||||
{
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'openai,
|
||||
model = "text-embedding-3-small",
|
||||
dimensions = 1536,
|
||||
api_key_env = "OPENAI_API_KEY",
|
||||
},
|
||||
}
|
||||
|
||||
# Self-hosted with Ollama
|
||||
{
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'ollama,
|
||||
model = "nomic-embed-text",
|
||||
dimensions = 768,
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Provider Selection</strong> (<code>kb-core/src/embeddings/mod.rs</code>):</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub fn create_provider(config: &EmbeddingConfig) -> Result<Box<dyn EmbeddingProvider>> {
|
||||
match config.provider {
|
||||
EmbeddingProviderType::FastEmbed => {
|
||||
Ok(Box::new(FastEmbedProvider::new(&config.model)?))
|
||||
}
|
||||
EmbeddingProviderType::OpenAI => {
|
||||
let api_key = std::env::var(&config.api_key_env)?;
|
||||
Ok(Box::new(RigEmbeddingProvider::new_openai(api_key, &config.model)?))
|
||||
}
|
||||
EmbeddingProviderType::Claude => {
|
||||
let api_key = std::env::var(&config.api_key_env)?;
|
||||
Ok(Box::new(RigEmbeddingProvider::new_claude(api_key, &config.model)?))
|
||||
}
|
||||
EmbeddingProviderType::Ollama => {
|
||||
Ok(Box::new(RigEmbeddingProvider::new_ollama(&config.model)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<hr />
|
||||
<h2 id="consequences"><a class="header" href="#consequences">Consequences</a></h2>
|
||||
<h3 id="positive"><a class="header" href="#positive">Positive</a></h3>
|
||||
<p>✅ <strong>Development Flexibility</strong>:</p>
|
||||
<ul>
|
||||
<li>Developers can use <code>fastembed</code> without API keys</li>
|
||||
<li>Fast feedback loop (local embeddings, no network calls)</li>
|
||||
<li>Works offline (train trips, flights)</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Production Quality</strong>:</p>
|
||||
<ul>
|
||||
<li>Production deployments can use OpenAI/Claude for better quality</li>
|
||||
<li>Latest embedding models available</li>
|
||||
<li>Scalable to millions of documents</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Privacy Control</strong>:</p>
|
||||
<ul>
|
||||
<li>Privacy-sensitive projects use local embeddings</li>
|
||||
<li>Public projects can use cloud providers</li>
|
||||
<li>User choice via configuration</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Cost Optimization</strong>:</p>
|
||||
<ul>
|
||||
<li>Small projects: free (fastembed)</li>
|
||||
<li>Large projects: pay for quality (cloud providers)</li>
|
||||
<li>Hybrid: important docs via cloud, bulk via local</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Unified Interface</strong>:</p>
|
||||
<ul>
|
||||
<li><code>EmbeddingProvider</code> trait abstracts provider details</li>
|
||||
<li>Query code doesn't know/care about provider</li>
|
||||
<li>Easy to add new providers</li>
|
||||
</ul>
|
||||
<h3 id="negative"><a class="header" href="#negative">Negative</a></h3>
|
||||
<p>❌ <strong>Dimension Mismatch</strong>:</p>
|
||||
<ul>
|
||||
<li>fastembed: 384 dimensions</li>
|
||||
<li>OpenAI: 1536 dimensions</li>
|
||||
<li>Cannot mix in same index</li>
|
||||
</ul>
|
||||
<p><strong>Mitigation</strong>:</p>
|
||||
<ul>
|
||||
<li>Store provider + dimensions in node metadata</li>
|
||||
<li>Rebuild index when changing providers</li>
|
||||
<li>Document dimension constraints</li>
|
||||
</ul>
|
||||
<p>❌ <strong>Model Download</strong>:</p>
|
||||
<ul>
|
||||
<li>First use of fastembed downloads ~100MB model</li>
|
||||
<li>Slow initial startup</li>
|
||||
</ul>
|
||||
<p><strong>Mitigation</strong>:</p>
|
||||
<ul>
|
||||
<li>Pre-download in Docker images</li>
|
||||
<li>Document model download in setup guide</li>
|
||||
<li>Cache models in <code>~/.cache/fastembed</code></li>
|
||||
</ul>
|
||||
<p>❌ <strong>Complex Configuration</strong>:</p>
|
||||
<ul>
|
||||
<li>Multiple provider options may confuse users</li>
|
||||
</ul>
|
||||
<p><strong>Mitigation</strong>:</p>
|
||||
<ul>
|
||||
<li>Sane default (fastembed)</li>
|
||||
<li>Clear examples for each provider</li>
|
||||
<li>Validation errors explain misconfigurations</li>
|
||||
</ul>
|
||||
<h3 id="neutral"><a class="header" href="#neutral">Neutral</a></h3>
|
||||
<p>⚪ <strong>Dependency Trade-off</strong>:</p>
|
||||
<ul>
|
||||
<li><code>fastembed</code> adds ~5MB to binary</li>
|
||||
<li><code>rig-core</code> adds ~2MB</li>
|
||||
<li>Total: ~7MB overhead</li>
|
||||
</ul>
|
||||
<p>Not a concern for CLI/MCP server use case.</p>
|
||||
<hr />
|
||||
<h2 id="provider-comparison"><a class="header" href="#provider-comparison">Provider Comparison</a></h2>
|
||||
<div class="table-wrapper"><table><thead><tr><th>Provider</th><th>Dimensions</th><th>Quality</th><th>Cost</th><th>Privacy</th><th>Offline</th></tr></thead><tbody>
|
||||
<tr><td><strong>fastembed</strong></td><td>384</td><td>Good</td><td>Free</td><td>✅ Local</td><td>✅ Yes</td></tr>
|
||||
<tr><td><strong>OpenAI</strong></td><td>1536</td><td>Excellent</td><td>$0.0001/1K</td><td>❌ Cloud</td><td>❌ No</td></tr>
|
||||
<tr><td><strong>Claude</strong></td><td>1024</td><td>Excellent</td><td>$0.00025/1K</td><td>❌ Cloud</td><td>❌ No</td></tr>
|
||||
<tr><td><strong>Ollama</strong></td><td>768</td><td>Very Good</td><td>Free</td><td>✅ Local</td><td>✅ Yes</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<p><strong>Recommendation by Use Case</strong>:</p>
|
||||
<ul>
|
||||
<li><strong>Development</strong>: fastembed (fast, free, offline)</li>
|
||||
<li><strong>Small Teams</strong>: fastembed or Ollama (privacy, no costs)</li>
|
||||
<li><strong>Enterprise</strong>: OpenAI or Claude (best quality, scalable)</li>
|
||||
<li><strong>Self-Hosted</strong>: Ollama (good quality, local control)</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="implementation-timeline"><a class="header" href="#implementation-timeline">Implementation Timeline</a></h2>
|
||||
<ol>
|
||||
<li>✅ Define <code>EmbeddingProvider</code> trait</li>
|
||||
<li>✅ Implement FastEmbedProvider (stub, feature-gated)</li>
|
||||
<li>✅ Implement RigEmbeddingProvider (stub, feature-gated)</li>
|
||||
<li>⏳ Complete FastEmbed integration with model download</li>
|
||||
<li>⏳ Complete rig-core integration (OpenAI, Claude, Ollama)</li>
|
||||
<li>⏳ Add query engine with similarity search</li>
|
||||
<li>⏳ Document provider selection and trade-offs</li>
|
||||
</ol>
|
||||
<hr />
|
||||
<h2 id="monitoring"><a class="header" href="#monitoring">Monitoring</a></h2>
|
||||
<p><strong>Success Criteria</strong>:</p>
|
||||
<ul>
|
||||
<li>Users can switch providers via config change</li>
|
||||
<li>Local embeddings work without API keys</li>
|
||||
<li>Production deployments use cloud providers successfully</li>
|
||||
<li>Query quality acceptable for both local and cloud embeddings</li>
|
||||
</ul>
|
||||
<p><strong>Metrics</strong>:</p>
|
||||
<ul>
|
||||
<li>Embedding generation latency (local vs cloud)</li>
|
||||
<li>Query accuracy (precision@10 for semantic search)</li>
|
||||
<li>API costs (cloud providers)</li>
|
||||
<li>User satisfaction (feedback on search quality)</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="references"><a class="header" href="#references">References</a></h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/Anush008/fastembed-rs">fastembed Documentation</a></li>
|
||||
<li><a href="https://github.com/0xPlaygrounds/rig">rig-core Documentation</a></li>
|
||||
<li><a href="https://platform.openai.com/docs/guides/embeddings">OpenAI Embeddings API</a></li>
|
||||
<li><a href="https://huggingface.co/BAAI/bge-small-en-v1.5">BAAI/bge Models</a></li>
|
||||
<li><a href="https://ollama.com/blog/embedding-models">Ollama Embeddings</a></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="revision-history"><a class="header" href="#revision-history">Revision History</a></h2>
|
||||
<div class="table-wrapper"><table><thead><tr><th>Date</th><th>Author</th><th>Change</th></tr></thead><tbody>
|
||||
<tr><td>2026-01-17</td><td>Architecture Team</td><td>Initial decision</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<hr />
|
||||
<p><strong>Previous ADR</strong>: <a href="001-nickel-vs-toml.html">ADR-001: Nickel vs TOML</a>
|
||||
<strong>Next ADR</strong>: <a href="003-hybrid-storage.html">ADR-003: Hybrid Storage Strategy</a></p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../../architecture/adrs/001-nickel-vs-toml.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/003-hybrid-storage.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../../architecture/adrs/001-nickel-vs-toml.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/003-hybrid-storage.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../../elasticlunr.min.js"></script>
|
||||
<script src="../../mark.min.js"></script>
|
||||
<script src="../../searcher.js"></script>
|
||||
|
||||
<script src="../../clipboard.min.js"></script>
|
||||
<script src="../../highlight.js"></script>
|
||||
<script src="../../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
629
docs/book/architecture/adrs/003-hybrid-storage.html
Normal file
@ -0,0 +1,629 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>ADR-003: Hybrid Storage Strategy - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../../favicon.svg">
|
||||
<link rel="shortcut icon" href="../../favicon.png">
|
||||
<link rel="stylesheet" href="../../css/variables.css">
|
||||
<link rel="stylesheet" href="../../css/general.css">
|
||||
<link rel="stylesheet" href="../../css/chrome.css">
|
||||
<link rel="stylesheet" href="../../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/adrs/003-hybrid-storage.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="adr-003-hybrid-storage-strategy"><a class="header" href="#adr-003-hybrid-storage-strategy">ADR-003: Hybrid Storage Strategy</a></h1>
|
||||
<p><strong>Status</strong>: Accepted</p>
|
||||
<p><strong>Date</strong>: 2026-01-17</p>
|
||||
<p><strong>Deciders</strong>: Architecture Team</p>
|
||||
<p><strong>Context</strong>: Storage Backend Strategy for Knowledge Base</p>
|
||||
<hr />
|
||||
<h2 id="context"><a class="header" href="#context">Context</a></h2>
|
||||
<p>The KOGRAL needs to store knowledge graphs with these requirements:</p>
|
||||
<ol>
|
||||
<li><strong>Git-Friendly</strong>: Knowledge should version alongside code</li>
|
||||
<li><strong>Scalable</strong>: Support small projects (10s of nodes) to large organizations (10,000+ nodes)</li>
|
||||
<li><strong>Queryable</strong>: Efficient graph queries and relationship traversal</li>
|
||||
<li><strong>Offline-Capable</strong>: Work without network access</li>
|
||||
<li><strong>Collaborative</strong>: Support shared organizational knowledge</li>
|
||||
<li><strong>Cost-Effective</strong>: Free for small projects, reasonable cost at scale</li>
|
||||
</ol>
|
||||
<p><strong>Constraints</strong>:</p>
|
||||
<ul>
|
||||
<li>Developers want to edit knowledge in text editors</li>
|
||||
<li>Organizations want centralized guideline management</li>
|
||||
<li>Git workflows essential for code-adjacent knowledge</li>
|
||||
<li>Large graphs need database performance</li>
|
||||
</ul>
|
||||
<h3 id="option-1-filesystem-only"><a class="header" href="#option-1-filesystem-only">Option 1: Filesystem Only</a></h3>
|
||||
<p><strong>Approach</strong>: Store everything as markdown files</p>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li>✅ Git-native (perfect for versioning)</li>
|
||||
<li>✅ Text editor friendly</li>
|
||||
<li>✅ No dependencies</li>
|
||||
<li>✅ Works offline</li>
|
||||
<li>✅ Free</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>❌ Poor performance for large graphs (100 0+ nodes)</li>
|
||||
<li>❌ No efficient graph queries</li>
|
||||
<li>❌ Difficult to share across projects</li>
|
||||
<li>❌ Manual sync for collaboration</li>
|
||||
</ul>
|
||||
<p><strong>Scalability</strong>: Good for < 100 nodes, poor beyond</p>
|
||||
<h3 id="option-2-database-only-surrealdb"><a class="header" href="#option-2-database-only-surrealdb">Option 2: Database Only (SurrealDB)</a></h3>
|
||||
<p><strong>Approach</strong>: Store all knowledge in SurrealDB graph database</p>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li>✅ Excellent query performance</li>
|
||||
<li>✅ Native graph relationships</li>
|
||||
<li>✅ Scalable to millions of nodes</li>
|
||||
<li>✅ Centralized for collaboration</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>❌ Not git-trackable</li>
|
||||
<li>❌ Requires running database server</li>
|
||||
<li>❌ Can't edit with text editor</li>
|
||||
<li>❌ Network dependency</li>
|
||||
<li>❌ Infrastructure cost</li>
|
||||
</ul>
|
||||
<p><strong>Scalability</strong>: Excellent, but loses developer workflow benefits</p>
|
||||
<h3 id="option-3-hybrid-filesystem--surrealdb"><a class="header" href="#option-3-hybrid-filesystem--surrealdb">Option 3: Hybrid (Filesystem + SurrealDB)</a></h3>
|
||||
<p><strong>Approach</strong>: Filesystem for local project knowledge, SurrealDB for shared organizational knowledge</p>
|
||||
<p><strong>Pros</strong>:</p>
|
||||
<ul>
|
||||
<li>✅ Git-friendly for project knowledge</li>
|
||||
<li>✅ Text editor friendly</li>
|
||||
<li>✅ Scalable for shared knowledge</li>
|
||||
<li>✅ Works offline (local graph)</li>
|
||||
<li>✅ Collaborative (shared graph)</li>
|
||||
<li>✅ Cost-effective (DB only for shared)</li>
|
||||
</ul>
|
||||
<p><strong>Cons</strong>:</p>
|
||||
<ul>
|
||||
<li>❌ More complex implementation</li>
|
||||
<li>❌ Sync mechanism needed</li>
|
||||
<li>❌ Two storage systems to manage</li>
|
||||
</ul>
|
||||
<p><strong>Scalability</strong>: Excellent - best of both worlds</p>
|
||||
<hr />
|
||||
<h2 id="decision"><a class="header" href="#decision">Decision</a></h2>
|
||||
<p><strong>We will use a hybrid storage strategy: Filesystem (local) + SurrealDB (shared).</strong></p>
|
||||
<p><strong>Architecture</strong>:</p>
|
||||
<pre><code>┌─────────────────────────────────────────────────────────────┐
|
||||
│ Project A (.kogral/) │
|
||||
│ Storage: Filesystem (git-tracked) │
|
||||
│ Scope: Project-specific notes, decisions, patterns │
|
||||
│ Access: Local only │
|
||||
└──────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
│ [inherits]
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Shared KB (SurrealDB or synced filesystem) │
|
||||
│ Storage: SurrealDB (scalable) or filesystem (synced) │
|
||||
│ Scope: Organization-wide guidelines, patterns │
|
||||
│ Access: All projects │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
</code></pre>
|
||||
<p><strong>Implementation</strong>:</p>
|
||||
<pre><code class="language-nickel"># Project config
|
||||
{
|
||||
storage = {
|
||||
primary = 'filesystem, # Local project knowledge
|
||||
secondary = {
|
||||
enabled = true,
|
||||
type = 'surrealdb, # Shared knowledge
|
||||
url = "ws://kb-central.company.com:8000",
|
||||
namespace = "organization",
|
||||
database = "shared-kb",
|
||||
},
|
||||
},
|
||||
|
||||
inheritance = {
|
||||
base = "surrealdb://organization/shared-kb", # Inherit from shared
|
||||
priority = 100, # Project overrides shared
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Sync Strategy</strong>:</p>
|
||||
<pre><code>.kogral/ (Filesystem)
|
||||
↓ [on save]
|
||||
Watch for changes
|
||||
↓ [debounced]
|
||||
Sync to SurrealDB
|
||||
↓
|
||||
Shared graph updated
|
||||
↓ [on query]
|
||||
Merge local + shared results
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h2 id="consequences"><a class="header" href="#consequences">Consequences</a></h2>
|
||||
<h3 id="positive"><a class="header" href="#positive">Positive</a></h3>
|
||||
<p>✅ <strong>Developer Workflow Preserved</strong>:</p>
|
||||
<pre><code class="language-bash"># Local knowledge workflow (unchanged)
|
||||
vim .kogral/notes/my-note.md
|
||||
git add .kogral/notes/my-note.md
|
||||
git commit -m "Add implementation note"
|
||||
git push
|
||||
</code></pre>
|
||||
<p>✅ <strong>Git Integration</strong>:</p>
|
||||
<ul>
|
||||
<li>Project knowledge versioned with code</li>
|
||||
<li>Branches include relevant knowledge</li>
|
||||
<li>Merges resolve knowledge conflicts</li>
|
||||
<li>PR reviews include knowledge changes</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Offline Development</strong>:</p>
|
||||
<ul>
|
||||
<li>Full functionality without network</li>
|
||||
<li>Shared guidelines cached locally</li>
|
||||
<li>Sync when reconnected</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Scalability</strong>:</p>
|
||||
<ul>
|
||||
<li>Projects: filesystem (100s of nodes, fine performance)</li>
|
||||
<li>Organization: SurrealDB (10,000+ nodes, excellent performance)</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Collaboration</strong>:</p>
|
||||
<ul>
|
||||
<li>Shared guidelines accessible to all projects</li>
|
||||
<li>Updates to shared knowledge propagate automatically</li>
|
||||
<li>Consistent practices across organization</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Cost-Effective</strong>:</p>
|
||||
<ul>
|
||||
<li>Small projects: free (filesystem only)</li>
|
||||
<li>Organizations: SurrealDB for shared only (not all project knowledge)</li>
|
||||
</ul>
|
||||
<p>✅ <strong>Gradual Adoption</strong>:</p>
|
||||
<ul>
|
||||
<li>Start with filesystem only</li>
|
||||
<li>Add SurrealDB when needed</li>
|
||||
<li>Feature-gated (<code>--features surrealdb</code>)</li>
|
||||
</ul>
|
||||
<h3 id="negative"><a class="header" href="#negative">Negative</a></h3>
|
||||
<p>❌ <strong>Complexity</strong>:</p>
|
||||
<ul>
|
||||
<li>Two storage implementations</li>
|
||||
<li>Sync mechanism required</li>
|
||||
<li>Conflict resolution needed</li>
|
||||
</ul>
|
||||
<p><strong>Mitigation</strong>:</p>
|
||||
<ul>
|
||||
<li>Storage trait abstracts differences</li>
|
||||
<li>Sync is optional (can disable)</li>
|
||||
<li>Conflicts rare (guidelines change infrequently)</li>
|
||||
</ul>
|
||||
<p>❌ <strong>Sync Latency</strong>:</p>
|
||||
<ul>
|
||||
<li>Changes to shared KB not instant in all projects</li>
|
||||
</ul>
|
||||
<p><strong>Mitigation</strong>:</p>
|
||||
<ul>
|
||||
<li>Acceptable latency (guidelines don't change rapidly)</li>
|
||||
<li>Manual sync command available (<code>kb sync</code>)</li>
|
||||
<li>Auto-sync on query (fetch latest)</li>
|
||||
</ul>
|
||||
<p>❌ <strong>Infrastructure Requirement</strong>:</p>
|
||||
<ul>
|
||||
<li>SurrealDB server needed for shared KB</li>
|
||||
</ul>
|
||||
<p><strong>Mitigation</strong>:</p>
|
||||
<ul>
|
||||
<li>Optional (can use synced filesystem instead)</li>
|
||||
<li>Docker Compose for easy setup</li>
|
||||
<li>Managed SurrealDB Cloud option</li>
|
||||
</ul>
|
||||
<h3 id="neutral"><a class="header" href="#neutral">Neutral</a></h3>
|
||||
<p>⚪ <strong>Storage Trait Implementation</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[async_trait]
|
||||
pub trait Storage {
|
||||
async fn save_graph(&self, graph: &Graph) -> Result<()>;
|
||||
async fn load_graph(&self, name: &str) -> Result<Graph>;
|
||||
async fn list_graphs(&self) -> Result<Vec<String>>;
|
||||
}
|
||||
|
||||
// Three implementations
|
||||
impl Storage for FilesystemStorage { /* ... */ }
|
||||
impl Storage for SurrealDbStorage { /* ... */ }
|
||||
impl Storage for MemoryStorage { /* ... */ }
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p>Abstraction makes multi-backend manageable.</p>
|
||||
<hr />
|
||||
<h2 id="use-cases"><a class="header" href="#use-cases">Use Cases</a></h2>
|
||||
<h3 id="small-project-solo-developer"><a class="header" href="#small-project-solo-developer">Small Project (Solo Developer)</a></h3>
|
||||
<p><strong>Config</strong>:</p>
|
||||
<pre><code class="language-nickel">{ storage = { primary = 'filesystem } }
|
||||
</code></pre>
|
||||
<p><strong>Behavior</strong>:</p>
|
||||
<ul>
|
||||
<li>All knowledge in <code>.kogral/</code> directory</li>
|
||||
<li>Git-tracked with code</li>
|
||||
<li>No database required</li>
|
||||
<li>Works offline</li>
|
||||
</ul>
|
||||
<h3 id="medium-project-team"><a class="header" href="#medium-project-team">Medium Project (Team)</a></h3>
|
||||
<p><strong>Config</strong>:</p>
|
||||
<pre><code class="language-nickel">{
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = {
|
||||
enabled = true,
|
||||
type = 'surrealdb,
|
||||
url = "ws://team-kb.local:8000",
|
||||
},
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Behavior</strong>:</p>
|
||||
<ul>
|
||||
<li>Project knowledge in <code>.kogral/</code> (git-tracked)</li>
|
||||
<li>Shared team patterns in SurrealDB</li>
|
||||
<li>Automatic sync</li>
|
||||
<li>Offline fallback to cached</li>
|
||||
</ul>
|
||||
<h3 id="large-organization"><a class="header" href="#large-organization">Large Organization</a></h3>
|
||||
<p><strong>Config</strong>:</p>
|
||||
<pre><code class="language-nickel">{
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = {
|
||||
enabled = true,
|
||||
type = 'surrealdb,
|
||||
url = "ws://kb.company.com:8000",
|
||||
namespace = "engineering",
|
||||
database = "shared-kb",
|
||||
},
|
||||
},
|
||||
|
||||
inheritance = {
|
||||
base = "surrealdb://engineering/shared-kb",
|
||||
guidelines = [
|
||||
"surrealdb://engineering/rust-guidelines",
|
||||
"surrealdb://engineering/security-policies",
|
||||
],
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Behavior</strong>:</p>
|
||||
<ul>
|
||||
<li>Project-specific in <code>.kogral/</code></li>
|
||||
<li>Organization guidelines in SurrealDB</li>
|
||||
<li>Security policies enforced</li>
|
||||
<li>Automatic guideline updates</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="sync-mechanism"><a class="header" href="#sync-mechanism">Sync Mechanism</a></h2>
|
||||
<h3 id="filesystem--surrealdb"><a class="header" href="#filesystem--surrealdb">Filesystem → SurrealDB</a></h3>
|
||||
<p><strong>Trigger</strong>: File changes detected (via <code>notify</code> crate)</p>
|
||||
<p><strong>Process</strong>:</p>
|
||||
<ol>
|
||||
<li>Parse changed markdown file</li>
|
||||
<li>Convert to Node struct</li>
|
||||
<li>Upsert to SurrealDB</li>
|
||||
<li>Update relationships</li>
|
||||
</ol>
|
||||
<p><strong>Debouncing</strong>: 500ms (configurable)</p>
|
||||
<h3 id="surrealdb--filesystem"><a class="header" href="#surrealdb--filesystem">SurrealDB → Filesystem</a></h3>
|
||||
<p><strong>Trigger</strong>: Query for shared knowledge</p>
|
||||
<p><strong>Process</strong>:</p>
|
||||
<ol>
|
||||
<li>Query SurrealDB for shared nodes</li>
|
||||
<li>Cache locally (in-memory or filesystem)</li>
|
||||
<li>Merge with local results</li>
|
||||
<li>Return combined</li>
|
||||
</ol>
|
||||
<p><strong>Caching</strong>: TTL-based (5 minutes default)</p>
|
||||
<h3 id="conflict-resolution"><a class="header" href="#conflict-resolution">Conflict Resolution</a></h3>
|
||||
<p><strong>Strategy</strong>: Last-write-wins with version tracking</p>
|
||||
<p><strong>Example</strong>:</p>
|
||||
<pre><code>Project A: Updates shared guideline (v1 → v2)
|
||||
Project B: Has cached v1
|
||||
|
||||
On Project B query:
|
||||
- Detects v2 available
|
||||
- Fetches v2
|
||||
- Updates cache
|
||||
- Uses v2 going forward
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h2 id="alternatives-considered"><a class="header" href="#alternatives-considered">Alternatives Considered</a></h2>
|
||||
<h3 id="git-submodules-for-shared-knowledge"><a class="header" href="#git-submodules-for-shared-knowledge">Git Submodules for Shared Knowledge</a></h3>
|
||||
<p><strong>Rejected</strong>: Cumbersome workflow</p>
|
||||
<ul>
|
||||
<li>Requires manual submodule update</li>
|
||||
<li>Merge conflicts in shared submodule</li>
|
||||
<li>Not discoverable (need to know submodule exists)</li>
|
||||
</ul>
|
||||
<h3 id="syncthing-for-filesystem-sync"><a class="header" href="#syncthing-for-filesystem-sync">Syncthing for Filesystem Sync</a></h3>
|
||||
<p><strong>Rejected</strong>: Not designed for this use case</p>
|
||||
<ul>
|
||||
<li>No query optimization</li>
|
||||
<li>No relationship indexes</li>
|
||||
<li>Sync conflicts difficult to resolve</li>
|
||||
</ul>
|
||||
<h3 id="postgresql-with-json"><a class="header" href="#postgresql-with-json">PostgreSQL with JSON</a></h3>
|
||||
<p><strong>Rejected</strong>: Not a graph database</p>
|
||||
<ul>
|
||||
<li>Poor graph query performance</li>
|
||||
<li>Relationship traversal requires complex SQL joins</li>
|
||||
<li>No native graph features</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="migration-path"><a class="header" href="#migration-path">Migration Path</a></h2>
|
||||
<h3 id="phase-1-filesystem-only-current"><a class="header" href="#phase-1-filesystem-only-current">Phase 1: Filesystem Only (Current)</a></h3>
|
||||
<ul>
|
||||
<li>All storage via filesystem</li>
|
||||
<li>Git-tracked</li>
|
||||
<li>No database required</li>
|
||||
</ul>
|
||||
<h3 id="phase-2-optional-surrealdb"><a class="header" href="#phase-2-optional-surrealdb">Phase 2: Optional SurrealDB</a></h3>
|
||||
<ul>
|
||||
<li>Add SurrealDB support (feature-gated)</li>
|
||||
<li>Manual sync command</li>
|
||||
<li>Shared KB opt-in</li>
|
||||
</ul>
|
||||
<h3 id="phase-3-automatic-sync"><a class="header" href="#phase-3-automatic-sync">Phase 3: Automatic Sync</a></h3>
|
||||
<ul>
|
||||
<li>File watching</li>
|
||||
<li>Auto-sync on changes</li>
|
||||
<li>Background sync</li>
|
||||
</ul>
|
||||
<h3 id="phase-4-multi-tenant-surrealdb"><a class="header" href="#phase-4-multi-tenant-surrealdb">Phase 4: Multi-Tenant SurrealDB</a></h3>
|
||||
<ul>
|
||||
<li>Organization namespaces</li>
|
||||
<li>Access control</li>
|
||||
<li>Audit logs</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="monitoring"><a class="header" href="#monitoring">Monitoring</a></h2>
|
||||
<p><strong>Success Criteria</strong>:</p>
|
||||
<ul>
|
||||
<li>Developers don't notice hybrid complexity</li>
|
||||
<li>Sync completes < 1 second for typical changes</li>
|
||||
<li>Shared guidelines accessible in < 100ms</li>
|
||||
<li>Zero data loss in sync</li>
|
||||
</ul>
|
||||
<p><strong>Metrics</strong>:</p>
|
||||
<ul>
|
||||
<li>Sync latency (P50, P95, P99)</li>
|
||||
<li>Cache hit rate (shared knowledge)</li>
|
||||
<li>Conflict rate (expect < 0.1%)</li>
|
||||
<li>User satisfaction</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="references"><a class="header" href="#references">References</a></h2>
|
||||
<ul>
|
||||
<li><a href="https://surrealdb.com/docs">SurrealDB Documentation</a></li>
|
||||
<li><a href="../../crates/kb-core/src/storage/mod.rs">Storage Trait Implementation</a></li>
|
||||
<li><a href="../../crates/kb-core/src/storage/filesystem.rs">FilesystemStorage</a></li>
|
||||
<li><a href="../../crates/kb-core/src/storage/surrealdb.rs">SurrealDbStorage</a></li>
|
||||
<li><a href="../../scripts/kb-sync.nu">Sync Mechanism</a></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="revision-history"><a class="header" href="#revision-history">Revision History</a></h2>
|
||||
<div class="table-wrapper"><table><thead><tr><th>Date</th><th>Author</th><th>Change</th></tr></thead><tbody>
|
||||
<tr><td>2026-01-17</td><td>Architecture Team</td><td>Initial decision</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<hr />
|
||||
<p><strong>Previous ADR</strong>: <a href="002-fastembed-ai-providers.html">ADR-002: FastEmbed via AI Providers</a>
|
||||
<strong>Next ADR</strong>: <a href="004-logseq-compatibility.html">ADR-004: Logseq Compatibility</a></p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../../architecture/adrs/002-fastembed-ai-providers.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/004-logseq-blocks-support.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../../architecture/adrs/002-fastembed-ai-providers.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/004-logseq-blocks-support.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../../elasticlunr.min.js"></script>
|
||||
<script src="../../mark.min.js"></script>
|
||||
<script src="../../searcher.js"></script>
|
||||
|
||||
<script src="../../clipboard.min.js"></script>
|
||||
<script src="../../highlight.js"></script>
|
||||
<script src="../../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
391
docs/book/architecture/adrs/004-logseq-blocks-support.html
Normal file
@ -0,0 +1,391 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>ADR-004: Logseq Blocks Support - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../../favicon.svg">
|
||||
<link rel="shortcut icon" href="../../favicon.png">
|
||||
<link rel="stylesheet" href="../../css/variables.css">
|
||||
<link rel="stylesheet" href="../../css/general.css">
|
||||
<link rel="stylesheet" href="../../css/chrome.css">
|
||||
<link rel="stylesheet" href="../../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/adrs/004-logseq-blocks-support.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="adr-004-logseq-blocks-support"><a class="header" href="#adr-004-logseq-blocks-support">ADR-004: Logseq Blocks Support</a></h1>
|
||||
<h2 id="status"><a class="header" href="#status">Status</a></h2>
|
||||
<p><strong>Proposed</strong> (Design phase)</p>
|
||||
<h2 id="context"><a class="header" href="#context">Context</a></h2>
|
||||
<p>Logseq uses <strong>content blocks</strong> as the fundamental unit of information, not full documents. KB currently treats <code>Node.content</code> as flat markdown string, which loses block-level features on import/export:</p>
|
||||
<p><strong>Lost features</strong>:</p>
|
||||
<ul>
|
||||
<li>Block properties (<code>#card</code>, <code>TODO</code>, custom properties)</li>
|
||||
<li>Block hierarchy (outliner nesting)</li>
|
||||
<li>Block references (<code>((block-uuid))</code>)</li>
|
||||
<li>Block-level queries (find all flashcards, TODOs)</li>
|
||||
</ul>
|
||||
<p><strong>User requirement</strong>: Round-trip Logseq import/export with full fidelity:</p>
|
||||
<pre><code>Logseq → KOGRAL Import → KOGRAL Storage → KOGRAL Export → Logseq
|
||||
(blocks preserved at every step)
|
||||
</code></pre>
|
||||
<h2 id="decision"><a class="header" href="#decision">Decision</a></h2>
|
||||
<p>Implement <strong>Hybrid Block Support</strong> (structured + markdown):</p>
|
||||
<h3 id="1-add-block-data-structure"><a class="header" href="#1-add-block-data-structure">1. Add Block Data Structure</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub struct Block {
|
||||
pub id: String, // UUID
|
||||
pub content: String, // Block text
|
||||
pub properties: BlockProperties, // Tags, status, custom
|
||||
pub children: Vec<Block>, // Nested blocks
|
||||
// ... timestamps ...
|
||||
}
|
||||
|
||||
pub struct BlockProperties {
|
||||
pub tags: Vec<String>, // #card, #important
|
||||
pub status: Option<TaskStatus>, // TODO, DONE, etc.
|
||||
pub custom: HashMap<String, String>, // property:: value
|
||||
pub block_refs: Vec<String>, // ((uuid))
|
||||
pub page_refs: Vec<String>, // [[page]]
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="2-extend-node-model"><a class="header" href="#2-extend-node-model">2. Extend Node Model</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub struct Node {
|
||||
// ... existing fields ...
|
||||
pub content: String, // Source of truth (markdown)
|
||||
pub blocks: Option<Vec<Block>>, // Cached structure (optional)
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="3-bidirectional-parser"><a class="header" href="#3-bidirectional-parser">3. Bidirectional Parser</a></h3>
|
||||
<ul>
|
||||
<li><strong>Parse</strong>: Markdown → <code>Vec<Block></code> (lazy, on-demand)</li>
|
||||
<li><strong>Serialize</strong>: <code>Vec<Block></code> → Markdown (for export)</li>
|
||||
</ul>
|
||||
<h3 id="4-storage-strategy"><a class="header" href="#4-storage-strategy">4. Storage Strategy</a></h3>
|
||||
<p><strong>Filesystem</strong> (git-friendly, Logseq-compatible):</p>
|
||||
<pre><code class="language-markdown">- Block 1 #card
|
||||
- Nested answer
|
||||
- TODO Block 2
|
||||
priority:: high
|
||||
</code></pre>
|
||||
<p><strong>SurrealDB</strong> (queryable):</p>
|
||||
<pre><code class="language-sql">DEFINE TABLE block;
|
||||
DEFINE FIELD node_id ON block TYPE record(node);
|
||||
DEFINE FIELD block_id ON block TYPE string;
|
||||
DEFINE FIELD properties ON block TYPE object;
|
||||
DEFINE INDEX block_tags ON block COLUMNS properties.tags;
|
||||
</code></pre>
|
||||
<h3 id="5-query-extensions"><a class="header" href="#5-query-extensions">5. Query Extensions</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>// Find all flashcards
|
||||
graph.find_blocks_by_tag("card")
|
||||
|
||||
// Find all TODOs
|
||||
graph.find_all_todos()
|
||||
|
||||
// Find blocks with custom property
|
||||
node.find_blocks_by_property("priority", "high")
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="consequences"><a class="header" href="#consequences">Consequences</a></h2>
|
||||
<h3 id="positive"><a class="header" href="#positive">Positive</a></h3>
|
||||
<p>✅ <strong>Full Logseq Compatibility</strong> - Import/export preserves all block features
|
||||
✅ <strong>Queryable Blocks</strong> - Find #card, TODO, custom properties across KOGRAL
|
||||
✅ <strong>Backward Compatible</strong> - Existing nodes without blocks still work
|
||||
✅ <strong>Type-Safe</strong> - Structured data instead of regex parsing everywhere
|
||||
✅ <strong>Extensible</strong> - Custom block properties supported
|
||||
✅ <strong>Hierarchy Preserved</strong> - Nested blocks maintain parent-child relationships</p>
|
||||
<h3 id="negative"><a class="header" href="#negative">Negative</a></h3>
|
||||
<p>⚠️ <strong>Added Complexity</strong> - New data structures, parser, sync logic
|
||||
⚠️ <strong>Dual Representation</strong> - Must keep <code>content</code> and <code>blocks</code> in sync
|
||||
⚠️ <strong>Storage Overhead</strong> - SurrealDB stores both markdown and structure
|
||||
⚠️ <strong>Migration Required</strong> - Existing data needs parsing to populate blocks</p>
|
||||
<h3 id="neutral"><a class="header" href="#neutral">Neutral</a></h3>
|
||||
<p>⚙️ <strong>Lazy Parsing</strong> - Blocks parsed on-demand (not stored by default)
|
||||
⚙️ <strong>Opt-In</strong> - Config flag <code>blocks.enabled</code> to activate features
|
||||
⚙️ <strong>Gradual Adoption</strong> - Can implement in phases</p>
|
||||
<h2 id="implementation-phases"><a class="header" href="#implementation-phases">Implementation Phases</a></h2>
|
||||
<p><strong>Phase 1: Foundation</strong> (No behavior change)</p>
|
||||
<ul>
|
||||
<li>Add <code>Block</code> struct to <code>models/block.rs</code></li>
|
||||
<li>Add optional <code>blocks</code> field to <code>Node</code></li>
|
||||
<li>Add config: <code>blocks.enabled = false</code> (default off)</li>
|
||||
</ul>
|
||||
<p><strong>Phase 2: Parser</strong></p>
|
||||
<ul>
|
||||
<li>Implement <code>BlockParser::parse()</code> (markdown → blocks)</li>
|
||||
<li>Implement <code>BlockParser::serialize()</code> (blocks → markdown)</li>
|
||||
<li>Add <code>Node::get_blocks()</code> method (lazy parsing)</li>
|
||||
</ul>
|
||||
<p><strong>Phase 3: Logseq Integration</strong></p>
|
||||
<ul>
|
||||
<li>Update <code>LogseqImporter</code> to parse blocks</li>
|
||||
<li>Update <code>LogseqExporter</code> to serialize blocks</li>
|
||||
<li>Test round-trip (Logseq → KB → Logseq)</li>
|
||||
</ul>
|
||||
<p><strong>Phase 4: Query API</strong></p>
|
||||
<ul>
|
||||
<li>Add <code>Graph::find_blocks_by_tag()</code></li>
|
||||
<li>Add <code>Graph::find_all_todos()</code></li>
|
||||
<li>Add <code>Node::find_blocks_by_property()</code></li>
|
||||
</ul>
|
||||
<p><strong>Phase 5: MCP/CLI Integration</strong></p>
|
||||
<ul>
|
||||
<li>Add <code>kb/find_blocks</code> MCP tool</li>
|
||||
<li>Add <code>kb find-cards</code> CLI command</li>
|
||||
<li>Add <code>kb find-todos</code> CLI command</li>
|
||||
</ul>
|
||||
<p><strong>Phase 6: SurrealDB Backend</strong></p>
|
||||
<ul>
|
||||
<li>Create <code>block</code> table schema</li>
|
||||
<li>Index on tags, status, properties</li>
|
||||
<li>Store blocks alongside nodes</li>
|
||||
</ul>
|
||||
<h2 id="alternatives-considered"><a class="header" href="#alternatives-considered">Alternatives Considered</a></h2>
|
||||
<h3 id="alternative-1-blocks-as-first-class-nodes"><a class="header" href="#alternative-1-blocks-as-first-class-nodes">Alternative 1: Blocks as First-Class Nodes</a></h3>
|
||||
<p>Convert each Logseq block to a separate KOGRAL Node.</p>
|
||||
<p><strong>Rejected</strong>: Too granular, explosion of nodes, loses document context.</p>
|
||||
<h3 id="alternative-2-parser-only-no-storage"><a class="header" href="#alternative-2-parser-only-no-storage">Alternative 2: Parser-Only (No Storage)</a></h3>
|
||||
<p>Keep <code>content: String</code>, parse blocks on every access.</p>
|
||||
<p><strong>Rejected</strong>: Can't query blocks in database, parse overhead, can't index.</p>
|
||||
<h3 id="alternative-3-metadata-field"><a class="header" href="#alternative-3-metadata-field">Alternative 3: Metadata Field</a></h3>
|
||||
<p>Store blocks in <code>metadata: HashMap<String, Value></code>.</p>
|
||||
<p><strong>Rejected</strong>: Not type-safe, harder to query, no schema validation.</p>
|
||||
<h2 id="references"><a class="header" href="#references">References</a></h2>
|
||||
<ul>
|
||||
<li><a href="https://docs.logseq.com/#/page/blocks">Logseq Block Format</a></li>
|
||||
<li><a href="../logseq-blocks-design.html">Full Design Document</a></li>
|
||||
<li><a href="https://github.com/.../issues/XXX">Implementation Tracking</a></li>
|
||||
</ul>
|
||||
<h2 id="notes"><a class="header" href="#notes">Notes</a></h2>
|
||||
<p><strong>Backward Compatibility Strategy</strong>:</p>
|
||||
<ul>
|
||||
<li><code>content</code> remains source of truth</li>
|
||||
<li><code>blocks</code> is optional enhancement</li>
|
||||
<li>Old code works unchanged</li>
|
||||
<li>New features opt-in via config</li>
|
||||
</ul>
|
||||
<p><strong>Migration Path</strong>:</p>
|
||||
<ul>
|
||||
<li>Existing users: blocks disabled by default</li>
|
||||
<li>New users: blocks enabled, parsed on import</li>
|
||||
<li>Manual: <code>kb reindex --parse-blocks</code> to populate</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<p><strong>Decision Date</strong>: 2026-01-17
|
||||
<strong>Approvers</strong>: TBD
|
||||
<strong>Review Date</strong>: After Phase 2 implementation</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../../architecture/adrs/003-hybrid-storage.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/005-mcp-protocol.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../../architecture/adrs/003-hybrid-storage.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../architecture/adrs/005-mcp-protocol.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../../elasticlunr.min.js"></script>
|
||||
<script src="../../mark.min.js"></script>
|
||||
<script src="../../searcher.js"></script>
|
||||
|
||||
<script src="../../clipboard.min.js"></script>
|
||||
<script src="../../highlight.js"></script>
|
||||
<script src="../../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/architecture/adrs/005-mcp-protocol.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>ADR-005: MCP Protocol for AI Integration - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../../favicon.svg">
|
||||
<link rel="shortcut icon" href="../../favicon.png">
|
||||
<link rel="stylesheet" href="../../css/variables.css">
|
||||
<link rel="stylesheet" href="../../css/general.css">
|
||||
<link rel="stylesheet" href="../../css/chrome.css">
|
||||
<link rel="stylesheet" href="../../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/adrs/005-mcp-protocol.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="adr-005-mcp-protocol-for-ai-integration"><a class="header" href="#adr-005-mcp-protocol-for-ai-integration">ADR-005: MCP Protocol for AI Integration</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../../architecture/adrs/004-logseq-blocks-support.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../setup/prerequisites.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../../architecture/adrs/004-logseq-blocks-support.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../../setup/prerequisites.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../../elasticlunr.min.js"></script>
|
||||
<script src="../../mark.min.js"></script>
|
||||
<script src="../../searcher.js"></script>
|
||||
|
||||
<script src="../../clipboard.min.js"></script>
|
||||
<script src="../../highlight.js"></script>
|
||||
<script src="../../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
660
docs/book/architecture/config-driven.html
Normal file
@ -0,0 +1,660 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Config-Driven Design - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/config-driven.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="config-driven-architecture"><a class="header" href="#config-driven-architecture">Config-Driven Architecture</a></h1>
|
||||
<p>The KOGRAL follows a <strong>config-driven architecture</strong> where all behavior is defined through Nickel configuration files rather than hardcoded in Rust.</p>
|
||||
<h2 id="philosophy"><a class="header" href="#philosophy">Philosophy</a></h2>
|
||||
<p><strong>"Configuration, not code, defines behavior"</strong></p>
|
||||
<p>Instead of hardcoding storage backends, embedding providers, or query parameters, KB uses a layered configuration system that composes settings from multiple sources:</p>
|
||||
<ol>
|
||||
<li><strong>Schema contracts</strong> (type definitions)</li>
|
||||
<li><strong>Defaults</strong> (base values)</li>
|
||||
<li><strong>Mode overlays</strong> (dev/prod/test optimizations)</li>
|
||||
<li><strong>User customizations</strong> (project-specific overrides)</li>
|
||||
</ol>
|
||||
<p>This approach provides:</p>
|
||||
<ul>
|
||||
<li>✅ <strong>Type safety</strong> - Nickel contracts validate configuration before runtime</li>
|
||||
<li>✅ <strong>Composability</strong> - Mix and match configurations for different environments</li>
|
||||
<li>✅ <strong>Discoverability</strong> - Self-documenting schemas with inline documentation</li>
|
||||
<li>✅ <strong>Hot-reload</strong> - Change behavior without recompiling Rust code</li>
|
||||
<li>✅ <strong>Double validation</strong> - Nickel contracts + serde ensure correctness</li>
|
||||
</ul>
|
||||
<h2 id="configuration-composition-flow"><a class="header" href="#configuration-composition-flow">Configuration Composition Flow</a></h2>
|
||||
<p><img src="../diagrams/config-composition.svg" alt="Configuration Composition" /></p>
|
||||
<p>The configuration system uses a <strong>four-layer composition pattern</strong>:</p>
|
||||
<h3 id="layer-1-schema-contracts"><a class="header" href="#layer-1-schema-contracts">Layer 1: Schema Contracts</a></h3>
|
||||
<p><strong>Location</strong>: <code>schemas/kb/contracts.ncl</code></p>
|
||||
<p><strong>Purpose</strong>: Define types and validation rules using Nickel contracts.</p>
|
||||
<p><strong>Example</strong>:</p>
|
||||
<pre><code class="language-nickel">{
|
||||
StorageType = [| 'filesystem, 'memory, 'surrealdb |],
|
||||
|
||||
StorageConfig = {
|
||||
primary | StorageType
|
||||
| doc "Primary storage backend"
|
||||
| default = 'filesystem,
|
||||
|
||||
secondary | SecondaryStorageConfig
|
||||
| doc "Optional secondary storage"
|
||||
| default = { enabled = false },
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Benefits</strong>:</p>
|
||||
<ul>
|
||||
<li>Enum validation (only valid storage types accepted)</li>
|
||||
<li>Required vs optional fields</li>
|
||||
<li>Default values for optional fields</li>
|
||||
<li>Documentation attached to types</li>
|
||||
</ul>
|
||||
<h3 id="layer-2-defaults"><a class="header" href="#layer-2-defaults">Layer 2: Defaults</a></h3>
|
||||
<p><strong>Location</strong>: <code>schemas/kb/defaults.ncl</code></p>
|
||||
<p><strong>Purpose</strong>: Provide sensible base values for all configuration options.</p>
|
||||
<p><strong>Example</strong>:</p>
|
||||
<pre><code class="language-nickel">{
|
||||
base = {
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = {
|
||||
enabled = false,
|
||||
type = 'surrealdb,
|
||||
url = "ws://localhost:8000",
|
||||
},
|
||||
},
|
||||
embeddings = {
|
||||
enabled = true,
|
||||
provider = 'fastembed,
|
||||
model = "BAAI/bge-small-en-v1.5",
|
||||
dimensions = 384,
|
||||
},
|
||||
} | contracts.KbConfig,
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Validated by</strong>: <code>contracts.KbConfig</code> contract ensures defaults are valid.</p>
|
||||
<h3 id="layer-3-mode-overlays"><a class="header" href="#layer-3-mode-overlays">Layer 3: Mode Overlays</a></h3>
|
||||
<p><strong>Location</strong>: <code>schemas/kb/modes/{dev,prod,test}.ncl</code></p>
|
||||
<p><strong>Purpose</strong>: Environment-specific optimizations and tuning.</p>
|
||||
<h4 id="development-mode-devncl"><a class="header" href="#development-mode-devncl">Development Mode (<code>dev.ncl</code>)</a></h4>
|
||||
<p>Optimized for: Fast iteration, local development, debugging</p>
|
||||
<pre><code class="language-nickel">{
|
||||
storage = {
|
||||
primary = 'filesystem,
|
||||
secondary = { enabled = false }, # No database overhead
|
||||
},
|
||||
embeddings = {
|
||||
provider = 'fastembed, # Local, no API costs
|
||||
},
|
||||
sync = {
|
||||
auto_index = false, # Manual control
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<h4 id="production-mode-prodncl"><a class="header" href="#production-mode-prodncl">Production Mode (<code>prod.ncl</code>)</a></h4>
|
||||
<p>Optimized for: Performance, reliability, scalability</p>
|
||||
<pre><code class="language-nickel">{
|
||||
storage = {
|
||||
secondary = { enabled = true }, # SurrealDB for scale
|
||||
},
|
||||
embeddings = {
|
||||
provider = 'openai, # High-quality cloud embeddings
|
||||
model = "text-embedding-3-small",
|
||||
dimensions = 1536,
|
||||
},
|
||||
sync = {
|
||||
auto_index = true,
|
||||
debounce_ms = 300, # Fast response
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<h4 id="test-mode-testncl"><a class="header" href="#test-mode-testncl">Test Mode (<code>test.ncl</code>)</a></h4>
|
||||
<p>Optimized for: Fast tests, isolation, determinism</p>
|
||||
<pre><code class="language-nickel">{
|
||||
storage = {
|
||||
primary = 'memory, # Ephemeral, no disk I/O
|
||||
},
|
||||
embeddings = {
|
||||
enabled = false, # Disable for speed
|
||||
},
|
||||
sync = {
|
||||
auto_index = false,
|
||||
debounce_ms = 0, # No delays in tests
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="layer-4-user-customizations"><a class="header" href="#layer-4-user-customizations">Layer 4: User Customizations</a></h3>
|
||||
<p><strong>Location</strong>: <code>.kb-config/core/kb.ncl</code> or <code>.kb-config/platform/{dev,prod,test}.ncl</code></p>
|
||||
<p><strong>Purpose</strong>: Project-specific or deployment-specific overrides.</p>
|
||||
<p><strong>Example</strong> (user project config):</p>
|
||||
<pre><code class="language-nickel">let mode = import "../../schemas/kb/modes/dev.ncl" in
|
||||
|
||||
let user_custom = {
|
||||
graph = {
|
||||
name = "my-project",
|
||||
},
|
||||
embeddings = {
|
||||
provider = 'claude, # Override to use Claude
|
||||
model = "claude-3-haiku-20240307",
|
||||
},
|
||||
query = {
|
||||
similarity_threshold = 0.7, # Stricter threshold
|
||||
},
|
||||
} in
|
||||
|
||||
helpers.compose_config defaults.base mode user_custom
|
||||
| contracts.KbConfig
|
||||
</code></pre>
|
||||
<h2 id="composition-mechanism"><a class="header" href="#composition-mechanism">Composition Mechanism</a></h2>
|
||||
<p>The <code>helpers.ncl</code> module provides the composition function:</p>
|
||||
<pre><code class="language-nickel">{
|
||||
# Recursively merge with override precedence
|
||||
merge_with_override = fun base override => /* ... */,
|
||||
|
||||
# Compose three layers
|
||||
compose_config = fun defaults mode_config user_custom =>
|
||||
let with_mode = merge_with_override defaults mode_config in
|
||||
merge_with_override with_mode user_custom,
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Merge behavior</strong>:</p>
|
||||
<ul>
|
||||
<li>Records are merged recursively</li>
|
||||
<li>Override values take precedence over base values</li>
|
||||
<li>Arrays are not merged, override replaces base</li>
|
||||
<li>Null in override keeps base value</li>
|
||||
</ul>
|
||||
<p><strong>Example merge</strong>:</p>
|
||||
<pre><code class="language-nickel">base = { storage = { primary = 'filesystem }, embeddings = { enabled = true } }
|
||||
override = { storage = { primary = 'memory } }
|
||||
# Result: { storage = { primary = 'memory }, embeddings = { enabled = true } }
|
||||
</code></pre>
|
||||
<h2 id="export-to-json"><a class="header" href="#export-to-json">Export to JSON</a></h2>
|
||||
<p>Once composed, the Nickel configuration is exported to JSON for Rust consumption:</p>
|
||||
<pre><code class="language-bash">nickel export --format json .kb-config/core/kb.ncl > .kb-config/targets/kb-core.json
|
||||
</code></pre>
|
||||
<p><strong>Output</strong> (<code>.kb-config/targets/kb-core.json</code>):</p>
|
||||
<pre><code class="language-json">{
|
||||
"graph": {
|
||||
"name": "my-project",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"storage": {
|
||||
"primary": "memory",
|
||||
"secondary": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"embeddings": {
|
||||
"enabled": true,
|
||||
"provider": "claude",
|
||||
"model": "claude-3-haiku-20240307",
|
||||
"dimensions": 768
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="rust-integration"><a class="header" href="#rust-integration">Rust Integration</a></h2>
|
||||
<p>The Rust code deserializes the JSON into typed structs using serde:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct KbConfig {
|
||||
pub graph: GraphConfig,
|
||||
pub storage: StorageConfig,
|
||||
pub embeddings: EmbeddingConfig,
|
||||
pub templates: TemplateConfig,
|
||||
pub query: QueryConfig,
|
||||
pub mcp: McpConfig,
|
||||
pub sync: SyncConfig,
|
||||
}
|
||||
|
||||
impl KbConfig {
|
||||
pub fn from_file(path: &Path) -> Result<Self> {
|
||||
let json = std::fs::read_to_string(path)?;
|
||||
let config: KbConfig = serde_json::from_str(&json)?;
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Usage in kb-core</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let config = KbConfig::from_file(".kb-config/targets/kb-core.json")?;
|
||||
|
||||
// Config drives behavior
|
||||
let storage: Box<dyn Storage> = match config.storage.primary {
|
||||
StorageType::Filesystem => Box::new(FilesystemStorage::new(&config)?),
|
||||
StorageType::Memory => Box::new(MemoryStorage::new()),
|
||||
StorageType::SurrealDb => Box::new(SurrealDbStorage::new(&config).await?),
|
||||
};
|
||||
|
||||
let embeddings: Box<dyn EmbeddingProvider> = match config.embeddings.provider {
|
||||
EmbeddingProviderType::FastEmbed => Box::new(FastEmbedProvider::new()?),
|
||||
EmbeddingProviderType::OpenAI => Box::new(RigEmbeddingProvider::openai(&config)?),
|
||||
EmbeddingProviderType::Claude => Box::new(RigEmbeddingProvider::claude(&config)?),
|
||||
EmbeddingProviderType::Ollama => Box::new(RigEmbeddingProvider::ollama(&config)?),
|
||||
};
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="double-validation"><a class="header" href="#double-validation">Double Validation</a></h2>
|
||||
<p>Configuration is validated <strong>twice</strong>:</p>
|
||||
<h3 id="1-nickel-contract-validation"><a class="header" href="#1-nickel-contract-validation">1. Nickel Contract Validation</a></h3>
|
||||
<p>At export time, Nickel validates:</p>
|
||||
<ul>
|
||||
<li>✅ Types match contracts (e.g., <code>primary | StorageType</code>)</li>
|
||||
<li>✅ Required fields are present</li>
|
||||
<li>✅ Enums have valid values</li>
|
||||
<li>✅ Nested structure is correct</li>
|
||||
</ul>
|
||||
<p><strong>Error example</strong>:</p>
|
||||
<pre><code>error: contract broken by a value
|
||||
┌─ .kb-config/core/kb.ncl:15:5
|
||||
│
|
||||
15│ primary = 'invalid,
|
||||
│ ^^^^^^^^^^^^^^^^^^^ applied to this expression
|
||||
│
|
||||
= This value is not in the enum ['filesystem, 'memory, 'surrealdb]
|
||||
</code></pre>
|
||||
<h3 id="2-serde-deserialization-validation"><a class="header" href="#2-serde-deserialization-validation">2. Serde Deserialization Validation</a></h3>
|
||||
<p>At runtime, serde validates:</p>
|
||||
<ul>
|
||||
<li>✅ JSON structure matches Rust types</li>
|
||||
<li>✅ Field names match (with rename support)</li>
|
||||
<li>✅ Values can be converted to Rust types</li>
|
||||
<li>✅ Required fields are not null</li>
|
||||
</ul>
|
||||
<p><strong>Error example</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>Error: missing field `graph` at line 1 column 123
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="benefits-of-config-driven-architecture"><a class="header" href="#benefits-of-config-driven-architecture">Benefits of Config-Driven Architecture</a></h2>
|
||||
<h3 id="1-zero-hardcoding"><a class="header" href="#1-zero-hardcoding">1. Zero Hardcoding</a></h3>
|
||||
<p><strong>Bad</strong> (hardcoded):</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>// Hardcoded - requires recompilation to change
|
||||
let storage = FilesystemStorage::new("/fixed/path");
|
||||
let threshold = 0.6;
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Good</strong> (config-driven):</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>// Config-driven - change via .ncl file
|
||||
let storage = create_storage(&config)?;
|
||||
let threshold = config.query.similarity_threshold;
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="2-environment-flexibility"><a class="header" href="#2-environment-flexibility">2. Environment Flexibility</a></h3>
|
||||
<p>Same codebase, different behavior:</p>
|
||||
<pre><code class="language-bash"># Development
|
||||
nickel export .kb-config/platform/dev.ncl > targets/kb-core.json
|
||||
# → Filesystem storage, fastembed, no auto-sync
|
||||
|
||||
# Production
|
||||
nickel export .kb-config/platform/prod.ncl > targets/kb-core.json
|
||||
# → SurrealDB enabled, OpenAI embeddings, auto-sync
|
||||
|
||||
# Testing
|
||||
nickel export .kb-config/platform/test.ncl > targets/kb-core.json
|
||||
# → In-memory storage, no embeddings, isolated
|
||||
</code></pre>
|
||||
<h3 id="3-self-documenting"><a class="header" href="#3-self-documenting">3. Self-Documenting</a></h3>
|
||||
<p>Nickel contracts include inline documentation:</p>
|
||||
<pre><code class="language-nickel">StorageType = [| 'filesystem, 'memory, 'surrealdb |]
|
||||
| doc "Storage backend type: filesystem (git-tracked), memory (ephemeral), surrealdb (scalable)",
|
||||
</code></pre>
|
||||
<p>IDEs can show this documentation when editing <code>.ncl</code> files.</p>
|
||||
<h3 id="4-type-safe-evolution"><a class="header" href="#4-type-safe-evolution">4. Type-Safe Evolution</a></h3>
|
||||
<p>When adding new features:</p>
|
||||
<ol>
|
||||
<li>Update contract in <code>contracts.ncl</code></li>
|
||||
<li>Add default in <code>defaults.ncl</code></li>
|
||||
<li>Export validates existing configs</li>
|
||||
<li>Rust compilation validates deserialization</li>
|
||||
</ol>
|
||||
<p>Breaking changes are caught <strong>before runtime</strong>.</p>
|
||||
<h3 id="5-testability"><a class="header" href="#5-testability">5. Testability</a></h3>
|
||||
<p>Different test scenarios without code changes:</p>
|
||||
<pre><code class="language-nickel"># test-semantic-search.ncl
|
||||
let test_config = defaults.base & {
|
||||
embeddings = { enabled = true, provider = 'fastembed },
|
||||
query = { similarity_threshold = 0.3 },
|
||||
} in test_config
|
||||
</code></pre>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[test]
|
||||
fn test_semantic_search() {
|
||||
let config = KbConfig::from_file("test-semantic-search.json")?;
|
||||
// Config drives test behavior
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="configuration-discovery"><a class="header" href="#configuration-discovery">Configuration Discovery</a></h2>
|
||||
<p>KB tools automatically discover configuration:</p>
|
||||
<ol>
|
||||
<li><strong>Check <code>.kb-config/targets/kb-core.json</code></strong> (pre-exported)</li>
|
||||
<li><strong>Check <code>.kb-config/core/kb.ncl</code></strong> (export on-demand)</li>
|
||||
<li><strong>Check environment variable <code>KB_CONFIG</code></strong></li>
|
||||
<li><strong>Fall back to embedded defaults</strong></li>
|
||||
</ol>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>impl KbConfig {
|
||||
pub fn discover() -> Result<Self> {
|
||||
if let Ok(config) = Self::from_file(".kb-config/targets/kb-core.json") {
|
||||
return Ok(config);
|
||||
}
|
||||
|
||||
if Path::new(".kb-config/core/kb.ncl").exists() {
|
||||
// Export and load
|
||||
let output = Command::new("nickel")
|
||||
.args(["export", "--format", "json", ".kb-config/core/kb.ncl"])
|
||||
.output()?;
|
||||
return serde_json::from_slice(&output.stdout)?;
|
||||
}
|
||||
|
||||
if let Ok(path) = std::env::var("KB_CONFIG") {
|
||||
return Self::from_file(&path);
|
||||
}
|
||||
|
||||
Ok(Self::default()) // Embedded defaults
|
||||
}
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="integration-with-justfile"><a class="header" href="#integration-with-justfile">Integration with justfile</a></h2>
|
||||
<p>The <code>justfile</code> integrates configuration validation:</p>
|
||||
<pre><code class="language-just"># Validate all Nickel configs
|
||||
nickel-validate-all:
|
||||
@echo "Validating Nickel schemas..."
|
||||
nickel typecheck schemas/kb/contracts.ncl
|
||||
nickel typecheck schemas/kb/defaults.ncl
|
||||
nickel typecheck schemas/kb/helpers.ncl
|
||||
nickel typecheck schemas/kb/modes/dev.ncl
|
||||
nickel typecheck schemas/kb/modes/prod.ncl
|
||||
nickel typecheck schemas/kb/modes/test.ncl
|
||||
nickel typecheck .kb-config/core/kb.ncl
|
||||
|
||||
# Export all platform configs
|
||||
nickel-export-all:
|
||||
@echo "Exporting platform configs to JSON..."
|
||||
@mkdir -p .kb-config/targets
|
||||
nickel export --format json .kb-config/platform/dev.ncl > .kb-config/targets/kb-dev.json
|
||||
nickel export --format json .kb-config/platform/prod.ncl > .kb-config/targets/kb-prod.json
|
||||
nickel export --format json .kb-config/platform/test.ncl > .kb-config/targets/kb-test.json
|
||||
@echo "Exported 3 configurations to .kb-config/targets/"
|
||||
</code></pre>
|
||||
<p>Usage:</p>
|
||||
<pre><code class="language-bash">just nickel::validate-all # Check configs are valid
|
||||
just nickel::export-all # Generate JSON for all environments
|
||||
</code></pre>
|
||||
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
|
||||
<h3 id="1-never-hardcode"><a class="header" href="#1-never-hardcode">1. Never Hardcode</a></h3>
|
||||
<p>If it's behavior, it's config:</p>
|
||||
<ul>
|
||||
<li>Storage paths</li>
|
||||
<li>API endpoints</li>
|
||||
<li>Thresholds</li>
|
||||
<li>Timeouts</li>
|
||||
<li>Feature flags</li>
|
||||
<li>Provider selection</li>
|
||||
</ul>
|
||||
<h3 id="2-use-modes-for-environment-differences"><a class="header" href="#2-use-modes-for-environment-differences">2. Use Modes for Environment Differences</a></h3>
|
||||
<p>Don't put environment-specific values in user config:</p>
|
||||
<p><strong>Bad</strong>:</p>
|
||||
<pre><code class="language-nickel"># user config with env-specific values
|
||||
{
|
||||
storage = {
|
||||
url = if env == "prod" then "prod-url" else "dev-url" # Don't do this
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>Good</strong>:</p>
|
||||
<pre><code class="language-nickel"># modes/prod.ncl
|
||||
{ storage = { url = "prod-url" } }
|
||||
|
||||
# modes/dev.ncl
|
||||
{ storage = { url = "dev-url" } }
|
||||
|
||||
# user config is environment-agnostic
|
||||
{ graph = { name = "my-project" } }
|
||||
</code></pre>
|
||||
<h3 id="3-document-complex-fields"><a class="header" href="#3-document-complex-fields">3. Document Complex Fields</a></h3>
|
||||
<p>Use Nickel's <code>doc</code> metadata:</p>
|
||||
<pre><code class="language-nickel">similarity_threshold | Number
|
||||
| doc "Minimum cosine similarity (0-1) for semantic search matches. Higher = stricter."
|
||||
| default = 0.6,
|
||||
</code></pre>
|
||||
<h3 id="4-validate-early"><a class="header" href="#4-validate-early">4. Validate Early</a></h3>
|
||||
<p>Run <code>nickel typecheck</code> in CI/CD before building Rust code.</p>
|
||||
<h3 id="5-version-configs"><a class="header" href="#5-version-configs">5. Version Configs</a></h3>
|
||||
<p>Track <code>.ncl</code> files in git, ignore <code>.kb-config/targets/*.json</code> (generated).</p>
|
||||
<h2 id="see-also"><a class="header" href="#see-also">See Also</a></h2>
|
||||
<ul>
|
||||
<li><strong>Schema Reference</strong>: <a href="../config/schema.html">Configuration Schema</a></li>
|
||||
<li><strong>User Guide</strong>: <a href="../config/overview.html">Configuration Guide</a></li>
|
||||
<li><strong>ADR</strong>: <a href="adrs/001-nickel-vs-toml.html">Why Nickel vs TOML</a></li>
|
||||
<li><strong>Examples</strong>: <code>.kb-config/core/kb.ncl</code>, <code>.kb-config/platform/*.ncl</code></li>
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../architecture/graph-model.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/storage-architecture.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../architecture/graph-model.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/storage-architecture.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/architecture/graph-model.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Graph Model - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/graph-model.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="graph-model"><a class="header" href="#graph-model">Graph Model</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../architecture/overview.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/config-driven.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../architecture/overview.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/config-driven.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1129
docs/book/architecture/logseq-blocks-design.html
Normal file
667
docs/book/architecture/overview.html
Normal file
@ -0,0 +1,667 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>System Overview - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/overview.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="system-architecture"><a class="header" href="#system-architecture">System Architecture</a></h1>
|
||||
<p>Comprehensive overview of the KOGRAL architecture.</p>
|
||||
<h2 id="high-level-architecture"><a class="header" href="#high-level-architecture">High-Level Architecture</a></h2>
|
||||
<p><img src="../diagrams/architecture-overview.svg" alt="Architecture Overview" /></p>
|
||||
<p>The KOGRAL consists of three main layers:</p>
|
||||
<ol>
|
||||
<li><strong>User Interfaces</strong>: kb-cli (terminal), kb-mcp (AI integration), NuShell scripts (automation)</li>
|
||||
<li><strong>Core Library (kb-core)</strong>: Rust library with graph engine, storage abstraction, embeddings, query engine</li>
|
||||
<li><strong>Storage Backends</strong>: Filesystem (git-friendly), SurrealDB (scalable), In-Memory (cache/testing)</li>
|
||||
</ol>
|
||||
<h2 id="component-details"><a class="header" href="#component-details">Component Details</a></h2>
|
||||
<h3 id="kb-cli-command-line-interface"><a class="header" href="#kb-cli-command-line-interface">kb-cli (Command-Line Interface)</a></h3>
|
||||
<p><strong>Purpose</strong>: Primary user interface for local knowledge management.</p>
|
||||
<p><strong>Commands</strong> (13 total):</p>
|
||||
<ul>
|
||||
<li><code>init</code>: Initialize <code>.kogral/</code> directory</li>
|
||||
<li><code>add</code>: Create nodes (note, decision, guideline, pattern, journal)</li>
|
||||
<li><code>search</code>: Text and semantic search</li>
|
||||
<li><code>link</code>: Create relationships between nodes</li>
|
||||
<li><code>list</code>: List all nodes</li>
|
||||
<li><code>show</code>: Display node details</li>
|
||||
<li><code>delete</code>: Remove nodes</li>
|
||||
<li><code>graph</code>: Visualize knowledge graph</li>
|
||||
<li><code>sync</code>: Sync filesystem ↔ SurrealDB</li>
|
||||
<li><code>serve</code>: Start MCP server</li>
|
||||
<li><code>import</code>: Import from Logseq</li>
|
||||
<li><code>export</code>: Export to Logseq/JSON</li>
|
||||
<li><code>config</code>: Manage configuration</li>
|
||||
</ul>
|
||||
<p><strong>Technology</strong>: Rust + clap (derive API)</p>
|
||||
<p><strong>Features</strong>:</p>
|
||||
<ul>
|
||||
<li>Colored terminal output</li>
|
||||
<li>Interactive prompts</li>
|
||||
<li>Dry-run modes</li>
|
||||
<li>Validation before operations</li>
|
||||
</ul>
|
||||
<h3 id="kb-mcp-mcp-server"><a class="header" href="#kb-mcp-mcp-server">kb-mcp (MCP Server)</a></h3>
|
||||
<p><strong>Purpose</strong>: AI integration via Model Context Protocol.</p>
|
||||
<p><strong>Protocol</strong>: JSON-RPC 2.0 over stdio</p>
|
||||
<p><strong>Components</strong>:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Tools</strong> (7):</p>
|
||||
<ul>
|
||||
<li><code>kogral/search</code>: Query knowledge base</li>
|
||||
<li><code>kb/add_note</code>: Create notes</li>
|
||||
<li><code>kb/add_decision</code>: Create ADRs</li>
|
||||
<li><code>kb/link</code>: Create relationships</li>
|
||||
<li><code>kb/get_guidelines</code>: Retrieve guidelines with inheritance</li>
|
||||
<li><code>kb/list_graphs</code>: List available graphs</li>
|
||||
<li><code>kb/export</code>: Export to formats</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Resources</strong> (6 URIs):</p>
|
||||
<ul>
|
||||
<li><code>kogral://project/notes</code></li>
|
||||
<li><code>kogral://project/decisions</code></li>
|
||||
<li><code>kogral://project/guidelines</code></li>
|
||||
<li><code>kogral://project/patterns</code></li>
|
||||
<li><code>kogral://shared/guidelines</code></li>
|
||||
<li><code>kogral://shared/patterns</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Prompts</strong> (2):</p>
|
||||
<ul>
|
||||
<li><code>kb/summarize_project</code>: Generate project summary</li>
|
||||
<li><code>kb/find_related</code>: Find related nodes</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p><strong>Integration</strong>: Claude Code via <code>~/.config/claude/config.json</code></p>
|
||||
<h3 id="nushell-scripts"><a class="header" href="#nushell-scripts">NuShell Scripts</a></h3>
|
||||
<p><strong>Purpose</strong>: Automation and maintenance tasks.</p>
|
||||
<p><strong>Scripts</strong> (6):</p>
|
||||
<ul>
|
||||
<li><code>kb-sync.nu</code>: Filesystem ↔ SurrealDB sync</li>
|
||||
<li><code>kb-backup.nu</code>: Archive knowledge base</li>
|
||||
<li><code>kb-reindex.nu</code>: Rebuild embeddings</li>
|
||||
<li><code>kb-import-logseq.nu</code>: Import from Logseq</li>
|
||||
<li><code>kb-export-logseq.nu</code>: Export to Logseq</li>
|
||||
<li><code>kb-stats.nu</code>: Graph statistics</li>
|
||||
</ul>
|
||||
<p><strong>Features</strong>:</p>
|
||||
<ul>
|
||||
<li>Colored output</li>
|
||||
<li>Dry-run modes</li>
|
||||
<li>Progress indicators</li>
|
||||
<li>Error handling</li>
|
||||
</ul>
|
||||
<h2 id="core-library-kb-core"><a class="header" href="#core-library-kb-core">Core Library (kb-core)</a></h2>
|
||||
<h3 id="models"><a class="header" href="#models">Models</a></h3>
|
||||
<p><strong>Graph</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub struct Graph {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub nodes: HashMap<String, Node>, // ID → Node
|
||||
pub edges: Vec<Edge>,
|
||||
pub metadata: HashMap<String, Value>,
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Node</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub struct Node {
|
||||
pub id: String,
|
||||
pub node_type: NodeType,
|
||||
pub title: String,
|
||||
pub content: String,
|
||||
pub tags: Vec<String>,
|
||||
pub status: NodeStatus,
|
||||
pub created: DateTime<Utc>,
|
||||
pub modified: DateTime<Utc>,
|
||||
// ... relationships, metadata
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Edge</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub struct Edge {
|
||||
pub from: String,
|
||||
pub to: String,
|
||||
pub relation: EdgeType,
|
||||
pub strength: f32,
|
||||
pub created: DateTime<Utc>,
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="storage-trait"><a class="header" href="#storage-trait">Storage Trait</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
async fn save_graph(&self, graph: &Graph) -> Result<()>;
|
||||
async fn load_graph(&self, name: &str) -> Result<Graph>;
|
||||
async fn delete_graph(&self, name: &str) -> Result<()>;
|
||||
async fn list_graphs(&self) -> Result<Vec<String>>;
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Implementations</strong>:</p>
|
||||
<ol>
|
||||
<li><code>FilesystemStorage</code>: Git-friendly markdown files</li>
|
||||
<li><code>MemoryStorage</code>: In-memory with DashMap</li>
|
||||
<li><code>SurrealDbStorage</code>: Scalable graph database</li>
|
||||
</ol>
|
||||
<h3 id="embedding-provider-trait"><a class="header" href="#embedding-provider-trait">Embedding Provider Trait</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[async_trait]
|
||||
pub trait EmbeddingProvider: Send + Sync {
|
||||
async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
|
||||
fn dimensions(&self) -> usize;
|
||||
fn model_name(&self) -> &str;
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Implementations</strong>:</p>
|
||||
<ol>
|
||||
<li><code>FastEmbedProvider</code>: Local fastembed</li>
|
||||
<li><code>RigEmbeddingProvider</code>: OpenAI, Claude, Ollama (via rig-core)</li>
|
||||
</ol>
|
||||
<h3 id="parser"><a class="header" href="#parser">Parser</a></h3>
|
||||
<p><strong>Input</strong>: Markdown file with YAML frontmatter</p>
|
||||
<p><strong>Output</strong>: <code>Node</code> struct</p>
|
||||
<p><strong>Features</strong>:</p>
|
||||
<ul>
|
||||
<li>YAML frontmatter extraction</li>
|
||||
<li>Markdown body parsing</li>
|
||||
<li>Wikilink detection (<code>[[linked-note]]</code>)</li>
|
||||
<li>Code reference parsing (<code>@file.rs:42</code>)</li>
|
||||
</ul>
|
||||
<p><strong>Example</strong>:</p>
|
||||
<pre><code class="language-markdown">---
|
||||
id: note-123
|
||||
type: note
|
||||
title: My Note
|
||||
tags: [rust, async]
|
||||
---
|
||||
|
||||
# My Note
|
||||
|
||||
Content with [[other-note]] and @src/main.rs:10
|
||||
</code></pre>
|
||||
<p>→</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>Node {
|
||||
id: "note-123",
|
||||
node_type: NodeType::Note,
|
||||
title: "My Note",
|
||||
content: "Content with [[other-note]] and @src/main.rs:10",
|
||||
tags: vec!["rust", "async"],
|
||||
// ... parsed wikilinks, code refs
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="configuration-system"><a class="header" href="#configuration-system">Configuration System</a></h2>
|
||||
<h3 id="nickel-schema"><a class="header" href="#nickel-schema">Nickel Schema</a></h3>
|
||||
<pre><code class="language-nickel"># schemas/kb-config.ncl
|
||||
{
|
||||
KbConfig = {
|
||||
graph | GraphConfig,
|
||||
storage | StorageConfig,
|
||||
embeddings | EmbeddingConfig,
|
||||
templates | TemplateConfig,
|
||||
query | QueryConfig,
|
||||
mcp | McpConfig,
|
||||
sync | SyncConfig,
|
||||
},
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="loading-process"><a class="header" href="#loading-process">Loading Process</a></h3>
|
||||
<pre><code>User writes: .kogral/config.ncl
|
||||
↓ [nickel export --format json]
|
||||
JSON intermediate
|
||||
↓ [serde_json::from_str]
|
||||
KbConfig struct (Rust)
|
||||
↓
|
||||
Runtime behavior
|
||||
</code></pre>
|
||||
<p><strong>Double Validation</strong>:</p>
|
||||
<ol>
|
||||
<li>Nickel contracts: Type-safe, enum validation</li>
|
||||
<li>Serde deserialization: Rust type checking</li>
|
||||
</ol>
|
||||
<p><strong>Benefits</strong>:</p>
|
||||
<ul>
|
||||
<li>Errors caught at export time</li>
|
||||
<li>Runtime guaranteed valid config</li>
|
||||
<li>Self-documenting schemas</li>
|
||||
</ul>
|
||||
<h2 id="storage-architecture"><a class="header" href="#storage-architecture">Storage Architecture</a></h2>
|
||||
<h3 id="hybrid-strategy"><a class="header" href="#hybrid-strategy">Hybrid Strategy</a></h3>
|
||||
<p><strong>Local Graph</strong> (per project):</p>
|
||||
<ul>
|
||||
<li>Storage: Filesystem (<code>.kogral/</code> directory)</li>
|
||||
<li>Format: Markdown + YAML frontmatter</li>
|
||||
<li>Version control: Git</li>
|
||||
<li>Scope: Project-specific knowledge</li>
|
||||
</ul>
|
||||
<p><strong>Shared Graph</strong> (organization):</p>
|
||||
<ul>
|
||||
<li>Storage: SurrealDB (or synced filesystem)</li>
|
||||
<li>Format: Same markdown (for compatibility)</li>
|
||||
<li>Version control: Optional</li>
|
||||
<li>Scope: Organization-wide guidelines</li>
|
||||
</ul>
|
||||
<p><strong>Sync</strong>:</p>
|
||||
<pre><code>Filesystem (.kogral/)
|
||||
↕ [bidirectional sync]
|
||||
SurrealDB (central)
|
||||
</code></pre>
|
||||
<h3 id="file-layout"><a class="header" href="#file-layout">File Layout</a></h3>
|
||||
<pre><code>.kogral/
|
||||
├── config.toml # Graph metadata
|
||||
├── notes/
|
||||
│ ├── async-patterns.md # Individual note
|
||||
│ └── error-handling.md
|
||||
├── decisions/
|
||||
│ ├── 0001-use-rust.md # ADR format
|
||||
│ └── 0002-surrealdb.md
|
||||
├── guidelines/
|
||||
│ ├── rust-errors.md # Project guideline
|
||||
│ └── testing.md
|
||||
├── patterns/
|
||||
│ └── repository.md
|
||||
└── journal/
|
||||
├── 2026-01-17.md # Daily journal
|
||||
└── 2026-01-18.md
|
||||
</code></pre>
|
||||
<h2 id="query-engine"><a class="header" href="#query-engine">Query Engine</a></h2>
|
||||
<h3 id="text-search"><a class="header" href="#text-search">Text Search</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let results = graph.nodes.values()
|
||||
.filter(|node| {
|
||||
node.title.contains(&query) ||
|
||||
node.content.contains(&query) ||
|
||||
node.tags.iter().any(|tag| tag.contains(&query))
|
||||
})
|
||||
.collect();
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="semantic-search"><a class="header" href="#semantic-search">Semantic Search</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let query_embedding = embeddings.embed(vec![query]).await?;
|
||||
let mut scored: Vec<_> = graph.nodes.values()
|
||||
.filter_map(|node| {
|
||||
let node_embedding = node.embedding.as_ref()?;
|
||||
let similarity = cosine_similarity(&query_embedding[0], node_embedding);
|
||||
(similarity >= threshold).then_some((node, similarity))
|
||||
})
|
||||
.collect();
|
||||
scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h3 id="cross-graph-query"><a class="header" href="#cross-graph-query">Cross-Graph Query</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>// Query both project and shared graphs
|
||||
let project_results = project_graph.search(&query).await?;
|
||||
let shared_results = shared_graph.search(&query).await?;
|
||||
|
||||
// Merge with deduplication
|
||||
let combined = merge_results(project_results, shared_results);
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="mcp-protocol-flow"><a class="header" href="#mcp-protocol-flow">MCP Protocol Flow</a></h2>
|
||||
<pre><code>Claude Code kb-mcp kb-core
|
||||
│ │ │
|
||||
├─ JSON-RPC request ───→ │ │
|
||||
│ kb/search │ │
|
||||
│ {"query": "rust"} │ │
|
||||
│ ├─ search() ──────────→ │
|
||||
│ │ │
|
||||
│ │ Query engine
|
||||
│ │ Text + semantic
|
||||
│ │ │
|
||||
│ │ ←──── results ─────────┤
|
||||
│ │ │
|
||||
│ ←─ JSON-RPC response ──┤ │
|
||||
│ {"results": [...]} │ │
|
||||
</code></pre>
|
||||
<h2 id="template-system"><a class="header" href="#template-system">Template System</a></h2>
|
||||
<p><strong>Engine</strong>: Tera (Jinja2-like)</p>
|
||||
<p><strong>Templates</strong>:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Document Templates</strong> (6):</p>
|
||||
<ul>
|
||||
<li><code>note.md.tera</code></li>
|
||||
<li><code>decision.md.tera</code></li>
|
||||
<li><code>guideline.md.tera</code></li>
|
||||
<li><code>pattern.md.tera</code></li>
|
||||
<li><code>journal.md.tera</code></li>
|
||||
<li><code>execution.md.tera</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Export Templates</strong> (4):</p>
|
||||
<ul>
|
||||
<li><code>logseq-page.md.tera</code></li>
|
||||
<li><code>logseq-journal.md.tera</code></li>
|
||||
<li><code>summary.md.tera</code></li>
|
||||
<li><code>graph.json.tera</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p><strong>Usage</strong>:</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let mut tera = Tera::new("templates/**/*.tera")?;
|
||||
let rendered = tera.render("note.md.tera", &context)?;
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<h2 id="error-handling"><a class="header" href="#error-handling">Error Handling</a></h2>
|
||||
<p><strong>Strategy</strong>: <code>thiserror</code> for structured errors</p>
|
||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[derive(Error, Debug)]
|
||||
pub enum KbError {
|
||||
#[error("Storage error: {0}")]
|
||||
Storage(String),
|
||||
|
||||
#[error("Node not found: {0}")]
|
||||
NodeNotFound(String),
|
||||
|
||||
#[error("Configuration error: {0}")]
|
||||
Config(String),
|
||||
|
||||
#[error("Parse error: {0}")]
|
||||
Parse(String),
|
||||
|
||||
#[error("Embedding error: {0}")]
|
||||
Embedding(String),
|
||||
}
|
||||
<span class="boring">}</span></code></pre></pre>
|
||||
<p><strong>Propagation</strong>: <code>?</code> operator throughout</p>
|
||||
<h2 id="testing-strategy"><a class="header" href="#testing-strategy">Testing Strategy</a></h2>
|
||||
<p><strong>Unit Tests</strong>: Per module (models, parser, storage)</p>
|
||||
<p><strong>Integration Tests</strong>: Full workflow (add → save → load → query)</p>
|
||||
<p><strong>Test Coverage</strong>:</p>
|
||||
<ul>
|
||||
<li>kb-core: 48 tests</li>
|
||||
<li>kb-mcp: 5 tests</li>
|
||||
<li>Total: 56 tests</li>
|
||||
</ul>
|
||||
<p><strong>Test Data</strong>: Fixtures in <code>tests/fixtures/</code></p>
|
||||
<h2 id="performance-considerations"><a class="header" href="#performance-considerations">Performance Considerations</a></h2>
|
||||
<p><strong>Node Lookup</strong>: O(1) via HashMap</p>
|
||||
<p><strong>Semantic Search</strong>: O(n) with early termination (threshold filter)</p>
|
||||
<p><strong>Storage</strong>:</p>
|
||||
<ul>
|
||||
<li>Filesystem: Lazy loading (load on demand)</li>
|
||||
<li>Memory: Full graph in RAM</li>
|
||||
<li>SurrealDB: Query optimization (indexes)</li>
|
||||
</ul>
|
||||
<p><strong>Embeddings</strong>:</p>
|
||||
<ul>
|
||||
<li>Cache embeddings in node metadata</li>
|
||||
<li>Batch processing (configurable batch size)</li>
|
||||
<li>Async generation (non-blocking)</li>
|
||||
</ul>
|
||||
<h2 id="security"><a class="header" href="#security">Security</a></h2>
|
||||
<p><strong>No unsafe code</strong>: <code>#![forbid(unsafe_code)]</code></p>
|
||||
<p><strong>Input validation</strong>:</p>
|
||||
<ul>
|
||||
<li>Nickel contracts validate config</li>
|
||||
<li>serde validates JSON</li>
|
||||
<li>Custom validation for user input</li>
|
||||
</ul>
|
||||
<p><strong>File operations</strong>:</p>
|
||||
<ul>
|
||||
<li>Path sanitization (no <code>../</code> traversal)</li>
|
||||
<li>Permissions checking</li>
|
||||
<li>Atomic writes (temp file + rename)</li>
|
||||
</ul>
|
||||
<h2 id="scalability"><a class="header" href="#scalability">Scalability</a></h2>
|
||||
<p><strong>Small Projects</strong> (< 1000 nodes):</p>
|
||||
<ul>
|
||||
<li>Filesystem storage</li>
|
||||
<li>In-memory search</li>
|
||||
<li>Local embeddings (fastembed)</li>
|
||||
</ul>
|
||||
<p><strong>Medium Projects</strong> (1000-10,000 nodes):</p>
|
||||
<ul>
|
||||
<li>Filesystem + SurrealDB sync</li>
|
||||
<li>Semantic search with caching</li>
|
||||
<li>Cloud embeddings (OpenAI/Claude)</li>
|
||||
</ul>
|
||||
<p><strong>Large Organizations</strong> (> 10,000 nodes):</p>
|
||||
<ul>
|
||||
<li>SurrealDB primary</li>
|
||||
<li>Distributed embeddings</li>
|
||||
<li>Multi-graph federation</li>
|
||||
</ul>
|
||||
<h2 id="next-steps"><a class="header" href="#next-steps">Next Steps</a></h2>
|
||||
<ul>
|
||||
<li><strong>Graph Model Details</strong>: <a href="graph-model.html">Graph Model</a></li>
|
||||
<li><strong>Storage Deep Dive</strong>: <a href="storage-architecture.html">Storage Architecture</a></li>
|
||||
<li><strong>ADRs</strong>: <a href="adrs/001-nickel-vs-toml.html">Architectural Decisions</a></li>
|
||||
<li><strong>Implementation</strong>: <a href="../contributing/development.html">Development Guide</a></li>
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../guides/best-practices.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/graph-model.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../guides/best-practices.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/graph-model.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/architecture/storage-architecture.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Storage Architecture - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./architecture/storage-architecture.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="storage-architecture"><a class="header" href="#storage-architecture">Storage Architecture</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../architecture/config-driven.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/logseq-blocks-design.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../architecture/config-driven.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../architecture/logseq-blocks-design.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
78
docs/book/ayu-highlight.css
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Based off of the Ayu theme
|
||||
Original by Dempfi (https://github.com/dempfi/ayu)
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #191f26;
|
||||
color: #e6e1cf;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #5c6773;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #ff7733;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #ffee99;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-bullet {
|
||||
color: #b8cc52;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-built_in,
|
||||
.hljs-section {
|
||||
color: #ffb454;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-symbol {
|
||||
color: #ff7733;
|
||||
}
|
||||
|
||||
.hljs-name {
|
||||
color: #36a3d9;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #00568d;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #91b362;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #d96c75;
|
||||
}
|
||||
818
docs/book/book.js
Normal file
@ -0,0 +1,818 @@
|
||||
'use strict';
|
||||
|
||||
/* global default_theme, default_dark_theme, default_light_theme, hljs, ClipboardJS */
|
||||
|
||||
// Fix back button cache problem
|
||||
window.onunload = function() { };
|
||||
|
||||
// Global variable, shared between modules
|
||||
function playground_text(playground, hidden = true) {
|
||||
const code_block = playground.querySelector('code');
|
||||
|
||||
if (window.ace && code_block.classList.contains('editable')) {
|
||||
const editor = window.ace.edit(code_block);
|
||||
return editor.getValue();
|
||||
} else if (hidden) {
|
||||
return code_block.textContent;
|
||||
} else {
|
||||
return code_block.innerText;
|
||||
}
|
||||
}
|
||||
|
||||
(function codeSnippets() {
|
||||
function fetch_with_timeout(url, options, timeout = 6000) {
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),
|
||||
]);
|
||||
}
|
||||
|
||||
const playgrounds = Array.from(document.querySelectorAll('.playground'));
|
||||
if (playgrounds.length > 0) {
|
||||
fetch_with_timeout('https://play.rust-lang.org/meta/crates', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
// get list of crates available in the rust playground
|
||||
const playground_crates = response.crates.map(item => item['id']);
|
||||
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
|
||||
});
|
||||
}
|
||||
|
||||
function handle_crate_list_update(playground_block, playground_crates) {
|
||||
// update the play buttons after receiving the response
|
||||
update_play_button(playground_block, playground_crates);
|
||||
|
||||
// and install on change listener to dynamically update ACE editors
|
||||
if (window.ace) {
|
||||
const code_block = playground_block.querySelector('code');
|
||||
if (code_block.classList.contains('editable')) {
|
||||
const editor = window.ace.edit(code_block);
|
||||
editor.addEventListener('change', () => {
|
||||
update_play_button(playground_block, playground_crates);
|
||||
});
|
||||
// add Ctrl-Enter command to execute rust code
|
||||
editor.commands.addCommand({
|
||||
name: 'run',
|
||||
bindKey: {
|
||||
win: 'Ctrl-Enter',
|
||||
mac: 'Ctrl-Enter',
|
||||
},
|
||||
exec: _editor => run_rust_code(playground_block),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updates the visibility of play button based on `no_run` class and
|
||||
// used crates vs ones available on https://play.rust-lang.org
|
||||
function update_play_button(pre_block, playground_crates) {
|
||||
const play_button = pre_block.querySelector('.play-button');
|
||||
|
||||
// skip if code is `no_run`
|
||||
if (pre_block.querySelector('code').classList.contains('no_run')) {
|
||||
play_button.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// get list of `extern crate`'s from snippet
|
||||
const txt = playground_text(pre_block);
|
||||
const re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
|
||||
const snippet_crates = [];
|
||||
let item;
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while (item = re.exec(txt)) {
|
||||
snippet_crates.push(item[1]);
|
||||
}
|
||||
|
||||
// check if all used crates are available on play.rust-lang.org
|
||||
const all_available = snippet_crates.every(function(elem) {
|
||||
return playground_crates.indexOf(elem) > -1;
|
||||
});
|
||||
|
||||
if (all_available) {
|
||||
play_button.classList.remove('hidden');
|
||||
} else {
|
||||
play_button.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function run_rust_code(code_block) {
|
||||
let result_block = code_block.querySelector('.result');
|
||||
if (!result_block) {
|
||||
result_block = document.createElement('code');
|
||||
result_block.className = 'result hljs language-bash';
|
||||
|
||||
code_block.append(result_block);
|
||||
}
|
||||
|
||||
const text = playground_text(code_block);
|
||||
const classes = code_block.querySelector('code').classList;
|
||||
let edition = '2015';
|
||||
classes.forEach(className => {
|
||||
if (className.startsWith('edition')) {
|
||||
edition = className.slice(7);
|
||||
}
|
||||
});
|
||||
const params = {
|
||||
version: 'stable',
|
||||
optimize: '0',
|
||||
code: text,
|
||||
edition: edition,
|
||||
};
|
||||
|
||||
if (text.indexOf('#![feature') !== -1) {
|
||||
params.version = 'nightly';
|
||||
}
|
||||
|
||||
result_block.innerText = 'Running...';
|
||||
|
||||
fetch_with_timeout('https://play.rust-lang.org/evaluate.json', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.result.trim() === '') {
|
||||
result_block.innerText = 'No output';
|
||||
result_block.classList.add('result-no-output');
|
||||
} else {
|
||||
result_block.innerText = response.result;
|
||||
result_block.classList.remove('result-no-output');
|
||||
}
|
||||
})
|
||||
.catch(error => result_block.innerText = 'Playground Communication: ' + error.message);
|
||||
}
|
||||
|
||||
// Syntax highlighting Configuration
|
||||
hljs.configure({
|
||||
tabReplace: ' ', // 4 spaces
|
||||
languages: [], // Languages used for auto-detection
|
||||
});
|
||||
|
||||
const code_nodes = Array
|
||||
.from(document.querySelectorAll('code'))
|
||||
// Don't highlight `inline code` blocks in headers.
|
||||
.filter(function(node) {
|
||||
return !node.parentElement.classList.contains('header');
|
||||
});
|
||||
|
||||
if (window.ace) {
|
||||
// language-rust class needs to be removed for editable
|
||||
// blocks or highlightjs will capture events
|
||||
code_nodes
|
||||
.filter(function(node) {
|
||||
return node.classList.contains('editable');
|
||||
})
|
||||
.forEach(function(block) {
|
||||
block.classList.remove('language-rust');
|
||||
});
|
||||
|
||||
code_nodes
|
||||
.filter(function(node) {
|
||||
return !node.classList.contains('editable');
|
||||
})
|
||||
.forEach(function(block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
} else {
|
||||
code_nodes.forEach(function(block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
}
|
||||
|
||||
// Adding the hljs class gives code blocks the color css
|
||||
// even if highlighting doesn't apply
|
||||
code_nodes.forEach(function(block) {
|
||||
block.classList.add('hljs');
|
||||
});
|
||||
|
||||
Array.from(document.querySelectorAll('code.hljs')).forEach(function(block) {
|
||||
|
||||
const lines = Array.from(block.querySelectorAll('.boring'));
|
||||
// If no lines were hidden, return
|
||||
if (!lines.length) {
|
||||
return;
|
||||
}
|
||||
block.classList.add('hide-boring');
|
||||
|
||||
const buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
buttons.innerHTML = '<button class="fa fa-eye" title="Show hidden lines" \
|
||||
aria-label="Show hidden lines"></button>';
|
||||
|
||||
// add expand button
|
||||
const pre_block = block.parentNode;
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
|
||||
pre_block.querySelector('.buttons').addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('fa-eye')) {
|
||||
e.target.classList.remove('fa-eye');
|
||||
e.target.classList.add('fa-eye-slash');
|
||||
e.target.title = 'Hide lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.remove('hide-boring');
|
||||
} else if (e.target.classList.contains('fa-eye-slash')) {
|
||||
e.target.classList.remove('fa-eye-slash');
|
||||
e.target.classList.add('fa-eye');
|
||||
e.target.title = 'Show hidden lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.add('hide-boring');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
Array.from(document.querySelectorAll('pre code')).forEach(function(block) {
|
||||
const pre_block = block.parentNode;
|
||||
if (!pre_block.classList.contains('playground')) {
|
||||
let buttons = pre_block.querySelector('.buttons');
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
const clipButton = document.createElement('button');
|
||||
clipButton.className = 'clip-button';
|
||||
clipButton.title = 'Copy to clipboard';
|
||||
clipButton.setAttribute('aria-label', clipButton.title);
|
||||
clipButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
|
||||
buttons.insertBefore(clipButton, buttons.firstChild);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process playground code blocks
|
||||
Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) {
|
||||
// Add play button
|
||||
let buttons = pre_block.querySelector('.buttons');
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
const runCodeButton = document.createElement('button');
|
||||
runCodeButton.className = 'fa fa-play play-button';
|
||||
runCodeButton.hidden = true;
|
||||
runCodeButton.title = 'Run this code';
|
||||
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||
|
||||
buttons.insertBefore(runCodeButton, buttons.firstChild);
|
||||
runCodeButton.addEventListener('click', () => {
|
||||
run_rust_code(pre_block);
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
const copyCodeClipboardButton = document.createElement('button');
|
||||
copyCodeClipboardButton.className = 'clip-button';
|
||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||
|
||||
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
||||
}
|
||||
|
||||
const code_block = pre_block.querySelector('code');
|
||||
if (window.ace && code_block.classList.contains('editable')) {
|
||||
const undoChangesButton = document.createElement('button');
|
||||
undoChangesButton.className = 'fa fa-history reset-button';
|
||||
undoChangesButton.title = 'Undo changes';
|
||||
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
|
||||
|
||||
buttons.insertBefore(undoChangesButton, buttons.firstChild);
|
||||
|
||||
undoChangesButton.addEventListener('click', function() {
|
||||
const editor = window.ace.edit(code_block);
|
||||
editor.setValue(editor.originalCode);
|
||||
editor.clearSelection();
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function themes() {
|
||||
const html = document.querySelector('html');
|
||||
const themeToggleButton = document.getElementById('theme-toggle');
|
||||
const themePopup = document.getElementById('theme-list');
|
||||
const themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||
const themeIds = [];
|
||||
themePopup.querySelectorAll('button.theme').forEach(function(el) {
|
||||
themeIds.push(el.id);
|
||||
});
|
||||
const stylesheets = {
|
||||
ayuHighlight: document.querySelector('#ayu-highlight-css'),
|
||||
tomorrowNight: document.querySelector('#tomorrow-night-css'),
|
||||
highlight: document.querySelector('#highlight-css'),
|
||||
};
|
||||
|
||||
function showThemes() {
|
||||
themePopup.style.display = 'block';
|
||||
themeToggleButton.setAttribute('aria-expanded', true);
|
||||
themePopup.querySelector('button#' + get_theme()).focus();
|
||||
}
|
||||
|
||||
function updateThemeSelected() {
|
||||
themePopup.querySelectorAll('.theme-selected').forEach(function(el) {
|
||||
el.classList.remove('theme-selected');
|
||||
});
|
||||
const selected = get_saved_theme() ?? 'default_theme';
|
||||
let element = themePopup.querySelector('button#' + selected);
|
||||
if (element === null) {
|
||||
// Fall back in case there is no "Default" item.
|
||||
element = themePopup.querySelector('button#' + get_theme());
|
||||
}
|
||||
element.classList.add('theme-selected');
|
||||
}
|
||||
|
||||
function hideThemes() {
|
||||
themePopup.style.display = 'none';
|
||||
themeToggleButton.setAttribute('aria-expanded', false);
|
||||
themeToggleButton.focus();
|
||||
}
|
||||
|
||||
function get_saved_theme() {
|
||||
let theme = null;
|
||||
try {
|
||||
theme = localStorage.getItem('mdbook-theme');
|
||||
} catch (e) {
|
||||
// ignore error.
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
|
||||
function delete_saved_theme() {
|
||||
localStorage.removeItem('mdbook-theme');
|
||||
}
|
||||
|
||||
function get_theme() {
|
||||
const theme = get_saved_theme();
|
||||
if (theme === null || theme === undefined || !themeIds.includes(theme)) {
|
||||
if (typeof default_dark_theme === 'undefined') {
|
||||
// A customized index.hbs might not define this, so fall back to
|
||||
// old behavior of determining the default on page load.
|
||||
return default_theme;
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? default_dark_theme
|
||||
: default_light_theme;
|
||||
} else {
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
|
||||
let previousTheme = default_theme;
|
||||
function set_theme(theme, store = true) {
|
||||
let ace_theme;
|
||||
|
||||
if (theme === 'coal' || theme === 'navy') {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = false;
|
||||
stylesheets.highlight.disabled = true;
|
||||
|
||||
ace_theme = 'ace/theme/tomorrow_night';
|
||||
} else if (theme === 'ayu') {
|
||||
stylesheets.ayuHighlight.disabled = false;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = true;
|
||||
ace_theme = 'ace/theme/tomorrow_night';
|
||||
} else {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = false;
|
||||
ace_theme = 'ace/theme/dawn';
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor;
|
||||
}, 1);
|
||||
|
||||
if (window.ace && window.editors) {
|
||||
window.editors.forEach(function(editor) {
|
||||
editor.setTheme(ace_theme);
|
||||
});
|
||||
}
|
||||
|
||||
if (store) {
|
||||
try {
|
||||
localStorage.setItem('mdbook-theme', theme);
|
||||
} catch (e) {
|
||||
// ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
html.classList.remove(previousTheme);
|
||||
html.classList.add(theme);
|
||||
previousTheme = theme;
|
||||
updateThemeSelected();
|
||||
}
|
||||
|
||||
const query = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
query.onchange = function() {
|
||||
set_theme(get_theme(), false);
|
||||
};
|
||||
|
||||
// Set theme.
|
||||
set_theme(get_theme(), false);
|
||||
|
||||
themeToggleButton.addEventListener('click', function() {
|
||||
if (themePopup.style.display === 'block') {
|
||||
hideThemes();
|
||||
} else {
|
||||
showThemes();
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('click', function(e) {
|
||||
let theme;
|
||||
if (e.target.className === 'theme') {
|
||||
theme = e.target.id;
|
||||
} else if (e.target.parentElement.className === 'theme') {
|
||||
theme = e.target.parentElement.id;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (theme === 'default_theme' || theme === null) {
|
||||
delete_saved_theme();
|
||||
set_theme(get_theme(), false);
|
||||
} else {
|
||||
set_theme(theme);
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('focusout', function(e) {
|
||||
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
|
||||
if (!!e.relatedTarget &&
|
||||
!themeToggleButton.contains(e.relatedTarget) &&
|
||||
!themePopup.contains(e.relatedTarget)
|
||||
) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
// Should not be needed, but it works around an issue on macOS & iOS:
|
||||
// https://github.com/rust-lang/mdBook/issues/628
|
||||
document.addEventListener('click', function(e) {
|
||||
if (themePopup.style.display === 'block' &&
|
||||
!themeToggleButton.contains(e.target) &&
|
||||
!themePopup.contains(e.target)
|
||||
) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
if (!themePopup.contains(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let li;
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
hideThemes();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
li = document.activeElement.parentElement;
|
||||
if (li && li.previousElementSibling) {
|
||||
li.previousElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
li = document.activeElement.parentElement;
|
||||
if (li && li.nextElementSibling) {
|
||||
li.nextElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:first-child button').focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:last-child button').focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function sidebar() {
|
||||
const body = document.querySelector('body');
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const sidebarLinks = document.querySelectorAll('#sidebar a');
|
||||
const sidebarToggleButton = document.getElementById('sidebar-toggle');
|
||||
const sidebarToggleAnchor = document.getElementById('sidebar-toggle-anchor');
|
||||
const sidebarResizeHandle = document.getElementById('sidebar-resize-handle');
|
||||
let firstContact = null;
|
||||
|
||||
function showSidebar() {
|
||||
body.classList.remove('sidebar-hidden');
|
||||
body.classList.add('sidebar-visible');
|
||||
Array.from(sidebarLinks).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', 0);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', true);
|
||||
sidebar.setAttribute('aria-hidden', false);
|
||||
try {
|
||||
localStorage.setItem('mdbook-sidebar', 'visible');
|
||||
} catch (e) {
|
||||
// Ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
function hideSidebar() {
|
||||
body.classList.remove('sidebar-visible');
|
||||
body.classList.add('sidebar-hidden');
|
||||
Array.from(sidebarLinks).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', -1);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', false);
|
||||
sidebar.setAttribute('aria-hidden', true);
|
||||
try {
|
||||
localStorage.setItem('mdbook-sidebar', 'hidden');
|
||||
} catch (e) {
|
||||
// Ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle sidebar
|
||||
sidebarToggleAnchor.addEventListener('change', function sidebarToggle() {
|
||||
if (sidebarToggleAnchor.checked) {
|
||||
const current_width = parseInt(
|
||||
document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10);
|
||||
if (current_width < 150) {
|
||||
document.documentElement.style.setProperty('--sidebar-target-width', '150px');
|
||||
}
|
||||
showSidebar();
|
||||
} else {
|
||||
hideSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
|
||||
|
||||
function initResize() {
|
||||
window.addEventListener('mousemove', resize, false);
|
||||
window.addEventListener('mouseup', stopResize, false);
|
||||
body.classList.add('sidebar-resizing');
|
||||
}
|
||||
function resize(e) {
|
||||
let pos = e.clientX - sidebar.offsetLeft;
|
||||
if (pos < 20) {
|
||||
hideSidebar();
|
||||
} else {
|
||||
if (body.classList.contains('sidebar-hidden')) {
|
||||
showSidebar();
|
||||
}
|
||||
pos = Math.min(pos, window.innerWidth - 100);
|
||||
document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px');
|
||||
}
|
||||
}
|
||||
//on mouseup remove windows functions mousemove & mouseup
|
||||
function stopResize() {
|
||||
body.classList.remove('sidebar-resizing');
|
||||
window.removeEventListener('mousemove', resize, false);
|
||||
window.removeEventListener('mouseup', stopResize, false);
|
||||
}
|
||||
|
||||
document.addEventListener('touchstart', function(e) {
|
||||
firstContact = {
|
||||
x: e.touches[0].clientX,
|
||||
time: Date.now(),
|
||||
};
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchmove', function(e) {
|
||||
if (!firstContact) {
|
||||
return;
|
||||
}
|
||||
|
||||
const curX = e.touches[0].clientX;
|
||||
const xDiff = curX - firstContact.x,
|
||||
tDiff = Date.now() - firstContact.time;
|
||||
|
||||
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
|
||||
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) {
|
||||
showSidebar();
|
||||
} else if (xDiff < 0 && curX < 300) {
|
||||
hideSidebar();
|
||||
}
|
||||
|
||||
firstContact = null;
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
(function chapterNavigation() {
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey) {
|
||||
return;
|
||||
}
|
||||
if (window.search && window.search.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
const html = document.querySelector('html');
|
||||
|
||||
function next() {
|
||||
const nextButton = document.querySelector('.nav-chapters.next');
|
||||
if (nextButton) {
|
||||
window.location.href = nextButton.href;
|
||||
}
|
||||
}
|
||||
function prev() {
|
||||
const previousButton = document.querySelector('.nav-chapters.previous');
|
||||
if (previousButton) {
|
||||
window.location.href = previousButton.href;
|
||||
}
|
||||
}
|
||||
function showHelp() {
|
||||
const container = document.getElementById('mdbook-help-container');
|
||||
const overlay = document.getElementById('mdbook-help-popup');
|
||||
container.style.display = 'flex';
|
||||
|
||||
// Clicking outside the popup will dismiss it.
|
||||
const mouseHandler = event => {
|
||||
if (overlay.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.removeEventListener('mousedown', mouseHandler);
|
||||
hideHelp();
|
||||
};
|
||||
|
||||
// Pressing esc will dismiss the popup.
|
||||
const escapeKeyHandler = event => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.removeEventListener('keydown', escapeKeyHandler, true);
|
||||
hideHelp();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', escapeKeyHandler, true);
|
||||
document.getElementById('mdbook-help-container')
|
||||
.addEventListener('mousedown', mouseHandler);
|
||||
}
|
||||
function hideHelp() {
|
||||
document.getElementById('mdbook-help-container').style.display = 'none';
|
||||
}
|
||||
|
||||
// Usually needs the Shift key to be pressed
|
||||
switch (e.key) {
|
||||
case '?':
|
||||
e.preventDefault();
|
||||
showHelp();
|
||||
break;
|
||||
}
|
||||
|
||||
// Rest of the keys are only active when the Shift key is not pressed
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
e.preventDefault();
|
||||
if (html.dir === 'rtl') {
|
||||
prev();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
if (html.dir === 'rtl') {
|
||||
next();
|
||||
} else {
|
||||
prev();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function clipboard() {
|
||||
const clipButtons = document.querySelectorAll('.clip-button');
|
||||
|
||||
function hideTooltip(elem) {
|
||||
elem.firstChild.innerText = '';
|
||||
elem.className = 'clip-button';
|
||||
}
|
||||
|
||||
function showTooltip(elem, msg) {
|
||||
elem.firstChild.innerText = msg;
|
||||
elem.className = 'clip-button tooltipped';
|
||||
}
|
||||
|
||||
const clipboardSnippets = new ClipboardJS('.clip-button', {
|
||||
text: function(trigger) {
|
||||
hideTooltip(trigger);
|
||||
const playground = trigger.closest('pre');
|
||||
return playground_text(playground, false);
|
||||
},
|
||||
});
|
||||
|
||||
Array.from(clipButtons).forEach(function(clipButton) {
|
||||
clipButton.addEventListener('mouseout', function(e) {
|
||||
hideTooltip(e.currentTarget);
|
||||
});
|
||||
});
|
||||
|
||||
clipboardSnippets.on('success', function(e) {
|
||||
e.clearSelection();
|
||||
showTooltip(e.trigger, 'Copied!');
|
||||
});
|
||||
|
||||
clipboardSnippets.on('error', function(e) {
|
||||
showTooltip(e.trigger, 'Clipboard error!');
|
||||
});
|
||||
})();
|
||||
|
||||
(function scrollToTop() {
|
||||
const menuTitle = document.querySelector('.menu-title');
|
||||
|
||||
menuTitle.addEventListener('click', function() {
|
||||
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
})();
|
||||
|
||||
(function controllMenu() {
|
||||
const menu = document.getElementById('menu-bar');
|
||||
|
||||
(function controllPosition() {
|
||||
let scrollTop = document.scrollingElement.scrollTop;
|
||||
let prevScrollTop = scrollTop;
|
||||
const minMenuY = -menu.clientHeight - 50;
|
||||
// When the script loads, the page can be at any scroll (e.g. if you reforesh it).
|
||||
menu.style.top = scrollTop + 'px';
|
||||
// Same as parseInt(menu.style.top.slice(0, -2), but faster
|
||||
let topCache = menu.style.top.slice(0, -2);
|
||||
menu.classList.remove('sticky');
|
||||
let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
|
||||
document.addEventListener('scroll', function() {
|
||||
scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
|
||||
// `null` means that it doesn't need to be updated
|
||||
let nextSticky = null;
|
||||
let nextTop = null;
|
||||
const scrollDown = scrollTop > prevScrollTop;
|
||||
const menuPosAbsoluteY = topCache - scrollTop;
|
||||
if (scrollDown) {
|
||||
nextSticky = false;
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextTop = prevScrollTop;
|
||||
}
|
||||
} else {
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextSticky = true;
|
||||
} else if (menuPosAbsoluteY < minMenuY) {
|
||||
nextTop = prevScrollTop + minMenuY;
|
||||
}
|
||||
}
|
||||
if (nextSticky === true && stickyCache === false) {
|
||||
menu.classList.add('sticky');
|
||||
stickyCache = true;
|
||||
} else if (nextSticky === false && stickyCache === true) {
|
||||
menu.classList.remove('sticky');
|
||||
stickyCache = false;
|
||||
}
|
||||
if (nextTop !== null) {
|
||||
menu.style.top = nextTop + 'px';
|
||||
topCache = nextTop;
|
||||
}
|
||||
prevScrollTop = scrollTop;
|
||||
}, { passive: true });
|
||||
})();
|
||||
(function controllBorder() {
|
||||
function updateBorder() {
|
||||
if (menu.offsetTop === 0) {
|
||||
menu.classList.remove('bordered');
|
||||
} else {
|
||||
menu.classList.add('bordered');
|
||||
}
|
||||
}
|
||||
updateBorder();
|
||||
document.addEventListener('scroll', updateBorder, { passive: true });
|
||||
})();
|
||||
})();
|
||||
38
docs/book/book.toml
Normal file
@ -0,0 +1,38 @@
|
||||
[book]
|
||||
title = "KOGRAL Documentation"
|
||||
authors = ["KOGRAL Team"]
|
||||
description = "Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams"
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "."
|
||||
|
||||
[build]
|
||||
build-dir = "book"
|
||||
create-missing = true
|
||||
|
||||
[output.html]
|
||||
default-theme = "rust"
|
||||
preferred-dark-theme = "navy"
|
||||
git-repository-url = "https://github.com/your-org/knowledge-base"
|
||||
edit-url-template = "https://github.com/your-org/knowledge-base/edit/main/docs/{path}"
|
||||
|
||||
[output.html.print]
|
||||
enable = true
|
||||
|
||||
[output.html.fold]
|
||||
enable = true
|
||||
level = 1
|
||||
|
||||
[output.html.search]
|
||||
enable = true
|
||||
limit-results = 30
|
||||
teaser-word-count = 30
|
||||
use-boolean-and = true
|
||||
boost-title = 2
|
||||
boost-hierarchy = 1
|
||||
boost-paragraph = 1
|
||||
expand = true
|
||||
heading-split-level = 3
|
||||
|
||||
[preprocessor.links]
|
||||
[preprocessor.index]
|
||||
227
docs/book/cli/commands.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Commands Reference - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./cli/commands.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="commands-reference"><a class="header" href="#commands-reference">Commands Reference</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../cli/overview.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../cli/workflows.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../cli/overview.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../cli/workflows.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/cli/overview.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>CLI Overview - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./cli/overview.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="cli-overview"><a class="header" href="#cli-overview">CLI Overview</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../templates/customization.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../cli/commands.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../templates/customization.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../cli/commands.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/cli/workflows.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Workflows - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./cli/workflows.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="workflows"><a class="header" href="#workflows">Workflows</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../cli/commands.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../cli/nushell-scripts.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../cli/commands.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../cli/nushell-scripts.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
7
docs/book/clipboard.min.js
vendored
Normal file
227
docs/book/config/examples.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Examples - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./config/examples.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="examples"><a class="header" href="#examples">Examples</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../config/inheritance.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../storage/backends.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../config/inheritance.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../storage/backends.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
227
docs/book/config/graph-settings.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="rust sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Graph Settings - KOGRAL Documentation</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
|
||||
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "../";
|
||||
const default_light_theme = "rust";
|
||||
const default_dark_theme = "navy";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('rust')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">KOGRAL Documentation</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/your-org/knowledge-base/edit/main/docs/./config/graph-settings.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="graph-settings"><a class="header" href="#graph-settings">Graph Settings</a></h1>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../config/nickel-schemas.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../config/inheritance.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../config/nickel-schemas.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../config/inheritance.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||