[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80544":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":12,"stars30d":16,"stars90d":14,"forks30d":14,"starsTrendScore":17,"compositeScore":18,"rankGlobal":9,"rankLanguage":9,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":9,"pushedAt":9,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":14,"starSnapshotCount":14,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},80544,"graf","reekta92\u002Fgraf","reekta92","A TUI graph view for markdown files with wikilinks. Customizable.",null,"Rust",62,5,56,0,4,6,12,2.33,"GNU General Public License v3.0",false,"main",true,[],"2026-06-12 02:04:03","# graf\n\nA terminal-based force-directed graph visualizer for markdown wikilinks. Run `graf` in any directory and see your markdown files as an interactive, pannable, zoomable network graph.\n\n# DISCLAIMER\n`graf` is originally meant to be a feature for my main project `clin-rs`. When this project is fully developed it will be merged with the `clin-rs` project as a \"graph view\" feature. Currently **all the big features are implemented** and only the testing, bugfixing phase remains. So please create a issue for any problem you encounter using `graf`!\n\n> Part of **[clin-rs](https:\u002F\u002Fgithub.com\u002Freekta92\u002Fclin-rs)**\n\n# Plans\n- [X] Custom themes\n- [X] Hot reloading the config\n- [ ] More modes for different usage purposes:\n> - [X] Obsidian mode: creates a graph of `.md ` files according to [[wikilinks]]\n> - [ ] Obsidian single mode: similar to Obsidian mode but instead of creating graph of different files and their connections, creates graph of a single file(god node) and headlines as nodes\n> - [ ] Coding mode: creates a graph of a project by assigning the source files as god nodes and codeblocks inside them are nodes connected to it\n> - [ ] Org mode: same logic as Obsidian mode but for `.org` files\n\n# Showcase\n\u003Ctable>\n  \u003Ctr>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fbd3b94b3-db1c-49d8-818c-3b397f76730e\" \u002F>\u003C\u002Ftd>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F51a21b0e-261d-4d03-bd72-f6866a83a03d\" \u002F>\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F34c3ade5-6d90-4d3b-9df1-d7b2bb400b0c\" \u002F>\u003C\u002Ftd>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fcae64831-faa3-445f-97e8-250be23bd05c\" \u002F>\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F2a9b881d-518b-43e9-abec-763a3854ed95\" \u002F>\u003C\u002Ftd>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fcd2fd347-0e75-4d91-9833-063b1768e502\" \u002F>\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F65a2c54c-c416-4b19-be6f-f305df9decda\" \u002F>\u003C\u002Ftd>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fa9fc970b-8cc3-47f7-8fd9-7a9534b1d367\" \u002F>\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fb78004d4-e7ff-4bdf-a3a1-ad663f3c21ab\" \u002F>\u003C\u002Ftd>\n    \u003Ctd>\u003Cimg width=\"1888\" height=\"1012\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F55e9f34a-9bfa-44e8-b147-8b9ff2dda365\" \u002F>\u003C\u002Ftd>\n  \u003C\u002Ftr>\n\u003C\u002Ftable>\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fde06ffda-a1f6-4317-9cd2-f7a222c13f18\n\n- - -\n\n## Features\n\n**Link Parsing**\n\n- **Wikilinks** — extracts `[[Title]]` and `[[Title|Display Text]]` patterns from file content.\n- **Frontmatter** — reads YAML `title:` and `tags: []` from `---`-delimited blocks.\n- **Title resolution** — fallback chain: frontmatter `title:` → first `# heading` → filename stem. Links are resolved by case-insensitive matching against titles.\n\n**Search**\n\n- Press `f` to open a popup that fuzzy-matches against node titles, relative file paths, and tags (case-insensitive). Navigate results with arrows or Tab, press Enter to jump.\n\n**Configuration**\n\n- **3-layer override** — defaults → TOML config file (`~\u002F.config\u002Fgraf\u002Fconfig.toml`) → CLI arguments → `GRAF_*` environment variables.\n- **11 built-in themes** — default, Tokyo Night, Catppuccin Mocha, One Dark, Gruvbox, Dracula, Nord, Rose Pine, Everforest, Kanagawa, Solarized.\n- **Hex color overrides** — per-element (`node_color`, `edge_color`, `label_color`, etc.) on top of any theme.\n- **Background modes** — `transparent` (passes through terminal transparency) or `solid` (theme background color).\n\n**Overlays**\n\n- **Minimap** - toggleable minimap that shows the entire canvas.\n- **Status bar** — template-based with variables: `{files}`, `{links}`, `{selected}`, `{date}`, `{time}`, `{size}`, `{ratio}`.\n- **Legend** — shows tag or folder color mapping; configurable position and max items.\n- **Grid overlay** — configurable number of divisions per axis.\n- **Help overlay** — press `?` for a quick reference of all controls.\n\n- - -\n\n## Installation\n\n### Debian, Fedora, Arch Linux\nRefer to the [releases](https:\u002F\u002Fgithub.com\u002Freekta92\u002Fgraf\u002Freleases) page for the latest release.\n\n### Cargo\n\n```bash\ncargo install graf-rs\n```\n\n### From source\n\n```bash\ngit clone \u003Crepo-url>\ncd graf\ncargo install --path .\n```\n\n#### Dependencies\n\n- Rust 1.85+ (Edition 2024)\n\n- - -\n\n## Usage\n\nNavigate to any directory containing markdown files and run:\n\n```bash\ncd ~\u002Fdocs\u002Fmy-wiki\ngraf\n```\n\n`graf` recursively scans the current working directory for `*.md` and `*.mdx` files, parses `[[wikilinks]]` and YAML frontmatter tags, then renders a force-directed graph in your terminal.\n\nDouble-click a node or press `Enter` to open the linked file. The editor is read from the `EDITOR` environment variable (defaults to `vim`).\n\n```bash\nEDITOR=nvim graf\n```\n\n- - -\n\n## Controls\n\n### Keyboard\n\n| Key | Action |\n|-----|--------|\n| `↑` `↓` `←` `→` \u002F `k` `j` `h` `l` | Move between nodes |\n| `+` `=` \u002F `Ctrl+J` | Zoom in |\n| `-` \u002F `Ctrl+K` | Zoom out |\n| `Enter` | Open selected file in editor |\n| `a` | Auto-fit view to all nodes |\n| `f` | Activate search |\n| `r` | Refresh file scan |\n| `Shift+M` | Toggle minimap |\n| `Shift+L` | Toggle legend |\n| `Shift+G` | Toggle grid |\n| `Shift+S` | Toggle status bar |\n| `?` | Toggle help overlay |\n| `q` \u002F `Esc` | Quit |\n\n### Mouse\n\n| Action | Effect |\n|--------|--------|\n| Scroll wheel | Zoom in\u002Fout |\n| Click & drag background | Pan view (natural: drag up → view goes down) |\n| Click & drag node | Reposition node |\n| Single click | Select node |\n| Double-click | Open file in editor |\n| Double-click empty area | Deselect node |\n\n### Search\n\nPress `f` to activate the search popup. Search matches against node titles, file paths (relative), and tags (case-insensitive).\n\n### Search controls\n\n| Key | Action |\n|-----|--------|\n| `Enter` | Select result and jump to node |\n| `↑` `↓` | Navigate results |\n| `Tab` \u002F `Shift+Tab` | Cycle forward\u002Fbackward |\n| `Esc` | Close search |\n| `Ctrl+A` \u002F `Ctrl+E` | Move to start\u002Fend of query |\n| `Ctrl+U` | Clear entire query |\n| `Ctrl+W` | Delete word backward |\n\n- - -\n\n## Configuration\n\nConfig file location: `~\u002F.config\u002Fgraf\u002Fconfig.toml`\n\nIf the file doesn't exist, `graf` uses all defaults. Every setting is optional — only include the ones you want to override.\n\n### Full example\n\n```toml\n[visual]\ntheme = \"catppuccin-mocha\"\nbackground = \"transparent\"\nnode_color_mode = \"tag\"\nedge_color_mode = \"source\"\nlabel_mode = \"selected\"\nlabel_max_length = 20\nnode_size = 2.0\nnode_size_mode = \"link_count\"\nedge_thickness = 1\nshow_legend = true\nshow_grid = false\nshow_minimap = true\nminimap_position = \"bottom_right\"\nminimap_width = 30\nminimap_height = 12\ncanvas_marker = \"braille\"\nnode_shape = \"circle\"\nlabel_offset = 4.0\ngrid_divisions = 10\n\n[visual.colors]\nnode_color = \"#ff6600\"\nedge_color = \"#333333\"\nlabel_color = \"#aaaaaa\"\nselection_ring_color = \"#ffffff\"\nborder_color = \"#555555\"\ngrid_color = \"#222222\"\n\n[physics]\nideal_distance = 80.0\ndamping = 0.95\nmax_iterations = 800\ngravity = 0.01\ncooling = true\nprevent_overlapping = true\ntimestep = 0.016\nthread_sleep_ms = 16\n\n[interaction]\ndouble_click_ms = 300\nzoom_factor = 1.15\ndrag_sensitivity = 0.5\nauto_fit_padding = 1.4\ndrag_scale = 200.0\n\n[display]\nshow_status_bar = true\nstatus_format = \"Files: {files} | Links: {links} | Selected: {selected}\"\nborder_style = \"rounded\"\nborder_title = \"graf\"\n\n[filter]\nexclude_tags = [\"draft\", \"private\"]\nexclude_patterns = [\"*.bak\", \"archive\u002F*\"]\nmin_links = 0\nmax_nodes = 500\n\n[legend]\nposition = \"top_right\"\nmax_items = 10\n\n[search]\nmax_results = 20\nmax_visible = 10\npopup_width = 50\npopup_y = 3\ncursor_glyph = \"▎\"\n\n[editor]\ncommand = \"\"\n```\n\n### `[visual]`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `theme` | string | `\"default\"` | Color scheme preset (see **Themes** below) |\n| `background` | string | `\"transparent\"` | `\"transparent\"` passes through terminal transparency, `\"solid\"` fills with theme background color |\n| `node_color_mode` | string | `\"tag\"` | How nodes are colored: `\"tag\"`, `\"folder\"`, `\"link_count\"`, `\"uniform\"` |\n| `edge_color_mode` | string | `\"source\"` | Edge color: `\"source\"`, `\"target\"`, `\"uniform\"` |\n| `label_mode` | string | `\"selected\"` | Which labels to show: `\"selected\"`, `\"neighbors\"`, `\"all\"`, `\"none\"` |\n| `label_max_length` | int | `20` | Max characters for title labels (1–60) |\n| `node_size` | float | `2.0` | Base node radius (1–5) |\n| `node_size_mode` | string | `\"fixed\"` | `\"fixed\"` constant size, `\"link_count\"` scales with connections |\n| `edge_thickness` | int | `1` | Line thickness for edges (1–3) |\n| `show_legend` | bool | `true` | Show tag legend |\n| `show_grid` | bool | `false` | Show background grid overlay |\n| `show_minimap` | bool | `true` | Show minimap in corner |\n| `minimap_position` | string | `\"top_right\"` | Minimap position: `\"top_right\"`, `\"top_left\"`, `\"bottom_right\"`, `\"bottom_left\"` |\n| `minimap_width` | int | `30` | Minimap width in columns |\n| `minimap_height` | int | `12` | Minimap height in rows |\n| `canvas_marker` | string | `\"braille\"` | Canvas rendering marker: `\"braille\"`, `\"halfblock\"`, `\"dot\"` |\n| `minimap_marker` | string | `\"braille\"` | Minimap rendering marker: `\"braille\"`, `\"halfblock\"`, `\"dot\"` |\n| `node_shape` | string | `\"circle\"` | Node shape: `\"circle\"`, `\"square\"`, `\"diamond\"` |\n| `label_offset` | float | `4.0` | Distance between node edge and label |\n| `grid_divisions` | int | `10` | Number of grid lines per axis (2–50) |\n\n### `[visual.colors]`\n\nOptional hex color overrides applied on top of the selected theme. All fields are optional.\n\n| Key | Type | Description |\n|-----|------|-------------|\n| `node_color` | hex string | Override all node colors (e.g. `\"#ff6600\"`) |\n| `edge_color` | hex string | Override edge color |\n| `label_color` | hex string | Override label text color |\n| `selection_ring_color` | hex string | Override selection ring color |\n| `border_color` | hex string | Override border, legend border, and minimap border |\n| `title_color` | hex string | Override title color |\n| `grid_color` | hex string | Override grid line color |\n| `legend_text_color` | hex string | Override legend text color |\n| `status_bar_color` | hex string | Override status bar text color |\n| `background_color` | hex string | Override canvas and minimap background color |\n\n### `[physics]`\n\nControls the force-directed layout simulation.\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `ideal_distance` | float | `80.0` | Target distance between connected nodes |\n| `damping` | float | `0.95` | Velocity damping per step (lower = faster settling) |\n| `max_iterations` | int | `800` | Maximum simulation steps before stopping |\n| `gravity` | float | `0.01` | Center pull to prevent drift |\n| `cooling` | bool | `true` | Whether simulation cools down over time |\n| `prevent_overlapping` | bool | `true` | Prevent nodes from overlapping |\n| `timestep` | float | `0.016` | Simulation timestep (lower = smoother but slower) |\n| `thread_sleep_ms` | int | `16` | Milliseconds between simulation steps (~60fps) |\n\n### `[interaction]`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `double_click_ms` | int | `300` | Time window in milliseconds to register a double-click |\n| `zoom_factor` | float | `1.15` | Zoom multiplier per scroll or key press |\n| `drag_sensitivity` | float | `0.5` | Mouse drag speed multiplier |\n| `auto_fit_padding` | float | `1.4` | Padding multiplier when auto-fitting view (higher = more zoomed out) |\n| `drag_scale` | float | `200.0` | Internal scaling factor for background drag panning |\n\n### `[display]`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `show_status_bar` | bool | `true` | Show the status bar at the bottom |\n| `status_format` | string | `\"Files: {files} \\| Links: {links} \\| Selected: {selected}\"` | Status bar text template. Variables: `{files}`, `{links}`, `{selected}`, `{date}`, `{time}`, `{size}`, `{ratio}` |\n| `border_style` | string | `\"rounded\"` | Border style: `\"plain\"`, `\"rounded\"`, `\"double\"`, `\"none\"` |\n| `border_title` | string | `\"graf\"` | Window title. Supports `{cwd}` variable (current directory name) |\n\n### `[filter]`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `exclude_tags` | array | `[]` | Tags to exclude from the graph |\n| `exclude_patterns` | array | `[]` | Glob patterns for files to exclude |\n| `min_links` | int | `0` | Only show nodes with at least this many connections |\n| `max_nodes` | int | `500` | Maximum nodes to display (`0` = unlimited) |\n\n### `[legend]`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `position` | string | `\"top_right\"` | Legend position: `\"top_right\"`, `\"top_left\"`, `\"bottom_right\"`, `\"bottom_left\"` |\n| `max_items` | int | `10` | Maximum tag groups to show in the legend |\n\n### `[search]`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `max_results` | int | `20` | Maximum search results to return |\n| `max_visible` | int | `10` | Maximum results visible without scrolling |\n| `popup_width` | int | `50` | Search popup width in columns |\n| `popup_y` | int | `3` | Vertical position of search popup from top |\n| `cursor_glyph` | string | `\"▎\"` | Cursor character in search input |\n\n### `[editor]`\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `command` | string | `\"\"` | Editor command. Falls back to `$EDITOR` env var, then `vim` |\n\n- - -\n\n## CLI Arguments\n\n```\ngraf [OPTIONS]\n\nOptions:\n  -d, --dir \u003CDIR>              Directory to scan [default: current]\n  -c, --config \u003CCONFIG>        Path to config file\n      --theme \u003CTHEME>          Theme preset\n      --max-nodes \u003CMAX_NODES>  Maximum nodes to display\n      --exclude \u003CEXCLUDE>      Exclude glob patterns (repeatable)\n      --exclude-tags \u003CTAGS>    Exclude tags (comma-separated)\n      --node-color-mode \u003CMODE> Node color mode\n      --edge-color-mode \u003CMODE> Edge color mode\n      --label-mode \u003CMODE>      Label display mode\n      --labels                 Show all labels (shorthand for --label-mode all)\n      --no-status              Hide status bar\n      --grid                   Show grid\n      --no-minimap             Hide minimap\n      --no-legend              Hide legend\n      --background \u003CBG>        Background style\n      --border-style \u003CSTYLE>   Border style for overlays\n      --editor \u003CEDITOR>        Editor command to open files\n  -h, --help                   Print help\n  -V, --version                Print version\n```\n\n> CLI arguments override config file values, which override defaults.\n\n- - -\n\n## Environment variables\n\nConfig values can be overridden with `GRAF_*` environment variables (uppercase, underscores):\n\n```bash\nGRAF_VISUAL_THEME=\"dracula\" GRAF_FILTER_MAX_NODES=200 graf\n```\n\nCommon variables:\n- `GRAF_VISUAL_THEME` — Theme preset\n- `GRAF_EDITOR_COMMAND` — Editor command\n- `GRAF_FILTER_MAX_NODES` — Maximum nodes\n- `GRAF_FILTER_MIN_LINKS` — Minimum links threshold\n- `GRAF_VISUAL_NODE_COLOR_MODE` — Node color mode\n- `GRAF_DISPLAY_SHOW_STATUS_BAR` — Show status bar (true\u002Ffalse)\n\nFormat: `GRAF_{SECTION}_{KEY}` in all caps with underscores.\n\n- - -\n\n## Themes\n\n### Presets\n\n| Value | Description |\n|-------|-------------|\n| `\"default\"` | Inherits colors from your terminal's color scheme. No hardcoded palette. |\n| `\"tokyo-night\"` | Tokyo Night (dark) palette |\n| `\"catppuccin-mocha\"` | Catppuccin Mocha palette |\n| `\"onedark\"` | One Dark palette |\n| `\"gruvbox\"` | Gruvbox Dark palette |\n| `\"dracula\"` | Dracula palette |\n| `\"nord\"` | Nord Frost palette |\n| `\"rose-pine\"` | Rose Pine palette |\n| `\"everforest\"` | Everforest palette |\n| `\"kanagawa\"` | Kanagawa palette |\n| `\"solarized\"` | Solarized Dark palette |\n\n### Background modes\n\n- `\"transparent\"` — The canvas background is not drawn, allowing your terminal's background (including transparency effects) to show through.\n- `\"solid\"` — The canvas background is filled with the theme's configured background color. Only relevant when using a non-default theme.\n\n- - -\n\n## Comparison with Obsidian's Graph View\n\nObsidian's [graph view](https:\u002F\u002Fhelp.obsidian.md\u002FPlugins\u002FGraph+view) was the direct inspiration for this project. Both share core capabilities: force-directed layout, node drag, pan\u002Fzoom, wikilink parsing, frontmatter tags, and search. The table below highlights where they differ:\n\n| | Obsidian Graph View | graf |\n|---|---|---|\n| Platform | GUI (Electron desktop app) | Terminal (any emulator, SSH, tmux) |\n| Input | Mouse | Mouse + full keyboard navigation |\n| Node coloring | Tag (via community plugins) | Tag, folder, link count, uniform (built-in) |\n| Node shapes | Circle only | Circle, square, diamond |\n| Minimap | No | Yes (interactive, 4 positions) |\n| Label modes | Hover to show | Selected, neighbors, all, none |\n| Config | GUI settings | TOML file + CLI flags + env vars |\n| Editor | Built-in Obsidian editor | Any `$EDITOR` (vim, nvim, helix, etc.) |\n| Live file watching | Yes | Manual refresh (`r`) |\n\n\n- - -\n\n## How it works\n\n### File scanning\n\n`graf` starts at the current working directory and recursively collects all `*.md` and `*.mdx` files. Files matching patterns in `exclude_patterns` are skipped. Up to `max_nodes` files are loaded.\n\n### Link parsing\n\nFor each file, `graf` extracts:\n\n1. **Wikilinks** — `[[Note Title]]` or `[[Note Title|Display Text]]` patterns from the file content\n2. **Title** — From YAML frontmatter (`title: \"My Note\"`) or the first `# heading`\n3. **Tags** — From YAML frontmatter (`tags: [tag1, tag2]`)\n\nLinks are resolved by matching wikilink text (case-insensitive) against file titles. Unresolved links are silently ignored.\n\n### Color assignment\n\n- **Tag mode**: Each unique tag across all files is assigned a unique color using golden-ratio distributed HSL values across the full 360° hue spectrum. No two tags ever share the same color.\n- **Link count mode**: Colors are interpolated across the theme's node palette based on how many connections each node has.\n- **Multi-tag nodes**: The primary (first) tag determines the main node color. Additional tags appear as small colored dots orbiting the node.\n\n### Physics\n\nA force-directed layout runs on a background thread at ~60fps using `fdg-sim`. Repulsive forces push all nodes apart, spring forces pull connected nodes together, and optional gravity prevents drift. The simulation auto-stops when total energy drops below a threshold.\n\n- - -\n\n## Tech stack\n\n| Crate | Purpose |\n|-------|---------|\n| `ratatui` | Terminal UI framework |\n| `crossterm` | Terminal raw mode, mouse capture, alternate screen |\n| `fdg-sim` | Force-directed graph simulation |\n| `petgraph` | Graph data structures |\n| `regex` | Wikilink and frontmatter parsing |\n| `toml` + `serde` | Configuration loading |\n| `clap` | CLI argument parsing |\n| `glob` | File discovery |\n| `chrono` | Date\u002Ftime formatting for status bar |\n| `directories` | XDG config path resolution |\n\n- - -\n\n## Project structure\n\n```\ngraf\u002F\n├── Cargo.toml\n├── README.md\n└── src\u002F\n    ├── main.rs          # Entry point, terminal setup, event loop\n    ├── cli.rs           # CLI argument definitions (clap)\n    ├── app.rs           # App state management, graph initialization\n    ├── config.rs        # Config loading, validation, color overrides\n    ├── config\u002F\n    │   └── themes.rs    # 11 theme palettes and builder\n    ├── linker.rs        # File scanning, wikilink extraction, frontmatter parsing\n    ├── ui.rs            # Help overlay, search popup, config error rendering\n    ├── util.rs          # Shared utilities (text truncation)\n    └── graph\u002F\n        ├── mod.rs       # GraphState, ForceGraph construction, search\n        ├── render.rs    # Canvas drawing (nodes, edges, labels, legend, grid, minimap)\n        ├── input.rs     # Keyboard and mouse event handling\n        ├── viewport.rs  # Pan, zoom, screen-to-world conversion, hit-testing\n        └── physics.rs   # Background thread for force simulation\n```\n\n- - -\n\n## Performance\n\n- Simulation runs at ~60fps (default `thread_sleep_ms = 16`)\n- Simulation auto-stops when total energy drops below `0.05 * node_count`\n- Files are sorted alphabetically by path before graph construction\n- Max zoom: ~20x initial; Min zoom: 0.01 (hardcoded floor)\n","graf 是一个基于终端的图形化工具，用于可视化包含维基链接的 Markdown 文件网络。它使用 Rust 语言开发，支持自定义主题和热重载配置，允许用户在任何目录下运行 `graf` 命令后，以交互式、可缩放和平移的方式查看文件间的连接关系。该工具特别适用于需要整理和理解大量相互引用的 Markdown 文档集的场景，如个人知识库或项目文档管理。目前，主要功能已经实现，正处于测试和完善阶段。",2,"2026-06-11 04:01:10","CREATED_QUERY"]