codemap-python 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. {codemap_python-0.1.0 → codemap_python-0.1.1}/PKG-INFO +15 -9
  2. {codemap_python-0.1.0 → codemap_python-0.1.1}/README.md +12 -7
  3. {codemap_python-0.1.0 → codemap_python-0.1.1}/codemap_python.egg-info/PKG-INFO +15 -9
  4. {codemap_python-0.1.0 → codemap_python-0.1.1}/codemap_python.egg-info/requires.txt +2 -1
  5. {codemap_python-0.1.0 → codemap_python-0.1.1}/pyproject.toml +3 -2
  6. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/app.py +22 -1
  7. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/static/app.js +50 -0
  8. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/static/styles.css +127 -0
  9. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/templates/index.html +30 -0
  10. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/__init__.py +0 -0
  11. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/architecture/__init__.py +0 -0
  12. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/architecture/architecture_engine.py +0 -0
  13. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/architecture/dependency_cycles.py +0 -0
  14. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/architecture/risk_radar.py +0 -0
  15. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/__init__.py +0 -0
  16. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/call_extractor.py +0 -0
  17. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/call_graph_builder.py +0 -0
  18. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/call_resolver.py +0 -0
  19. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/context_models.py +0 -0
  20. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/cross_file_resolver.py +0 -0
  21. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/execution_tracker.py +0 -0
  22. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/flow_builder.py +0 -0
  23. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/call_graph/models.py +0 -0
  24. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/core/__init__.py +0 -0
  25. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/core/ast_context.py +0 -0
  26. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/core/ast_parser.py +0 -0
  27. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/core/class_extractor.py +0 -0
  28. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/core/function_extractor.py +0 -0
  29. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/core/import_extractor.py +0 -0
  30. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/__init__.py +0 -0
  31. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/docstring_extractor.py +0 -0
  32. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/explain_runner.py +0 -0
  33. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/repo_summary_generator.py +0 -0
  34. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/return_analyzer.py +0 -0
  35. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/risk_flags.py +0 -0
  36. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/signature_extractor.py +0 -0
  37. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/explain/summary_generator.py +0 -0
  38. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/graph/__init__.py +0 -0
  39. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/graph/callgraph_index.py +0 -0
  40. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/graph/entrypoint_detector.py +0 -0
  41. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/graph/impact_analyzer.py +0 -0
  42. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/indexing/__init__.py +0 -0
  43. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/indexing/import_resolver.py +0 -0
  44. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/indexing/symbol_index.py +0 -0
  45. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/runners/__init__.py +0 -0
  46. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/runners/phase4_runner.py +0 -0
  47. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/utils/__init__.py +0 -0
  48. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/utils/ast_helpers.py +0 -0
  49. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/utils/cache_manager.py +0 -0
  50. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/utils/path_resolver.py +0 -0
  51. {codemap_python-0.1.0 → codemap_python-0.1.1}/analysis/utils/repo_fetcher.py +0 -0
  52. {codemap_python-0.1.0 → codemap_python-0.1.1}/cli.py +0 -0
  53. {codemap_python-0.1.0 → codemap_python-0.1.1}/codemap_cli.py +0 -0
  54. {codemap_python-0.1.0 → codemap_python-0.1.1}/codemap_python.egg-info/SOURCES.txt +0 -0
  55. {codemap_python-0.1.0 → codemap_python-0.1.1}/codemap_python.egg-info/dependency_links.txt +0 -0
  56. {codemap_python-0.1.0 → codemap_python-0.1.1}/codemap_python.egg-info/entry_points.txt +0 -0
  57. {codemap_python-0.1.0 → codemap_python-0.1.1}/codemap_python.egg-info/top_level.txt +0 -0
  58. {codemap_python-0.1.0 → codemap_python-0.1.1}/security_utils.py +0 -0
  59. {codemap_python-0.1.0 → codemap_python-0.1.1}/setup.cfg +0 -0
  60. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_cache_cli_commands.py +0 -0
  61. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_cache_retention.py +0 -0
  62. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_no_key_persistence.py +0 -0
  63. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_registry_session_mode.py +0 -0
  64. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_security_cli_integration.py +0 -0
  65. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_security_redaction.py +0 -0
  66. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_symbol_explain_cache.py +0 -0
  67. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_symbol_info_endpoint.py +0 -0
  68. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_ui_private_mode_security.py +0 -0
  69. {codemap_python-0.1.0 → codemap_python-0.1.1}/tests/test_ui_retention_controls.py +0 -0
  70. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/__init__.py +0 -0
  71. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/device_id.py +0 -0
  72. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/utils/__init__.py +0 -0
  73. {codemap_python-0.1.0 → codemap_python-0.1.1}/ui/utils/registry_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codemap-python
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Local Python code analysis tool - understand architecture, dependencies, and call graphs
5
5
  Author-email: ADITYA <aditykushwaha69@gmail.com>
6
6
  License: MIT
@@ -22,8 +22,9 @@ Requires-Python: >=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  Requires-Dist: fastapi>=0.110
24
24
  Requires-Dist: uvicorn>=0.23
25
- Requires-Dist: jinja2>=3.1
25
+ Requires-Dist: jinja2>=3.1.4
26
26
  Requires-Dist: requests>=2.31
27
+ Requires-Dist: starlette==0.40.0
27
28
 
28
29
  # CodeMap
29
30
 
@@ -47,12 +48,8 @@ Requires-Dist: requests>=2.31
47
48
  ### Step 1️⃣: Install CodeMap
48
49
 
49
50
  ```bash
50
- # Clone the repository
51
- git clone https://github.com/ADITYA-kus/codemap_ai.git
52
- cd codemap_ai
53
-
54
- # Install as a local package
55
- pip install -e .
51
+ # Install from PyPI (easiest)
52
+ pip install codemap-python
56
53
  ```
57
54
 
58
55
  **Verify installation:**
@@ -65,6 +62,13 @@ You should see:
65
62
  usage: codemap [-h] {analyze,dashboard,open,cache} ...
66
63
  ```
67
64
 
65
+ > **For developers:** To modify source code or contribute, clone the repository:
66
+ > ```bash
67
+ > git clone https://github.com/ADITYA-kus/codemap_ai.git
68
+ > cd codemap_ai
69
+ > pip install -e .
70
+ > ```
71
+
68
72
  ---
69
73
 
70
74
  ### Step 2️⃣: Analyze Your First Repository
@@ -188,8 +192,10 @@ echo "YOUR_TOKEN" | codemap analyze --github https://github.com/owner/repo --tok
188
192
 
189
193
  ## Directory Structure
190
194
 
195
+ This is the source code structure for developers. **If you installed via pip, these files are automatically installed in your Python environment.**
196
+
191
197
  ```
192
- codemap_ai_clean/
198
+ codemap_ai/ (source code)
193
199
  ├── README.md # This file
194
200
  ├── cli.py # Command-line interface
195
201
  ├── security_utils.py # Security & secret redaction
@@ -20,12 +20,8 @@
20
20
  ### Step 1️⃣: Install CodeMap
21
21
 
22
22
  ```bash
23
- # Clone the repository
24
- git clone https://github.com/ADITYA-kus/codemap_ai.git
25
- cd codemap_ai
26
-
27
- # Install as a local package
28
- pip install -e .
23
+ # Install from PyPI (easiest)
24
+ pip install codemap-python
29
25
  ```
30
26
 
31
27
  **Verify installation:**
@@ -38,6 +34,13 @@ You should see:
38
34
  usage: codemap [-h] {analyze,dashboard,open,cache} ...
39
35
  ```
40
36
 
37
+ > **For developers:** To modify source code or contribute, clone the repository:
38
+ > ```bash
39
+ > git clone https://github.com/ADITYA-kus/codemap_ai.git
40
+ > cd codemap_ai
41
+ > pip install -e .
42
+ > ```
43
+
41
44
  ---
42
45
 
43
46
  ### Step 2️⃣: Analyze Your First Repository
@@ -161,8 +164,10 @@ echo "YOUR_TOKEN" | codemap analyze --github https://github.com/owner/repo --tok
161
164
 
162
165
  ## Directory Structure
163
166
 
167
+ This is the source code structure for developers. **If you installed via pip, these files are automatically installed in your Python environment.**
168
+
164
169
  ```
165
- codemap_ai_clean/
170
+ codemap_ai/ (source code)
166
171
  ├── README.md # This file
167
172
  ├── cli.py # Command-line interface
168
173
  ├── security_utils.py # Security & secret redaction
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codemap-python
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Local Python code analysis tool - understand architecture, dependencies, and call graphs
5
5
  Author-email: ADITYA <aditykushwaha69@gmail.com>
6
6
  License: MIT
@@ -22,8 +22,9 @@ Requires-Python: >=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  Requires-Dist: fastapi>=0.110
24
24
  Requires-Dist: uvicorn>=0.23
25
- Requires-Dist: jinja2>=3.1
25
+ Requires-Dist: jinja2>=3.1.4
26
26
  Requires-Dist: requests>=2.31
27
+ Requires-Dist: starlette==0.40.0
27
28
 
28
29
  # CodeMap
29
30
 
@@ -47,12 +48,8 @@ Requires-Dist: requests>=2.31
47
48
  ### Step 1️⃣: Install CodeMap
48
49
 
49
50
  ```bash
50
- # Clone the repository
51
- git clone https://github.com/ADITYA-kus/codemap_ai.git
52
- cd codemap_ai
53
-
54
- # Install as a local package
55
- pip install -e .
51
+ # Install from PyPI (easiest)
52
+ pip install codemap-python
56
53
  ```
57
54
 
58
55
  **Verify installation:**
@@ -65,6 +62,13 @@ You should see:
65
62
  usage: codemap [-h] {analyze,dashboard,open,cache} ...
66
63
  ```
67
64
 
65
+ > **For developers:** To modify source code or contribute, clone the repository:
66
+ > ```bash
67
+ > git clone https://github.com/ADITYA-kus/codemap_ai.git
68
+ > cd codemap_ai
69
+ > pip install -e .
70
+ > ```
71
+
68
72
  ---
69
73
 
70
74
  ### Step 2️⃣: Analyze Your First Repository
@@ -188,8 +192,10 @@ echo "YOUR_TOKEN" | codemap analyze --github https://github.com/owner/repo --tok
188
192
 
189
193
  ## Directory Structure
190
194
 
195
+ This is the source code structure for developers. **If you installed via pip, these files are automatically installed in your Python environment.**
196
+
191
197
  ```
192
- codemap_ai_clean/
198
+ codemap_ai/ (source code)
193
199
  ├── README.md # This file
194
200
  ├── cli.py # Command-line interface
195
201
  ├── security_utils.py # Security & secret redaction
@@ -1,4 +1,5 @@
1
1
  fastapi>=0.110
2
2
  uvicorn>=0.23
3
- jinja2>=3.1
3
+ jinja2>=3.1.4
4
4
  requests>=2.31
5
+ starlette==0.40.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codemap-python"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Local Python code analysis tool - understand architecture, dependencies, and call graphs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -36,8 +36,9 @@ classifiers = [
36
36
  dependencies = [
37
37
  "fastapi>=0.110",
38
38
  "uvicorn>=0.23",
39
- "jinja2>=3.1",
39
+ "jinja2>=3.1.4",
40
40
  "requests>=2.31",
41
+ "starlette==0.40.0",
41
42
  ]
42
43
 
43
44
  [project.urls]
@@ -35,6 +35,23 @@ from ui.utils.registry_manager import (
35
35
  from security_utils import redact_payload, redact_secrets
36
36
 
37
37
 
38
+ # Custom cache class that doesn't cache (to avoid TypeError with unhashable Request objects)
39
+ class NoCache:
40
+ """A cache implementation that doesn't cache anything.
41
+
42
+ This prevents Jinja2 from trying to cache templates with unhashable objects
43
+ like the Starlette Request in the context.
44
+ """
45
+ def get(self, key: Any, default: Any = None) -> Any:
46
+ return default
47
+
48
+ def set(self, key: Any, value: Any, timeout: Any = None) -> None:
49
+ pass
50
+
51
+ def clear(self) -> None:
52
+ pass
53
+
54
+
38
55
  PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__))
39
56
  ANALYSIS_ROOT = os.path.join(PROJECT_ROOT, "analysis")
40
57
  DEFAULT_REPO = os.getenv("CODEMAP_UI_REPO", "testing_repo")
@@ -50,7 +67,11 @@ _SESSION_WORKSPACE_READY = False
50
67
 
51
68
 
52
69
  app = FastAPI(title="CodeMap UI")
53
- templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "templates"))
70
+ templates_dir = os.path.join(os.path.dirname(__file__), "templates")
71
+ templates = Jinja2Templates(directory=templates_dir)
72
+ # Disable Jinja2 template caching to prevent TypeError with unhashable Request objects
73
+ # This avoids issues when Jinja2 tries to cache templates containing the Request context
74
+ templates.env.cache = NoCache()
54
75
  app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")), name="static")
55
76
  SEARCH_INDEX_CACHE: Dict[str, List[Dict[str, Any]]] = {}
56
77
  GRAPH_INDEX_CACHE: Dict[str, Dict[str, Any]] = {}
@@ -5,6 +5,9 @@
5
5
  const repoSelectEl = document.getElementById("repo-select");
6
6
  const addRepoBtnEl = document.getElementById("add-repo-btn");
7
7
  const aiSettingsBtnEl = document.getElementById("ai-settings-btn");
8
+ const themeToggleBtnEl = document.getElementById("theme-toggle-btn");
9
+ const themeWhiteOptionEl = document.getElementById("theme-white");
10
+ const themeDarkOptionEl = document.getElementById("theme-dark");
8
11
  const repoListModePillEl = document.getElementById("repo-list-mode-pill");
9
12
  const treeStatusEl = document.getElementById("tree-status");
10
13
  const treeEl = document.getElementById("tree");
@@ -2014,6 +2017,53 @@
2014
2017
  }
2015
2018
 
2016
2019
  function bindAiSettingsControls() {
2020
+ // Theme toggle handlers
2021
+ const setTheme = (themeName) => {
2022
+ localStorage.setItem("codemap-theme", themeName);
2023
+ if (themeName === "dark") {
2024
+ document.documentElement.classList.add("dark-theme");
2025
+ document.body.classList.add("dark-theme");
2026
+ if (themeToggleBtnEl) themeToggleBtnEl.textContent = "☀️ Light";
2027
+ if (themeDarkOptionEl) {
2028
+ themeDarkOptionEl.classList.add("active");
2029
+ }
2030
+ if (themeWhiteOptionEl) {
2031
+ themeWhiteOptionEl.classList.remove("active");
2032
+ }
2033
+ } else {
2034
+ document.documentElement.classList.remove("dark-theme");
2035
+ document.body.classList.remove("dark-theme");
2036
+ if (themeToggleBtnEl) themeToggleBtnEl.textContent = "🌙 Dark";
2037
+ if (themeWhiteOptionEl) {
2038
+ themeWhiteOptionEl.classList.add("active");
2039
+ }
2040
+ if (themeDarkOptionEl) {
2041
+ themeDarkOptionEl.classList.remove("active");
2042
+ }
2043
+ }
2044
+ };
2045
+
2046
+ // Initialize theme from localStorage (default is DARK)
2047
+ const savedTheme = localStorage.getItem("codemap-theme") || "dark";
2048
+ setTheme(savedTheme);
2049
+
2050
+ // Theme toggle button (top bar)
2051
+ if (themeToggleBtnEl) {
2052
+ themeToggleBtnEl.addEventListener("click", () => {
2053
+ const isDark = document.documentElement.classList.contains("dark-theme");
2054
+ setTheme(isDark ? "light" : "dark");
2055
+ });
2056
+ }
2057
+
2058
+ // Theme option buttons (settings modal)
2059
+ if (themeWhiteOptionEl) {
2060
+ themeWhiteOptionEl.addEventListener("click", () => setTheme("light"));
2061
+ }
2062
+ if (themeDarkOptionEl) {
2063
+ themeDarkOptionEl.addEventListener("click", () => setTheme("dark"));
2064
+ }
2065
+
2066
+ // Original AI settings handlers
2017
2067
  if (aiSettingsBtnEl) {
2018
2068
  aiSettingsBtnEl.addEventListener("click", async () => {
2019
2069
  await openAiSettingsModal();
@@ -9,6 +9,18 @@
9
9
  --active-border: #4f84ff;
10
10
  }
11
11
 
12
+ html.dark-theme,
13
+ body.dark-theme {
14
+ --bg: #1a1a1a;
15
+ --panel: #252525;
16
+ --line: #404040;
17
+ --text: #e5e5e5;
18
+ --muted: #a0a0a0;
19
+ --accent: #4fa3ff;
20
+ --active-bg: #2a3a4a;
21
+ --active-border: #5fa3ff;
22
+ }
23
+
12
24
  * {
13
25
  box-sizing: border-box;
14
26
  }
@@ -1200,6 +1212,121 @@ body.modal-open {
1200
1212
  margin: 10px 0;
1201
1213
  }
1202
1214
 
1215
+ .theme-selector {
1216
+ display: flex;
1217
+ gap: 10px;
1218
+ margin-bottom: 10px;
1219
+ padding: 8px;
1220
+ background: var(--panel);
1221
+ border-radius: 8px;
1222
+ border: 1px solid var(--line);
1223
+ }
1224
+
1225
+ .theme-option {
1226
+ flex: 1;
1227
+ display: flex;
1228
+ flex-direction: column;
1229
+ align-items: center;
1230
+ gap: 6px;
1231
+ padding: 12px;
1232
+ border: 2px solid var(--line);
1233
+ border-radius: 8px;
1234
+ background: var(--panel);
1235
+ color: var(--text);
1236
+ cursor: pointer;
1237
+ transition: all 200ms ease;
1238
+ font-size: 12px;
1239
+ font-weight: 500;
1240
+ }
1241
+
1242
+ .theme-option:hover {
1243
+ border-color: var(--accent);
1244
+ box-shadow: 0 0 0 3px rgba(79, 132, 255, 0.15);
1245
+ transform: translateY(-1px);
1246
+ }
1247
+
1248
+ .theme-option.active {
1249
+ border-color: var(--accent);
1250
+ background: var(--active-bg);
1251
+ color: #0f3f9c;
1252
+ font-weight: 600;
1253
+ box-shadow: 0 0 0 3px rgba(79, 132, 255, 0.2);
1254
+ }
1255
+
1256
+ body.dark-theme .theme-option.active {
1257
+ background: #3a4a5f;
1258
+ color: #4fa3ff;
1259
+ border-color: #4fa3ff;
1260
+ }
1261
+
1262
+ .theme-icon {
1263
+ font-size: 20px;
1264
+ line-height: 1;
1265
+ }
1266
+
1267
+ .theme-label {
1268
+ font-size: 11px;
1269
+ text-transform: uppercase;
1270
+ letter-spacing: 0.04em;
1271
+ font-weight: 600;
1272
+ }
1273
+
1274
+ /* Dark theme specific styles */
1275
+ html.dark-theme .topbar,
1276
+ body.dark-theme .topbar {
1277
+ background: #2a2a2a;
1278
+ }
1279
+
1280
+ html.dark-theme .card,
1281
+ html.dark-theme .repo-row,
1282
+ html.dark-theme .confirm-card,
1283
+ html.dark-theme .modal-form input,
1284
+ html.dark-theme .modal-form select,
1285
+ html.dark-theme .repo-select,
1286
+ html.dark-theme .repo-btn,
1287
+ html.dark-theme .tab-btn,
1288
+ html.dark-theme .graph-controls select,
1289
+ html.dark-theme .graph-controls input[type="text"],
1290
+ html.dark-theme .search-results,
1291
+ html.dark-theme .graph-node,
1292
+ html.dark-theme .kpi-card,
1293
+ html.dark-theme .risk-file-row,
1294
+ html.dark-theme .risk-target,
1295
+ html.dark-theme .impact-file-row,
1296
+ body.dark-theme .card,
1297
+ body.dark-theme .repo-row,
1298
+ body.dark-theme .confirm-card,
1299
+ body.dark-theme .modal-form input,
1300
+ body.dark-theme .modal-form select,
1301
+ body.dark-theme .repo-select,
1302
+ body.dark-theme .repo-btn,
1303
+ body.dark-theme .tab-btn,
1304
+ body.dark-theme .graph-controls select,
1305
+ body.dark-theme .graph-controls input[type="text"],
1306
+ body.dark-theme .search-results,
1307
+ body.dark-theme .graph-node,
1308
+ body.dark-theme .kpi-card,
1309
+ body.dark-theme .risk-file-row,
1310
+ body.dark-theme .risk-target,
1311
+ body.dark-theme .impact-file-row {
1312
+ background: var(--panel);
1313
+ color: var(--text);
1314
+ border-color: var(--line);
1315
+ }
1316
+
1317
+ html.dark-theme #symbol-search-input,
1318
+ body.dark-theme #symbol-search-input {
1319
+ background: var(--panel);
1320
+ color: var(--text);
1321
+ border-color: var(--line);
1322
+ }
1323
+
1324
+ html.dark-theme .toast,
1325
+ body.dark-theme .toast {
1326
+ background: #2a2a2a;
1327
+ color: #e5e5e5;
1328
+ }
1329
+
1203
1330
  @media (max-width: 1000px) {
1204
1331
  .layout {
1205
1332
  grid-template-columns: 1fr;
@@ -5,6 +5,20 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>CodeMap UI</title>
7
7
  <link rel="stylesheet" href="/static/styles.css" />
8
+ <script>
9
+ // Apply theme IMMEDIATELY on page load, before rendering
10
+ (function() {
11
+ try {
12
+ const theme = localStorage.getItem("codemap-theme") || "dark";
13
+ if (theme === "dark") {
14
+ document.documentElement.classList.add("dark-theme");
15
+ }
16
+ } catch (e) {
17
+ // fallback: use dark as default
18
+ document.documentElement.classList.add("dark-theme");
19
+ }
20
+ })();
21
+ </script>
8
22
  </head>
9
23
  <body>
10
24
  <header class="topbar">
@@ -18,6 +32,7 @@
18
32
  <div class="workspace-controls">
19
33
  <select id="repo-select" class="repo-select"></select>
20
34
  <button id="add-repo-btn" class="repo-btn" type="button">+ Add repo...</button>
35
+ <button id="theme-toggle-btn" class="repo-btn" type="button" title="Toggle theme">🌙 Dark</button>
21
36
  <button id="ai-settings-btn" class="repo-btn" type="button" onclick="var m=document.getElementById('ai-settings-modal'); if(m){ m.classList.remove('hidden'); document.body.classList.add('modal-open'); }">Settings</button>
22
37
  <span id="repo-list-mode-pill" class="repo-badge">Session Mode</span>
23
38
  </div>
@@ -73,6 +88,21 @@
73
88
  <div id="ai-settings-modal" class="confirm-modal hidden">
74
89
  <div class="confirm-card settings-card">
75
90
  <div class="section-title">Settings</div>
91
+ <div class="path">Appearance</div>
92
+ <label class="path">
93
+ <span>Theme</span>
94
+ </label>
95
+ <div class="theme-selector">
96
+ <button id="theme-white" class="theme-option" type="button" data-theme="light">
97
+ <span class="theme-icon">☀️</span>
98
+ <span class="theme-label">Light</span>
99
+ </button>
100
+ <button id="theme-dark" class="theme-option active" type="button" data-theme="dark">
101
+ <span class="theme-icon">🌙</span>
102
+ <span class="theme-label">Dark</span>
103
+ </button>
104
+ </div>
105
+ <div class="divider"></div>
76
106
  <div class="path">General</div>
77
107
  <label class="path">
78
108
  <input id="ai-settings-remember-repos" type="checkbox" />
File without changes
File without changes