diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 8b824ab..67018c5 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "understand-anything", "description": "AI-powered codebase understanding — analyze, visualize, and explain any project", - "version": "2.6.3", + "version": "2.7.3", "author": { "name": "Lum1104" }, diff --git a/.copilot-plugin/plugin.json b/.copilot-plugin/plugin.json index 34ee7e2..5c2fa9c 100644 --- a/.copilot-plugin/plugin.json +++ b/.copilot-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "understand-anything", "description": "AI-powered codebase understanding — analyze, visualize, and explain any project", - "version": "2.6.3", + "version": "2.7.3", "author": { "name": "Lum1104" }, diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 11662ee..8f9f665 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "understand-anything", "displayName": "Understand Anything", "description": "AI-powered codebase understanding — analyze, visualize, and explain any project", - "version": "2.6.3", + "version": "2.7.3", "author": { "name": "Lum1104" }, diff --git a/README.md b/README.md index 712f311..469b5eb 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,7 @@

- English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe -

- -

- - - - - Star History Rank - - + English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский

@@ -46,14 +36,11 @@ --- -> [!TIP] -> **A huge thank you to the community!** The support for Understand-Anything has been incredible. If this tool saves you a few minutes of digging through complexity, that's all I wanted. 🚀 - **You just joined a new team. The codebase is 200,000 lines of code. Where do you even start?** Understand Anything is a [Claude Code Plugin](https://code.claude.com/docs/en/plugins-reference#plugins-reference) that analyzes your project with a multi-agent pipeline, builds a knowledge graph of every file, function, class, and dependency, then gives you an interactive dashboard to explore it all visually. Stop reading code blind. Start seeing the big picture. -> **Graphs that teach > graphs that impress.** +> **The goal isn't a graph that wows you with how complex your codebase is — it's a graph that quietly teaches you how every piece fits together.** --- @@ -66,18 +53,10 @@ Understand Anything is a [Claude Code Plugin](https://code.claude.com/docs/en/pl Navigate your codebase as an interactive knowledge graph — every file, function, and class is a node you can click, search, and explore. Select any node to see plain-English summaries, relationships, and guided tours. -

- Structural graph — explore files, functions, classes and their relationships -

- ### Understand business logic Switch to the domain view and see how your code maps to real business processes — domains, flows, and steps laid out as a horizontal graph. -

- Domain graph — business domains, flows, and process steps -

- ### Analyze knowledge bases Point `/understand-knowledge` at a [Karpathy-pattern LLM wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) and get a force-directed knowledge graph with community clustering. The deterministic parser extracts wikilinks and categories from `index.md`, then LLM agents discover implicit relationships, extract entities, and surface claims — turning your wiki into a navigable graph of interconnected ideas. @@ -134,6 +113,20 @@ Point `/understand-knowledge` at a [Karpathy-pattern LLM wiki](https://gist.gith A multi-agent pipeline scans your project, extracts every file, function, class, and dependency, then builds a knowledge graph saved to `.understand-anything/knowledge-graph.json`. +**Localized output:** Use `--language` to generate content in your preferred language: + +```bash +# Generate Chinese content (知识图节点描述和 Dashboard UI) +/understand --language zh + +# Supported languages: en (default), zh, zh-TW, ja, ko, ru +``` + +The `--language` parameter affects: +- Node summaries and descriptions in the knowledge graph +- Dashboard UI labels, buttons, and tooltips +- Guided tour explanations + ### 3. Explore the dashboard ```bash @@ -177,7 +170,7 @@ Understand-Anything works across multiple AI coding platforms. /plugin install understand-anything ``` -### One-line install (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot) +### One-line install (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) **macOS / Linux:** ```bash @@ -193,7 +186,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins The installer clones the repo to `~/.understand-anything/repo` and creates the right symlinks for the chosen platform. Restart your CLI/IDE afterwards. -- Supported `` values: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode` +- Supported `` values: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode`, `hermes`, `cline`, `kimi` - Update later: `./install.sh --update` - Uninstall: `./install.sh --uninstall ` @@ -228,6 +221,9 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin | Gemini CLI | ✅ Supported | `install.sh gemini` | | Pi Agent | ✅ Supported | `install.sh pi` | | Vibe CLI | ✅ Supported | `install.sh vibe` | +| Hermes | ✅ Supported | `install.sh hermes` | +| Cline | ✅ Supported | `install.sh cline` | +| KIMI CLI | ✅ Supported | `install.sh kimi` | --- @@ -303,6 +299,10 @@ Please open an issue first for major changes so we can discuss the approach. +

+ Thanks to everyone who's used and contributed — knowing this saves people time is what made it worth building. +

+

MIT License © Lum1104

diff --git a/READMEs/README.es-ES.md b/READMEs/README.es-ES.md index 7ad9b44..536a14f 100644 --- a/READMEs/README.es-ES.md +++ b/READMEs/README.es-ES.md @@ -6,17 +6,7 @@

- English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe -

- -

- - - - - Star History Rank - - + English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский

@@ -44,14 +34,11 @@ --- -> [!TIP] -> **¡Un enorme agradecimiento a la comunidad!** El apoyo a Understand-Anything ha sido increíble. Si esta herramienta te ahorra unos minutos de buscar entre la complejidad, eso es todo lo que quería. 🚀 - **Acabas de unirte a un nuevo equipo. El código tiene 200,000 líneas. ¿Por dónde empiezas?** Understand Anything es un [Claude Code Plugin](https://code.claude.com/docs/en/plugins-reference#plugins-reference) que analiza tu proyecto con un pipeline multi-agente, construye un grafo de conocimiento de cada archivo, función, clase y dependencia, y luego te ofrece un panel interactivo para explorarlo visualmente. Deja de leer código a ciegas. Empieza a ver el panorama completo. -> **Grafos que enseñan > grafos que solo presumen.** +> **El objetivo no es un grafo que te impresione mostrándote lo complejo que es tu código — es un grafo que, sin alardes, te enseña cómo encaja cada pieza.** --- @@ -64,18 +51,10 @@ Understand Anything es un [Claude Code Plugin](https://code.claude.com/docs/en/p Navega tu código como un grafo de conocimiento interactivo: cada archivo, función y clase es un nodo que puedes hacer clic, buscar y explorar. Selecciona cualquier nodo para ver resúmenes en lenguaje natural, relaciones y recorridos guiados. -

- Grafo estructural — explora archivos, funciones, clases y sus relaciones -

- ### Comprende la lógica de negocio Cambia a la vista de dominio y observa cómo tu código se mapea a procesos de negocio reales: dominios, flujos y pasos representados como un grafo horizontal. -

- Grafo de dominio — dominios de negocio, flujos y pasos de proceso -

- ### Analiza bases de conocimiento Apunta `/understand-knowledge` a un [wiki LLM con patrón Karpathy](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) y obtén un grafo de conocimiento dirigido por fuerzas con agrupación por comunidad. El parser determinístico extrae wikilinks y categorías de `index.md`, luego los agentes LLM descubren relaciones implícitas, extraen entidades y revelan afirmaciones, convirtiendo tu wiki en un grafo navegable de ideas interconectadas. @@ -132,6 +111,20 @@ Apunta `/understand-knowledge` a un [wiki LLM con patrón Karpathy](https://gist Un pipeline multi-agente escanea tu proyecto, extrae cada archivo, función, clase y dependencia, y construye un grafo de conocimiento guardado en `.understand-anything/knowledge-graph.json`. +**Salida localizada:** Usa `--language` para generar contenido en tu idioma preferido: + +```bash +# Genera contenido en el idioma preferido (descripciones de nodos y UI del dashboard) +/understand --language en + +# Idiomas soportados: en (default), zh, zh-TW, ja, ko, ru +``` + +El parámetro `--language` afecta: +- Resúmenes y descripciones de nodos en el grafo de conocimiento +- Etiquetas, botones y tooltips de la UI del dashboard +- Explicaciones de los tours guiados + ### 3. Explora el panel ```bash @@ -175,7 +168,7 @@ Understand-Anything funciona en múltiples plataformas de codificación con IA. /plugin install understand-anything ``` -### Instalación de una línea (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / VS Code Copilot) +### Instalación de una línea (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) **macOS / Linux:** ```bash @@ -191,7 +184,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins El instalador clona el repositorio en `~/.understand-anything/repo` y crea los enlaces simbólicos correspondientes para la plataforma elegida. Reinicia tu CLI/IDE al terminar. -- Valores soportados de ``: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vscode` +- Valores soportados de ``: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode`, `hermes`, `cline`, `kimi` - Actualizar más adelante: `./install.sh --update` - Desinstalar: `./install.sh --uninstall ` @@ -225,6 +218,10 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin | Antigravity | ✅ Soportado | `install.sh antigravity` | | Gemini CLI | ✅ Soportado | `install.sh gemini` | | Pi Agent | ✅ Soportado | `install.sh pi` | +| Vibe CLI | ✅ Soportado | `install.sh vibe` | +| Hermes | ✅ Soportado | `install.sh hermes` | +| Cline | ✅ Soportado | `install.sh cline` | +| KIMI CLI | ✅ Soportado | `install.sh kimi` | --- @@ -300,6 +297,10 @@ Para cambios importantes, abre primero un issue para que podamos discutir el enf +

+ Gracias a todas las personas que lo han usado y han contribuido — saber que les ahorra tiempo es lo que hizo que valiera la pena construirlo. +

+

Licencia MIT © Lum1104

diff --git a/READMEs/README.ja-JP.md b/READMEs/README.ja-JP.md index fed875b..d981250 100644 --- a/READMEs/README.ja-JP.md +++ b/READMEs/README.ja-JP.md @@ -7,17 +7,7 @@

- English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe -

- -

- - - - - Star History Rank - - + English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский

@@ -45,14 +35,11 @@ --- -> [!TIP] -> **コミュニティの皆さんに感謝!** Understand-Anythingへのサポートは本当に素晴らしいものです。このツールが複雑なコードを理解する時間を少しでも短縮できたなら、それが私の望みです。🚀 - **新しいチームに参加したばかり。コードベースは20万行。どこから手をつければいいのか?** Understand Anything は [Claude Code Plugin](https://code.claude.com/docs/en/plugins-reference#plugins-reference) です。マルチエージェントパイプラインでプロジェクトを分析し、すべてのファイル・関数・クラス・依存関係のナレッジグラフを構築して、インタラクティブなダッシュボードで視覚的に探索できるようにします。コードを闇雲に読むのはやめて、全体像を把握しましょう。 -> **寓教於楽のグラフ > 見栄えだけのグラフ。** +> **目指すのは、コードベースの複雑さで圧倒するグラフではなく、すべてのパーツがどう噛み合っているかを静かに教えてくれるグラフ。** --- @@ -65,18 +52,10 @@ Understand Anything は [Claude Code Plugin](https://code.claude.com/docs/en/plu コードベースをインタラクティブなナレッジグラフとして表示——すべてのファイル、関数、クラスがクリック・検索・探索可能なノードです。ノードを選択すると、わかりやすい要約、依存関係、ガイド付きツアーが表示されます。 -

- 構造グラフ——ファイル、関数、クラスとその関係を探索 -

- ### ビジネスロジックを理解 ドメインビューに切り替えると、コードが実際のビジネスプロセスにどう対応するかが一目でわかります——ドメイン、フロー、ステップが横方向のグラフとして表示されます。 -

- ドメイングラフ——ビジネスドメイン、フロー、プロセスステップ -

- ### ナレッジベースを分析 `/understand-knowledge` を [Karpathy パターンの LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) に向けると、コミュニティクラスタリング付きのフォースディレクテッドナレッジグラフが生成されます。決定論的パーサーが `index.md` から wikilinks とカテゴリを抽出し、LLM エージェントが暗黙の関係を発見、エンティティを抽出、主張を浮き彫りにして、wiki をナビゲート可能な相互接続されたアイデアのグラフに変換します。 @@ -133,6 +112,20 @@ Understand Anything は [Claude Code Plugin](https://code.claude.com/docs/en/plu マルチエージェントパイプラインがプロジェクトをスキャンし、すべてのファイル・関数・クラス・依存関係を抽出して、`.understand-anything/knowledge-graph.json` にナレッジグラフを保存します。 +**ローカライズされた出力:** `--language` を使用して、希望の言語でコンテンツを生成: + +```bash +# 日本語でコンテンツを生成(ナレッジグラフのノード説明とダッシュボードUI) +/understand --language ja + +# サポート言語:en(デフォルト)、zh、zh-TW、ja、ko、ru +``` + +`--language` パラメータは以下に影響します: +- ナレッジグラフのノードサマリーと説明 +- ダッシュボードUIのラベル、ボタン、ツールチップ +- ガイド付きツアーの説明 + ### 3. ダッシュボードで探索 ```bash @@ -176,7 +169,7 @@ Understand-Anythingは複数のAIコーディングプラットフォームで /plugin install understand-anything ``` -### ワンラインインストール(Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / VS Code Copilot) +### ワンラインインストール(Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) **macOS / Linux:** ```bash @@ -192,7 +185,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins インストーラーはリポジトリを `~/.understand-anything/repo` にクローンし、選択したプラットフォーム用のシンボリックリンクを作成します。完了後はCLI/IDEを再起動してください。 -- サポートされる `` 値:`gemini`、`codex`、`opencode`、`pi`、`openclaw`、`antigravity`、`vscode` +- サポートされる `` 値:`gemini`、`codex`、`opencode`、`pi`、`openclaw`、`antigravity`、`vibe`、`vscode`、`hermes`、`cline`、`kimi` - 後で更新:`./install.sh --update` - アンインストール:`./install.sh --uninstall ` @@ -226,6 +219,10 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin | Antigravity | ✅ サポート | `install.sh antigravity` | | Gemini CLI | ✅ サポート | `install.sh gemini` | | Pi Agent | ✅ サポート | `install.sh pi` | +| Vibe CLI | ✅ サポート | `install.sh vibe` | +| Hermes | ✅ サポート | `install.sh hermes` | +| Cline | ✅ サポート | `install.sh cline` | +| KIMI CLI | ✅ サポート | `install.sh kimi` | --- @@ -301,6 +298,10 @@ git add .gitattributes .understand-anything/ +

+ 使ってくれた、貢献してくれたすべての方へ ── 少しでも時間を節約できていると知ること、それがこれを作って良かったと思える理由です。 +

+

MIT License © Lum1104

diff --git a/READMEs/README.ko-KR.md b/READMEs/README.ko-KR.md index 6a8e182..986f8e6 100644 --- a/READMEs/README.ko-KR.md +++ b/READMEs/README.ko-KR.md @@ -6,17 +6,7 @@

- English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe -

- -

- - - - - Star History Rank - - + English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский

@@ -44,14 +34,11 @@ --- -> [!TIP] -> **커뮤니티의 엄청난 응원에 감사드립니다!** Understand-Anything에 대한 관심이 정말 놀라웠습니다. 이 도구가 복잡한 코드를 파악하는 시간을 단 몇 분이라도 줄여드린다면, 그것만으로도 충분합니다. 🚀 - **새 팀에 합류했습니다. 코드베이스가 20만 줄입니다. 어디서부터 시작하시겠습니까?** Understand Anything은 [Claude Code Plugin](https://code.claude.com/docs/en/plugins-reference#plugins-reference)으로, 멀티 에이전트 파이프라인을 통해 프로젝트를 분석하고, 모든 파일, 함수, 클래스, 의존성에 대한 지식 그래프를 구축한 뒤, 이를 시각적으로 탐색할 수 있는 인터랙티브 대시보드를 제공합니다. 더 이상 코드를 맹목적으로 읽지 마세요. 전체 그림을 파악하세요. -> **배움을 주는 그래프 > 과시만 하는 그래프.** +> **목표는 코드베이스의 복잡함으로 감탄을 자아내는 그래프가 아니라, 각 조각이 어떻게 맞물리는지 조용히 가르쳐주는 그래프입니다.** --- @@ -64,18 +51,10 @@ Understand Anything은 [Claude Code Plugin](https://code.claude.com/docs/en/plug 코드베이스를 인터랙티브 지식 그래프로 탐색하세요. 모든 파일, 함수, 클래스가 클릭, 검색, 탐색 가능한 노드입니다. 노드를 선택하면 이해하기 쉬운 요약, 관계, 가이드 투어를 확인할 수 있습니다. -

- 구조 그래프 — 파일, 함수, 클래스 및 관계 탐색 -

- ### 비즈니스 로직 이해 도메인 뷰로 전환하면 코드가 실제 비즈니스 프로세스에 어떻게 매핑되는지 확인할 수 있습니다. 도메인, 흐름, 단계가 수평 그래프로 표시됩니다. -

- 도메인 그래프 — 비즈니스 도메인, 흐름 및 프로세스 단계 -

- ### 지식 베이스 분석 `/understand-knowledge`를 [Karpathy 패턴 LLM 위키](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f)에 연결하면 커뮤니티 클러스터링이 적용된 힘 기반 지식 그래프를 생성합니다. 결정론적 파서가 `index.md`에서 위키링크와 카테고리를 추출한 후, LLM 에이전트가 암묵적 관계를 발견하고, 엔티티를 추출하며, 주장을 도출하여 위키를 탐색 가능한 상호 연결된 아이디어 그래프로 변환합니다. @@ -132,6 +111,20 @@ Understand Anything은 [Claude Code Plugin](https://code.claude.com/docs/en/plug 멀티 에이전트 파이프라인이 프로젝트를 스캔하고, 모든 파일, 함수, 클래스, 의존성을 추출한 뒤, `.understand-anything/knowledge-graph.json`에 지식 그래프를 저장합니다. +**로컬라이즈된 출력:** `--language`를 사용하여 원하는 언어로 내용을 생성: + +```bash +# 한국어로 내용 생성 (지식 그래프 노드 설명과 대시보드 UI) +/understand --language ko + +# 지원 언어: en(기본값), zh, zh-TW, ja, ko, ru +``` + +`--language` 매개변수는 다음에 영향합니다: +- 지식 그래프의 노드 요약과 설명 +- 대시보드 UI의 레이블, 버튼, 툴팁 +- 가이드 투어의 설명 + ### 3. 대시보드 탐색 ```bash @@ -175,7 +168,7 @@ Understand-Anything은 다양한 AI 코딩 플랫폼에서 사용할 수 있습 /plugin install understand-anything ``` -### 한 줄 설치 (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / VS Code Copilot) +### 한 줄 설치 (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) **macOS / Linux:** ```bash @@ -191,7 +184,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins 설치 스크립트는 저장소를 `~/.understand-anything/repo`에 클론하고 선택한 플랫폼에 맞는 심볼릭 링크를 생성합니다. 설치 후 CLI 또는 IDE를 재시작하세요. -- 지원되는 `` 값: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vscode` +- 지원되는 `` 값: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode`, `hermes`, `cline`, `kimi` - 이후 업데이트: `./install.sh --update` - 제거: `./install.sh --uninstall ` @@ -225,6 +218,10 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin | Antigravity | ✅ 지원 | `install.sh antigravity` | | Gemini CLI | ✅ 지원 | `install.sh gemini` | | Pi Agent | ✅ 지원 | `install.sh pi` | +| Vibe CLI | ✅ 지원 | `install.sh vibe` | +| Hermes | ✅ 지원 | `install.sh hermes` | +| Cline | ✅ 지원 | `install.sh cline` | +| KIMI CLI | ✅ 지원 | `install.sh kimi` | --- @@ -300,6 +297,10 @@ git add .gitattributes .understand-anything/ +

+ 이 도구를 사용해 주시고 기여해 주신 모든 분들께 감사드립니다 — 누군가의 시간을 아껴드리고 있다는 사실이, 이걸 만들 가치가 있게 만든 이유였습니다. +

+

MIT 라이선스 © Lum1104

diff --git a/READMEs/README.ru-RU.md b/READMEs/README.ru-RU.md new file mode 100644 index 0000000..ad8894a --- /dev/null +++ b/READMEs/README.ru-RU.md @@ -0,0 +1,307 @@ +

Understand Anything

+ +

+ Превращай любую кодовую базу, базу знаний или документацию в интерактивный граф знаний, который можно исследовать, искать в нём и задавать вопросы. +
+ Работает с Claude Code, Codex, Cursor, Copilot, Gemini CLI и другими. +

+ +

+ English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский +

+ +

+ Quick Start + License: MIT + Claude Code + Codex + Copilot + Copilot CLI + Gemini CLI + OpenCode + Homepage + Live Demo +

+ +

+ Understand Anything — Превратите любую кодовую базу в интерактивный граф знаний +

+ +

+ 💬 Присоединяйтесь к сообществу в Discord → +
+ Задавайте вопросы, делитесь тем, что вы построили, получайте помощь от сообщества. +

+ +--- + +**Вы только что присоединились к новой команде. Кодовая база — 200 000 строк. С чего вообще начинать?** + +Understand Anything — это [плагин для Claude Code](https://code.claude.com/docs/en/plugins-reference#plugins-reference), который анализирует ваш проект с помощью мультиагентного пайплайна, строит граф знаний из всех файлов, функций, классов и зависимостей, а затем предоставляет интерактивную панель, чтобы исследовать всё это визуально. Хватит читать код вслепую. Пора увидеть общую картину. + +> **Цель — не граф, который поражает сложностью вашей кодовой базы, а граф, который ненавязчиво объясняет, как все части складываются вместе.** + +--- + +## ✨ Возможности + +> [!NOTE] +> **Хотите пропустить чтение?** Попробуйте [живое демо](https://understand-anything.com/demo/) на нашем [сайте](https://understand-anything.com/) — полностью интерактивная панель, по которой можно перемещаться, масштабировать, искать и исследовать прямо в браузере. + +### Исследуйте структурный граф + +Перемещайтесь по своему коду как по интерактивному графу знаний — каждый файл, функция и класс является узлом, который можно кликнуть, найти и изучить. Выберите любой узел, чтобы увидеть понятные описания, связи и пошаговые обзоры. + +### Понимайте бизнес-логику + +Переключитесь на доменное представление и увидите, как ваш код отображается на реальные бизнес-процессы — домены, потоки и шаги, выстроенные в виде горизонтального графа. + +### Анализируйте базы знаний + +Направьте `/understand-knowledge` на [LLM-вики в стиле Карпати](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) и получите force-directed граф знаний с кластеризацией по сообществам. Детерминированный парсер извлекает wikilinks и категории из `index.md`, а LLM-агенты находят неявные связи, извлекают сущности и выявляют утверждения — превращая вашу вики в навигируемый граф взаимосвязанных идей. + + + + + + + + + + + + + + +
+

🧭 Пошаговые обзоры

+

Автоматически создаваемые экскурсии по архитектуре, упорядоченные по зависимостям. Изучайте кодовую базу в правильном порядке.

+
+

🔍 Нечёткий и семантический поиск

+

Находите что угодно по имени или по смыслу. Поищите «какие части отвечают за авторизацию?» и получите релевантные результаты по всему графу.

+
+

📊 Анализ влияния изменений

+

Смотрите, какие части системы затрагивают ваши изменения, ещё до коммита. Понимайте каскадные эффекты по всей кодовой базе.

+
+

🎭 UI, адаптирующийся к роли

+

Панель подстраивает уровень детализации под пользователя — junior-разработчика, PM или продвинутого пользователя.

+
+

🏗️ Визуализация слоёв

+

Автоматическая группировка по архитектурным слоям — API, Service, Data, UI, Utility — с цветовой легендой.

+
+

📚 Концепции языка

+

12 шаблонов программирования (дженерики, замыкания, декораторы и т.д.) объясняются в контексте там, где они встречаются.

+
+ +--- + +## 🚀 Быстрый старт + +### 1. Установите плагин + +```bash +/plugin marketplace add Lum1104/Understand-Anything +/plugin install understand-anything +``` + +### 2. Проанализируйте кодовую базу + +```bash +/understand +``` + +Мультиагентный пайплайн сканирует ваш проект, извлекает каждый файл, функцию, класс и зависимость, а затем строит граф знаний и сохраняет его в `.understand-anything/knowledge-graph.json`. + +**Локализованный вывод:** используйте `--language`, чтобы генерировать контент на нужном языке: + +```bash +# Генерация контента на русском (описания узлов графа знаний и UI панели) +/understand --language ru + +# Поддерживаемые языки: en (по умолчанию), zh, zh-TW, ja, ko, ru +``` + +Параметр `--language` влияет на: +- Резюме и описания узлов в графе знаний +- Подписи, кнопки и подсказки UI панели +- Объяснения в пошаговых обзорах + +### 3. Откройте панель + +```bash +/understand-dashboard +``` + +Открывается интерактивная веб-панель с визуализацией вашей кодовой базы в виде графа — с цветовой кодировкой по архитектурным слоям, поиском и кликабельными узлами. Выберите любой узел, чтобы увидеть его код, связи и описание простым языком. + +### 4. Продолжайте учиться + +```bash +# Задайте любой вопрос о кодовой базе +/understand-chat How does the payment flow work? + +# Проанализируйте влияние ваших текущих изменений +/understand-diff + +# Подробно разберитесь с конкретным файлом или функцией +/understand-explain src/auth/login.ts + +# Сгенерируйте онбординг-гайд для новых членов команды +/understand-onboard + +# Извлеките знания о бизнес-доменах (домены, потоки, шаги) +/understand-domain + +# Проанализируйте LLM-вики в стиле Карпати +/understand-knowledge ~/path/to/wiki +``` + +--- + +## 🌐 Установка на разных платформах + +Understand-Anything работает с несколькими платформами AI-разработки. + +### Claude Code (нативно) + +```bash +/plugin marketplace add Lum1104/Understand-Anything +/plugin install understand-anything +``` + +### Установка одной командой (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) + +**macOS / Linux:** +```bash +curl -fsSL https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/install.sh | bash +# или передайте платформу, чтобы пропустить интерактивный выбор: +curl -fsSL https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/install.sh | bash -s codex +``` + +**Windows (PowerShell):** +```powershell +iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/install.ps1 | iex +``` + +Установщик клонирует репозиторий в `~/.understand-anything/repo` и создаёт нужные симлинки для выбранной платформы. После установки перезапустите свой CLI/IDE. + +- Поддерживаемые значения ``: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode`, `hermes`, `cline`, `kimi` +- Обновление: `./install.sh --update` +- Удаление: `./install.sh --uninstall ` + +### Cursor + +Cursor автоматически обнаруживает плагин через `.cursor-plugin/plugin.json` при клонировании этого репозитория. Ручная установка не требуется — просто склонируйте и откройте в Cursor. + +### VS Code + GitHub Copilot + +VS Code с GitHub Copilot (v1.108+) автоматически обнаруживает плагин через `.copilot-plugin/plugin.json` при клонировании этого репозитория. Ручная установка не требуется — просто склонируйте и откройте в VS Code. + +Для персональных skills (доступных во всех проектах) запустите `install.sh` выше с платформой `vscode`. + +### Copilot CLI + +```bash +copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin +``` + +### Совместимость с платформами + +| Платформа | Статус | Способ установки | +|----------|--------|----------------| +| Claude Code | ✅ Нативно | Marketplace плагинов | +| Cursor | ✅ Поддерживается | Автообнаружение | +| VS Code + GitHub Copilot | ✅ Поддерживается | Автообнаружение | +| Copilot CLI | ✅ Поддерживается | Установка плагина | +| Codex | ✅ Поддерживается | `install.sh codex` | +| OpenCode | ✅ Поддерживается | `install.sh opencode` | +| OpenClaw | ✅ Поддерживается | `install.sh openclaw` | +| Antigravity | ✅ Поддерживается | `install.sh antigravity` | +| Gemini CLI | ✅ Поддерживается | `install.sh gemini` | +| Pi Agent | ✅ Поддерживается | `install.sh pi` | +| Vibe CLI | ✅ Поддерживается | `install.sh vibe` | +| Hermes | ✅ Поддерживается | `install.sh hermes` | +| Cline | ✅ Поддерживается | `install.sh cline` | +| KIMI CLI | ✅ Поддерживается | `install.sh kimi` | + +--- + +## 📦 Поделитесь графом с командой + +Граф — это просто JSON. **Зафиксируйте его один раз, и коллеги смогут пропустить весь пайплайн.** Полезно для онбординга, ревью PR и подхода docs-as-code. + +> **Пример:** [GoogleCloudPlatform/microservices-demo (форк)](https://github.com/Lum1104/microservices-demo) — мультиязыковой проект (Go / Java / Python / Node) с уже зафиксированным графом. + +**Что коммитить:** всё содержимое `.understand-anything/`, *кроме* `intermediate/` и `diff-overlay.json` (это локальные временные файлы). + +```gitignore +.understand-anything/intermediate/ +.understand-anything/diff-overlay.json +``` + +**Держите граф в актуальном состоянии:** включите `/understand --auto-update` — post-commit хук будет инкрементально обновлять граф, так что каждый коммит сопровождается соответствующим графом. Либо запускайте `/understand` вручную перед релизами. + +**Большие графы (10 МБ+):** храните через **git-lfs**. + +```bash +git lfs install +git lfs track ".understand-anything/*.json" +git add .gitattributes .understand-anything/ +``` + +--- + +## 🔧 Под капотом + +### Мультиагентный пайплайн + +Команда `/understand` оркестрирует 5 специализированных агентов, а `/understand-domain` добавляет шестого: + +| Агент | Роль | +|-------|------| +| `project-scanner` | Обнаружение файлов, определение языков и фреймворков | +| `file-analyzer` | Извлечение функций, классов, импортов; создание узлов и рёбер графа | +| `architecture-analyzer` | Определение архитектурных слоёв | +| `tour-builder` | Генерация пошаговых обучающих обзоров | +| `graph-reviewer` | Проверка полноты и целостности ссылок графа (по умолчанию выполняется inline; используйте `--review` для полного ревью с участием LLM) | +| `domain-analyzer` | Извлечение бизнес-доменов, потоков и шагов процессов (используется командой `/understand-domain`) | +| `article-analyzer` | Извлечение сущностей, утверждений и неявных связей из статей вики (используется командой `/understand-knowledge`) | + +Анализаторы файлов работают параллельно (до 5 одновременно, 20–30 файлов на батч). Поддерживаются инкрементальные обновления — повторно анализируются только файлы, изменившиеся с прошлого запуска. + +--- + +## 🤝 Вклад в проект + +Будем рады вашим контрибьюшенам! Как начать: + +1. Сделайте форк репозитория +2. Создайте ветку для фичи (`git checkout -b feature/my-feature`) +3. Запустите тесты (`pnpm --filter @understand-anything/core test`) +4. Закоммитьте изменения и откройте pull request + +Для крупных изменений сначала откройте issue, чтобы можно было обсудить подход. + +--- + +

+ Хватит читать код вслепую. Начните понимать всё. +

+ +## История звёзд + + + + + + Star History Chart + + + +

+ Спасибо всем, кто пользовался проектом и вкладывался в него — знание того, что это экономит людям время, и было главной причиной, ради которой стоило его делать. +

+ +

+ Лицензия MIT © Lum1104 +

diff --git a/READMEs/README.tr-TR.md b/READMEs/README.tr-TR.md index 8517ebc..8e1c928 100644 --- a/READMEs/README.tr-TR.md +++ b/READMEs/README.tr-TR.md @@ -7,17 +7,7 @@

- English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe -

- -

- - - - - Star History Rank - - + English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский

@@ -45,14 +35,11 @@ --- -> [!TIP] -> **Topluluğa çok teşekkürler!** Understand-Anything'e gösterilen destek inanılmaz oldu. Bu araç sana karmaşıklığı anlamak için birkaç dakika kazandırıyorsa, istediğim tek şey buydu. 🚀 - **Yeni bir ekibe katıldın. Kod tabanı 200.000 satır kod. Nereden başlayacaksın bile bilemiyorsun?** Understand Anything, projenizi çok-ajan hattıyla analiz eden, her dosya, fonksiyon, sınıf ve bağımlılığın bilgi grafiğini oluşturan ve hepsini görsel olarak keşfetmen için interaktif bir kontrol paneli sunan bir [Claude Code Plugin](https://code.claude.com/docs/en/plugins-reference#plugins-reference)'dir. Kodu körü körüne okumayı bırak. Büyük resmi görmeye başla. -> **Öğreten grafikler > sadece gösteriş yapan grafikler.** +> **Amaç, kod tabanının ne kadar karmaşık olduğunu görkemle gösteren bir grafik değil — her parçanın nasıl birbirine geçtiğini sessizce öğreten bir grafik.** --- @@ -65,18 +52,10 @@ Understand Anything, projenizi çok-ajan hattıyla analiz eden, her dosya, fonks Kod tabanınızı interaktif bir bilgi grafiği olarak görüntüleyin — her dosya, fonksiyon ve sınıf tıklanabilir, aranabilir ve keşfedilebilir bir düğümdür. Herhangi bir düğümü seçerek anlaşılır özetleri, bağımlılıkları ve rehberli turları görün. -

- Yapısal grafik — dosyaları, fonksiyonları, sınıfları ve ilişkilerini keşfedin -

- ### İş mantığını anlayın Alan görünümüne geçin ve kodunuzun gerçek iş süreçleriyle nasıl eşleştiğini görün — alanlar, akışlar ve adımlar yatay bir grafik olarak sunulur. -

- Alan grafiği — iş alanları, akışlar ve süreç adımları -

- ### Bilgi tabanlarını analiz et `/understand-knowledge` komutunu bir [Karpathy deseni LLM Wiki'sine](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) yönlendirin ve topluluk kümeleme ile kuvvet yönelimli bir bilgi grafiği elde edin. Deterministik ayrıştırıcı `index.md`'den wikilinkleri ve kategorileri çıkarır, ardından LLM ajanları örtük ilişkileri keşfeder, varlıkları çıkarır ve iddiaları ortaya çıkarır — wiki'nizi gezinilebilir, birbirine bağlı fikirler grafiğine dönüştürür. @@ -133,6 +112,20 @@ Alan görünümüne geçin ve kodunuzun gerçek iş süreçleriyle nasıl eşle Çok-ajan hattı projenizi tarar, her dosya, fonksiyon, sınıf ve bağımlılığı çıkarır, ardından `.understand-anything/knowledge-graph.json` dosyasına kaydedilen bir bilgi grafiği oluşturur. +**Yerelleştirilmiş çıktı:** İstediğiniz dilde içerik oluşturmak için `--language` kullanın: + +```bash +# İstediğiniz dilde içerik oluştur (düğüm açıklamaları ve dashboard UI) +/understand --language en + +# Desteklenen diller: en (varsayılan), zh, zh-TW, ja, ko, ru +``` + +`--language` parametresi şunları etkiler: +- Bilgi grafiğindeki düğüm özetleri ve açıklamalar +- Dashboard UI etiketleri, butonlar ve araç ipuçları +- Rehberli tur açıklamaları + ### 3. Kontrol panelini keşfet ```bash @@ -176,7 +169,7 @@ Understand-Anything birden fazla AI kodlama platformunda çalışır. /plugin install understand-anything ``` -### Tek satırlık kurulum (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / VS Code Copilot) +### Tek satırlık kurulum (Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) **macOS / Linux:** ```bash @@ -192,7 +185,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins Kurulum betiği depoyu `~/.understand-anything/repo` dizinine klonlar ve seçilen platform için uygun sembolik bağlantıları oluşturur. Sonrasında CLI/IDE'ni yeniden başlat. -- Desteklenen `` değerleri: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vscode` +- Desteklenen `` değerleri: `gemini`, `codex`, `opencode`, `pi`, `openclaw`, `antigravity`, `vibe`, `vscode`, `hermes`, `cline`, `kimi` - Daha sonra güncelle: `./install.sh --update` - Kaldır: `./install.sh --uninstall ` @@ -226,6 +219,10 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin | Antigravity | ✅ Destekleniyor | `install.sh antigravity` | | Gemini CLI | ✅ Destekleniyor | `install.sh gemini` | | Pi Agent | ✅ Destekleniyor | `install.sh pi` | +| Vibe CLI | ✅ Destekleniyor | `install.sh vibe` | +| Hermes | ✅ Destekleniyor | `install.sh hermes` | +| Cline | ✅ Destekleniyor | `install.sh cline` | +| KIMI CLI | ✅ Destekleniyor | `install.sh kimi` | --- @@ -301,6 +298,10 @@ Büyük değişiklikler için lütfen önce bir issue aç ki yaklaşımı tartı +

+ Kullanan ve katkıda bulunan herkese teşekkürler — bunun insanlara zaman kazandırdığını bilmek, yapmaya değer kılan tek şeydi. +

+

MIT Lisansı © Lum1104

diff --git a/READMEs/README.zh-CN.md b/READMEs/README.zh-CN.md index a612932..11f670f 100644 --- a/READMEs/README.zh-CN.md +++ b/READMEs/README.zh-CN.md @@ -6,17 +6,7 @@

- English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe -

- -

- - - - - Star History Rank - - + English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский

@@ -44,14 +34,11 @@ --- -> [!TIP] -> **衷心感谢社区的支持!** Understand-Anything 收到的关注超出了我的预期。如果这个工具能帮你从复杂的资料中理出一点头绪,少走几分钟弯路,那我开发它的初衷就达到了。🚀 - **当你刚加入一个新团队,面对 20 万行代码,你从哪里开始?** Understand Anything 是一个 [Claude Code Plugin](https://code.claude.com/docs/en/plugins-reference#plugins-reference),通过多智能体(multi-agent)架构分析你的项目,构建包含文件、函数、类以及依赖关系的知识图谱,并提供一个可视化交互界面,帮助你理解整个系统。不再”盲读代码”,而是从全局视角理解系统结构。 -> **寓教于乐的图表 > 仅为炫技的图表。** +> **目标不是用代码库的复杂程度来惊艳你 —— 而是默默告诉你每一块是怎么拼在一起的。** --- @@ -64,18 +51,10 @@ Understand Anything 是一个 [Claude Code Plugin](https://code.claude.com/docs/ 将你的代码库以交互式知识图谱的形式呈现——每个文件、函数和类都是可点击、可搜索、可探索的节点。选择任意节点即可查看通俗易懂的摘要、依赖关系和引导式学习路径。 -

- 结构图——探索文件、函数、类及其关系 -

- ### 理解业务逻辑 切换到领域视图,查看代码如何映射到真实的业务流程——以水平图的形式展示领域、流程和步骤。 -

- 领域图——业务领域、流程和处理步骤 -

- ### 分析知识库 将 `/understand-knowledge` 指向一个 [Karpathy 模式的 LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f),即可获得带有社区聚类的力导向知识图谱。确定性解析器从 `index.md` 中提取 wikilinks 和分类,然后 LLM 代理发现隐式关系、提取实体并挖掘论断——将你的 wiki 转化为可导航的互联思想图谱。 @@ -132,6 +111,20 @@ Understand Anything 是一个 [Claude Code Plugin](https://code.claude.com/docs/ 多智能体(multi-agent)架构会:扫描你的项目,提取函数 / 类 / 依赖,构建知识图谱保存至`.understand-anything/knowledge-graph.json`. +**本地化输出:** 使用 `--language` 参数生成中文内容: + +```bash +# 生成中文内容(知识图节点描述和 Dashboard UI) +/understand --language zh + +# 支持的语言:en(默认)、zh、zh-TW、ja、ko、ru +``` + +`--language` 参数会影响: +- 知识图谱中的节点摘要和描述 +- Dashboard UI 的标签、按钮和提示 +- 导览路线的解释说明 + ### 3. 打开数据看板 ```bash @@ -175,7 +168,7 @@ Understand-Anything 可在多个 AI 编码平台上运行。 /plugin install understand-anything ``` -### 一行命令安装(Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / VS Code Copilot) +### 一行命令安装(Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) **macOS / Linux:** ```bash @@ -191,7 +184,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins 安装脚本会将仓库克隆到 `~/.understand-anything/repo`,并为所选平台创建相应的符号链接。安装完成后请重启 CLI 或 IDE。 -- 支持的 `` 取值:`gemini`、`codex`、`opencode`、`pi`、`openclaw`、`antigravity`、`vscode` +- 支持的 `` 取值:`gemini`、`codex`、`opencode`、`pi`、`openclaw`、`antigravity`、`vibe`、`vscode`、`hermes`、`cline`、`kimi` - 后续更新:`./install.sh --update` - 卸载:`./install.sh --uninstall ` @@ -225,6 +218,10 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin | Antigravity | ✅ 支持 | `install.sh antigravity` | | Gemini CLI | ✅ 支持 | `install.sh gemini` | | Pi Agent | ✅ 支持 | `install.sh pi` | +| Vibe CLI | ✅ 支持 | `install.sh vibe` | +| Hermes | ✅ 支持 | `install.sh hermes` | +| Cline | ✅ 支持 | `install.sh cline` | +| KIMI CLI | ✅ 支持 | `install.sh kimi` | --- @@ -300,6 +297,10 @@ git add .gitattributes .understand-anything/ +

+ 感谢每一位使用过、贡献过的朋友 —— 知道它替你们省下了一些时间,就是当初做它最值得的理由。 +

+

MIT 许可证 © Lum1104

diff --git a/READMEs/README.zh-TW.md b/READMEs/README.zh-TW.md index b251c50..1fea5be 100644 --- a/READMEs/README.zh-TW.md +++ b/READMEs/README.zh-TW.md @@ -6,17 +6,7 @@

- English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe -

- -

- - - - - Star History Rank - - + English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Türkçe | Русский

@@ -44,14 +34,11 @@ --- -> [!TIP] -> **衷心感謝社群的支持!** Understand-Anything 收到的關注超出了我的預期。如果這個工具能幫你從複雜的資料中理出一點頭緒,少走幾分鐘彎路,那我開發它的初衷就達到了。🚀 - **當你剛加入一個新團隊,面對 20 萬行程式碼,你從哪裡開始?** Understand Anything 是一個 [Claude Code Plugin](https://code.claude.com/docs/en/plugins-reference#plugins-reference),透過多智能體(multi-agent)架構分析你的專案,建構包含檔案、函式、類別以及相依關係的知識圖譜,並提供一個視覺化互動介面,幫助你理解整個系統。不再「盲讀程式碼」,而是從全局視角理解系統結構。 -> **寓教於樂的圖表 > 僅為炫技的圖表。** +> **目標不是用程式碼庫的複雜程度驚豔你 —— 而是默默告訴你每一塊是怎麼拼在一起的。** --- @@ -64,18 +51,10 @@ Understand Anything 是一個 [Claude Code Plugin](https://code.claude.com/docs/ 將你的程式碼庫以互動式知識圖譜呈現——每個檔案、函式和類別都是可點擊、可搜尋、可探索的節點。選取任意節點即可檢視淺顯易懂的摘要、依賴關係和引導式學習路徑。 -

- 結構圖——探索檔案、函式、類別及其關係 -

- ### 理解業務邏輯 切換到領域視圖,查看程式碼如何對應到真實的業務流程——以水平圖的形式展示領域、流程和步驟。 -

- 領域圖——業務領域、流程和處理步驟 -

- ### 分析知識庫 將 `/understand-knowledge` 指向一個 [Karpathy 模式的 LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f),即可獲得帶有社群聚類的力導向知識圖譜。確定性解析器從 `index.md` 中提取 wikilinks 和分類,然後 LLM 代理發現隱式關係、提取實體並挖掘論斷——將你的 wiki 轉化為可導航的互聯思想圖譜。 @@ -132,6 +111,20 @@ Understand Anything 是一個 [Claude Code Plugin](https://code.claude.com/docs/ 多智能體(multi-agent)架構會:掃描你的專案,提取函式 / 類別 / 相依關係,建構知識圖譜並儲存至 `.understand-anything/knowledge-graph.json`。 +**在地化輸出:** 使用 `--language` 參數產生中文內容: + +```bash +# 產生繁體中文內容(知識圖節點描述和 Dashboard UI) +/understand --language zh-TW + +# 支援的語言:en(預設)、zh、zh-TW、ja、ko、ru +``` + +`--language` 參數會影響: +- 知識圖譜中的節點摘要和描述 +- Dashboard UI 的標籤、按鈕和提示 +-導覽路線的解釋說明 + ### 3. 開啟資料看板 ```bash @@ -175,7 +168,7 @@ Understand-Anything 可在多個 AI 編碼平台上執行。 /plugin install understand-anything ``` -### 一行指令安裝(Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / VS Code Copilot) +### 一行指令安裝(Codex / OpenCode / OpenClaw / Antigravity / Gemini CLI / Pi Agent / Vibe CLI / VS Code Copilot / Hermes / Cline / KIMI CLI) **macOS / Linux:** ```bash @@ -191,7 +184,7 @@ iwr -useb https://raw.githubusercontent.com/Lum1104/Understand-Anything/main/ins 安裝指令稿會將儲存庫複製到 `~/.understand-anything/repo`,並為所選平台建立相應的符號連結。安裝完成後請重新啟動 CLI 或 IDE。 -- 支援的 `` 取值:`gemini`、`codex`、`opencode`、`pi`、`openclaw`、`antigravity`、`vscode` +- 支援的 `` 取值:`gemini`、`codex`、`opencode`、`pi`、`openclaw`、`antigravity`、`vibe`、`vscode`、`hermes`、`cline`、`kimi` - 後續更新:`./install.sh --update` - 解除安裝:`./install.sh --uninstall ` @@ -225,6 +218,10 @@ copilot plugin install Lum1104/Understand-Anything:understand-anything-plugin | Antigravity | ✅ 支援 | `install.sh antigravity` | | Gemini CLI | ✅ 支援 | `install.sh gemini` | | Pi Agent | ✅ 支援 | `install.sh pi` | +| Vibe CLI | ✅ 支援 | `install.sh vibe` | +| Hermes | ✅ 支援 | `install.sh hermes` | +| Cline | ✅ 支援 | `install.sh cline` | +| KIMI CLI | ✅ 支援 | `install.sh kimi` | --- @@ -300,6 +297,10 @@ git add .gitattributes .understand-anything/ +

+ 感謝每一位使用過、貢獻過的朋友 —— 知道它替你們省下了一些時間,就是當初做它最值得的理由。 +

+

MIT 授權條款 © Lum1104

diff --git a/assets/overview-domain.gif b/assets/overview-domain.gif deleted file mode 100644 index 3efd90d..0000000 Binary files a/assets/overview-domain.gif and /dev/null differ diff --git a/assets/overview-structural.gif b/assets/overview-structural.gif deleted file mode 100644 index 6b5df3a..0000000 Binary files a/assets/overview-structural.gif and /dev/null differ diff --git a/homepage/src/components/Hero.astro b/homepage/src/components/Hero.astro index 3f54d1b..40dfc52 100644 --- a/homepage/src/components/Hero.astro +++ b/homepage/src/components/Hero.astro @@ -51,6 +51,13 @@ const discordUrl = 'https://discord.gg/pydat66RY'; + + Author + · + Lum1104 + + +
Subscribe to release updates @@ -333,6 +340,60 @@ const discordUrl = 'https://discord.gg/pydat66RY'; transform: translateX(3px); } + /* Author homepage — pill mirroring Enterprise, sits directly under it */ + .hero-author { + margin-top: 0.6rem; + display: inline-flex; + align-items: center; + gap: 0.55rem; + padding: 0.45rem 1.1rem; + border: 1px solid rgba(212, 165, 116, 0.3); + border-radius: 100px; + background: rgba(212, 165, 116, 0.04); + text-decoration: none; + transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.2s ease, + background-color 0.25s ease; + } + + .hero-author:hover { + border-color: rgba(212, 165, 116, 0.55); + background: rgba(212, 165, 116, 0.08); + box-shadow: 0 0 24px rgba(212, 165, 116, 0.15); + transform: translateY(-1px); + text-decoration: none; + } + + .hero-author-label { + font-family: var(--font-code); + font-size: 0.7rem; + letter-spacing: 0.14em; + text-transform: uppercase; + color: #d4a574; + } + + .hero-author-divider { + color: rgba(212, 165, 116, 0.45); + font-size: 0.85rem; + line-height: 1; + } + + .hero-author-handle { + font-family: var(--font-code); + font-size: 0.82rem; + color: #e8e2d8; + } + + .hero-author-arrow { + color: #d4a574; + font-size: 0.95rem; + line-height: 1; + transition: transform 0.2s ease; + } + + .hero-author:hover .hero-author-arrow { + transform: translateX(3px); + } + /* Subscribe */ .hero-subscribe { margin-top: 2rem; @@ -465,6 +526,14 @@ const discordUrl = 'https://discord.gg/pydat66RY'; } .hero-enterprise-divider { display: none; } .hero-enterprise-email { font-size: 0.78rem; } + .hero-author { + flex-direction: column; + gap: 0.25rem; + padding: 0.6rem 1.25rem; + max-width: 100%; + } + .hero-author-divider { display: none; } + .hero-author-handle { font-size: 0.78rem; } } @media (max-width: 480px) { diff --git a/install.ps1 b/install.ps1 index a066856..4eb6ece 100644 --- a/install.ps1 +++ b/install.ps1 @@ -35,6 +35,9 @@ $Platforms = [ordered]@{ openclaw = @{ Target = (Join-Path $HOME '.openclaw\skills'); Style = 'folder' } antigravity = @{ Target = (Join-Path $HOME '.gemini\antigravity\skills'); Style = 'folder' } vscode = @{ Target = (Join-Path $HOME '.copilot\skills'); Style = 'per-skill' } + hermes = @{ Target = (Join-Path $HOME '.hermes\skills'); Style = 'folder' } + cline = @{ Target = (Join-Path $HOME '.cline\skills'); Style = 'folder' } + kimi = @{ Target = (Join-Path $HOME '.kimi\skills'); Style = 'folder' } } function Show-Usage { diff --git a/install.sh b/install.sh index d6262ea..b23f8ae 100755 --- a/install.sh +++ b/install.sh @@ -36,6 +36,9 @@ openclaw|$HOME/.openclaw/skills|folder antigravity|$HOME/.gemini/antigravity/skills|folder vibe|$HOME/.vibe/skills|per-skill vscode|$HOME/.copilot/skills|per-skill +hermes|$HOME/.hermes/skills|folder +cline|$HOME/.cline/skills|folder +kimi|$HOME/.kimi/skills|folder EOF } diff --git a/package.json b/package.json index 504c00a..3b79775 100644 --- a/package.json +++ b/package.json @@ -14,5 +14,22 @@ "devDependencies": { "typescript": "^5.7.0", "vitest": "^3.1.0" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "esbuild", + "sharp", + "tree-sitter-c", + "tree-sitter-c-sharp", + "tree-sitter-cpp", + "tree-sitter-go", + "tree-sitter-java", + "tree-sitter-javascript", + "tree-sitter-php", + "tree-sitter-python", + "tree-sitter-ruby", + "tree-sitter-rust", + "tree-sitter-typescript" + ] } } diff --git a/understand-anything-plugin/.claude-plugin/plugin.json b/understand-anything-plugin/.claude-plugin/plugin.json index 8b824ab..67018c5 100644 --- a/understand-anything-plugin/.claude-plugin/plugin.json +++ b/understand-anything-plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "understand-anything", "description": "AI-powered codebase understanding — analyze, visualize, and explain any project", - "version": "2.6.3", + "version": "2.7.3", "author": { "name": "Lum1104" }, diff --git a/understand-anything-plugin/agents/architecture-analyzer.md b/understand-anything-plugin/agents/architecture-analyzer.md index b6dab09..58c005c 100644 --- a/understand-anything-plugin/agents/architecture-analyzer.md +++ b/understand-anything-plugin/agents/architecture-analyzer.md @@ -14,6 +14,11 @@ You are an expert software architect. Your job is to analyze a codebase's file s Given a list of file nodes (with paths, summaries, tags, and node types) and import edges, identify 3-10 logical architecture layers and assign every file node to exactly one layer. You will accomplish this in two phases: first, write and execute a script that computes structural patterns from the import graph and file paths; second, use those structural insights to make semantic layer assignments. +**Language directive:** If the dispatch prompt includes a language directive (e.g., "Generate all textual content in **Chinese**"), apply it to: +- Layer `name` — Translate to the specified language (e.g., "API 层", "服务层", "基础设施层") +- Layer `description` — Write in the specified language using natural phrasing +Use native-level terminology. Keep established English terms when appropriate (e.g., "CI/CD", "ORM", "REST API" may remain untranslated in some languages). + --- ## Phase 1 -- Structural Analysis Script diff --git a/understand-anything-plugin/agents/file-analyzer.md b/understand-anything-plugin/agents/file-analyzer.md index d7d2e69..6d0e506 100644 --- a/understand-anything-plugin/agents/file-analyzer.md +++ b/understand-anything-plugin/agents/file-analyzer.md @@ -17,6 +17,12 @@ For each file in the batch provided to you, extract structural data via a script **File categories in this batch:** Each file has a `fileCategory` field indicating its type: `code`, `config`, `docs`, `infra`, `data`, `script`, or `markup`. Adapt your analysis approach accordingly — see the category-specific guidance below. +**Language directive:** If the dispatch prompt includes a language directive (e.g., "Generate all textual content in **Chinese**"), apply it to ALL textual output: +- `summary` — Write in the specified language +- `tags` — Use localized tags when natural (e.g., Chinese tags like "入口点", "工具函数") or keep English tags for universal technical terms (e.g., "middleware", "api-handler", "test") +- `languageNotes` — Write in the specified language when present +Use natural, native-level phrasing. Keep technical terms in English when no standard translation exists. + --- ## Phase 1 -- Structural Extraction (Bundled Script) diff --git a/understand-anything-plugin/agents/project-scanner.md b/understand-anything-plugin/agents/project-scanner.md index ed84a80..2cedacc 100644 --- a/understand-anything-plugin/agents/project-scanner.md +++ b/understand-anything-plugin/agents/project-scanner.md @@ -14,6 +14,8 @@ You are a meticulous project inventory specialist. Your job is to scan a codebas Scan the project directory provided in the prompt and produce a JSON inventory. You will accomplish this in two phases: first, write and execute a discovery script that performs all deterministic file scanning; second, review the script's results and add a human-readable project description. +**Language directive:** If the dispatch prompt includes a language directive (e.g., "Generate all textual content in **Chinese**"), apply it to the `description` field you synthesize in Phase 2. Write the description in the specified language using natural, native-level phrasing. Keep technical terms in English when no standard translation exists (e.g., "middleware", "hook", "barrel"). + --- ## Phase 1 -- Discovery Script diff --git a/understand-anything-plugin/agents/tour-builder.md b/understand-anything-plugin/agents/tour-builder.md index 41f5ef9..ce12d1b 100644 --- a/understand-anything-plugin/agents/tour-builder.md +++ b/understand-anything-plugin/agents/tour-builder.md @@ -14,6 +14,12 @@ You are an expert technical educator who designs learning paths through codebase Given a codebase's nodes, edges, and layers, design a guided tour that teaches the project's architecture and key concepts. The tour must reference only real node IDs from the provided graph data. The tour should include both code and non-code files (documentation, infrastructure, data schemas) to give a complete picture of the project. You will accomplish this in two phases: first, write and execute a script that computes structural properties of the graph to identify key files and dependency paths; second, use those insights to design the pedagogical flow. +**Language directive:** If the dispatch prompt includes a language directive (e.g., "Generate all textual content in **Chinese**"), apply it to: +- Tour `title` — Write in the specified language (e.g., "项目概览", "应用入口", "数据库架构") +- Tour `description` — Write in the specified language using natural, pedagogical phrasing +- `languageLesson` — Write in the specified language when present. Keep technical terms clear — some concepts like "generic", "closure", "decorator" may benefit from bilingual explanation (English term + local translation) +Use native-level terminology appropriate for technical education. + --- ## Phase 1 -- Graph Topology Script diff --git a/understand-anything-plugin/hooks/auto-update-prompt.md b/understand-anything-plugin/hooks/auto-update-prompt.md index 13d5584..b20aa18 100644 --- a/understand-anything-plugin/hooks/auto-update-prompt.md +++ b/understand-anything-plugin/hooks/auto-update-prompt.md @@ -37,6 +37,58 @@ Incrementally update the knowledge graph using deterministic structural fingerpr mkdir -p $PROJECT_ROOT/.understand-anything/intermediate ``` +9. **Apply `.understandignore` exclusions** (same semantics as `/understand` Step 2.5 in `agents/project-scanner.md`). + + Without this step, files in user-excluded paths (migrations, vendored code, tests) are counted as structural changes and can spuriously escalate the action to `FULL_UPDATE` even when the real change set is tiny. + + 1. If neither `$PROJECT_ROOT/.understand-anything/.understandignore` nor `$PROJECT_ROOT/.understandignore` exists, the step 7 extension filter is sufficient — skip to Phase 1. + + 2. Write the step 7 file list to `$PROJECT_ROOT/.understand-anything/intermediate/changed-files-pre.json` as a JSON array of relative paths. + + 3. Resolve `$PLUGIN_ROOT`: + - Use `$CLAUDE_PLUGIN_ROOT` if set (Claude Code's hook context sets this). + - Otherwise try `$HOME/.understand-anything-plugin`. + - Validate the chosen candidate by checking `$candidate/packages/core/dist/ignore-filter.js` exists. + - If neither resolves: report "Cannot locate plugin install at `$CLAUDE_PLUGIN_ROOT` or `$HOME/.understand-anything-plugin`; auto-update aborted. Run `/understand` to re-baseline." and **STOP**. Do **not** silently skip — silent skip reproduces issue #153. + + 4. Write `$PROJECT_ROOT/.understand-anything/intermediate/ignore-filter.mjs`: + ```javascript + import { readFileSync, writeFileSync } from 'node:fs'; + import { pathToFileURL } from 'node:url'; + import path from 'node:path'; + + const PROJECT_ROOT = process.cwd(); + const PLUGIN_ROOT = process.argv[2]; + const inputPath = process.argv[3]; + + const modUrl = pathToFileURL( + path.join(PLUGIN_ROOT, 'packages/core/dist/ignore-filter.js'), + ).href; + const { createIgnoreFilter } = await import(modUrl); + const filter = createIgnoreFilter(PROJECT_ROOT); + + const input = JSON.parse(readFileSync(inputPath, 'utf-8')); + const kept = input.filter((p) => !filter.isIgnored(p)); + const removed = input.length - kept.length; + + writeFileSync( + path.join(PROJECT_ROOT, '.understand-anything/intermediate/changed-files.json'), + JSON.stringify({ kept, removed, total: input.length }, null, 2), + ); + console.log(`.understandignore: kept ${kept.length}/${input.length} (removed ${removed})`); + ``` + + 5. Run it: + ```bash + node $PROJECT_ROOT/.understand-anything/intermediate/ignore-filter.mjs \ + "$PLUGIN_ROOT" \ + $PROJECT_ROOT/.understand-anything/intermediate/changed-files-pre.json + ``` + + 6. Read `$PROJECT_ROOT/.understand-anything/intermediate/changed-files.json`. Pass the `kept` array as the input file list for Phase 1's fingerprint-check script. + + 7. If `kept.length === 0`: update `meta.json` with the new commit hash, report "All changed source files are in ignored paths. Metadata updated." and **STOP**. + --- ## Phase 1 — Structural Fingerprint Check (Zero LLM Tokens) @@ -188,12 +240,54 @@ Perform lightweight validation (no graph-reviewer agent): } ``` -3. **Update fingerprints:** Write and execute a Node.js script that: - - Reads the existing `fingerprints.json` - - For each re-analyzed file: computes new content hash and extracts structural elements via regex - - For deleted files: removes their entries - - Merges with existing fingerprints (keep unchanged files as-is) - - Writes updated `fingerprints.json` +3. **Update fingerprints (LOAD-PATCH-SAVE, not OVERWRITE).** + + The most common failure mode here: writing only the freshly-computed batch entries to `fingerprints.json`, discarding every other file's fingerprint. The next auto-update then sees all those files as new (no stored fingerprint), classifies them as STRUCTURAL, and escalates to FULL_UPDATE permanently (issue #152). The script must LOAD ALL existing entries, PATCH only the re-analyzed ones, and SAVE the full dict back. + + Write and execute a Node.js script in this exact ordering: + + ```javascript + import { readFileSync, writeFileSync, existsSync } from 'node:fs'; + import { createHash } from 'node:crypto'; + import path from 'node:path'; + + const fpPath = path.join(PROJECT_ROOT, '.understand-anything', 'fingerprints.json'); + const existedAndNonEmpty = existsSync(fpPath) && readFileSync(fpPath, 'utf-8').trim().length > 0; + + // 1. LOAD ALL existing entries (NEVER skip — preserves un-analyzed files) + const all = existedAndNonEmpty + ? JSON.parse(readFileSync(fpPath, 'utf-8')) + : {}; + const before = Object.keys(all).length; + + // 2. PATCH (file still exists) or REMOVE (file deleted) for each re-analyzed path. + // `filesToReanalyze` may include paths that were deleted in this commit — + // handle both branches inline rather than expecting a separate deleted list. + for (const filePath of filesToReanalyze) { + const fullPath = path.join(PROJECT_ROOT, filePath); + if (!existsSync(fullPath)) { + delete all[filePath]; + continue; + } + const content = readFileSync(fullPath, 'utf-8'); + const contentHash = createHash('sha256').update(content).digest('hex'); + // Extract functions, classes, imports, exports via the same regex as Phase 1. + all[filePath] = { contentHash, functions, classes, imports, exports }; + } + + // 3. GUARD against silent load failure: if fingerprints.json existed and was + // non-empty but `before` came out as 0, refuse to overwrite — something + // went wrong reading the file and writing now would clobber every entry. + if (existedAndNonEmpty && before === 0) { + throw new Error('fingerprints.json existed and was non-empty but loaded as {} — refusing to overwrite'); + } + + // 4. SAVE ALL entries back (full dict — not just the patched subset) + writeFileSync(fpPath, JSON.stringify(all, null, 2)); + console.log(`Fingerprints: ${before} → ${Object.keys(all).length}`); + ``` + + The `existedAndNonEmpty && before === 0` guard catches the silent-load-failure case before it corrupts the store. If the count shrinks from N to a small number that matches the batch size, the LOAD step was skipped — abort the write rather than persist the wrong dict. 4. Clean up intermediate files: ```bash diff --git a/understand-anything-plugin/package.json b/understand-anything-plugin/package.json index 60c9ca0..def2ade 100644 --- a/understand-anything-plugin/package.json +++ b/understand-anything-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@understand-anything/skill", - "version": "2.6.3", + "version": "2.7.3", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/understand-anything-plugin/packages/core/src/persistence/index.ts b/understand-anything-plugin/packages/core/src/persistence/index.ts index f14b011..d69857b 100644 --- a/understand-anything-plugin/packages/core/src/persistence/index.ts +++ b/understand-anything-plugin/packages/core/src/persistence/index.ts @@ -130,7 +130,7 @@ export function loadFingerprints(projectRoot: string): FingerprintStore | null { } } -const DEFAULT_CONFIG: ProjectConfig = { autoUpdate: false }; +const DEFAULT_CONFIG: ProjectConfig = { autoUpdate: false, outputLanguage: "en" }; export function saveConfig(projectRoot: string, config: ProjectConfig): void { const dir = ensureDir(projectRoot); diff --git a/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts b/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts index 02e0a1c..3a694fb 100644 --- a/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts +++ b/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts @@ -189,7 +189,7 @@ describe("persistence", () => { it("should return default config when no file exists", () => { const loaded = loadConfig(tempDir); - expect(loaded).toEqual({ autoUpdate: false }); + expect(loaded).toEqual({ autoUpdate: false, outputLanguage: "en" }); }); it("should return default config when config.json is corrupted", () => { @@ -198,7 +198,7 @@ describe("persistence", () => { writeFileSync(join(dir, "config.json"), "not json!!", "utf-8"); const loaded = loadConfig(tempDir); - expect(loaded).toEqual({ autoUpdate: false }); + expect(loaded).toEqual({ autoUpdate: false, outputLanguage: "en" }); }); }); }); diff --git a/understand-anything-plugin/packages/core/src/types.ts b/understand-anything-plugin/packages/core/src/types.ts index 890a9f0..b7a0fa6 100644 --- a/understand-anything-plugin/packages/core/src/types.ts +++ b/understand-anything-plugin/packages/core/src/types.ts @@ -113,9 +113,10 @@ export interface AnalysisMeta { theme?: ThemeConfig; } -// Project config (for auto-update opt-in) +// Project config (for auto-update opt-in and language preference) export interface ProjectConfig { autoUpdate: boolean; + outputLanguage?: string; } // Non-code structural sub-interfaces diff --git a/understand-anything-plugin/packages/dashboard/src/App.tsx b/understand-anything-plugin/packages/dashboard/src/App.tsx index 570c076..cc7c0b5 100644 --- a/understand-anything-plugin/packages/dashboard/src/App.tsx +++ b/understand-anything-plugin/packages/dashboard/src/App.tsx @@ -23,6 +23,7 @@ import type { KeyboardShortcut } from "./hooks/useKeyboardShortcuts"; import { ThemeProvider } from "./themes/index.ts"; import { ThemePicker } from "./components/ThemePicker.tsx"; import type { ThemeConfig } from "./themes/index.ts"; +import { I18nProvider, useI18n } from "./contexts/I18nContext.tsx"; // Lazy-load heavy / optional components so they ship in separate chunks. const CodeViewer = lazy(() => import("./components/CodeViewer")); @@ -45,6 +46,7 @@ function dataUrl(fileName: string, token: string | null): string { "domain-graph.json": import.meta.env.VITE_DOMAIN_GRAPH_URL, "meta.json": import.meta.env.VITE_META_URL, "diff-overlay.json": import.meta.env.VITE_DIFF_OVERLAY_URL, + "config.json": import.meta.env.VITE_CONFIG_URL, }; const url = envMap[fileName]; if (url) return url; @@ -96,38 +98,13 @@ function App() { } function Dashboard({ accessToken }: { accessToken: string }) { - const graph = useDashboardStore((s) => s.graph); const setGraph = useDashboardStore((s) => s.setGraph); - const selectedNodeId = useDashboardStore((s) => s.selectedNodeId); - const tourActive = useDashboardStore((s) => s.tourActive); - const persona = useDashboardStore((s) => s.persona); - const codeViewerOpen = useDashboardStore((s) => s.codeViewerOpen); - const codeViewerExpanded = useDashboardStore((s) => s.codeViewerExpanded); - const expandCodeViewer = useDashboardStore((s) => s.expandCodeViewer); - const collapseCodeViewer = useDashboardStore((s) => s.collapseCodeViewer); + const setDomainGraph = useDashboardStore((s) => s.setDomainGraph); const setDiffOverlay = useDashboardStore((s) => s.setDiffOverlay); - const pathFinderOpen = useDashboardStore((s) => s.pathFinderOpen); - const togglePathFinder = useDashboardStore((s) => s.togglePathFinder); - const nodeTypeFilters = useDashboardStore((s) => s.nodeTypeFilters); - const toggleNodeTypeFilter = useDashboardStore((s) => s.toggleNodeTypeFilter); const [loadError, setLoadError] = useState(null); const [graphIssues, setGraphIssues] = useState([]); - const [showKeyboardHelp, setShowKeyboardHelp] = useState(false); const [metaTheme, setMetaTheme] = useState(null); - const [sidebarTab, setSidebarTab] = useState("info"); - const viewMode = useDashboardStore((s) => s.viewMode); - const setViewMode = useDashboardStore((s) => s.setViewMode); - const isKnowledgeGraph = useDashboardStore((s) => s.isKnowledgeGraph); - const domainGraph = useDashboardStore((s) => s.domainGraph); - const setDomainGraph = useDashboardStore((s) => s.setDomainGraph); - const layoutIssues = useDashboardStore((s) => s.layoutIssues); - const isMobile = useIsMobile(); - // Schema issues + ELK layout issues share the WarningBanner — graph-load - // problems and dashboard rendering problems are equally surfaced. - const allIssues = useMemo( - () => [...graphIssues, ...layoutIssues], - [graphIssues, layoutIssues], - ); + const [outputLanguage, setOutputLanguage] = useState(); useEffect(() => { fetch(dataUrl("meta.json", accessToken)) @@ -136,130 +113,14 @@ function Dashboard({ accessToken }: { accessToken: string }) { if (meta?.theme) setMetaTheme(meta.theme); }) .catch(() => {}); + fetch(dataUrl("config.json", accessToken)) + .then((r) => (r.ok ? r.json() : null)) + .then((config) => { + if (config?.outputLanguage) setOutputLanguage(config.outputLanguage); + }) + .catch(() => {}); }, []); - useEffect(() => { - if (selectedNodeId) setSidebarTab("info"); - }, [selectedNodeId]); - - // Define keyboard shortcuts - const shortcuts = useMemo( - () => [ - // Help - { - key: "?", - shiftKey: true, - description: "Show keyboard shortcuts", - action: () => setShowKeyboardHelp((prev) => !prev), - category: "General", - }, - // Navigation - { - key: "Escape", - description: "Close panels and modals / go back to overview", - action: () => { - // Read from store at invocation time to avoid stale closures - const state = useDashboardStore.getState(); - if (state.pathFinderOpen) { - state.togglePathFinder(); - } else if (state.filterPanelOpen) { - state.toggleFilterPanel(); - } else if (state.exportMenuOpen) { - state.toggleExportMenu(); - } else if (state.codeViewerExpanded) { - state.collapseCodeViewer(); - } else if (state.codeViewerOpen) { - state.closeCodeViewer(); - } else if (state.selectedNodeId) { - state.selectNode(null); - } else if (state.navigationLevel === "layer-detail") { - state.navigateToOverview(); - } else if (state.tourActive) { - state.stopTour(); - } else { - setShowKeyboardHelp(false); - } - }, - category: "Navigation", - }, - { - key: "/", - description: "Focus search bar", - action: () => { - const searchInput = document.querySelector( - 'input[placeholder*="Search"]' - ); - searchInput?.focus(); - }, - category: "Navigation", - }, - // Tour controls - { - key: "ArrowRight", - description: "Next tour step", - action: () => { - const state = useDashboardStore.getState(); - if (state.tourActive) { - state.nextTourStep(); - } - }, - category: "Tour", - }, - { - key: "ArrowLeft", - description: "Previous tour step", - action: () => { - const state = useDashboardStore.getState(); - if (state.tourActive) { - state.prevTourStep(); - } - }, - category: "Tour", - }, - // View toggles - { - key: "d", - description: "Toggle diff mode", - action: () => { - const state = useDashboardStore.getState(); - state.toggleDiffMode(); - }, - category: "View", - }, - { - key: "f", - description: "Toggle filter panel", - action: () => { - const state = useDashboardStore.getState(); - state.toggleFilterPanel(); - }, - category: "View", - }, - { - key: "e", - description: "Toggle export menu", - action: () => { - const state = useDashboardStore.getState(); - state.toggleExportMenu(); - }, - category: "View", - }, - { - key: "p", - description: "Open path finder", - action: () => { - const state = useDashboardStore.getState(); - state.togglePathFinder(); - }, - category: "View", - }, - ], - [] - ); - - // Register keyboard shortcuts - useKeyboardShortcuts(shortcuts); - useEffect(() => { fetch(dataUrl("knowledge-graph.json", accessToken)) .then((res) => res.json()) @@ -268,9 +129,8 @@ function Dashboard({ accessToken }: { accessToken: string }) { if (result.success && result.data) { setGraph(result.data); setGraphIssues(result.issues); - // Auto-detect knowledge graph kind if ((data as Record).kind === "knowledge") { - setViewMode("knowledge"); + useDashboardStore.getState().setViewMode("knowledge"); useDashboardStore.getState().setIsKnowledgeGraph(true); } for (const issue of result.issues) { @@ -315,9 +175,7 @@ function Dashboard({ accessToken }: { accessToken: string }) { } } }) - .catch(() => { - // Silently ignore - diff overlay is optional - }); + .catch(() => {}); }, [setDiffOverlay]); useEffect(() => { @@ -335,11 +193,183 @@ function Dashboard({ accessToken }: { accessToken: string }) { console.warn(`[domain-graph] validation failed: ${result.fatal}`); } }) - .catch(() => { - // Silently ignore — domain graph is optional - }); + .catch(() => {}); }, [setDomainGraph]); + return ( + + + + + + ); +} + +function DashboardContent({ + accessToken, + loadError, + graphIssues, +}: { + accessToken: string; + loadError: string | null; + graphIssues: GraphIssue[]; +}) { + const graph = useDashboardStore((s) => s.graph); + const selectedNodeId = useDashboardStore((s) => s.selectedNodeId); + const tourActive = useDashboardStore((s) => s.tourActive); + const persona = useDashboardStore((s) => s.persona); + const codeViewerOpen = useDashboardStore((s) => s.codeViewerOpen); + const codeViewerExpanded = useDashboardStore((s) => s.codeViewerExpanded); + const expandCodeViewer = useDashboardStore((s) => s.expandCodeViewer); + const collapseCodeViewer = useDashboardStore((s) => s.collapseCodeViewer); + const pathFinderOpen = useDashboardStore((s) => s.pathFinderOpen); + const togglePathFinder = useDashboardStore((s) => s.togglePathFinder); + const nodeTypeFilters = useDashboardStore((s) => s.nodeTypeFilters); + const toggleNodeTypeFilter = useDashboardStore((s) => s.toggleNodeTypeFilter); + const detailLevel = useDashboardStore((s) => s.detailLevel); + const setDetailLevel = useDashboardStore((s) => s.setDetailLevel); + const showFunctionsInClassView = useDashboardStore((s) => s.showFunctionsInClassView); + const toggleShowFunctionsInClassView = useDashboardStore((s) => s.toggleShowFunctionsInClassView); + const [showKeyboardHelp, setShowKeyboardHelp] = useState(false); + const [sidebarTab, setSidebarTab] = useState("info"); + const viewMode = useDashboardStore((s) => s.viewMode); + const setViewMode = useDashboardStore((s) => s.setViewMode); + const isKnowledgeGraph = useDashboardStore((s) => s.isKnowledgeGraph); + const domainGraph = useDashboardStore((s) => s.domainGraph); + const layoutIssues = useDashboardStore((s) => s.layoutIssues); + const isMobile = useIsMobile(); + const { t } = useI18n(); + const allIssues = useMemo( + () => [...graphIssues, ...layoutIssues], + [graphIssues, layoutIssues], + ); + + useEffect(() => { + if (selectedNodeId) setSidebarTab("info"); + }, [selectedNodeId]); + + // Define keyboard shortcuts + const shortcuts = useMemo( + () => [ + // Help + { + key: "?", + shiftKey: true, + description: t.keyboardShortcuts.showHelp, + action: () => setShowKeyboardHelp((prev) => !prev), + category: "General", + }, + // Navigation + { + key: "Escape", + description: t.keyboardShortcuts.escapeDesc, + action: () => { + // Read from store at invocation time to avoid stale closures + const state = useDashboardStore.getState(); + if (state.pathFinderOpen) { + state.togglePathFinder(); + } else if (state.filterPanelOpen) { + state.toggleFilterPanel(); + } else if (state.exportMenuOpen) { + state.toggleExportMenu(); + } else if (state.codeViewerExpanded) { + state.collapseCodeViewer(); + } else if (state.codeViewerOpen) { + state.closeCodeViewer(); + } else if (state.selectedNodeId) { + state.selectNode(null); + } else if (state.navigationLevel === "layer-detail") { + state.navigateToOverview(); + } else if (state.tourActive) { + state.stopTour(); + } else { + setShowKeyboardHelp(false); + } + }, + category: "Navigation", + }, + { + key: "/", + description: t.keyboardShortcuts.focusSearch, + action: () => { + const searchInput = document.querySelector( + '[data-testid="search-input"]' + ); + searchInput?.focus(); + }, + category: "Navigation", + }, + // Tour controls + { + key: "ArrowRight", + description: t.keyboardShortcuts.nextStep, + action: () => { + const state = useDashboardStore.getState(); + if (state.tourActive) { + state.nextTourStep(); + } + }, + category: "Tour", + }, + { + key: "ArrowLeft", + description: t.keyboardShortcuts.prevStep, + action: () => { + const state = useDashboardStore.getState(); + if (state.tourActive) { + state.prevTourStep(); + } + }, + category: "Tour", + }, + // View toggles + { + key: "d", + description: t.keyboardShortcuts.toggleDiff, + action: () => { + const state = useDashboardStore.getState(); + state.toggleDiffMode(); + }, + category: "View", + }, + { + key: "f", + description: t.keyboardShortcuts.toggleFilter, + action: () => { + const state = useDashboardStore.getState(); + state.toggleFilterPanel(); + }, + category: "View", + }, + { + key: "e", + description: t.keyboardShortcuts.toggleExport, + action: () => { + const state = useDashboardStore.getState(); + state.toggleExportMenu(); + }, + category: "View", + }, + { + key: "p", + description: t.keyboardShortcuts.openPathFinder, + action: () => { + const state = useDashboardStore.getState(); + state.togglePathFinder(); + }, + category: "View", + }, + ], + [t] + ); + + // Register keyboard shortcuts + useKeyboardShortcuts(shortcuts); + // Determine sidebar content // NodeInfo always takes priority when a node is selected. // Learn mode adds LearnPanel below it; otherwise ProjectOverview shows when idle. @@ -370,7 +400,7 @@ function Dashboard({ accessToken }: { accessToken: string }) { : "text-text-muted hover:text-text-primary hover:bg-elevated" }`} > - {tab === "info" ? "Info" : "Files"} + {tab === "info" ? t.sidebar.info : t.sidebar.files} ))} @@ -382,28 +412,25 @@ function Dashboard({ accessToken }: { accessToken: string }) { if (isMobile) { return ( - - - + ); } return ( -
{/* Header */}
{/* Left — fixed */}

- {graph?.project.name ?? "Understand Anything"} + {graph?.project.name ?? t.common.appName}

@@ -414,26 +441,26 @@ function Dashboard({ accessToken }: { accessToken: string }) {
@@ -444,17 +471,63 @@ function Dashboard({ accessToken }: { accessToken: string }) {
+ {/* Detail level: file view (architecture) / class view (code structure) */} + {!isKnowledgeGraph && viewMode !== "domain" && ( + <> +
+
+ + +
+ {detailLevel === "class" && ( + + )} + + )}
{(isKnowledgeGraph ? [ - { key: "knowledge" as const, label: "All", color: "var(--color-node-article)" }, + { key: "knowledge" as const, label: t.nodeTypeLabels.all, color: "var(--color-node-article)" }, ] : [ - { key: "code" as const, label: "Code", color: "var(--color-node-file)" }, - { key: "config" as const, label: "Config", color: "var(--color-node-config)" }, - { key: "docs" as const, label: "Docs", color: "var(--color-node-document)" }, - { key: "infra" as const, label: "Infra", color: "var(--color-node-service)" }, - { key: "data" as const, label: "Data", color: "var(--color-node-table)" }, - { key: "domain" as const, label: "Domain", color: "var(--color-node-concept)" }, - { key: "knowledge" as const, label: "Knowledge", color: "var(--color-node-article)" }, + { key: "code" as const, label: t.nodeTypeLabels.code, color: "var(--color-node-file)" }, + { key: "config" as const, label: t.nodeTypeLabels.config, color: "var(--color-node-config)" }, + { key: "docs" as const, label: t.nodeTypeLabels.docs, color: "var(--color-node-document)" }, + { key: "infra" as const, label: t.nodeTypeLabels.infra, color: "var(--color-node-service)" }, + { key: "data" as const, label: t.nodeTypeLabels.data, color: "var(--color-node-table)" }, + { key: "domain" as const, label: t.nodeTypeLabels.domain, color: "var(--color-node-concept)" }, + { key: "knowledge" as const, label: t.nodeTypeLabels.knowledge, color: "var(--color-node-article)" }, ]).map((cat) => (
@@ -617,7 +690,6 @@ function Dashboard({ accessToken }: { accessToken: string }) {
- ); } diff --git a/understand-anything-plugin/packages/dashboard/src/components/Breadcrumb.tsx b/understand-anything-plugin/packages/dashboard/src/components/Breadcrumb.tsx index 9bbb47b..bb00949 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/Breadcrumb.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/Breadcrumb.tsx @@ -1,10 +1,12 @@ import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; export default function Breadcrumb() { const navigationLevel = useDashboardStore((s) => s.navigationLevel); const activeLayerId = useDashboardStore((s) => s.activeLayerId); const graph = useDashboardStore((s) => s.graph); const navigateToOverview = useDashboardStore((s) => s.navigateToOverview); + const { t } = useI18n(); const activeLayer = graph?.layers.find((l) => l.id === activeLayerId); @@ -12,7 +14,7 @@ export default function Breadcrumb() {
{navigationLevel === "overview" && (
- Project Overview + {t.breadcrumb.projectOverview}
)} @@ -22,14 +24,14 @@ export default function Breadcrumb() { onClick={navigateToOverview} className="text-gold hover:text-gold-bright transition-colors" > - Project + {t.breadcrumb.project} - {activeLayer?.name ?? "Layer"} + {activeLayer?.name ?? t.layer.defaultName} - (Esc to go back) + ({t.breadcrumb.escBack})
)} diff --git a/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx b/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx index 66ad67c..592fb1a 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo, useState } from "react"; import { Highlight, themes } from "prism-react-renderer"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; interface CodeViewerProps { accessToken: string; @@ -78,6 +79,7 @@ export default function CodeViewer({ source: null, error: null, }); + const { t } = useI18n(); useEffect(() => { if (!node?.filePath) { @@ -125,7 +127,7 @@ export default function CodeViewer({ if (!node) { return (
-

No file selected

+

{t.codeViewer.noFile}

); } @@ -133,8 +135,8 @@ export default function CodeViewer({ const source = state.source; const language = source?.language ?? fallbackLanguage(node.filePath); const lineInfo = highlightedRange - ? `Lines ${highlightedRange.start}-${highlightedRange.end}` - : "Full file"; + ? `${t.codeViewer.lines} ${highlightedRange.start}-${highlightedRange.end}` + : t.codeViewer.fullFile; const isModal = presentation === "modal"; const handleClose = onClose ?? closeCodeViewer; @@ -170,8 +172,8 @@ export default function CodeViewer({ type="button" onClick={onExpand} className="text-text-muted hover:text-text-primary transition-colors" - title="Open larger code viewer" - aria-label="Open larger code viewer" + title={t.codeViewer.openLarger} + aria-label={t.codeViewer.openLarger} > @@ -182,8 +184,8 @@ export default function CodeViewer({ type="button" onClick={handleClose} className="text-text-muted hover:text-text-primary transition-colors" - title={isModal ? "Close expanded code viewer" : "Close code viewer"} - aria-label={isModal ? "Close expanded code viewer" : "Close code viewer"} + title={isModal ? t.codeViewer.closeExpanded : t.codeViewer.closeViewer} + aria-label={isModal ? t.codeViewer.closeExpanded : t.codeViewer.closeViewer} > @@ -194,13 +196,13 @@ export default function CodeViewer({
{state.status === "loading" && ( -
Loading source...
+
{t.codeViewer.loading}
)} {state.status === "error" && (
-
Source unavailable
+
{t.codeViewer.sourceUnavailable}

{state.error}

@@ -209,7 +211,7 @@ export default function CodeViewer({ {source && ( <>
- {source.lineCount} lines + {source.lineCount} {t.codeViewer.linesLabel} {formatBytes(source.sizeBytes)}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/CustomNode.tsx b/understand-anything-plugin/packages/dashboard/src/components/CustomNode.tsx index bfc50b4..5dffd2d 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/CustomNode.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/CustomNode.tsx @@ -2,6 +2,7 @@ import { memo } from "react"; import { Handle, Position } from "@xyflow/react"; import type { NodeProps, Node } from "@xyflow/react"; import type { NodeType } from "@understand-anything/core/types"; +import { useI18n } from "../contexts/I18nContext"; // Color maps keyed by NodeType — must be kept in sync with core NodeType union. const typeColors: Record = { @@ -88,6 +89,7 @@ function CustomNodeComponent({ const barColor = typeColors[knownType] ?? typeColors.file; const textColor = typeTextColors[knownType] ?? typeTextColors.file; const complexityColor = complexityColors[data.complexity] ?? complexityColors.simple; + const { t } = useI18n(); if (import.meta.env.DEV && !(knownType in typeColors)) { console.warn(`[CustomNode] Unknown node type "${data.nodeType}" — using "file" colors`); @@ -159,8 +161,8 @@ function CustomNodeComponent({ )}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/DiffToggle.tsx b/understand-anything-plugin/packages/dashboard/src/components/DiffToggle.tsx index f912d1a..a3dd2ff 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/DiffToggle.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/DiffToggle.tsx @@ -1,10 +1,12 @@ import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; export default function DiffToggle() { const diffMode = useDashboardStore((s) => s.diffMode); const toggleDiffMode = useDashboardStore((s) => s.toggleDiffMode); const changedNodeIds = useDashboardStore((s) => s.changedNodeIds); const affectedNodeIds = useDashboardStore((s) => s.affectedNodeIds); + const { t } = useI18n(); const hasDiff = changedNodeIds.size > 0; @@ -23,9 +25,9 @@ export default function DiffToggle() { title={ hasDiff ? diffMode - ? "Hide diff overlay" - : "Show diff overlay" - : "No diff data loaded" + ? t.diffToggle.hideOverlay + : t.diffToggle.showOverlay + : t.diffToggle.noData } > Diff {diffMode && hasDiff ? "ON" : "OFF"} @@ -39,7 +41,7 @@ export default function DiffToggle() { style={{ backgroundColor: "var(--color-diff-changed)" }} /> - Changed + {t.diffToggle.changed} ({changedNodeIds.size}) @@ -51,7 +53,7 @@ export default function DiffToggle() { style={{ backgroundColor: "var(--color-diff-affected)" }} /> - Affected + {t.diffToggle.affected} ({affectedNodeIds.size}) diff --git a/understand-anything-plugin/packages/dashboard/src/components/DomainGraphView.tsx b/understand-anything-plugin/packages/dashboard/src/components/DomainGraphView.tsx index 6730000..ff15846 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/DomainGraphView.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/DomainGraphView.tsx @@ -17,6 +17,7 @@ import type { FlowFlowNode } from "./FlowNode"; import StepNode from "./StepNode"; import type { StepFlowNode } from "./StepNode"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; import { mergeElkPositions, nodesToElkInput } from "../utils/layout"; import { applyElkLayout } from "../utils/elk-layout"; import type { KnowledgeGraph, GraphNode } from "@understand-anything/core/types"; @@ -167,6 +168,7 @@ function DomainGraphViewInner() { const domainGraph = useDashboardStore((s) => s.domainGraph); const activeDomainId = useDashboardStore((s) => s.activeDomainId); const clearActiveDomain = useDashboardStore((s) => s.clearActiveDomain); + const { t } = useI18n(); // Build structural nodes/edges/dims synchronously; only the layout call // itself is async, so we memo the structural pieces and run ELK in an @@ -237,7 +239,7 @@ function DomainGraphViewInner() { onClick={() => clearActiveDomain()} className="px-3 py-1.5 text-xs rounded-lg bg-elevated border border-border-subtle text-text-secondary hover:text-text-primary transition-colors" > - Back to domains + {t.domainView.backToDomains}
)} diff --git a/understand-anything-plugin/packages/dashboard/src/components/ExportMenu.tsx b/understand-anything-plugin/packages/dashboard/src/components/ExportMenu.tsx index e3ed709..2a35fc9 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/ExportMenu.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/ExportMenu.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef } from "react"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; import type { KnowledgeGraph } from "@understand-anything/core/types"; import { filterNodes, filterEdges } from "../utils/filters"; @@ -26,6 +27,7 @@ export default function ExportMenu() { const toggleExportMenu = useDashboardStore((s) => s.toggleExportMenu); const reactFlowInstance = useDashboardStore((s) => s.reactFlowInstance); const persona = useDashboardStore((s) => s.persona); + const { t } = useI18n(); const containerRef = useRef(null); @@ -218,7 +220,7 @@ export default function ExportMenu() { {exportMenuOpen && ( @@ -247,7 +249,7 @@ export default function ExportMenu() { - Export as PNG + {t.export.asPNG}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx b/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx index a0697d1..e4940f6 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx @@ -1,6 +1,7 @@ import { useMemo, useState } from "react"; import type { GraphNode } from "@understand-anything/core/types"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; interface FileEntry { name: string; @@ -142,6 +143,7 @@ export default function FileExplorer() { const graph = useDashboardStore((s) => s.graph); const openCodeViewer = useDashboardStore((s) => s.openCodeViewer); const navigateToNode = useDashboardStore((s) => s.navigateToNode); + const { t } = useI18n(); const entries = useMemo(() => buildFileTree(graph?.nodes ?? []), [graph]); const [expanded, setExpanded] = useState>(() => new Set()); @@ -176,7 +178,7 @@ export default function FileExplorer() { if (!graph) { return (
- No graph loaded + {t.common.noGraphLoaded}
); } @@ -185,15 +187,15 @@ export default function FileExplorer() {
- Analyzed Files + {t.fileExplorer.analyzedFiles}
- {totalFiles} files from the current knowledge graph + {totalFiles} {t.fileExplorer.filesFromGraph}
{entries.length === 0 ? ( -
No file paths found.
+
{t.fileExplorer.noFilePathsFound}
) : ( entries.map((entry) => ( s.graph); @@ -10,6 +11,7 @@ export default function FilterPanel() { const hasActiveFilters = useDashboardStore((s) => s.hasActiveFilters); const filterPanelOpen = useDashboardStore((s) => s.filterPanelOpen); const toggleFilterPanel = useDashboardStore((s) => s.toggleFilterPanel); + const { t } = useI18n(); const containerRef = useRef(null); @@ -97,7 +99,7 @@ export default function FilterPanel() { d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /> - Filter + {t.common.filter} {filterPanelOpen && ( @@ -106,7 +108,7 @@ export default function FilterPanel() { {/* Node Types */}

- Node Types + {t.filterPanel.nodeTypes}

{allNodeTypes.map((type) => ( @@ -129,7 +131,7 @@ export default function FilterPanel() { {/* Complexity */}

- Complexity + {t.filterPanel.complexity}

{allComplexities.map((complexity) => ( @@ -153,7 +155,7 @@ export default function FilterPanel() { {layers.length > 0 && (

- Layers + {t.filterPanel.layers}

{layers.map((layer) => ( @@ -178,7 +180,7 @@ export default function FilterPanel() { {/* Edge Categories */}

- Edge Categories + {t.filterPanel.edgeCategories}

{allEdgeCategories.map((category) => ( @@ -206,7 +208,7 @@ export default function FilterPanel() { onClick={resetFilters} className="w-full px-3 py-1.5 text-sm bg-elevated hover:bg-gold/20 text-text-secondary hover:text-gold rounded-lg transition-colors" > - Reset All + {t.common.resetAll} )}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx b/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx index 4ba8d1b..2a5037a 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx @@ -366,6 +366,7 @@ function useLayerDetailTopology(): LayerDetailTopology & { layoutStatus: "computing" | "ready"; } { const graph = useDashboardStore((s) => s.graph); + const nodesById = useDashboardStore((s) => s.nodesById); const activeLayerId = useDashboardStore((s) => s.activeLayerId); const selectNode = useDashboardStore((s) => s.selectNode); const persona = useDashboardStore((s) => s.persona); @@ -375,6 +376,8 @@ function useLayerDetailTopology(): LayerDetailTopology & { const focusNodeId = useDashboardStore((s) => s.focusNodeId); const nodeTypeFilters = useDashboardStore((s) => s.nodeTypeFilters); const drillIntoLayer = useDashboardStore((s) => s.drillIntoLayer); + const detailLevel = useDashboardStore((s) => s.detailLevel); + const showFunctionsInClassView = useDashboardStore((s) => s.showFunctionsInClassView); const handleNodeSelect = useCallback( (nodeId: string) => { @@ -404,10 +407,20 @@ function useLayerDetailTopology(): LayerDetailTopology & { // Expand layer membership to include sub-file nodes (function/class) // whose parent file is in this layer. Joined via "contains" edges. + // File view: file nodes only (architecture-level dependencies). + // Class view: file nodes + class nodes, functions only when toggle is on. const expandedLayerNodeIds = new Set(layerNodeIds); - for (const edge of graph.edges) { - if (edge.type === "contains" && layerNodeIds.has(edge.source)) { - expandedLayerNodeIds.add(edge.target); + if (detailLevel !== "file") { + for (const edge of graph.edges) { + if (edge.type === "contains" && layerNodeIds.has(edge.source)) { + const child = nodesById.get(edge.target); + if (!child) continue; + if (child.type === "class") { + expandedLayerNodeIds.add(edge.target); + } else if (child.type === "function" && showFunctionsInClassView) { + expandedLayerNodeIds.add(edge.target); + } + } } } @@ -626,6 +639,7 @@ function useLayerDetailTopology(): LayerDetailTopology & { }; }, [ graph, + nodesById, activeLayerId, persona, diffMode, @@ -634,6 +648,8 @@ function useLayerDetailTopology(): LayerDetailTopology & { focusNodeId, nodeTypeFilters, drillIntoLayer, + detailLevel, + showFunctionsInClassView, handleNodeSelect, handleContainerToggle, ]); diff --git a/understand-anything-plugin/packages/dashboard/src/components/KeyboardShortcutsHelp.tsx b/understand-anything-plugin/packages/dashboard/src/components/KeyboardShortcutsHelp.tsx index b05c65e..87d0381 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/KeyboardShortcutsHelp.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/KeyboardShortcutsHelp.tsx @@ -1,5 +1,6 @@ import type { KeyboardShortcut } from "../hooks/useKeyboardShortcuts"; import { formatShortcutKey } from "../hooks/useKeyboardShortcuts"; +import { useI18n } from "../contexts/I18nContext"; interface KeyboardShortcutsHelpProps { shortcuts: KeyboardShortcut[]; @@ -10,6 +11,8 @@ export default function KeyboardShortcutsHelp({ shortcuts, onClose, }: KeyboardShortcutsHelpProps) { + const { t } = useI18n(); + // Group shortcuts by category const groupedShortcuts = shortcuts.reduce((acc, shortcut) => { if (!acc[shortcut.category]) { @@ -19,6 +22,14 @@ export default function KeyboardShortcutsHelp({ return acc; }, {} as Record); + // Translate category names + const categoryTranslations: Record = { + "General": t.keyboardShortcuts.general, + "Navigation": t.keyboardShortcuts.navigation, + "Tour": t.keyboardShortcuts.tour, + "View": t.keyboardShortcuts.view, + }; + return (

- Keyboard Shortcuts + {t.keyboardShortcuts.title}

- Press ? anytime to toggle this help + {t.keyboardShortcuts.toggleHint}

- Steps + {t.learnPanel.steps}

{tourSteps.map((step, i) => (

- Tour + {t.learnPanel.tour}

{currentTourStep + 1} / {totalSteps} @@ -97,7 +99,7 @@ export default function LearnPanel() { onClick={stopTour} className="text-[10px] text-text-muted hover:text-text-secondary transition-colors" > - Exit Tour + {t.learnPanel.exitTour}
@@ -213,13 +215,13 @@ export default function LearnPanel() { disabled={isFirst} className="flex-1 text-xs bg-elevated text-text-secondary py-1.5 rounded-lg hover:bg-surface disabled:opacity-40 disabled:cursor-not-allowed transition-colors" > - Prev + {t.learnPanel.prev}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/MobileBottomNav.tsx b/understand-anything-plugin/packages/dashboard/src/components/MobileBottomNav.tsx index 44b42ef..83662ca 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/MobileBottomNav.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/MobileBottomNav.tsx @@ -1,4 +1,5 @@ import type { ReactNode } from "react"; +import { useI18n } from "../contexts/I18nContext"; export type MobileTab = "graph" | "info" | "files"; @@ -7,61 +8,58 @@ interface Props { onTabChange: (tab: MobileTab) => void; } -const tabs: { id: MobileTab; label: string; icon: ReactNode }[] = [ - { - id: "graph", - label: "Graph", - icon: ( - - - - - - - ), - }, - { - id: "info", - label: "Info", - icon: ( - - - - - ), - }, - { - id: "files", - label: "Files", - icon: ( - - - - ), - }, -]; +const tabIcons: Record = { + graph: ( + + + + + + + ), + info: ( + + + + + ), + files: ( + + + + ), +}; + +const tabOrder: MobileTab[] = ["graph", "info", "files"]; export default function MobileBottomNav({ activeTab, onTabChange }: Props) { + const { t } = useI18n(); + const labels: Record = { + graph: t.mobile.graph, + info: t.mobile.info, + files: t.mobile.files, + }; + return (
)}
- Diff overlay + {t.drawer.diffOverlay}
- Node types + {t.drawer.nodeTypes}
{filterDefs.map((cat) => { const active = nodeTypeFilters[cat.key] !== false; @@ -203,7 +207,7 @@ export default function MobileDrawer({ {graph && (graph.layers?.length ?? 0) > 0 && (
- Layers + {t.drawer.layers}
@@ -211,7 +215,7 @@ export default function MobileDrawer({ )}
- Tools + {t.drawer.tools}
@@ -231,7 +235,7 @@ export default function MobileDrawer({ d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" /> - Path + {t.drawer.path}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/MobileLayout.tsx b/understand-anything-plugin/packages/dashboard/src/components/MobileLayout.tsx index 72d3d4e..27e4801 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/MobileLayout.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/MobileLayout.tsx @@ -1,6 +1,7 @@ import { lazy, Suspense, useEffect, useState } from "react"; import type { GraphIssue } from "@understand-anything/core/schema"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; import GraphView from "./GraphView"; import DomainGraphView from "./DomainGraphView"; import KnowledgeGraphView from "./KnowledgeGraphView"; @@ -45,6 +46,7 @@ export default function MobileLayout({ const closeCodeViewer = useDashboardStore((s) => s.closeCodeViewer); const pathFinderOpen = useDashboardStore((s) => s.pathFinderOpen); const togglePathFinder = useDashboardStore((s) => s.togglePathFinder); + const { t } = useI18n(); const [activeTab, setActiveTab] = useState("graph"); const [drawerOpen, setDrawerOpen] = useState(false); @@ -96,7 +98,7 @@ export default function MobileLayout({

- {graph?.project.name ?? "Understand Anything"} + {graph?.project.name ?? t.common.appName}

{historyNodes.slice(-3).map((h, i, arr) => ( @@ -419,7 +376,7 @@ export default function NodeInfo() { : "text-text-muted border border-border-subtle hover:text-gold hover:border-gold/30" }`} > - {focusNodeId === node.id ? "Unfocus" : "Focus"} + {focusNodeId === node.id ? t.common.unfocus : t.common.focus}
@@ -431,7 +388,7 @@ export default function NodeInfo() {
-
File
+
{t.common.file}
{node.filePath} {node.lineRange && ( @@ -446,7 +403,7 @@ export default function NodeInfo() { onClick={() => openCodeViewer(node.id)} className="shrink-0 text-[10px] font-semibold uppercase tracking-wider px-2.5 py-1 rounded border border-accent/30 text-accent hover:text-accent-bright hover:border-accent/60 transition-colors" > - Open code + {t.common.openCode}
@@ -466,7 +423,7 @@ export default function NodeInfo() { > - Language Concepts + {t.nodeInfo.languageConcepts} {languageExpanded && (
@@ -481,7 +438,7 @@ export default function NodeInfo() { {node.tags.length > 0 && (

- Tags + {t.common.tags}

{node.tags.map((tag) => ( @@ -510,7 +467,7 @@ export default function NodeInfo() { {childNodes.length > 0 && (

- Defined in this file ({childNodes.length}) + {t.nodeInfo.definedInThisFile} ({childNodes.length})

{childNodes.map((child) => { @@ -548,14 +505,14 @@ export default function NodeInfo() { {otherConnections.length > 0 && (

- Connections ({otherConnections.length}) + {t.common.connections} ({otherConnections.length})

{otherConnections.map((edge, i) => { const isSource = edge.source === node.id; const otherId = isSource ? edge.target : edge.source; const otherNode = activeGraph?.nodes.find((n) => n.id === otherId); - const dirLabel = getDirectionalLabel(edge.type, isSource); + const dirLabel = getDirectionalLabel(edge.type, isSource, t); const arrow = isSource ? "\u2192" : "\u2190"; return ( diff --git a/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx b/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx index d496a7c..941e3a2 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx @@ -1,27 +1,29 @@ import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; import type { Persona } from "../store"; -const personas: { id: Persona; label: string; description: string }[] = [ - { - id: "non-technical", - label: "Overview", - description: "High-level architecture view", - }, - { - id: "junior", - label: "Learn", - description: "Full dashboard with guided learning", - }, - { - id: "experienced", - label: "Deep Dive", - description: "Code-focused with chat", - }, -]; - export default function PersonaSelector() { const persona = useDashboardStore((s) => s.persona); const setPersona = useDashboardStore((s) => s.setPersona); + const { t } = useI18n(); + + const personas: { id: Persona; label: string; description: string }[] = [ + { + id: "non-technical", + label: t.personaSelector.overview, + description: t.personaSelector.overviewDesc, + }, + { + id: "junior", + label: t.personaSelector.learn, + description: t.personaSelector.learnDesc, + }, + { + id: "experienced", + label: t.personaSelector.deepDive, + description: t.personaSelector.deepDiveDesc, + }, + ]; return (
diff --git a/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx b/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx index 1760437..c9cb478 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx @@ -1,13 +1,15 @@ import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; export default function ProjectOverview() { const graph = useDashboardStore((s) => s.graph); const startTour = useDashboardStore((s) => s.startTour); + const { t } = useI18n(); if (!graph) { return (
-

Loading project...

+

{t.common.loading}

); } @@ -15,13 +17,11 @@ export default function ProjectOverview() { const { project, nodes, edges, layers } = graph; const hasTour = graph.tour.length > 0; - // Count node types const typeCounts: Record = {}; for (const node of nodes) { typeCounts[node.type] = (typeCounts[node.type] ?? 0) + 1; } - // Count complexity const complexityCounts: Record = { simple: 0, moderate: 0, complex: 0 }; for (const node of nodes) { if (node.complexity) { @@ -29,7 +29,6 @@ export default function ProjectOverview() { } } - // Find top connected nodes const nodeConnections = new Map(); for (const edge of edges) { nodeConnections.set(edge.source, (nodeConnections.get(edge.source) ?? 0) + 1); @@ -45,16 +44,15 @@ export default function ProjectOverview() { const avgConnections = nodes.length > 0 ? (edges.length * 2 / nodes.length).toFixed(1) : "0"; - // Category breakdowns const categoryBreakdown = [ - { label: "Code", color: "var(--color-node-file)", count: (typeCounts["file"] ?? 0) + (typeCounts["function"] ?? 0) + (typeCounts["class"] ?? 0) + (typeCounts["module"] ?? 0) + (typeCounts["concept"] ?? 0) }, - { label: "Config", color: "var(--color-node-config)", count: typeCounts["config"] ?? 0 }, - { label: "Docs", color: "var(--color-node-document)", count: typeCounts["document"] ?? 0 }, - { label: "Infra", color: "var(--color-node-service)", count: (typeCounts["service"] ?? 0) + (typeCounts["resource"] ?? 0) + (typeCounts["pipeline"] ?? 0) }, - { label: "Data", color: "var(--color-node-table)", count: (typeCounts["table"] ?? 0) + (typeCounts["endpoint"] ?? 0) + (typeCounts["schema"] ?? 0) }, - { label: "Domain", color: "var(--color-node-concept)", count: (typeCounts["domain"] ?? 0) + (typeCounts["flow"] ?? 0) + (typeCounts["step"] ?? 0) }, + { label: t.projectOverview.code, color: "var(--color-node-file)", count: (typeCounts["file"] ?? 0) + (typeCounts["function"] ?? 0) + (typeCounts["class"] ?? 0) + (typeCounts["module"] ?? 0) + (typeCounts["concept"] ?? 0) }, + { label: t.projectOverview.config, color: "var(--color-node-config)", count: typeCounts["config"] ?? 0 }, + { label: t.projectOverview.docs, color: "var(--color-node-document)", count: typeCounts["document"] ?? 0 }, + { label: t.projectOverview.infra, color: "var(--color-node-service)", count: (typeCounts["service"] ?? 0) + (typeCounts["resource"] ?? 0) + (typeCounts["pipeline"] ?? 0) }, + { label: t.projectOverview.data, color: "var(--color-node-table)", count: (typeCounts["table"] ?? 0) + (typeCounts["endpoint"] ?? 0) + (typeCounts["schema"] ?? 0) }, + { label: t.projectOverview.domain, color: "var(--color-node-concept)", count: (typeCounts["domain"] ?? 0) + (typeCounts["flow"] ?? 0) + (typeCounts["step"] ?? 0) }, ]; - const hasNonCodeNodes = categoryBreakdown.some((c) => c.label !== "Code" && c.count > 0); + const hasNonCodeNodes = categoryBreakdown.some((c) => c.label !== t.projectOverview.code && c.count > 0); return (
@@ -66,26 +64,26 @@ export default function ProjectOverview() {
{nodes.length}
-
Nodes
+
{t.projectOverview.nodes}
{edges.length}
-
Edges
+
{t.projectOverview.edges}
{layers.length}
-
Layers
+
{t.projectOverview.layers}
{Object.keys(typeCounts).length}
-
Types
+
{t.projectOverview.types}
{/* File Types breakdown */} {hasNonCodeNodes && (
-

File Types

+

{t.projectOverview.fileTypes}

{categoryBreakdown.filter((c) => c.count > 0).map((cat) => (
@@ -104,7 +102,7 @@ export default function ProjectOverview() { {/* Languages */} {project.languages.length > 0 && (
-

Languages

+

{t.projectOverview.languages}

{project.languages.map((lang) => ( @@ -118,7 +116,7 @@ export default function ProjectOverview() { {/* Frameworks */} {project.frameworks.length > 0 && (
-

Frameworks

+

{t.projectOverview.frameworks}

{project.frameworks.map((fw) => ( @@ -131,7 +129,7 @@ export default function ProjectOverview() { {/* Node Type Breakdown */}
-

Node Type Distribution

+

{t.projectOverview.nodeTypeDistribution}

{Object.entries(typeCounts) .sort((a, b) => b[1] - a[1]) @@ -158,19 +156,19 @@ export default function ProjectOverview() { {/* Complexity Breakdown */} {Object.values(complexityCounts).some((c) => c > 0) && (
-

Complexity Distribution

+

{t.projectOverview.complexityDistribution}

{complexityCounts.simple}
-
Simple
+
{t.projectOverview.simple}
{complexityCounts.moderate}
-
Moderate
+
{t.projectOverview.moderate}
{complexityCounts.complex}
-
Complex
+
{t.projectOverview.complex}
@@ -179,7 +177,7 @@ export default function ProjectOverview() { {/* Top Connected Nodes */} {topNodes.length > 0 && (
-

Most Connected Nodes

+

{t.projectOverview.mostConnectedNodes}

{topNodes.map((node, idx) => (
- Avg Connections per Node + {t.projectOverview.avgConnectionsPerNode} {avgConnections}
{/* Analyzed at */}
- Analyzed: {new Date(project.analyzedAt).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })} + {t.common.analyzed}: {new Date(project.analyzedAt).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })}
{/* Start Tour button */} @@ -216,7 +214,7 @@ export default function ProjectOverview() { onClick={startTour} className="w-full bg-accent/10 border border-accent/30 text-accent text-sm font-medium py-2.5 px-4 rounded-lg hover:bg-accent/20 transition-all duration-200" > - Start Guided Tour + {t.common.startGuidedTour} )}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/SearchBar.tsx b/understand-anything-plugin/packages/dashboard/src/components/SearchBar.tsx index a7135e6..88ef3f5 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/SearchBar.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/SearchBar.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; const typeBadgeColors: Record = { file: "text-node-file border border-node-file/30 bg-node-file/10", @@ -28,6 +29,7 @@ export default function SearchBar() { const navigateToNodeInLayer = useDashboardStore((s) => s.navigateToNodeInLayer); const searchMode = useDashboardStore((s) => s.searchMode); const setSearchMode = useDashboardStore((s) => s.setSearchMode); + const { t } = useI18n(); const [dropdownOpen, setDropdownOpen] = useState(false); const containerRef = useRef(null); @@ -104,7 +106,8 @@ export default function SearchBar() { value={searchQuery} onChange={handleInputChange} onFocus={() => setDropdownOpen(true)} - placeholder="Search nodes by name, summary, or tags..." + placeholder={t.search.placeholder} + data-testid="search-input" className="flex-1 min-w-0 bg-elevated text-text-primary text-sm rounded-lg px-3 py-1.5 border border-border-subtle focus:outline-none focus:border-accent/50 placeholder-text-muted" />
@@ -116,7 +119,7 @@ export default function SearchBar() { : "text-text-muted hover:text-text-secondary" }`} > - Fuzzy + {t.search.fuzzy}
{searchQuery.trim() && ( - {searchResults.length} result{searchResults.length !== 1 ? "s" : ""}{" "} + {searchResults.length} {t.search.result}{searchResults.length !== 1 ? "s" : ""}{" "} ({searchMode}) )} diff --git a/understand-anything-plugin/packages/dashboard/src/components/ThemePicker.tsx b/understand-anything-plugin/packages/dashboard/src/components/ThemePicker.tsx index 49eea73..8c7e9e7 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/ThemePicker.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/ThemePicker.tsx @@ -1,11 +1,13 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { useTheme, PRESETS } from "../themes/index.ts"; import type { HeadingFont } from "../themes/index.ts"; +import { useI18n } from "../contexts/I18nContext"; export function ThemePicker() { const { config, preset, setPreset, setAccent, setHeadingFont } = useTheme(); const [open, setOpen] = useState(false); const ref = useRef(null); + const { t } = useI18n(); // Close on outside click useEffect(() => { @@ -41,7 +43,7 @@ export function ThemePicker() { {open && ( @@ -67,7 +69,7 @@ export function ThemePicker() { {/* Presets */}
- Theme + {t.themePicker.theme}
{PRESETS.map((p) => ( @@ -119,7 +121,7 @@ export function ThemePicker() { {/* Accent swatches */}
- Accent Color + {t.themePicker.accentColor}
{preset.accentSwatches.map((swatch) => ( @@ -141,13 +143,13 @@ export function ThemePicker() { {/* Heading font */}
- Heading Font + {t.themePicker.headingFont}
{([ - { id: "serif" as HeadingFont, label: "Serif", sample: "Aa" }, - { id: "sans" as HeadingFont, label: "Sans", sample: "Aa" }, - { id: "mono" as HeadingFont, label: "Mono", sample: "Aa" }, + { id: "serif" as HeadingFont, label: t.themePicker.serif, sample: "Aa" }, + { id: "sans" as HeadingFont, label: t.themePicker.sans, sample: "Aa" }, + { id: "mono" as HeadingFont, label: t.themePicker.mono, sample: "Aa" }, ]).map((opt) => (