ata-coder 2.4.2__py3-none-any.whl
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.
- ata_coder/__init__.py +1 -0
- ata_coder/agent.py +874 -0
- ata_coder/agent_compact.py +190 -0
- ata_coder/agent_controller.py +218 -0
- ata_coder/agent_extension.py +69 -0
- ata_coder/agent_routing.py +105 -0
- ata_coder/agent_subsystems.py +72 -0
- ata_coder/agent_tools.py +318 -0
- ata_coder/agent_undo.py +63 -0
- ata_coder/anthropic_client.py +465 -0
- ata_coder/change_tracker.py +368 -0
- ata_coder/clawd_integration.py +574 -0
- ata_coder/commands/__init__.py +128 -0
- ata_coder/commands/_core.py +184 -0
- ata_coder/commands/_safety.py +95 -0
- ata_coder/commands/_settings.py +241 -0
- ata_coder/commands/_workflow.py +451 -0
- ata_coder/commands.py +974 -0
- ata_coder/config.py +257 -0
- ata_coder/core/__init__.py +35 -0
- ata_coder/core/events.py +73 -0
- ata_coder/core/queue.py +85 -0
- ata_coder/core/state.py +17 -0
- ata_coder/event_queue.py +5 -0
- ata_coder/extension.py +654 -0
- ata_coder/extensions/__init__.py +1 -0
- ata_coder/extensions/hello_skill.py +47 -0
- ata_coder/fool_proof.py +295 -0
- ata_coder/git_workflow.py +371 -0
- ata_coder/gui.py +511 -0
- ata_coder/llm_client.py +543 -0
- ata_coder/main.py +814 -0
- ata_coder/mcp_client.py +1095 -0
- ata_coder/memory.py +539 -0
- ata_coder/model_registry.py +134 -0
- ata_coder/model_router.py +105 -0
- ata_coder/permissions.py +274 -0
- ata_coder/privilege.py +464 -0
- ata_coder/project.py +273 -0
- ata_coder/prompt_template.py +423 -0
- ata_coder/prompts/auto-mode.md +7 -0
- ata_coder/prompts/coding-rules.md +40 -0
- ata_coder/prompts/execution-guardrails.md +14 -0
- ata_coder/prompts/memory-system.md +24 -0
- ata_coder/prompts/output-style.md +23 -0
- ata_coder/prompts/safety.md +17 -0
- ata_coder/prompts/slash-commands.md +24 -0
- ata_coder/prompts/sub-agents.md +38 -0
- ata_coder/prompts/system-reminders.md +17 -0
- ata_coder/prompts/system.md +105 -0
- ata_coder/prompts/tool-policy.md +46 -0
- ata_coder/repl_theme.py +99 -0
- ata_coder/repl_tracker.py +89 -0
- ata_coder/repl_ui.py +1214 -0
- ata_coder/safety_guard.py +434 -0
- ata_coder/self_correct.py +346 -0
- ata_coder/server.py +882 -0
- ata_coder/server_session.py +159 -0
- ata_coder/server_shell.py +129 -0
- ata_coder/session.py +431 -0
- ata_coder/settings.py +439 -0
- ata_coder/setup_wizard.py +136 -0
- ata_coder/skill_extension.py +92 -0
- ata_coder/skills/architect/SKILL.md +42 -0
- ata_coder/skills/code-reviewer/SKILL.md +37 -0
- ata_coder/skills/codecraft/SKILL.md +452 -0
- ata_coder/skills/debugger/SKILL.md +45 -0
- ata_coder/skills/doc-writer/SKILL.md +36 -0
- ata_coder/skills/general-coder/SKILL.md +76 -0
- ata_coder/skills/math-calculator/README.md +40 -0
- ata_coder/skills/math-calculator/SKILL.md +59 -0
- ata_coder/skills/math-calculator/handler.py +103 -0
- ata_coder/skills/math-calculator/prompts/system.md +8 -0
- ata_coder/skills/math-calculator/requirements.txt +2 -0
- ata_coder/skills/math-calculator/resources/constants.json +8 -0
- ata_coder/skills/math-calculator/tests/test_handler.py +53 -0
- ata_coder/skills/security-auditor/SKILL.md +40 -0
- ata_coder/skills/test-writer/SKILL.md +36 -0
- ata_coder/skills/weather-skill/README.md +45 -0
- ata_coder/skills/weather-skill/handler.py +76 -0
- ata_coder/skills/weather-skill/manifest.json +48 -0
- ata_coder/skills/weather-skill/prompts/system_prompt.txt +9 -0
- ata_coder/skills/weather-skill/prompts/user_prompt_template.txt +3 -0
- ata_coder/skills/weather-skill/requirements.txt +1 -0
- ata_coder/skills/weather-skill/resources/city_list.json +17 -0
- ata_coder/skills/weather-skill/resources/error_messages.json +7 -0
- ata_coder/skills/weather-skill/tests/test_handler.py +28 -0
- ata_coder/skills/weather-skill/weather_utils.py +50 -0
- ata_coder/skills.py +1014 -0
- ata_coder/sub_agent.py +273 -0
- ata_coder/sub_agent_manager.py +203 -0
- ata_coder/system_prompt_builder.py +146 -0
- ata_coder/task_planner.py +391 -0
- ata_coder/terminal.py +318 -0
- ata_coder/test_runner.py +219 -0
- ata_coder/thread_supervisor.py +195 -0
- ata_coder/tool_defs.py +335 -0
- ata_coder/tools/__init__.py +11 -0
- ata_coder/tools/definitions.py +335 -0
- ata_coder/tools/executor.py +1036 -0
- ata_coder/tools/result.py +26 -0
- ata_coder/tools/subagent.py +332 -0
- ata_coder/tools/web.py +361 -0
- ata_coder/tools.py +1576 -0
- ata_coder/types.py +92 -0
- ata_coder/utils.py +113 -0
- ata_coder/web/css/style.css +180 -0
- ata_coder/web/index.html +84 -0
- ata_coder/web/js/app.js +489 -0
- ata_coder/web/package-lock.json +25 -0
- ata_coder/web/package.json +10 -0
- ata_coder/web/tsconfig.json +13 -0
- ata_coder-2.4.2.dist-info/METADATA +799 -0
- ata_coder-2.4.2.dist-info/RECORD +118 -0
- ata_coder-2.4.2.dist-info/WHEEL +5 -0
- ata_coder-2.4.2.dist-info/entry_points.txt +2 -0
- ata_coder-2.4.2.dist-info/licenses/LICENSE +21 -0
- ata_coder-2.4.2.dist-info/top_level.txt +1 -0
ata_coder/types.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared type aliases for ATA Coder.
|
|
3
|
+
|
|
4
|
+
Single source of truth for Message, ToolDef, and other
|
|
5
|
+
commonly-referenced types that were previously duplicated
|
|
6
|
+
across llm_client.py, anthropic_client.py, and agent.py.
|
|
7
|
+
|
|
8
|
+
TypedDict variants are available for static analysis (mypy, pyright).
|
|
9
|
+
The legacy ``Message`` and ``ToolDef`` aliases remain for runtime
|
|
10
|
+
compatibility and minimal-change migrations.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from typing import Any, AsyncIterator, Callable, TypedDict
|
|
15
|
+
|
|
16
|
+
# ── Legacy aliases (widely used, keep for backward compat) ──────────────────
|
|
17
|
+
|
|
18
|
+
# OpenAI-compatible message dict: {"role": "...", "content": "...", ...}
|
|
19
|
+
Message = dict[str, Any]
|
|
20
|
+
|
|
21
|
+
# OpenAI-compatible tool definition dict
|
|
22
|
+
ToolDef = dict[str, Any]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ── TypedDict variants (opt-in for stricter type checking) ──────────────────
|
|
26
|
+
|
|
27
|
+
class MessageDict(TypedDict, total=False):
|
|
28
|
+
"""OpenAI-compatible message. ``content`` may be None for tool_calls."""
|
|
29
|
+
role: str
|
|
30
|
+
content: str | None
|
|
31
|
+
tool_calls: list[dict[str, Any]]
|
|
32
|
+
tool_call_id: str
|
|
33
|
+
name: str
|
|
34
|
+
reasoning_content: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ToolDefDict(TypedDict, total=False):
|
|
38
|
+
"""OpenAI-compatible tool definition."""
|
|
39
|
+
type: str
|
|
40
|
+
function: dict[str, Any]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ToolCallDict(TypedDict, total=False):
|
|
44
|
+
"""A single tool-call within an assistant message."""
|
|
45
|
+
id: str
|
|
46
|
+
type: str
|
|
47
|
+
function: dict[str, Any]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BaseLLMClient(ABC):
|
|
51
|
+
"""Abstract base for async LLM clients (OpenAI, Anthropic, etc.).
|
|
52
|
+
|
|
53
|
+
Both LLMClient and AnthropicClient implement this interface so
|
|
54
|
+
higher-level code (CoderAgent, SubAgent) can be written against
|
|
55
|
+
the abstraction rather than branching on provider.
|
|
56
|
+
|
|
57
|
+
All I/O methods are async — the client runs on the asyncio event loop.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
async def chat(self, messages: list[Message], tools: list[ToolDef] | None = None,
|
|
62
|
+
system_prompt: str = "") -> Message:
|
|
63
|
+
"""Send messages and return the assistant response."""
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
async def chat_stream(self, messages: list[Message], tools: list[ToolDef] | None = None,
|
|
68
|
+
system_prompt: str = "") -> AsyncIterator[tuple[str, Any]]:
|
|
69
|
+
"""Stream a chat completion. Yields (delta_type, content) tuples."""
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def register_tools(self, tools: list[ToolDef]) -> None:
|
|
74
|
+
"""Register tool definitions for subsequent requests."""
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def count_tokens_approx(self, messages: list[Message]) -> int:
|
|
79
|
+
"""Estimate token count for a message list."""
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
async def close(self) -> None:
|
|
84
|
+
"""Release resources (HTTP sessions, etc.)."""
|
|
85
|
+
...
|
|
86
|
+
|
|
87
|
+
def on_usage(self, callback: Callable[[int, int], None]) -> None:
|
|
88
|
+
"""Register a callback for token usage: callback(prompt_tokens, completion_tokens).
|
|
89
|
+
|
|
90
|
+
Default no-op — override if the client tracks usage.
|
|
91
|
+
"""
|
|
92
|
+
pass
|
ata_coder/utils.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared display utilities — used by both the GUI and the API server.
|
|
3
|
+
|
|
4
|
+
- brief_args: truncate tool-call argument dicts for compact display
|
|
5
|
+
- enhance_api_error: add helpful suggestions to common API errors
|
|
6
|
+
- try_import_yaml: shared conditional yaml import (used by memory + skills)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# ── YAML availability (shared across memory.py, skills.py, etc.) ──────────
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
import yaml as _yaml
|
|
16
|
+
HAS_YAML = True
|
|
17
|
+
except ImportError:
|
|
18
|
+
_yaml = None # type: ignore
|
|
19
|
+
HAS_YAML = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def try_import_yaml():
|
|
23
|
+
"""Return (yaml_module, True) if PyYAML is installed, else (None, False).
|
|
24
|
+
"""
|
|
25
|
+
return _yaml, HAS_YAML
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def brief_args(args: dict[str, Any] | None, max_str_len: int = 100) -> str:
|
|
29
|
+
"""Format tool-call arguments into a compact single-line summary.
|
|
30
|
+
|
|
31
|
+
Returns an empty string for None/empty dicts. String values longer
|
|
32
|
+
than *max_str_len* are truncated with an ellipsis.
|
|
33
|
+
|
|
34
|
+
Used by:
|
|
35
|
+
gui.py — tool-call display in the chat pane
|
|
36
|
+
server.py — SSE stream event logging
|
|
37
|
+
"""
|
|
38
|
+
if not args:
|
|
39
|
+
return ""
|
|
40
|
+
parts = []
|
|
41
|
+
for k, v in args.items():
|
|
42
|
+
if isinstance(v, str):
|
|
43
|
+
s = f"{k}="
|
|
44
|
+
if len(v) > max_str_len:
|
|
45
|
+
s += f'"{v[:max_str_len]}…"'
|
|
46
|
+
else:
|
|
47
|
+
s += f'"{v}"'
|
|
48
|
+
parts.append(s)
|
|
49
|
+
else:
|
|
50
|
+
parts.append(f"{k}={v}")
|
|
51
|
+
return " ".join(parts)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def enhance_api_error(status_code: int, error_message: str, base_url: str = "") -> str:
|
|
55
|
+
"""Add helpful troubleshooting suggestions to common API errors."""
|
|
56
|
+
msg = error_message
|
|
57
|
+
|
|
58
|
+
if "model" in error_message.lower() and ("not found" in error_message.lower() or "not supported" in error_message.lower()):
|
|
59
|
+
msg += (
|
|
60
|
+
"\n\n💡 This usually means the model name is incorrect for this provider."
|
|
61
|
+
"\n → Check your ATA_CODER_DEFAULT_MODEL in settings.json"
|
|
62
|
+
"\n → For DeepSeek: try 'deepseek-chat' or 'deepseek-v4-pro' (no [1m] suffix)"
|
|
63
|
+
"\n → Run 'ata --list-models' to see what the API reports as available"
|
|
64
|
+
)
|
|
65
|
+
elif "unauthorized" in error_message.lower() or status_code == 401:
|
|
66
|
+
msg += (
|
|
67
|
+
"\n\n🔑 Authentication failed. Check your API key:"
|
|
68
|
+
"\n → Settings: ~/.ata_coder/settings.json → env.ATA_CODER_API_KEY"
|
|
69
|
+
"\n → Or set ATA_CODER_API_KEY environment variable"
|
|
70
|
+
)
|
|
71
|
+
elif "rate limit" in error_message.lower() or status_code == 429:
|
|
72
|
+
msg += (
|
|
73
|
+
"\n\n⏳ Rate limited — the API is throttling requests."
|
|
74
|
+
"\n → Wait a moment and try again"
|
|
75
|
+
"\n → Consider switching to a model with higher rate limits"
|
|
76
|
+
)
|
|
77
|
+
elif "connection" in error_message.lower() or status_code in (502, 503, 504):
|
|
78
|
+
msg += (
|
|
79
|
+
"\n\n🌐 Server connectivity issue:"
|
|
80
|
+
"\n → Check: is the API base URL correct? → " + (base_url or "check settings.json")
|
|
81
|
+
+ "\n → The server may be temporarily overloaded"
|
|
82
|
+
"\n → Try again in a few seconds"
|
|
83
|
+
)
|
|
84
|
+
elif "context length" in error_message.lower() or "too long" in error_message.lower():
|
|
85
|
+
msg += (
|
|
86
|
+
"\n\n📏 Context length exceeded:"
|
|
87
|
+
"\n → Try breaking your task into smaller steps"
|
|
88
|
+
"\n → Use /compact to shrink conversation history"
|
|
89
|
+
"\n → Increase ATA_CODER_MAX_OUTPUT_TOKENS for larger context"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return msg
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ── Surrogate sanitization ─────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def sanitize_surrogates(obj: Any) -> Any:
|
|
99
|
+
"""Recursively replace lone surrogates (U+D800–U+DFFF) in all strings.
|
|
100
|
+
|
|
101
|
+
Python's ``json.dumps(ensure_ascii=False)`` can emit lone surrogates that
|
|
102
|
+
are invalid UTF-8. When httpx or a file write tries ``.encode("utf-8")``
|
|
103
|
+
on the result, Python raises ``UnicodeEncodeError: surrogates not allowed``.
|
|
104
|
+
|
|
105
|
+
This round-trip through UTF-8 with ``errors="replace"`` strips them safely.
|
|
106
|
+
"""
|
|
107
|
+
if isinstance(obj, str):
|
|
108
|
+
return obj.encode("utf-8", errors="replace").decode("utf-8")
|
|
109
|
+
if isinstance(obj, dict):
|
|
110
|
+
return {sanitize_surrogates(k): sanitize_surrogates(v) for k, v in obj.items()}
|
|
111
|
+
if isinstance(obj, list):
|
|
112
|
+
return [sanitize_surrogates(v) for v in obj]
|
|
113
|
+
return obj
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/* ═══════════════════════════ One Dark Pro Theme ═══════════════════════════ */
|
|
2
|
+
:root {
|
|
3
|
+
--bg: #282C34; --bg-panel: #21252B; --bg-input: #1E2127;
|
|
4
|
+
--fg: #ABB2BF; --comment: #5C6370;
|
|
5
|
+
--red: #E06C75; --green: #98C379; --yellow: #D19A66;
|
|
6
|
+
--blue: #61AFEF; --purple: #C678DD; --cyan: #56B6C2;
|
|
7
|
+
--orange: #E5C07B; --teal: #7EE787;
|
|
8
|
+
--border: #3A3F4B; --radius: 10px;
|
|
9
|
+
--transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
10
|
+
--font-mono: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', Consolas, monospace;
|
|
11
|
+
--font-ui: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
15
|
+
|
|
16
|
+
body {
|
|
17
|
+
font-family: var(--font-ui); font-size: 14px;
|
|
18
|
+
background: var(--bg); color: var(--fg);
|
|
19
|
+
height: 100vh; overflow: hidden; display: flex;
|
|
20
|
+
-webkit-font-smoothing: antialiased;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* ═══════════════════════════ Sidebar ═══════════════════════════ */
|
|
24
|
+
.sidebar {
|
|
25
|
+
width: 260px; min-width: 260px; background: var(--bg-panel);
|
|
26
|
+
border-right: 1px solid var(--border); display: flex;
|
|
27
|
+
flex-direction: column; transition: transform var(--transition);
|
|
28
|
+
}
|
|
29
|
+
.sidebar-header {
|
|
30
|
+
padding: 20px 18px 14px; border-bottom: 1px solid var(--border);
|
|
31
|
+
}
|
|
32
|
+
.sidebar-header .logo { font-size: 18px; font-weight: 700; color: var(--blue); }
|
|
33
|
+
.sidebar-header .logo span { color: var(--fg); font-weight: 400; font-size: 12px; }
|
|
34
|
+
.sidebar-body { flex: 1; overflow-y: auto; padding: 12px 0; }
|
|
35
|
+
.sidebar-section { padding: 8px 18px; }
|
|
36
|
+
.sidebar-section h4 { font-size: 11px; text-transform: uppercase;
|
|
37
|
+
color: var(--comment); letter-spacing: 0.5px; margin-bottom: 6px; }
|
|
38
|
+
.sidebar-section .val { font-size: 13px; color: var(--fg); word-break: break-all; }
|
|
39
|
+
.sidebar-section select { width: 100%; background: var(--bg-input); color: var(--fg);
|
|
40
|
+
border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px;
|
|
41
|
+
font-size: 13px; cursor: pointer; }
|
|
42
|
+
.skill-tag { display: inline-block; font-size: 11px; padding: 2px 8px;
|
|
43
|
+
background: var(--bg); border: 1px solid var(--border); border-radius: 12px;
|
|
44
|
+
margin: 2px 3px 2px 0; color: var(--cyan); }
|
|
45
|
+
.sidebar-footer { padding: 12px 18px; border-top: 1px solid var(--border); }
|
|
46
|
+
.shortcut { font-size: 12px; color: var(--comment); padding: 4px 0; cursor: pointer; }
|
|
47
|
+
.shortcut:hover { color: var(--fg); }
|
|
48
|
+
.shortcut kbd { font-family: var(--font-mono); font-size: 11px; background: var(--bg);
|
|
49
|
+
border: 1px solid var(--border); border-radius: 3px; padding: 1px 5px; margin-right: 4px; }
|
|
50
|
+
|
|
51
|
+
/* ═══════════════════════════ Main Chat ═══════════════════════════ */
|
|
52
|
+
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
|
|
53
|
+
|
|
54
|
+
/* ── Top Bar ── */
|
|
55
|
+
.top-bar {
|
|
56
|
+
height: 44px; min-height: 44px; background: var(--bg-panel);
|
|
57
|
+
border-bottom: 1px solid var(--border); display: flex;
|
|
58
|
+
align-items: center; padding: 0 16px; gap: 8px;
|
|
59
|
+
}
|
|
60
|
+
.hamburger { display: none; background: none; border: none; color: var(--fg);
|
|
61
|
+
font-size: 20px; cursor: pointer; padding: 4px 8px; }
|
|
62
|
+
.top-bar .title { font-weight: 600; font-size: 14px; color: var(--blue); }
|
|
63
|
+
.top-bar .subtitle { font-size: 12px; color: var(--comment); margin-left: auto; }
|
|
64
|
+
.top-bar .dot { width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
|
|
65
|
+
.dot.ready { background: var(--green); }
|
|
66
|
+
.dot.thinking { background: var(--yellow); animation: pulse 1s infinite; }
|
|
67
|
+
.dot.error { background: var(--red); }
|
|
68
|
+
|
|
69
|
+
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
|
|
70
|
+
|
|
71
|
+
/* ── Chat Area ── */
|
|
72
|
+
.chat { flex: 1; overflow-y: auto; padding: 16px 20px;
|
|
73
|
+
scroll-behavior: smooth; line-height: 1.6; }
|
|
74
|
+
.chat::-webkit-scrollbar { width: 6px; }
|
|
75
|
+
.chat::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
76
|
+
|
|
77
|
+
/* ── Message Bubbles ── */
|
|
78
|
+
.msg { margin-bottom: 14px; max-width: 85%; animation: fadeIn 0.2s ease; }
|
|
79
|
+
@keyframes fadeIn { from{opacity:0;transform:translateY(4px)} to{opacity:1;transform:translateY(0)} }
|
|
80
|
+
|
|
81
|
+
.msg.user { margin-left: auto; background: var(--blue); color: #fff;
|
|
82
|
+
padding: 10px 16px; border-radius: 18px 18px 4px 18px; font-weight: 500; }
|
|
83
|
+
.msg.agent { padding: 2px 0; color: var(--fg); }
|
|
84
|
+
.msg.agent p { margin: 4px 0; }
|
|
85
|
+
.msg.agent h1, .msg.agent h2, .msg.agent h3 { font-weight: 700; margin: 10px 0 6px; }
|
|
86
|
+
.msg.agent h1 { font-size: 1.25em; color: var(--fg); }
|
|
87
|
+
.msg.agent h2 { font-size: 1.12em; color: var(--fg); }
|
|
88
|
+
.msg.agent h3 { font-size: 1.05em; color: var(--fg); }
|
|
89
|
+
.msg.agent code { font-family: var(--font-mono); font-size: 13px;
|
|
90
|
+
background: var(--bg-panel); padding: 2px 6px; border-radius: 4px; }
|
|
91
|
+
.msg.agent pre { font-family: var(--font-mono); font-size: 13px;
|
|
92
|
+
background: var(--bg-panel); padding: 12px 16px; border-radius: 8px;
|
|
93
|
+
overflow-x: auto; margin: 8px 0; border: 1px solid var(--border);
|
|
94
|
+
line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
|
|
95
|
+
|
|
96
|
+
.msg.think { margin-left: 28px; padding: 8px 14px; background: var(--bg-panel);
|
|
97
|
+
border-left: 2px solid var(--teal); border-radius: 0 8px 8px 0;
|
|
98
|
+
font-size: 13px; color: var(--teal); font-style: italic; }
|
|
99
|
+
.msg.tool { margin-left: 32px; padding: 6px 12px; font-size: 12px;
|
|
100
|
+
font-family: var(--font-mono); color: var(--purple); }
|
|
101
|
+
.msg.tool-ok { color: var(--green); margin-left: 44px; font-size: 12px; }
|
|
102
|
+
.msg.tool-err { color: var(--red); margin-left: 44px; font-size: 12px; }
|
|
103
|
+
.msg.error { padding: 10px 16px; background: rgba(224,108,117,0.1);
|
|
104
|
+
border: 1px solid var(--red); border-radius: 8px; color: var(--red);
|
|
105
|
+
font-weight: 600; }
|
|
106
|
+
.msg.status { text-align: center; font-size: 12px; color: var(--comment);
|
|
107
|
+
margin: 8px 0; }
|
|
108
|
+
.msg.sep { text-align: center; font-size: 11px; color: var(--comment);
|
|
109
|
+
border-top: 1px solid var(--border); margin: 16px 0; padding-top: 12px; }
|
|
110
|
+
|
|
111
|
+
/* ── Input Row ── */
|
|
112
|
+
.input-row {
|
|
113
|
+
display: flex; align-items: center; gap: 10px;
|
|
114
|
+
padding: 12px 16px; background: var(--bg-panel);
|
|
115
|
+
border-top: 1px solid var(--border);
|
|
116
|
+
}
|
|
117
|
+
#cmd-input {
|
|
118
|
+
flex: 1; background: var(--bg-input); color: var(--fg);
|
|
119
|
+
border: 1px solid var(--border); border-radius: 24px;
|
|
120
|
+
padding: 10px 18px; font-size: 14px; font-family: var(--font-ui);
|
|
121
|
+
outline: none; transition: border-color var(--transition);
|
|
122
|
+
}
|
|
123
|
+
#cmd-input:focus { border-color: var(--blue); }
|
|
124
|
+
#send-btn {
|
|
125
|
+
background: var(--blue); color: #fff; border: none;
|
|
126
|
+
border-radius: 24px; padding: 10px 24px; font-size: 14px;
|
|
127
|
+
font-weight: 600; cursor: pointer; transition: opacity var(--transition);
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
}
|
|
130
|
+
#send-btn:hover { opacity: 0.85; }
|
|
131
|
+
#send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
132
|
+
#stop-btn {
|
|
133
|
+
background: var(--red); color: #fff; border: none;
|
|
134
|
+
border-radius: 24px; padding: 10px 24px; font-size: 14px;
|
|
135
|
+
font-weight: 600; cursor: pointer; transition: opacity var(--transition);
|
|
136
|
+
white-space: nowrap;
|
|
137
|
+
}
|
|
138
|
+
#stop-btn:hover { opacity: 0.85; }
|
|
139
|
+
/* Streaming state: toggle buttons + disable input */
|
|
140
|
+
body:not(.streaming) #stop-btn { display: none; }
|
|
141
|
+
body.streaming #send-btn { display: none; }
|
|
142
|
+
body.streaming #cmd-input { opacity: 0.5; pointer-events: none; }
|
|
143
|
+
|
|
144
|
+
/* ── Command Popup ── */
|
|
145
|
+
.cmd-popup {
|
|
146
|
+
display: none; position: absolute; bottom: 72px; left: 16px; right: 16px;
|
|
147
|
+
background: var(--bg-panel); border: 1px solid var(--border);
|
|
148
|
+
border-radius: 8px; max-height: 240px; overflow-y: auto; z-index: 100;
|
|
149
|
+
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
|
|
150
|
+
}
|
|
151
|
+
.cmd-popup.show { display: block; }
|
|
152
|
+
.cmd-item { padding: 8px 16px; cursor: pointer; display: flex;
|
|
153
|
+
justify-content: space-between; font-size: 13px; }
|
|
154
|
+
.cmd-item:hover, .cmd-item.active { background: rgba(97,175,239,0.1); }
|
|
155
|
+
.cmd-item .name { font-family: var(--font-mono); color: var(--cyan); }
|
|
156
|
+
.cmd-item .desc { color: var(--comment); font-size: 12px; }
|
|
157
|
+
|
|
158
|
+
/* ── Status Bar ── */
|
|
159
|
+
.status-bar {
|
|
160
|
+
height: 26px; min-height: 26px; background: var(--bg-panel);
|
|
161
|
+
border-top: 1px solid var(--border); display: flex;
|
|
162
|
+
align-items: center; padding: 0 16px; font-size: 11px; color: var(--comment);
|
|
163
|
+
gap: 16px;
|
|
164
|
+
}
|
|
165
|
+
.status-bar span { white-space: nowrap; }
|
|
166
|
+
|
|
167
|
+
/* ═══════════════════════════ Mobile ═══════════════════════════ */
|
|
168
|
+
@media (max-width: 768px) {
|
|
169
|
+
.sidebar { position: fixed; left: 0; top: 0; bottom: 0; z-index: 200;
|
|
170
|
+
transform: translateX(-100%); box-shadow: 4px 0 20px rgba(0,0,0,0.3); }
|
|
171
|
+
.sidebar.open { transform: translateX(0); }
|
|
172
|
+
.hamburger { display: block; }
|
|
173
|
+
.msg { max-width: 95%; }
|
|
174
|
+
.msg.user { margin-left: 4px; }
|
|
175
|
+
#cmd-input { font-size: 16px; } /* prevent iOS zoom on focus */
|
|
176
|
+
.overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4);
|
|
177
|
+
z-index: 199; }
|
|
178
|
+
.overlay.show { display: block; }
|
|
179
|
+
.top-bar .subtitle { display: none; }
|
|
180
|
+
}
|
ata_coder/web/index.html
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
6
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
7
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
8
|
+
<meta name="theme-color" content="#282C34">
|
|
9
|
+
<title>ATA Coder</title>
|
|
10
|
+
<link rel="stylesheet" href="css/style.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<!-- ── Sidebar Overlay (mobile) ── -->
|
|
15
|
+
<div class="overlay" id="overlay"></div>
|
|
16
|
+
|
|
17
|
+
<!-- ── Sidebar ── -->
|
|
18
|
+
<aside class="sidebar" id="sidebar">
|
|
19
|
+
<div class="sidebar-header">
|
|
20
|
+
<div class="logo">⚡ ATA Coder <span id="logo-ver">v2.2.0</span></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="sidebar-body">
|
|
23
|
+
<div class="sidebar-section">
|
|
24
|
+
<h4>Model</h4>
|
|
25
|
+
<select id="model-select">
|
|
26
|
+
<option>loading…</option>
|
|
27
|
+
</select>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="sidebar-section">
|
|
30
|
+
<h4>Skills</h4>
|
|
31
|
+
<div id="skill-tags"><span class="skill-tag">general-coder</span></div>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="sidebar-section">
|
|
34
|
+
<h4>Workspace</h4>
|
|
35
|
+
<div class="val" id="ws-path">…</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="sidebar-footer">
|
|
39
|
+
<div class="shortcut" data-cmd="/clear"><kbd>/clear</kbd> New chat</div>
|
|
40
|
+
<div class="shortcut" data-cmd="/history"><kbd>/history</kbd> Sessions</div>
|
|
41
|
+
<div class="shortcut" data-cmd="/skills"><kbd>/skills</kbd> List skills</div>
|
|
42
|
+
<div class="shortcut" data-cmd="/models"><kbd>/models</kbd> List models</div>
|
|
43
|
+
</div>
|
|
44
|
+
</aside>
|
|
45
|
+
|
|
46
|
+
<!-- ── Main Area ── -->
|
|
47
|
+
<div class="main">
|
|
48
|
+
<!-- Top Bar -->
|
|
49
|
+
<div class="top-bar">
|
|
50
|
+
<button class="hamburger" aria-label="Menu">☰</button>
|
|
51
|
+
<span class="dot ready" id="status-dot"></span>
|
|
52
|
+
<span class="title">ATA Coder</span>
|
|
53
|
+
<span class="subtitle" id="top-subtitle"></span>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<!-- Chat -->
|
|
57
|
+
<div class="chat" id="chat">
|
|
58
|
+
<div class="msg status">⚡ ATA Coder — AI-powered coding assistant</div>
|
|
59
|
+
<div class="msg status" id="init-info">Connecting…</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Command Popup -->
|
|
63
|
+
<div class="cmd-popup" id="cmd-popup"></div>
|
|
64
|
+
|
|
65
|
+
<!-- Input Row -->
|
|
66
|
+
<div class="input-row" id="input-row">
|
|
67
|
+
<input type="text" id="cmd-input" placeholder="Ask anything… / for commands"
|
|
68
|
+
autocomplete="off" autocapitalize="off" spellcheck="false">
|
|
69
|
+
<button id="send-btn">Send</button>
|
|
70
|
+
<button id="stop-btn">Stop</button>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<!-- Status Bar -->
|
|
74
|
+
<div class="status-bar">
|
|
75
|
+
<span id="st-tokens">tokens: ~0</span>
|
|
76
|
+
<span id="st-skill">skill: general-coder</span>
|
|
77
|
+
<span id="st-model">model: …</span>
|
|
78
|
+
<span id="st-time" style="margin-left:auto">0.0s</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<script src="js/app.js"></script>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|