Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
Son Of Anton
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Son Of Anton
Commits
841414df
Commit
841414df
authored
Mar 30, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
test
parent
37b9873f
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
814 additions
and
86 deletions
+814
-86
main.py
backend/main.py
+14
-0
models.py
backend/models.py
+3
-0
gitlab_routes.py
backend/routes/gitlab_routes.py
+90
-18
code_analyzer.py
backend/services/code_analyzer.py
+657
-0
generation_manager.py
backend/services/generation_manager.py
+22
-55
api.js
frontend/src/api.js
+2
-0
ChatView.jsx
frontend/src/components/ChatView.jsx
+26
-13
No files found.
backend/main.py
View file @
841414df
...
...
@@ -53,6 +53,20 @@ def _run_migrations():
if
table_name
not
in
existing_tables
:
print
(
f
" Creating {table_name} table"
)
if
"linked_repos"
in
existing_tables
:
lr_columns
=
{
c
[
"name"
]
for
c
in
inspector
.
get_columns
(
"linked_repos"
)}
with
engine
.
connect
()
as
conn
:
if
"architecture_map"
not
in
lr_columns
:
conn
.
execute
(
text
(
"ALTER TABLE linked_repos ADD COLUMN architecture_map TEXT"
))
print
(
" Added linked_repos.architecture_map column"
)
if
"map_status"
not
in
lr_columns
:
conn
.
execute
(
text
(
"ALTER TABLE linked_repos ADD COLUMN map_status VARCHAR(20) DEFAULT 'none'"
))
print
(
" Added linked_repos.map_status column"
)
if
"map_generated_at"
not
in
lr_columns
:
conn
.
execute
(
text
(
"ALTER TABLE linked_repos ADD COLUMN map_generated_at DATETIME"
))
print
(
" Added linked_repos.map_generated_at column"
)
conn
.
commit
()
except
Exception
as
e
:
print
(
f
" Migration note: {e}"
)
...
...
backend/models.py
View file @
841414df
...
...
@@ -151,6 +151,9 @@ class LinkedRepo(Base):
default_branch
=
Column
(
String
(
100
),
default
=
"main"
)
web_url
=
Column
(
String
(
500
),
default
=
""
)
description
=
Column
(
Text
,
default
=
""
)
architecture_map
=
Column
(
Text
,
nullable
=
True
)
map_status
=
Column
(
String
(
20
),
default
=
"none"
)
map_generated_at
=
Column
(
DateTime
,
nullable
=
True
)
created_at
=
Column
(
DateTime
,
default
=
datetime
.
utcnow
)
actions
=
relationship
(
"PendingAction"
,
back_populates
=
"repo"
,
cascade
=
"all,delete-orphan"
)
...
...
backend/routes/gitlab_routes.py
View file @
841414df
...
...
@@ -3,6 +3,7 @@ GitLab CE integration routes — superadmin only.
Son of Anton v4.0.0
"""
import
asyncio
import
json
from
datetime
import
datetime
from
typing
import
Optional
...
...
@@ -14,7 +15,7 @@ from sqlalchemy.orm import Session
from
backend.database
import
get_db
from
backend.models
import
User
,
GitLabSettings
,
LinkedRepo
,
PendingAction
from
backend.auth
import
require_superadmin
from
backend.services
import
gitlab_service
from
backend.services
import
gitlab_service
,
code_analyzer
router
=
APIRouter
()
...
...
@@ -181,10 +182,18 @@ async def link_repo(body: LinkRepoBody, admin: User = Depends(require_superadmin
default_branch
=
project
.
get
(
"default_branch"
,
"main"
),
web_url
=
project
.
get
(
"web_url"
,
""
),
description
=
project
.
get
(
"description"
,
""
),
map_status
=
"analyzing"
,
)
db
.
add
(
repo
)
db
.
commit
()
db
.
refresh
(
repo
)
# Start background analysis
asyncio
.
create_task
(
_analyze_repo_background
(
repo
.
id
,
s
.
gitlab_url
,
s
.
gitlab_token
,
project
[
"id"
],
project
.
get
(
"default_branch"
,
"main"
),
))
return
_repo_dict
(
repo
)
...
...
@@ -196,6 +205,84 @@ def unlink_repo(repo_id: str, admin: User = Depends(require_superadmin), db: Ses
return
{
"ok"
:
True
}
# ═══════════════════════════════════════════════════
# Architecture Map
# ═══════════════════════════════════════════════════
async
def
_analyze_repo_background
(
repo_id
:
str
,
gitlab_url
:
str
,
gitlab_token
:
str
,
project_id
:
int
,
branch
:
str
):
"""Background task: load all files and generate architecture map."""
from
backend.database
import
SessionLocal
as
BgSession
db
=
BgSession
()
try
:
repo
=
db
.
query
(
LinkedRepo
)
.
filter
(
LinkedRepo
.
id
==
repo_id
)
.
first
()
if
not
repo
:
return
repo
.
map_status
=
"analyzing"
db
.
commit
()
result
=
await
gitlab_service
.
load_project_files
(
gitlab_url
,
gitlab_token
,
project_id
,
ref
=
branch
,
)
files
=
result
.
get
(
"files"
,
[])
if
not
files
:
repo
.
map_status
=
"failed"
repo
.
architecture_map
=
"[No files could be loaded for analysis]"
db
.
commit
()
return
architecture_map
=
code_analyzer
.
analyze_codebase
(
files
)
repo
.
architecture_map
=
architecture_map
repo
.
map_status
=
"ready"
repo
.
map_generated_at
=
datetime
.
utcnow
()
db
.
commit
()
print
(
f
" ✅ Architecture map generated for {repo.name} ({len(architecture_map)} chars)"
)
except
Exception
as
e
:
try
:
repo
=
db
.
query
(
LinkedRepo
)
.
filter
(
LinkedRepo
.
id
==
repo_id
)
.
first
()
if
repo
:
repo
.
map_status
=
"failed"
repo
.
architecture_map
=
f
"[Analysis failed: {str(e)[:200]}]"
db
.
commit
()
except
Exception
:
pass
print
(
f
" ❌ Architecture analysis failed for repo {repo_id}: {e}"
)
finally
:
db
.
close
()
@
router
.
post
(
"/repos/{repo_id}/analyze"
)
async
def
reanalyze_repo
(
repo_id
:
str
,
admin
:
User
=
Depends
(
require_superadmin
),
db
:
Session
=
Depends
(
get_db
)):
"""Re-generate the architecture map for a linked repo."""
s
=
_get_settings
(
db
)
repo
=
_get_repo
(
repo_id
,
db
)
repo
.
map_status
=
"analyzing"
db
.
commit
()
asyncio
.
create_task
(
_analyze_repo_background
(
repo
.
id
,
s
.
gitlab_url
,
s
.
gitlab_token
,
repo
.
gitlab_project_id
,
repo
.
default_branch
,
))
return
{
"ok"
:
True
,
"status"
:
"analyzing"
}
@
router
.
get
(
"/repos/{repo_id}/map"
)
def
get_repo_map
(
repo_id
:
str
,
admin
:
User
=
Depends
(
require_superadmin
),
db
:
Session
=
Depends
(
get_db
)):
"""Get the architecture map for a linked repo."""
repo
=
_get_repo
(
repo_id
,
db
)
return
{
"map_status"
:
repo
.
map_status
or
"none"
,
"map_generated_at"
:
str
(
repo
.
map_generated_at
)
if
repo
.
map_generated_at
else
None
,
"architecture_map"
:
repo
.
architecture_map
or
""
,
"map_size"
:
len
(
repo
.
architecture_map
or
""
),
}
# ═══════════════════════════════════════════════════
# Repository Operations
# ═══════════════════════════════════════════════════
...
...
@@ -300,23 +387,6 @@ async def create_mr(repo_id: str, body: MergeRequestBody, admin: User = Depends(
raise
HTTPException
(
e
.
status_code
,
e
.
detail
)
@
router
.
get
(
"/repos/{repo_id}/analyze"
)
async
def
analyze_project
(
repo_id
:
str
,
ref
:
Optional
[
str
]
=
Query
(
None
),
admin
:
User
=
Depends
(
require_superadmin
),
db
:
Session
=
Depends
(
get_db
),
):
s
=
_get_settings
(
db
)
repo
=
_get_repo
(
repo_id
,
db
)
branch
=
ref
or
repo
.
default_branch
try
:
result
=
await
gitlab_service
.
load_project_files
(
s
.
gitlab_url
,
s
.
gitlab_token
,
repo
.
gitlab_project_id
,
ref
=
branch
)
return
result
except
gitlab_service
.
GitLabError
as
e
:
raise
HTTPException
(
e
.
status_code
,
e
.
detail
)
# ═══════════════════════════════════════════════════
# Pending Actions
# ═══════════════════════════════════════════════════
...
...
@@ -419,6 +489,8 @@ def _repo_dict(r: LinkedRepo) -> dict:
"default_branch"
:
r
.
default_branch
,
"web_url"
:
r
.
web_url
,
"description"
:
r
.
description
,
"map_status"
:
r
.
map_status
or
"none"
,
"map_generated_at"
:
str
(
r
.
map_generated_at
)
if
r
.
map_generated_at
else
None
,
"created_at"
:
str
(
r
.
created_at
),
}
...
...
backend/services/code_analyzer.py
0 → 100644
View file @
841414df
This diff is collapsed.
Click to expand it.
backend/services/generation_manager.py
View file @
841414df
"""
Background generation manager — v4.1.0
Smart codebase loading for massive repos + persistent file context.
Smart codebase loading for massive repos + persistent file context
+ architecture mindmap
.
"""
import
asyncio
...
...
@@ -19,11 +19,9 @@ from backend.services import bedrock_service, memory_service, rag_service, attac
# Caches
# ═══════════════════════════════════════════════════
# Tree cache: repo_id:branch → (timestamp, tree_list)
_tree_cache
:
dict
[
str
,
tuple
[
float
,
list
[
dict
]]]
=
{}
TREE_CACHE_TTL
=
600
# 10 minutes
TREE_CACHE_TTL
=
600
# Tracks which files have been discussed per chat
_chat_file_history
:
dict
[
str
,
set
[
str
]]
=
{}
...
...
@@ -103,7 +101,6 @@ class GenerationManager:
await
asyncio
.
sleep
(
0.02
)
def
invalidate_repo_cache
(
self
,
repo_id
:
str
):
"""Call after a commit to force-refresh on next message."""
keys_to_remove
=
[
k
for
k
in
_tree_cache
if
k
.
startswith
(
f
"{repo_id}:"
)]
for
k
in
keys_to_remove
:
_tree_cache
.
pop
(
k
,
None
)
...
...
@@ -115,13 +112,6 @@ class GenerationManager:
async
def
_build_repo_context
(
self
,
db
,
chat
,
user_query
:
str
)
->
Optional
[
str
]:
"""
Build repo context using smart file selection.
For ANY size codebase:
1. Full file tree (paths only) — always included
2. Priority files (configs, entry points) — always loaded
3. Query-relevant files — loaded based on what user asked
"""
if
not
chat
.
linked_repo_id
:
return
None
...
...
@@ -138,7 +128,6 @@ class GenerationManager:
branch
=
repo
.
default_branch
try
:
# 1. Get tree (cached)
tree
=
_get_tree_cache
(
repo
.
id
,
branch
)
if
tree
is
None
:
tree
=
await
gitlab_service
.
get_tree
(
...
...
@@ -147,10 +136,8 @@ class GenerationManager:
)
_set_tree_cache
(
repo
.
id
,
branch
,
tree
)
# 2. Get previously discussed files for this chat
prev_files
=
_chat_file_history
.
get
(
chat
.
id
,
set
())
# 3. Smart-load files
result
=
await
gitlab_service
.
load_smart_files
(
gl_url
,
gl_token
,
repo
.
gitlab_project_id
,
ref
=
branch
,
tree
=
tree
,
...
...
@@ -158,7 +145,6 @@ class GenerationManager:
previous_files
=
prev_files
,
)
# 4. Track loaded files for future messages
loaded_paths
=
set
()
for
f
in
result
[
"priority_files"
]:
loaded_paths
.
add
(
f
[
"path"
])
...
...
@@ -168,11 +154,9 @@ class GenerationManager:
_chat_file_history
[
chat
.
id
]
=
set
()
_chat_file_history
[
chat
.
id
]
.
update
(
loaded_paths
)
# 5. Format the context
return
self
.
_format_smart_context
(
result
,
tree
,
repo
)
return
self
.
_format_smart_context
(
result
,
tree
,
repo
,
db
)
except
Exception
as
e
:
# Fallback: just the tree
try
:
tree
=
await
gitlab_service
.
get_tree
(
gl_url
,
gl_token
,
repo
.
gitlab_project_id
,
ref
=
branch
,
...
...
@@ -182,16 +166,10 @@ class GenerationManager:
return
f
"[Repository: {repo.name} — error: {str(e)[:200]}]"
def
_format_smart_context
(
self
,
result
:
dict
,
tree
:
list
[
dict
],
repo
self
,
result
:
dict
,
tree
:
list
[
dict
],
repo
,
db
)
->
str
:
"""Format loaded files into prompt context."""
# File tree
files_in_tree
=
sorted
(
[
i
[
"path"
]
for
i
in
tree
if
i
[
"type"
]
==
"blob"
]
)
dirs_in_tree
=
sorted
(
[
i
[
"path"
]
for
i
in
tree
if
i
[
"type"
]
==
"tree"
]
)
files_in_tree
=
sorted
([
i
[
"path"
]
for
i
in
tree
if
i
[
"type"
]
==
"blob"
])
dirs_in_tree
=
sorted
([
i
[
"path"
]
for
i
in
tree
if
i
[
"type"
]
==
"tree"
])
lines
=
[
f
"Repository: {repo.name}"
,
...
...
@@ -200,44 +178,45 @@ class GenerationManager:
f
"Total files: {len(files_in_tree)} | Directories: {len(dirs_in_tree)}"
,
f
"Files loaded into context: {result['files_loaded']}"
,
f
"Characters loaded: {result['total_characters']:,}"
,
""
,
"═"
*
60
,
"COMPLETE FILE TREE (all paths):"
,
"═"
*
60
,
]
# Architecture map
if
repo
.
architecture_map
and
repo
.
map_status
==
"ready"
:
lines
.
append
(
""
)
lines
.
append
(
repo
.
architecture_map
)
lines
.
append
(
""
)
# File tree
lines
.
append
(
"═"
*
60
)
lines
.
append
(
"COMPLETE FILE TREE:"
)
lines
.
append
(
"═"
*
60
)
for
fp
in
files_in_tree
:
lines
.
append
(
f
" {fp}"
)
# File contents
lines
.
append
(
""
)
lines
.
append
(
"═"
*
60
)
lines
.
append
(
"LOADED FILE CONTENTS:"
)
lines
.
append
(
"═"
*
60
)
# Priority files
if
result
[
"priority_files"
]:
lines
.
append
(
""
)
lines
.
append
(
"── Config & Entry Point Files ──"
)
lines
.
append
(
"
\n
── Config & Entry Point Files ──"
)
for
f
in
result
[
"priority_files"
]:
lines
.
append
(
f
"
\n
━━━ {f['path']} ━━━"
)
lines
.
append
(
f
[
"content"
])
lines
.
append
(
f
"━━━ end {f['path']} ━━━"
)
# Query-relevant files
if
result
[
"query_files"
]:
lines
.
append
(
""
)
lines
.
append
(
"── Files Relevant to Current Question ──"
)
lines
.
append
(
"
\n
── Files Relevant to Current Question ──"
)
for
f
in
result
[
"query_files"
]:
lines
.
append
(
f
"
\n
━━━ {f['path']} ━━━"
)
lines
.
append
(
f
[
"content"
])
lines
.
append
(
f
"━━━ end {f['path']} ━━━"
)
# Note about unloaded files
unloaded
=
len
(
files_in_tree
)
-
result
[
"files_loaded"
]
if
unloaded
>
0
:
lines
.
append
(
""
)
lines
.
append
(
f
"NOTE: {unloaded} additional files exist in the repository."
)
lines
.
append
(
"If you need to see a specific file, ask the user to mention it by name."
)
lines
.
append
(
"You can see ALL file paths in the tree above."
)
lines
.
append
(
f
"
\n
NOTE: {unloaded} additional files exist but are not loaded."
)
lines
.
append
(
"Mention specific file names to have them loaded in the next message."
)
return
"
\n
"
.
join
(
lines
)
...
...
@@ -266,7 +245,6 @@ class GenerationManager:
db_user
=
db
.
query
(
User
)
.
filter
(
User
.
id
==
user_id
)
.
first
()
# Quota reset
now
=
datetime
.
utcnow
()
if
db_user
.
quota_reset_date
and
now
>=
db_user
.
quota_reset_date
:
db_user
.
tokens_used_this_month
=
0
...
...
@@ -280,7 +258,6 @@ class GenerationManager:
state
.
events
.
append
({
"type"
:
"error"
,
"message"
:
"Monthly token quota exceeded."
})
return
# Process attachments
attachments
=
[]
if
attachment_ids
:
attachments
=
(
...
...
@@ -305,7 +282,6 @@ class GenerationManager:
if
attachments
:
db
.
commit
()
# RAG
kb_id
=
knowledge_base_id
or
chat
.
knowledge_base_id
rag_context
=
None
if
kb_id
:
...
...
@@ -314,29 +290,22 @@ class GenerationManager:
except
Exception
:
pass
# ── SMART REPO CONTEXT (query-aware file loading) ──
repo_context
=
await
self
.
_build_repo_context
(
db
,
chat
,
content
)
# ── PERSISTENT ATTACHMENT CONTEXT ──
attachment_context
=
memory_service
.
gather_attachment_context
(
chat_id
,
db
)
# Build system prompt
system_prompt
=
build_full_prompt
(
rag_context
=
rag_context
,
repo_context
=
repo_context
,
attachment_context
=
attachment_context
,
)
# Build conversation messages
messages
=
memory_service
.
build_messages
(
chat
,
db
)
# Inject multimodal content blocks for current attachments
if
attachments
and
messages
and
messages
[
-
1
][
"role"
]
==
"user"
:
content_blocks
=
attachment_service
.
build_claude_content_blocks
(
attachments
)
content_blocks
.
append
({
"type"
:
"text"
,
"text"
:
content
})
messages
[
-
1
][
"content"
]
=
content_blocks
# Thinking config
effective_max
=
max_tokens
thinking_config
=
None
if
reasoning_budget
>
0
:
...
...
@@ -387,7 +356,6 @@ class GenerationManager:
usage
=
event
.
get
(
"usage"
,
{})
output_tokens
=
usage
.
get
(
"output_tokens"
,
0
)
# Save assistant message
assistant_msg
=
Message
(
chat_id
=
chat_id
,
role
=
"assistant"
,
content
=
full_text
,
thinking_content
=
full_thinking
or
None
,
...
...
@@ -404,7 +372,6 @@ class GenerationManager:
state
.
message_id
=
assistant_msg
.
id
# Auto-title
msg_count
=
db
.
query
(
Message
)
.
filter
(
Message
.
chat_id
==
chat_id
)
.
count
()
if
msg_count
<=
2
and
chat
.
title
==
"New Chat"
:
try
:
...
...
frontend/src/api.js
View file @
841414df
...
...
@@ -176,6 +176,8 @@ export const gitlabCommitSingle = (token, repoId, data) => request("POST", `/git
export
const
gitlabCreateMR
=
(
token
,
repoId
,
data
)
=>
request
(
"POST"
,
`/gitlab/repos/
${
repoId
}
/merge-request`
,
token
,
data
);
export
const
gitlabAnalyzeProject
=
(
token
,
repoId
,
ref
)
=>
request
(
"GET"
,
`/gitlab/repos/
${
repoId
}
/analyze?ref=
${
encodeURIComponent
(
ref
||
""
)}
`
,
token
);
export
const
gitlabReanalyzeRepo
=
(
token
,
repoId
)
=>
request
(
"POST"
,
`/gitlab/repos/
${
repoId
}
/analyze`
,
token
);
export
const
gitlabGetRepoMap
=
(
token
,
repoId
)
=>
request
(
"GET"
,
`/gitlab/repos/
${
repoId
}
/map`
,
token
);
export
const
gitlabListActions
=
(
token
,
status
)
=>
request
(
"GET"
,
`/gitlab/actions?status=
${
status
||
"pending"
}
`
,
token
);
export
const
gitlabCreateAction
=
(
token
,
data
)
=>
request
(
"POST"
,
"/gitlab/actions"
,
token
,
data
);
export
const
gitlabApproveAction
=
(
token
,
actionId
)
=>
request
(
"POST"
,
`/gitlab/actions/
${
actionId
}
/approve`
,
token
);
...
...
frontend/src/components/ChatView.jsx
View file @
841414df
import
React
,
{
useState
,
useEffect
,
useRef
,
useCallback
,
useMemo
}
from
"react"
;
import
React
,
{
useState
,
useEffect
,
useRef
,
useCallback
}
from
"react"
;
import
{
useApp
}
from
"../store"
;
import
{
getMessages
,
downloadZip
,
listKnowledgeBases
,
updateChat
,
...
...
@@ -108,9 +108,6 @@ export default function ChatView({ chatId }) {
linked_repo_id
:
selectedRepoId
||
""
,
});
// ── THIS IS THE FIX ──
// Build the full linked_repo object from the local repos list
// so the UI immediately sees the repo banner, commit buttons, etc.
const
repoObj
=
selectedRepoId
?
repos
.
find
(
r
=>
r
.
id
===
selectedRepoId
)
||
null
:
null
;
...
...
@@ -124,7 +121,7 @@ export default function ChatView({ chatId }) {
reasoning_budget
:
reasoningBudget
,
knowledge_base_id
:
selectedKbId
,
linked_repo_id
:
selectedRepoId
,
linked_repo
:
repoObj
,
// ← was missing
linked_repo
:
repoObj
,
},
});
}
catch
{
}
...
...
@@ -164,7 +161,6 @@ export default function ChatView({ chatId }) {
if
(
!
msg
)
return
;
try
{
await
gitlabCommitSingle
(
state
.
token
,
linkedRepo
.
id
,
{
branch
,
file_path
:
filePath
,
content
:
code
,
commit_message
:
msg
,
action
});
// Refresh repo cache so AI sees updated code
try
{
await
refreshRepoContext
(
state
.
token
,
chatId
);
}
catch
{
}
}
catch
(
e
)
{
alert
(
`❌
${
e
.
message
}
`
);
throw
e
;
}
},
[
linkedRepo
,
state
.
token
,
chatId
]);
...
...
@@ -185,14 +181,31 @@ export default function ChatView({ chatId }) {
{
/* Repo banner */
}
{
linkedRepo
&&
(
<
div
className=
"px-3 py-1.5 bg-orange-500/10 border-b border-orange-500/20 flex items-center gap-2 text-xs"
>
<
div
className=
"px-3 py-1.5 bg-orange-500/10 border-b border-orange-500/20 flex items-center gap-2 text-xs
flex-wrap
"
>
<
GitBranch
size=
{
12
}
className=
"text-orange-400"
/>
<
span
className=
"text-orange-300 font-medium"
>
{
linkedRepo
.
name
}
</
span
>
<
span
className=
"text-orange-300/60"
>
(
{
linkedRepo
.
default_branch
}
)
</
span
>
<
span
className=
"text-orange-300/40"
>
Full codebase loaded
</
span
>
<
button
onClick=
{
handleRefreshRepo
}
disabled=
{
refreshingRepo
}
className=
"ml-auto text-orange-300/60 hover:text-orange-300 transition"
title=
"Refresh repo context"
>
<
RefreshCw
size=
{
12
}
className=
{
refreshingRepo
?
"animate-spin"
:
""
}
/>
</
button
>
{
linkedRepo
.
map_status
===
"ready"
&&
(
<
span
className=
"text-green-400/80 flex items-center gap-1"
>
<
span
className=
"w-1.5 h-1.5 bg-green-400 rounded-full"
/>
Mindmap ready
</
span
>
)
}
{
linkedRepo
.
map_status
===
"analyzing"
&&
(
<
span
className=
"text-amber-400/80 flex items-center gap-1"
>
<
Loader2
size=
{
10
}
className=
"animate-spin"
/>
Analyzing…
</
span
>
)
}
{
linkedRepo
.
map_status
===
"failed"
&&
(
<
span
className=
"text-red-400/80"
>
Map failed
</
span
>
)
}
{
(
!
linkedRepo
.
map_status
||
linkedRepo
.
map_status
===
"none"
)
&&
(
<
span
className=
"text-orange-300/40"
>
No mindmap
</
span
>
)
}
<
div
className=
"ml-auto flex items-center gap-2"
>
<
button
onClick=
{
handleRefreshRepo
}
disabled=
{
refreshingRepo
}
className=
"text-orange-300/60 hover:text-orange-300 transition"
title=
"Refresh repo cache"
>
<
RefreshCw
size=
{
12
}
className=
{
refreshingRepo
?
"animate-spin"
:
""
}
/>
</
button
>
</
div
>
</
div
>
)
}
...
...
@@ -246,9 +259,9 @@ export default function ChatView({ chatId }) {
<
label
className=
"text-xs text-anton-muted mb-1 flex items-center gap-1"
><
GitBranch
size=
{
12
}
className=
"text-orange-400"
/>
Repository (AI sees all files)
</
label
>
<
select
value=
{
selectedRepoId
||
""
}
onChange=
{
e
=>
setSelectedRepoId
(
e
.
target
.
value
||
null
)
}
className=
"w-full bg-anton-bg border border-anton-border rounded-lg px-3 py-2.5 text-white focus:outline-none focus:border-orange-400"
>
<
option
value=
""
>
None
</
option
>
{
repos
.
map
(
r
=>
<
option
key=
{
r
.
id
}
value=
{
r
.
id
}
>
🔀
{
r
.
name
}
(
{
r
.
default_branch
}
)
</
option
>)
}
{
repos
.
map
(
r
=>
<
option
key=
{
r
.
id
}
value=
{
r
.
id
}
>
🔀
{
r
.
name
}
(
{
r
.
default_branch
}
)
{
r
.
map_status
===
"ready"
?
" ✅"
:
r
.
map_status
===
"analyzing"
?
" ⏳"
:
""
}
</
option
>)
}
</
select
>
<
p
className=
"text-[9px] text-orange-400/60 mt-1"
>
When linked, AI loads the full codebase into context.
</
p
>
<
p
className=
"text-[9px] text-orange-400/60 mt-1"
>
When linked, AI loads the full codebase
+ architecture mindmap
into context.
</
p
>
</
div
>
)
}
</
div
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment