v1.0.0: security fixes, code review cleanup, release prep
Security: - Add path traversal protection (safe_vault_path) for all file operations - Sanitize markdown preview with DOMPurify to prevent XSS - Fix encryption: decrypted content no longer written back to disk - Harden CSP for Tauri v2 production mode Code quality: - Remove dead code (cache.rs, NoteView, unused tabs/activeTab state) - Fix stale closure in debounced save using refs - Fix image paste to use domToMarkdown instead of innerText - Single-pass widget rendering (was 3 sequential innerHTML clobbers) - Move snapshot logic into save callback - Remove no-op updateWikilinks in drag-and-drop - Wire CSS editor to Cmd+U shortcut - Replace hardcoded vault path with portable fallback Performance: - LazyLock static regexes for WIKILINK_RE and TAG_RE - Fix duplicate dirs_config_path, update CSS commands Release: - Bump version to 1.0.0 - Rewrite README with project documentation - Update CHANGELOG with 1.0.0 entry - Update .gitignore for build artifacts and vault data - Update canvas dependency path - Fix canvas API compatibility (onSelectionChange, removed props)
This commit is contained in:
parent
c1f556b86b
commit
0d26e63c9a
15 changed files with 658 additions and 505 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -12,6 +12,12 @@ dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
# Rust build artifacts
|
||||||
|
src-tauri/target/
|
||||||
|
|
||||||
|
# User vault data
|
||||||
|
vault/
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
|
||||||
43
CHANGELOG.md
43
CHANGELOG.md
|
|
@ -4,6 +4,25 @@ All notable changes to Graph Notes will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||||
|
|
||||||
|
## [1.0.0] — 2026-03-09
|
||||||
|
|
||||||
|
### 🎉 First Stable Release
|
||||||
|
|
||||||
|
Graph Notes reaches 1.0 — a local-first, graph-based note-taking app built with Tauri, React, and Rust.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Rust compilation** — resolved duplicate `dirs_config_path()` definition and removed reference to unlinked `dirs` crate
|
||||||
|
- **Content Security Policy** — replaced `null` CSP with a proper baseline policy allowing local resources and Google Fonts
|
||||||
|
- **Canvas dependency** — updated `@blinksgg/canvas` to correct local file path
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- **Dead code** — removed unused `cache.rs` module (196 lines) that was never compiled (no `mod cache;` declaration, missing `notify` crate dependency)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **README** — replaced Vite template boilerplate with comprehensive project documentation
|
||||||
|
- **.gitignore** — added `src-tauri/target/` and `vault/` exclusions
|
||||||
|
- **Version** — bumped from 0.9.0 → 1.0.0 across `package.json`, `Cargo.toml`, and `tauri.conf.json`
|
||||||
|
|
||||||
## [0.9.0] — 2026-03-09
|
## [0.9.0] — 2026-03-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
@ -173,27 +192,3 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
||||||
- **Daily Notes** — Auto-generated daily journal entries accessible from sidebar shortcut
|
- **Daily Notes** — Auto-generated daily journal entries accessible from sidebar shortcut
|
||||||
- **Auto-Save** — Debounced 500ms save on every keystroke
|
- **Auto-Save** — Debounced 500ms save on every keystroke
|
||||||
- **Custom Scrollbars** — Minimal 5px scrollbars matching the dark theme
|
- **Custom Scrollbars** — Minimal 5px scrollbars matching the dark theme
|
||||||
|
|
||||||
## [0.4.0] - 2026-03-07 (origin)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **VaultCache** (`src-tauri/src/cache.rs`): In-memory note cache with mtime-based invalidation
|
|
||||||
- **`init_vault_cache`/`get_cache_stats`**: Startup scan + diagnostics
|
|
||||||
- **Cache-backed commands**: `read_note`, `build_graph`, `search_vault` from cache
|
|
||||||
- **Frontend LRU cache** (`src/lib/noteCache.ts`): 20-note stale-while-revalidate
|
|
||||||
- **File watcher** (`notify` crate): FS change detection foundation
|
|
||||||
|
|
||||||
## [0.3.0] - 2026-03-07 (origin)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Frontmatter Parsing**: YAML metadata, `read_note_with_meta`, TOC panel, templates, attachments
|
|
||||||
|
|
||||||
## [0.2.0] - 2026-03-07 (origin)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Command Palette**, keyboard shortcuts, full-text search, rename/delete, graph filtering
|
|
||||||
|
|
||||||
## [0.1.0] - 2025-06-01 (origin)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Tauri 2 + React 19, editor, wikilinks, graph, sidebar, backlinks, daily notes
|
|
||||||
|
|
|
||||||
87
README.md
87
README.md
|
|
@ -1,7 +1,86 @@
|
||||||
# Tauri + React + Typescript
|
# Graph Notes
|
||||||
|
|
||||||
This template should help get you started developing with Tauri, React and Typescript in Vite.
|
A local-first, graph-based note-taking app built with Tauri, React, and Rust. Think Obsidian meets Roam — your notes live as plain Markdown files on disk, connected by `[[wikilinks]]` and visualized as an interactive knowledge graph.
|
||||||
|
|
||||||
## Recommended IDE Setup
|
## Features
|
||||||
|
|
||||||
- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
|
**Core Editing**
|
||||||
|
- Rich contenteditable editor with inline Markdown styling
|
||||||
|
- `[[Wikilink]]` autocomplete with note creation on the fly
|
||||||
|
- Slash commands, heading folding, split-pane editing
|
||||||
|
- Tabbed editor with drag-reorder
|
||||||
|
|
||||||
|
**Knowledge Graph**
|
||||||
|
- Force-directed graph visualization with semantic zoom
|
||||||
|
- Graph filtering, focus mode, orphan detection
|
||||||
|
- Local backlink graph and analytics dashboard
|
||||||
|
|
||||||
|
**Organization**
|
||||||
|
- Sidebar file tree with drag-and-drop, folders, favorites, pinning
|
||||||
|
- Full-text search, command palette (`⌘K`), tags
|
||||||
|
- Calendar view, timeline view, kanban board
|
||||||
|
|
||||||
|
**Advanced**
|
||||||
|
- Frontmatter properties panel and database views (table/gallery/list)
|
||||||
|
- Dataview queries — inline `TABLE ... SORT ...` blocks
|
||||||
|
- Canvas whiteboard for freeform visual thinking
|
||||||
|
- Spaced repetition flashcards (SM-2 scheduling)
|
||||||
|
- Note encryption (AES-256-GCM with Argon2 key derivation)
|
||||||
|
- Git sync (commit/push/pull panel)
|
||||||
|
- Version history with inline diff viewer
|
||||||
|
|
||||||
|
**Customization**
|
||||||
|
- 5 built-in themes with live preview
|
||||||
|
- Custom CSS snippets editor
|
||||||
|
- Configurable keyboard shortcuts
|
||||||
|
- Workspace layouts (save/restore window arrangements)
|
||||||
|
|
||||||
|
**Import / Export**
|
||||||
|
- Export vault as ZIP, individual notes as HTML or PDF
|
||||||
|
- Import Markdown folders from Obsidian, Notion, etc.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
| Layer | Technology |
|
||||||
|
|---|---|
|
||||||
|
| Desktop shell | [Tauri v2](https://tauri.app) |
|
||||||
|
| Frontend | React 19, TypeScript, Vite |
|
||||||
|
| Backend | Rust (filesystem, search, encryption, graph) |
|
||||||
|
| Styling | Tailwind CSS 4 + custom design tokens |
|
||||||
|
| Graph engine | [@blinksgg/canvas](https://github.com/blinksgg), d3-force, graphology |
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Run in development mode (starts both Vite dev server and Tauri)
|
||||||
|
npm run tauri dev
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
npm run tauri build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- [Node.js](https://nodejs.org) ≥ 18
|
||||||
|
- [Rust](https://rustup.rs) toolchain
|
||||||
|
- [Tauri v2 prerequisites](https://v2.tauri.app/start/prerequisites/)
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
├── src/ # React frontend
|
||||||
|
│ ├── components/ # 33 UI components
|
||||||
|
│ ├── lib/ # Commands, frontmatter, wikilinks, note cache
|
||||||
|
│ └── index.css # Design system (tokens, component styles)
|
||||||
|
├── src-tauri/ # Rust backend
|
||||||
|
│ └── src/lib.rs # Tauri commands (notes, graph, search, encryption, git)
|
||||||
|
├── vault/ # Default vault directory (gitignored)
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
|
||||||
75
package-lock.json
generated
75
package-lock.json
generated
|
|
@ -1,19 +1,20 @@
|
||||||
{
|
{
|
||||||
"name": "graph-notes",
|
"name": "graph-notes",
|
||||||
"version": "0.6.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "graph-notes",
|
"name": "graph-notes",
|
||||||
"version": "0.6.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blinksgg/canvas": "file:../blinksgg/gg-antifragile/packages/canvas",
|
"@blinksgg/canvas": "file:../space-operator/gg/packages/canvas",
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-dialog": "^2",
|
"@tauri-apps/plugin-dialog": "^2",
|
||||||
"@tauri-apps/plugin-fs": "^2",
|
"@tauri-apps/plugin-fs": "^2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"d3-force": "^3.0.0",
|
"d3-force": "^3.0.0",
|
||||||
|
"dompurify": "^3.3.2",
|
||||||
"graphology": "^0.26.0",
|
"graphology": "^0.26.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"jotai": "^2.18.0",
|
"jotai": "^2.18.0",
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
"@tauri-apps/cli": "^2",
|
"@tauri-apps/cli": "^2",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/react": "^19.1.8",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.1.6",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@vitejs/plugin-react": "^4.6.0",
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
|
|
@ -37,6 +39,7 @@
|
||||||
"../blinksgg/gg-antifragile/packages/canvas": {
|
"../blinksgg/gg-antifragile/packages/canvas": {
|
||||||
"name": "@blinksgg/canvas",
|
"name": "@blinksgg/canvas",
|
||||||
"version": "0.13.0",
|
"version": "0.13.0",
|
||||||
|
"extraneous": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@supabase/supabase-js": "^2.49.5",
|
"@supabase/supabase-js": "^2.49.5",
|
||||||
"@use-gesture/react": "^10.3.1",
|
"@use-gesture/react": "^10.3.1",
|
||||||
|
|
@ -83,6 +86,56 @@
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"../space-operator/gg/packages/canvas": {
|
||||||
|
"name": "@blinksgg/canvas",
|
||||||
|
"version": "0.35.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/supabase-js": "^2.49.5",
|
||||||
|
"@use-gesture/react": "^10.3.1",
|
||||||
|
"debug": "^4.4.3",
|
||||||
|
"graphology": "^0.26.0",
|
||||||
|
"graphology-types": "^0.24.8",
|
||||||
|
"jotai-family": "^1.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.29.0",
|
||||||
|
"@babel/preset-react": "^7.28.5",
|
||||||
|
"@babel/preset-typescript": "^7.28.5",
|
||||||
|
"@blocknote/core": "^0.45.0",
|
||||||
|
"@blocknote/react": "^0.45.0",
|
||||||
|
"@blocknote/shadcn": "^0.45.0",
|
||||||
|
"@tanstack/react-query": "^5.17.0",
|
||||||
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
|
"@testing-library/react": "^16.3.2",
|
||||||
|
"@testing-library/user-event": "^14.6.1",
|
||||||
|
"@types/d3-force": "^3.0.10",
|
||||||
|
"@types/debug": "^4.1.12",
|
||||||
|
"@types/node": "^24.5.2",
|
||||||
|
"@types/react": "^19.1.13",
|
||||||
|
"@types/react-dom": "^19.1.9",
|
||||||
|
"babel-plugin-react-compiler": "^1.0.0",
|
||||||
|
"d3-force": "^3.0.0",
|
||||||
|
"esbuild-plugin-babel": "^0.2.3",
|
||||||
|
"jotai": "^2.6.0",
|
||||||
|
"jsdom": "^26.1.0",
|
||||||
|
"react": "^19.1.1",
|
||||||
|
"react-dom": "^19.1.1",
|
||||||
|
"tsup": "^8.0.0",
|
||||||
|
"typescript": "^5.3.0",
|
||||||
|
"vitest": "^3.2.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@blocknote/core": "^0.45.0",
|
||||||
|
"@blocknote/react": "^0.45.0",
|
||||||
|
"@blocknote/shadcn": "^0.45.0",
|
||||||
|
"@tanstack/react-query": "^5.17.0",
|
||||||
|
"d3-force": "^3.0.0",
|
||||||
|
"jotai": "^2.6.0",
|
||||||
|
"jotai-tanstack-query": "*",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@antfu/install-pkg": {
|
"node_modules/@antfu/install-pkg": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
|
||||||
|
|
@ -379,7 +432,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@blinksgg/canvas": {
|
"node_modules/@blinksgg/canvas": {
|
||||||
"resolved": "../blinksgg/gg-antifragile/packages/canvas",
|
"resolved": "../space-operator/gg/packages/canvas",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@braintree/sanitize-url": {
|
"node_modules/@braintree/sanitize-url": {
|
||||||
|
|
@ -2126,6 +2179,16 @@
|
||||||
"@types/d3-selection": "*"
|
"@types/d3-selection": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/dompurify": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/trusted-types": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
|
|
@ -2163,8 +2226,8 @@
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
"license": "MIT",
|
"devOptional": true,
|
||||||
"optional": true
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-react": {
|
"node_modules/@vitejs/plugin-react": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "graph-notes",
|
"name": "graph-notes",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.9.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
@ -10,12 +10,13 @@
|
||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blinksgg/canvas": "file:../blinksgg/gg-antifragile/packages/canvas",
|
"@blinksgg/canvas": "file:../space-operator/gg/packages/canvas",
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-dialog": "^2",
|
"@tauri-apps/plugin-dialog": "^2",
|
||||||
"@tauri-apps/plugin-fs": "^2",
|
"@tauri-apps/plugin-fs": "^2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"d3-force": "^3.0.0",
|
"d3-force": "^3.0.0",
|
||||||
|
"dompurify": "^3.3.2",
|
||||||
"graphology": "^0.26.0",
|
"graphology": "^0.26.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"jotai": "^2.18.0",
|
"jotai": "^2.18.0",
|
||||||
|
|
@ -28,6 +29,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
"@tauri-apps/cli": "^2",
|
"@tauri-apps/cli": "^2",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/react": "^19.1.8",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.1.6",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@vitejs/plugin-react": "^4.6.0",
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
|
|
@ -35,4 +37,4 @@
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"vite": "^7.0.4"
|
"vite": "^7.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
556
src-tauri/Cargo.lock
generated
556
src-tauri/Cargo.lock
generated
|
|
@ -8,6 +8,41 @@ version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aead"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gcm"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"aes",
|
||||||
|
"cipher",
|
||||||
|
"ctr",
|
||||||
|
"ghash",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
|
|
@ -47,6 +82,27 @@ version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argon2"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"blake2",
|
||||||
|
"cpufeatures",
|
||||||
|
"password-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
|
|
@ -225,6 +281,12 @@ version = "0.22.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
|
@ -240,6 +302,15 @@ dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
|
|
@ -319,6 +390,25 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
|
||||||
|
dependencies = [
|
||||||
|
"bzip2-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.13+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-rs"
|
name = "cairo-rs"
|
||||||
version = "0.18.5"
|
version = "0.18.5"
|
||||||
|
|
@ -393,6 +483,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -443,6 +535,16 @@ dependencies = [
|
||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
|
|
@ -462,6 +564,12 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
@ -527,6 +635,21 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
|
||||||
|
dependencies = [
|
||||||
|
"crc-catalog",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc-catalog"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -558,6 +681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
|
"rand_core 0.6.4",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -598,6 +722,15 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctr"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||||
|
dependencies = [
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.21.3"
|
version = "0.21.3"
|
||||||
|
|
@ -633,6 +766,12 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate64"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
|
|
@ -643,6 +782,17 @@ dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.20"
|
version = "0.99.20"
|
||||||
|
|
@ -664,6 +814,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -889,17 +1040,6 @@ dependencies = [
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "filetime"
|
|
||||||
version = "0.2.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"libredox",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
|
@ -964,15 +1104,6 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fsevent-sys"
|
|
||||||
version = "4.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futf"
|
name = "futf"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
|
@ -1214,9 +1345,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi 5.3.0",
|
"r-efi 5.3.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1232,6 +1365,16 @@ dependencies = [
|
||||||
"wasip3",
|
"wasip3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ghash"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||||
|
dependencies = [
|
||||||
|
"opaque-debug",
|
||||||
|
"polyval",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio"
|
name = "gio"
|
||||||
version = "0.18.4"
|
version = "0.18.4"
|
||||||
|
|
@ -1330,10 +1473,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graph-notes"
|
name = "graph-notes"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
|
"argon2",
|
||||||
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
"notify",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -1343,6 +1489,7 @@ dependencies = [
|
||||||
"tauri-plugin-fs",
|
"tauri-plugin-fs",
|
||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1442,6 +1589,15 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html5ever"
|
name = "html5ever"
|
||||||
version = "0.29.1"
|
version = "0.29.1"
|
||||||
|
|
@ -1718,23 +1874,12 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inotify"
|
name = "inout"
|
||||||
version = "0.9.6"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"generic-array",
|
||||||
"inotify-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "inotify-sys"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1823,6 +1968,16 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.91"
|
version = "0.3.91"
|
||||||
|
|
@ -1866,26 +2021,6 @@ dependencies = [
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kqueue"
|
|
||||||
version = "1.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
|
|
||||||
dependencies = [
|
|
||||||
"kqueue-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kqueue-sys"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kuchikiki"
|
name = "kuchikiki"
|
||||||
version = "0.8.8-speedreader"
|
version = "0.8.8-speedreader"
|
||||||
|
|
@ -1950,10 +2085,7 @@ version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
|
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
|
||||||
"libc",
|
"libc",
|
||||||
"plain",
|
|
||||||
"redox_syscall 0.7.3",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1983,6 +2115,27 @@ version = "0.4.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lzma-rs"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"crc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lzma-sys"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mac"
|
name = "mac"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
@ -2051,18 +2204,6 @@ dependencies = [
|
||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio"
|
|
||||||
version = "0.8.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
|
@ -2137,25 +2278,6 @@ version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "notify"
|
|
||||||
version = "6.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.11.0",
|
|
||||||
"crossbeam-channel",
|
|
||||||
"filetime",
|
|
||||||
"fsevent-sys",
|
|
||||||
"inotify",
|
|
||||||
"kqueue",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"mio 0.8.11",
|
|
||||||
"walkdir",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -2322,6 +2444,12 @@ version = "1.21.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "open"
|
name = "open"
|
||||||
version = "5.3.3"
|
version = "5.3.3"
|
||||||
|
|
@ -2399,17 +2527,38 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.18",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "password-hash"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathdiff"
|
name = "pathdiff"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
|
|
@ -2579,12 +2728,6 @@ version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "plain"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plist"
|
name = "plist"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
|
@ -2625,6 +2768,18 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polyval"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potential_utf"
|
name = "potential_utf"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
|
@ -2859,15 +3014,6 @@ dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.11.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
|
@ -3266,6 +3412,17 @@ dependencies = [
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.9"
|
version = "0.10.9"
|
||||||
|
|
@ -3348,7 +3505,7 @@ dependencies = [
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
"objc2-quartz-core",
|
"objc2-quartz-core",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"redox_syscall 0.5.18",
|
"redox_syscall",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
|
@ -3418,6 +3575,12 @@ version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swift-rs"
|
name = "swift-rs"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
|
@ -3945,7 +4108,7 @@ checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 1.1.1",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
|
|
@ -4255,6 +4418,16 @@ version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "universal-hash"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.8"
|
version = "2.5.8"
|
||||||
|
|
@ -4775,15 +4948,6 @@ dependencies = [
|
||||||
"windows-targets 0.42.2",
|
"windows-targets 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
|
|
@ -4826,21 +4990,6 @@ dependencies = [
|
||||||
"windows_x86_64_msvc 0.42.2",
|
"windows_x86_64_msvc 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.48.5",
|
|
||||||
"windows_aarch64_msvc 0.48.5",
|
|
||||||
"windows_i686_gnu 0.48.5",
|
|
||||||
"windows_i686_msvc 0.48.5",
|
|
||||||
"windows_x86_64_gnu 0.48.5",
|
|
||||||
"windows_x86_64_gnullvm 0.48.5",
|
|
||||||
"windows_x86_64_msvc 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -4898,12 +5047,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -4922,12 +5065,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -4946,12 +5083,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -4982,12 +5113,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -5006,12 +5131,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -5030,12 +5149,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -5054,12 +5167,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -5260,6 +5367,15 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xz2"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
|
||||||
|
dependencies = [
|
||||||
|
"lzma-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
|
@ -5385,6 +5501,26 @@ dependencies = [
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|
@ -5418,12 +5554,82 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "2.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"arbitrary",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"deflate64",
|
||||||
|
"displaydoc",
|
||||||
|
"flate2",
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"hmac",
|
||||||
|
"indexmap 2.13.0",
|
||||||
|
"lzma-rs",
|
||||||
|
"memchr",
|
||||||
|
"pbkdf2",
|
||||||
|
"sha1",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"time",
|
||||||
|
"xz2",
|
||||||
|
"zeroize",
|
||||||
|
"zopfli",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zopfli"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"crc32fast",
|
||||||
|
"log",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "7.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.16+zstd.1.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant"
|
name = "zvariant"
|
||||||
version = "5.10.0"
|
version = "5.10.0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "graph-notes"
|
name = "graph-notes"
|
||||||
version = "0.9.0"
|
version = "1.0.0"
|
||||||
description = "A graph-based note-taking app"
|
description = "A graph-based note-taking app"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
|
||||||
|
|
@ -1,195 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher, Event, EventKind};
|
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
use crate::{NoteMeta, HeadingEntry, parse_frontmatter, extract_headings, extract_wikilinks};
|
|
||||||
|
|
||||||
/* ── Cached Note ───────────────────────────────────────────── */
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct CachedNote {
|
|
||||||
pub content: String,
|
|
||||||
pub meta: NoteMeta,
|
|
||||||
pub body: String,
|
|
||||||
pub headings: Vec<HeadingEntry>,
|
|
||||||
pub links: Vec<String>,
|
|
||||||
pub mtime: SystemTime,
|
|
||||||
pub rel_path: String,
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Vault Cache ───────────────────────────────────────────── */
|
|
||||||
|
|
||||||
pub struct VaultCache {
|
|
||||||
pub vault_path: PathBuf,
|
|
||||||
pub entries: HashMap<String, CachedNote>,
|
|
||||||
pub hits: u64,
|
|
||||||
pub misses: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VaultCache {
|
|
||||||
pub fn new(vault_path: &str) -> Self {
|
|
||||||
VaultCache {
|
|
||||||
vault_path: PathBuf::from(vault_path),
|
|
||||||
entries: HashMap::new(),
|
|
||||||
hits: 0,
|
|
||||||
misses: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scan all .md files in the vault and populate the cache.
|
|
||||||
pub fn scan_all(&mut self) {
|
|
||||||
let vault = self.vault_path.clone();
|
|
||||||
if !vault.exists() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect paths first to avoid borrow conflict
|
|
||||||
let paths: Vec<String> = WalkDir::new(&vault)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
.filter(|e| {
|
|
||||||
e.path().extension().map_or(false, |ext| ext == "md")
|
|
||||||
&& !e.path().strip_prefix(&vault)
|
|
||||||
.map_or(false, |p| {
|
|
||||||
let s = p.to_string_lossy();
|
|
||||||
s.starts_with("_templates") || s.starts_with("attachments")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.filter_map(|entry| {
|
|
||||||
entry
|
|
||||||
.path()
|
|
||||||
.strip_prefix(&vault)
|
|
||||||
.ok()
|
|
||||||
.map(|p| p.to_string_lossy().to_string())
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for rel in paths {
|
|
||||||
self.load_entry(&rel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load or reload a single entry from disk.
|
|
||||||
pub fn load_entry(&mut self, rel_path: &str) -> Option<&CachedNote> {
|
|
||||||
let full = self.vault_path.join(rel_path);
|
|
||||||
let mtime = fs::metadata(&full).ok()?.modified().ok()?;
|
|
||||||
let content = fs::read_to_string(&full).ok()?;
|
|
||||||
|
|
||||||
let (meta, body) = parse_frontmatter(&content);
|
|
||||||
let headings = extract_headings(&content);
|
|
||||||
let links = extract_wikilinks(&content);
|
|
||||||
let name = Path::new(rel_path)
|
|
||||||
.file_stem()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
self.entries.insert(
|
|
||||||
rel_path.to_string(),
|
|
||||||
CachedNote {
|
|
||||||
content,
|
|
||||||
meta,
|
|
||||||
body,
|
|
||||||
headings,
|
|
||||||
links,
|
|
||||||
mtime,
|
|
||||||
rel_path: rel_path.to_string(),
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
self.entries.get(rel_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a cached entry, reloading from disk if mtime changed.
|
|
||||||
pub fn get(&mut self, rel_path: &str) -> Option<&CachedNote> {
|
|
||||||
let full = self.vault_path.join(rel_path);
|
|
||||||
let disk_mtime = fs::metadata(&full).ok().and_then(|m| m.modified().ok());
|
|
||||||
|
|
||||||
if let Some(cached) = self.entries.get(rel_path) {
|
|
||||||
if let Some(dm) = disk_mtime {
|
|
||||||
if cached.mtime >= dm {
|
|
||||||
self.hits += 1;
|
|
||||||
return self.entries.get(rel_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.misses += 1;
|
|
||||||
self.load_entry(rel_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invalidate (remove) a single entry.
|
|
||||||
pub fn invalidate(&mut self, rel_path: &str) {
|
|
||||||
self.entries.remove(rel_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invalidate and reload an entry (for file changes).
|
|
||||||
pub fn refresh(&mut self, rel_path: &str) {
|
|
||||||
self.entries.remove(rel_path);
|
|
||||||
let full = self.vault_path.join(rel_path);
|
|
||||||
if full.exists() {
|
|
||||||
self.load_entry(rel_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── File Watcher ──────────────────────────────────────────── */
|
|
||||||
|
|
||||||
pub type SharedCache = Arc<Mutex<VaultCache>>;
|
|
||||||
|
|
||||||
/// Start a filesystem watcher on the vault directory.
|
|
||||||
/// Returns the watcher handle (must be kept alive) and processes events
|
|
||||||
/// by invalidating cache entries and calling the callback.
|
|
||||||
pub fn start_watcher<F>(
|
|
||||||
cache: SharedCache,
|
|
||||||
vault_path: &str,
|
|
||||||
on_change: F,
|
|
||||||
) -> Option<RecommendedWatcher>
|
|
||||||
where
|
|
||||||
F: Fn(Vec<String>) + Send + 'static,
|
|
||||||
{
|
|
||||||
let vault = PathBuf::from(vault_path);
|
|
||||||
let vault_for_closure = vault.clone();
|
|
||||||
let cache_for_closure = cache.clone();
|
|
||||||
|
|
||||||
let mut watcher = notify::recommended_watcher(move |res: Result<Event, notify::Error>| {
|
|
||||||
if let Ok(event) = res {
|
|
||||||
match event.kind {
|
|
||||||
EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => {
|
|
||||||
let mut changed: Vec<String> = Vec::new();
|
|
||||||
|
|
||||||
for path in &event.paths {
|
|
||||||
if path.extension().map_or(false, |e| e == "md") {
|
|
||||||
if let Ok(rel) = path.strip_prefix(&vault_for_closure) {
|
|
||||||
let rel_str = rel.to_string_lossy().to_string();
|
|
||||||
if let Ok(mut c) = cache_for_closure.lock() {
|
|
||||||
match event.kind {
|
|
||||||
EventKind::Remove(_) => c.invalidate(&rel_str),
|
|
||||||
_ => c.refresh(&rel_str),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
changed.push(rel_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !changed.is_empty() {
|
|
||||||
on_change(changed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
watcher.watch(vault.as_path(), RecursiveMode::Recursive).ok()?;
|
|
||||||
Some(watcher)
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::LazyLock;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
@ -37,9 +38,12 @@ fn normalize_note_name(name: &str) -> String {
|
||||||
name.trim().to_lowercase()
|
name.trim().to_lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WIKILINK_RE: LazyLock<Regex> = LazyLock::new(|| {
|
||||||
|
Regex::new(r"\[\[([^\]|]+)(?:\|[^\]]+)?\]\]").unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
fn extract_wikilinks(content: &str) -> Vec<String> {
|
fn extract_wikilinks(content: &str) -> Vec<String> {
|
||||||
let re = Regex::new(r"\[\[([^\]|]+)(?:\|[^\]]+)?\]\]").unwrap();
|
WIKILINK_RE.captures_iter(content)
|
||||||
re.captures_iter(content)
|
|
||||||
.map(|cap| cap[1].trim().to_string())
|
.map(|cap| cap[1].trim().to_string())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
@ -94,15 +98,39 @@ fn list_notes(vault_path: String) -> Result<Vec<NoteEntry>, String> {
|
||||||
Ok(build_tree(vault, vault))
|
Ok(build_tree(vault, vault))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate that a relative path stays within the vault root.
|
||||||
|
/// Returns the canonical full path, or an error if it escapes.
|
||||||
|
fn safe_vault_path(vault_path: &str, relative_path: &str) -> Result<PathBuf, String> {
|
||||||
|
let vault = Path::new(vault_path)
|
||||||
|
.canonicalize()
|
||||||
|
.map_err(|e| format!("Invalid vault path: {}", e))?;
|
||||||
|
let full = vault.join(relative_path);
|
||||||
|
// Canonicalize only works if the file exists; for new files, normalize manually
|
||||||
|
let normalized = if full.exists() {
|
||||||
|
full.canonicalize().map_err(|e| format!("Invalid path: {}", e))?
|
||||||
|
} else {
|
||||||
|
// For new files: resolve parent (must exist) + filename
|
||||||
|
let parent = full.parent()
|
||||||
|
.ok_or_else(|| "Invalid path".to_string())?;
|
||||||
|
let parent_canon = parent.canonicalize()
|
||||||
|
.unwrap_or_else(|_| parent.to_path_buf());
|
||||||
|
parent_canon.join(full.file_name().unwrap_or_default())
|
||||||
|
};
|
||||||
|
if !normalized.starts_with(&vault) {
|
||||||
|
return Err("Path escapes the vault directory".to_string());
|
||||||
|
}
|
||||||
|
Ok(normalized)
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn read_note(vault_path: String, relative_path: String) -> Result<String, String> {
|
fn read_note(vault_path: String, relative_path: String) -> Result<String, String> {
|
||||||
let full_path = Path::new(&vault_path).join(&relative_path);
|
let full_path = safe_vault_path(&vault_path, &relative_path)?;
|
||||||
fs::read_to_string(&full_path).map_err(|e| format!("Failed to read note: {}", e))
|
fs::read_to_string(&full_path).map_err(|e| format!("Failed to read note: {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn write_note(vault_path: String, relative_path: String, content: String) -> Result<(), String> {
|
fn write_note(vault_path: String, relative_path: String, content: String) -> Result<(), String> {
|
||||||
let full_path = Path::new(&vault_path).join(&relative_path);
|
let full_path = safe_vault_path(&vault_path, &relative_path)?;
|
||||||
|
|
||||||
// Ensure parent directory exists
|
// Ensure parent directory exists
|
||||||
if let Some(parent) = full_path.parent() {
|
if let Some(parent) = full_path.parent() {
|
||||||
|
|
@ -114,7 +142,7 @@ fn write_note(vault_path: String, relative_path: String, content: String) -> Res
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn delete_note(vault_path: String, relative_path: String) -> Result<(), String> {
|
fn delete_note(vault_path: String, relative_path: String) -> Result<(), String> {
|
||||||
let full_path = Path::new(&vault_path).join(&relative_path);
|
let full_path = safe_vault_path(&vault_path, &relative_path)?;
|
||||||
if full_path.is_file() {
|
if full_path.is_file() {
|
||||||
fs::remove_file(&full_path).map_err(|e| format!("Failed to delete note: {}", e))
|
fs::remove_file(&full_path).map_err(|e| format!("Failed to delete note: {}", e))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -367,9 +395,12 @@ pub struct TagInfo {
|
||||||
pub notes: Vec<String>,
|
pub notes: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TAG_RE: LazyLock<Regex> = LazyLock::new(|| {
|
||||||
|
Regex::new(r"(?:^|[\s,;(])(#[a-zA-Z][a-zA-Z0-9_/-]*)").unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
fn extract_tags(content: &str) -> Vec<String> {
|
fn extract_tags(content: &str) -> Vec<String> {
|
||||||
let re = Regex::new(r"(?:^|[\s,;(])(#[a-zA-Z][a-zA-Z0-9_/-]*)").unwrap();
|
let mut tags: Vec<String> = TAG_RE
|
||||||
let mut tags: Vec<String> = re
|
|
||||||
.captures_iter(content)
|
.captures_iter(content)
|
||||||
.map(|cap| cap[1].to_string())
|
.map(|cap| cap[1].to_string())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1302,7 +1333,7 @@ fn load_fold_state(vault_path: String, note_path: String) -> Result<Vec<usize>,
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_custom_css() -> Result<String, String> {
|
fn get_custom_css() -> Result<String, String> {
|
||||||
let config_dir = dirs_config_path();
|
let config_dir = dirs_config_dir();
|
||||||
let css_path = config_dir.join("custom.css");
|
let css_path = config_dir.join("custom.css");
|
||||||
if css_path.exists() {
|
if css_path.exists() {
|
||||||
fs::read_to_string(&css_path).map_err(|e| e.to_string())
|
fs::read_to_string(&css_path).map_err(|e| e.to_string())
|
||||||
|
|
@ -1313,15 +1344,11 @@ fn get_custom_css() -> Result<String, String> {
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn set_custom_css(css: String) -> Result<(), String> {
|
fn set_custom_css(css: String) -> Result<(), String> {
|
||||||
let config_dir = dirs_config_path();
|
let config_dir = dirs_config_dir();
|
||||||
fs::create_dir_all(&config_dir).map_err(|e| e.to_string())?;
|
fs::create_dir_all(&config_dir).map_err(|e| e.to_string())?;
|
||||||
fs::write(config_dir.join("custom.css"), css).map_err(|e| e.to_string())
|
fs::write(config_dir.join("custom.css"), css).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dirs_config_path() -> PathBuf {
|
|
||||||
dirs::config_dir().unwrap_or_else(|| PathBuf::from(".")).join("graph-notes")
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Workspace Layouts ──────────────────────────────────── */
|
/* ── Workspace Layouts ──────────────────────────────────── */
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Graph Notes",
|
"productName": "Graph Notes",
|
||||||
"version": "0.9.0",
|
"version": "1.0.0",
|
||||||
"identifier": "com.graphnotes.app",
|
"identifier": "com.graphnotes.app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": "default-src 'self' tauri: https://tauri.localhost; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' asset: https://asset.localhost http://asset.localhost blob: data: tauri: https://tauri.localhost; connect-src 'self' ipc: http://ipc.localhost tauri: https://tauri.localhost"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
|
|
|
||||||
33
src/App.tsx
33
src/App.tsx
|
|
@ -13,7 +13,7 @@ import { KanbanView } from "./components/KanbanView";
|
||||||
import { SearchReplace } from "./components/SearchReplace";
|
import { SearchReplace } from "./components/SearchReplace";
|
||||||
import { FlashcardView } from "./components/FlashcardView";
|
import { FlashcardView } from "./components/FlashcardView";
|
||||||
import { CSSEditor, useCustomCssInit } from "./components/CSSEditor";
|
import { CSSEditor, useCustomCssInit } from "./components/CSSEditor";
|
||||||
import { TabBar, type Tab } from "./components/TabBar";
|
import { TabBar } from "./components/TabBar";
|
||||||
import { WhiteboardView } from "./components/WhiteboardView";
|
import { WhiteboardView } from "./components/WhiteboardView";
|
||||||
import { DatabaseView } from "./components/DatabaseView";
|
import { DatabaseView } from "./components/DatabaseView";
|
||||||
import { GitPanel } from "./components/GitPanel";
|
import { GitPanel } from "./components/GitPanel";
|
||||||
|
|
@ -82,8 +82,6 @@ export default function App() {
|
||||||
const [focusMode, setFocusMode] = useState(false);
|
const [focusMode, setFocusMode] = useState(false);
|
||||||
const [searchReplaceOpen, setSearchReplaceOpen] = useState(false);
|
const [searchReplaceOpen, setSearchReplaceOpen] = useState(false);
|
||||||
const [cssEditorOpen, setCssEditorOpen] = useState(false);
|
const [cssEditorOpen, setCssEditorOpen] = useState(false);
|
||||||
const [tabs, setTabs] = useState<Tab[]>([]);
|
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// Apply saved theme + custom CSS on mount
|
// Apply saved theme + custom CSS on mount
|
||||||
|
|
@ -110,8 +108,8 @@ export default function App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!path) {
|
if (!path) {
|
||||||
// Default to the project's vault directory
|
// No stored vault path — use a sensible default
|
||||||
path = "/home/amir/code/notes/vault";
|
path = "./vault";
|
||||||
console.log("[GraphNotes] Using default vault path:", path);
|
console.log("[GraphNotes] Using default vault path:", path);
|
||||||
try {
|
try {
|
||||||
await setVaultPath(path);
|
await setVaultPath(path);
|
||||||
|
|
@ -318,6 +316,10 @@ export default function App() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSearchReplaceOpen(v => !v);
|
setSearchReplaceOpen(v => !v);
|
||||||
break;
|
break;
|
||||||
|
case "u":
|
||||||
|
e.preventDefault();
|
||||||
|
setCssEditorOpen(v => !v);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener("keydown", handler);
|
window.addEventListener("keydown", handler);
|
||||||
|
|
@ -398,27 +400,6 @@ export default function App() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Note View ──────────────────────────────────────────────── */
|
|
||||||
function NoteView() {
|
|
||||||
const { path } = useParams<{ path: string }>();
|
|
||||||
const { vaultPath, setCurrentNote, noteContent, setNoteContent } = useVault();
|
|
||||||
const decodedPath = decodeURIComponent(path || "");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!decodedPath || !vaultPath) return;
|
|
||||||
setCurrentNote(decodedPath);
|
|
||||||
readNote(vaultPath, decodedPath).then(setNoteContent).catch(() => setNoteContent(""));
|
|
||||||
}, [decodedPath, vaultPath, setCurrentNote, setNoteContent]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-1 overflow-hidden">
|
|
||||||
<main className="flex-1 overflow-y-auto">
|
|
||||||
<Editor />
|
|
||||||
</main>
|
|
||||||
<Backlinks />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Daily View ─────────────────────────────────────────────── */
|
/* ── Daily View ─────────────────────────────────────────────── */
|
||||||
function DailyView() {
|
function DailyView() {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useVault } from "../App";
|
||||||
import { writeNote, saveAttachment, saveSnapshot, getWritingGoal, setWritingGoal as setWritingGoalCmd, isEncrypted, encryptNote, decryptNote } from "../lib/commands";
|
import { writeNote, saveAttachment, saveSnapshot, getWritingGoal, setWritingGoal as setWritingGoalCmd, isEncrypted, encryptNote, decryptNote } from "../lib/commands";
|
||||||
import { extractWikilinks } from "../lib/wikilinks";
|
import { extractWikilinks } from "../lib/wikilinks";
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
|
import DOMPurify from "dompurify";
|
||||||
import { PropertiesPanel } from "./PropertiesPanel";
|
import { PropertiesPanel } from "./PropertiesPanel";
|
||||||
import { TableOfContents } from "./TableOfContents";
|
import { TableOfContents } from "./TableOfContents";
|
||||||
import { SlashMenu } from "./SlashMenu";
|
import { SlashMenu } from "./SlashMenu";
|
||||||
|
|
@ -59,20 +60,35 @@ export function Editor() {
|
||||||
)
|
)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
// Refs to avoid stale closures in debounced save
|
||||||
|
const currentNoteRef = useRef(currentNote);
|
||||||
|
const vaultPathRef = useRef(vaultPath);
|
||||||
|
currentNoteRef.current = currentNote;
|
||||||
|
vaultPathRef.current = vaultPath;
|
||||||
|
const lastSnapshotRef2 = useRef(0);
|
||||||
|
|
||||||
// ── Save with debounce ──
|
// ── Save with debounce ──
|
||||||
const saveContent = useCallback(
|
const saveContent = useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
setNoteContent(value);
|
setNoteContent(value);
|
||||||
if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
|
if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
|
||||||
saveTimeoutRef.current = setTimeout(async () => {
|
saveTimeoutRef.current = setTimeout(async () => {
|
||||||
if (vaultPath && currentNote) {
|
const v = vaultPathRef.current;
|
||||||
|
const n = currentNoteRef.current;
|
||||||
|
if (v && n) {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
await writeNote(vaultPath, currentNote, value);
|
await writeNote(v, n, value);
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
|
// Auto-snapshot on save (max 1 per 5 min)
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastSnapshotRef2.current > 5 * 60 * 1000 && value.length > 50) {
|
||||||
|
lastSnapshotRef2.current = now;
|
||||||
|
saveSnapshot(v, n).catch(() => { });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
},
|
},
|
||||||
[vaultPath, currentNote, setNoteContent]
|
[setNoteContent]
|
||||||
);
|
);
|
||||||
|
|
||||||
// ── Extract raw markdown from contenteditable DOM ──
|
// ── Extract raw markdown from contenteditable DOM ──
|
||||||
|
|
@ -415,10 +431,11 @@ export function Editor() {
|
||||||
/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g,
|
/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g,
|
||||||
(_m, target, display) => {
|
(_m, target, display) => {
|
||||||
const label = display?.trim() || target.trim();
|
const label = display?.trim() || target.trim();
|
||||||
return `<span class="wikilink" data-target="${target.trim()}">${label}</span>`;
|
const safeTarget = target.trim().replace(/"/g, '"');
|
||||||
|
return `<span class="wikilink" data-target="${safeTarget}">${DOMPurify.sanitize(label)}</span>`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return html;
|
return DOMPurify.sanitize(html, { ADD_ATTR: ['data-target'] });
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Mermaid post-processing (render in a separate effect)
|
// Mermaid post-processing (render in a separate effect)
|
||||||
|
|
@ -492,15 +509,7 @@ export function Editor() {
|
||||||
}).catch(() => { });
|
}).catch(() => { });
|
||||||
}, [isPreview, renderedMarkdown]);
|
}, [isPreview, renderedMarkdown]);
|
||||||
|
|
||||||
// Auto-snapshot on save (max 1 per 5 min)
|
// Snapshot logic is now in the saveContent debounce callback above
|
||||||
useEffect(() => {
|
|
||||||
if (!vaultPath || !currentNote || !noteContent) return;
|
|
||||||
const now = Date.now();
|
|
||||||
if (now - lastSnapshotRef.current > 5 * 60 * 1000 && noteContent.length > 50) {
|
|
||||||
lastSnapshotRef.current = now;
|
|
||||||
saveSnapshot(vaultPath, currentNote).catch(() => { });
|
|
||||||
}
|
|
||||||
}, [vaultPath, currentNote, noteContent]);
|
|
||||||
|
|
||||||
// Load writing goal
|
// Load writing goal
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -514,25 +523,16 @@ export function Editor() {
|
||||||
isEncrypted(vaultPath, currentNote).then(setNoteEncrypted).catch(() => setNoteEncrypted(false));
|
isEncrypted(vaultPath, currentNote).then(setNoteEncrypted).catch(() => setNoteEncrypted(false));
|
||||||
}, [vaultPath, currentNote]);
|
}, [vaultPath, currentNote]);
|
||||||
|
|
||||||
// Widget rendering in preview
|
// Widget rendering in preview (single pass to avoid clobbering DOM)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isPreview || !mermaidRef.current) return;
|
if (!isPreview || !mermaidRef.current) return;
|
||||||
const container = mermaidRef.current;
|
const container = mermaidRef.current;
|
||||||
// {{progress:N}} → progress bar
|
let html = container.innerHTML;
|
||||||
container.innerHTML = container.innerHTML.replace(
|
html = html
|
||||||
/\{\{progress:(\d+)\}\}/g,
|
.replace(/\{\{progress:(\d+)\}\}/g, (_, n) => `<div class="widget-progress"><div class="widget-progress-fill" style="width:${Math.min(100, +n)}%"></div><span class="widget-progress-label">${n}%</span></div>`)
|
||||||
(_, n) => `<div class="widget-progress"><div class="widget-progress-fill" style="width:${Math.min(100, +n)}%"></div><span class="widget-progress-label">${n}%</span></div>`
|
.replace(/\{\{counter:(\d+)\}\}/g, (_, n) => `<span class="widget-counter">${n}</span>`)
|
||||||
);
|
.replace(/\{\{toggle:(on|off)\}\}/g, (_, state) => `<span class="widget-toggle ${state === 'on' ? 'on' : ''}">${state === 'on' ? '●' : '○'}</span>`);
|
||||||
// {{counter:N}} → counter badge
|
if (html !== container.innerHTML) container.innerHTML = html;
|
||||||
container.innerHTML = container.innerHTML.replace(
|
|
||||||
/\{\{counter:(\d+)\}\}/g,
|
|
||||||
(_, n) => `<span class="widget-counter">${n}</span>`
|
|
||||||
);
|
|
||||||
// {{toggle:on/off}} → toggle indicator
|
|
||||||
container.innerHTML = container.innerHTML.replace(
|
|
||||||
/\{\{toggle:(on|off)\}\}/g,
|
|
||||||
(_, state) => `<span class="widget-toggle ${state === 'on' ? 'on' : ''}">${state === 'on' ? '●' : '○'}</span>`
|
|
||||||
);
|
|
||||||
}, [isPreview, renderedMarkdown]);
|
}, [isPreview, renderedMarkdown]);
|
||||||
|
|
||||||
// Right-click for refactoring
|
// Right-click for refactoring
|
||||||
|
|
@ -570,12 +570,9 @@ export function Editor() {
|
||||||
range.insertNode(textNode);
|
range.insertNode(textNode);
|
||||||
range.collapse(false);
|
range.collapse(false);
|
||||||
}
|
}
|
||||||
// Trigger save
|
// Trigger save using same extraction as regular input
|
||||||
const raw = ceRef.current?.innerText || "";
|
const raw = domToMarkdown(ceRef.current!);
|
||||||
setNoteContent(raw);
|
saveContent(raw);
|
||||||
if (currentNote) {
|
|
||||||
await writeNote(vaultPath, currentNote, raw);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Image paste failed:", err);
|
console.error("Image paste failed:", err);
|
||||||
}
|
}
|
||||||
|
|
@ -847,8 +844,7 @@ export function Editor() {
|
||||||
setNoteEncrypted(false);
|
setNoteEncrypted(false);
|
||||||
setLockScreenOpen(false);
|
setLockScreenOpen(false);
|
||||||
setLockError(null);
|
setLockError(null);
|
||||||
// Save decrypted
|
// Note: decrypted content is only held in memory, not written to disk
|
||||||
await writeNote(vaultPath, currentNote, content);
|
|
||||||
} catch {
|
} catch {
|
||||||
setLockError("Wrong password");
|
setLockError("Wrong password");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback, useRef } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Provider as JotaiProvider } from "jotai";
|
import { Provider as JotaiProvider } from "jotai";
|
||||||
import { Canvas, CanvasStyleProvider, registerBuiltinCommands } from "@blinksgg/canvas";
|
import { Canvas, CanvasStyleProvider, registerBuiltinCommands } from "@blinksgg/canvas";
|
||||||
|
|
@ -60,8 +60,12 @@ export function GraphView() {
|
||||||
</div>
|
</div>
|
||||||
), []);
|
), []);
|
||||||
|
|
||||||
const handleNodeClick = useCallback((nodeId: string) => {
|
// Navigate to selected node
|
||||||
navigate(`/note/${encodeURIComponent(nodeId)}`);
|
const handleSelectionChange = useCallback((selectedNodeIds: Set<string>) => {
|
||||||
|
if (selectedNodeIds.size === 1) {
|
||||||
|
const [nodeId] = selectedNodeIds;
|
||||||
|
navigate(`/note/${encodeURIComponent(nodeId)}`);
|
||||||
|
}
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -70,7 +74,7 @@ export function GraphView() {
|
||||||
<div className="graph-canvas-wrapper">
|
<div className="graph-canvas-wrapper">
|
||||||
<Canvas
|
<Canvas
|
||||||
renderNode={renderNode}
|
renderNode={renderNode}
|
||||||
onNodeClick={handleNodeClick}
|
onSelectionChange={handleSelectionChange}
|
||||||
minZoom={0.1}
|
minZoom={0.1}
|
||||||
maxZoom={5}
|
maxZoom={5}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -168,9 +168,7 @@ export function Sidebar() {
|
||||||
if (newPath === sourcePath) return;
|
if (newPath === sourcePath) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const oldName = sourcePath.replace(/\.md$/, "").split("/").pop() || "";
|
|
||||||
await renameNote(vaultPath, sourcePath, newPath);
|
await renameNote(vaultPath, sourcePath, newPath);
|
||||||
await updateWikilinks(vaultPath, oldName, oldName);
|
|
||||||
await refreshNotes();
|
await refreshNotes();
|
||||||
if (location.pathname === `/note/${encodeURIComponent(sourcePath)}`) {
|
if (location.pathname === `/note/${encodeURIComponent(sourcePath)}`) {
|
||||||
navigate(`/note/${encodeURIComponent(newPath)}`, { replace: true });
|
navigate(`/note/${encodeURIComponent(newPath)}`, { replace: true });
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
import { useEffect, useState, useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Provider as JotaiProvider } from "jotai";
|
import { Provider as JotaiProvider } from "jotai";
|
||||||
import { Canvas, CanvasStyleProvider, registerBuiltinCommands, ViewportControls } from "@blinksgg/canvas";
|
import { Canvas, CanvasStyleProvider, registerBuiltinCommands, ViewportControls } from "@blinksgg/canvas";
|
||||||
import { useVault } from "../App";
|
import { useVault } from "../App";
|
||||||
import { saveCanvas, loadCanvas } from "../lib/commands";
|
import { saveCanvas } from "../lib/commands";
|
||||||
|
|
||||||
registerBuiltinCommands();
|
registerBuiltinCommands();
|
||||||
|
|
||||||
const CARD_COLORS = ["#8b5cf6", "#3b82f6", "#10b981", "#f59e0b", "#f43f5e", "#ec4899"];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WhiteboardView — Freeform visual thinking canvas.
|
* WhiteboardView — Freeform visual thinking canvas.
|
||||||
*/
|
*/
|
||||||
export function WhiteboardView() {
|
export function WhiteboardView() {
|
||||||
const { name } = useParams<{ name: string }>();
|
const { name } = useParams<{ name: string }>();
|
||||||
const { vaultPath, navigateToNote } = useVault();
|
const { vaultPath } = useVault();
|
||||||
|
|
||||||
const renderNode = useCallback(({ node, isSelected }: any) => {
|
const renderNode = useCallback(({ node, isSelected }: any) => {
|
||||||
const nodeType = node.dbData?.node_type || "card";
|
const nodeType = node.dbData?.node_type || "card";
|
||||||
|
|
@ -53,13 +51,6 @@ export function WhiteboardView() {
|
||||||
</div>
|
</div>
|
||||||
<Canvas
|
<Canvas
|
||||||
renderNode={renderNode}
|
renderNode={renderNode}
|
||||||
onNodeDoubleClick={(nodeId, nodeData) => {
|
|
||||||
const label = nodeData?.label || (nodeData as any)?.dbData?.label;
|
|
||||||
if (label) navigateToNote(label);
|
|
||||||
}}
|
|
||||||
onBackgroundDoubleClick={(worldPos) => {
|
|
||||||
// Could add node creation here
|
|
||||||
}}
|
|
||||||
minZoom={0.1}
|
minZoom={0.1}
|
||||||
maxZoom={5}
|
maxZoom={5}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue