@agentprojectcontext/apx 1.32.0 → 1.33.0
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.
- package/package.json +6 -1
- package/skills/apc-context/SKILL.md +5 -2
- package/skills/apx/SKILL.md +3 -3
- package/skills/apx-agency-agents/SKILL.md +5 -5
- package/skills/apx-agent/SKILL.md +7 -7
- package/skills/apx-mcp/SKILL.md +6 -4
- package/skills/apx-mcp-builder/SKILL.md +4 -7
- package/skills/apx-project/SKILL.md +4 -5
- package/skills/apx-routine/SKILL.md +14 -12
- package/skills/apx-runtime/SKILL.md +5 -3
- package/skills/apx-sessions/SKILL.md +5 -5
- package/skills/apx-skill-builder/SKILL.md +10 -6
- package/skills/apx-task/SKILL.md +8 -8
- package/skills/apx-telegram/SKILL.md +23 -7
- package/skills/apx-voice/SKILL.md +8 -6
- package/src/core/{agent-system.js → agent/build-agent-system.js} +10 -12
- package/src/core/agent/index.js +0 -2
- package/src/core/{agent-memory.js → agent/memory.js} +2 -2
- package/src/core/agent/model-router.js +21 -43
- package/src/core/agent/prompt-builder.js +17 -63
- package/src/core/agent/prompts/action-discipline.md +24 -0
- package/src/core/agent/prompts/channels/code.md +8 -12
- package/src/core/agent/prompts/channels/desktop.md +6 -4
- package/src/core/agent/prompts/channels/routine.md +10 -1
- package/src/core/agent/prompts/channels/telegram.md +10 -1
- package/src/core/agent/prompts/channels/web_code.md +20 -0
- package/src/core/agent/prompts/modes/voice.md +2 -2
- package/src/core/agent/prompts/super-agent-base.md +2 -2
- package/src/core/agent/run-agent.js +37 -35
- package/src/core/agent/runtime-bridge.js +42 -0
- package/src/core/agent/self-memory.js +19 -9
- package/src/core/agent/skills/catalog.js +65 -0
- package/src/core/agent/skills/index.js +6 -0
- package/src/{host/daemon/skills-loader.js → core/agent/skills/loader.js} +3 -3
- package/src/core/agent/skills/rag.js +91 -0
- package/src/core/agent/skills/trigger.js +71 -0
- package/src/{host/daemon → core/agent}/super-agent.js +5 -5
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/add-project.js +3 -4
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-agent.js +2 -2
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-mcp.js +1 -2
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-runtime.js +10 -11
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/create-task.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/discover-tools.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/edit-file.js +1 -2
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/import-agent.js +4 -5
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-agents.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-skills.js +7 -2
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-tasks.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-vault-agents.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/load-skill.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-agent-memory.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-self-memory.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/remember.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/run-shell.js +1 -2
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-messages.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-sessions.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/send-telegram.js +0 -2
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-identity.js +1 -3
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-permission-mode.js +1 -3
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/tail-messages.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/transcribe-audio.js +1 -1
- package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/write-file.js +1 -2
- package/src/core/agent/tools/helpers.js +74 -0
- package/src/{host/daemon/super-agent-tools → core/agent/tools}/registry-bridge.js +3 -3
- package/src/{host/daemon/super-agent-tools/index.js → core/agent/tools/registry.js} +31 -32
- package/src/core/apc/agents-vault.js +37 -0
- package/src/core/{scaffold.js → apc/scaffold.js} +4 -5
- package/src/core/{config.js → config/index.js} +21 -27
- package/src/core/config/paths.js +32 -0
- package/src/core/constants/actors.js +8 -0
- package/src/core/constants/channels.js +19 -0
- package/src/core/constants/index.js +5 -0
- package/src/core/constants/permissions.js +17 -0
- package/src/core/constants/roles.js +9 -0
- package/src/core/engines/_streaming.js +63 -0
- package/src/core/engines/anthropic.js +11 -22
- package/src/core/engines/ollama.js +7 -16
- package/src/core/identity/index.js +8 -0
- package/src/core/{identity.js → identity/self.js} +5 -5
- package/src/core/{telegram-identity.js → identity/telegram.js} +1 -1
- package/src/core/logging.js +1 -1
- package/src/core/mascot.js +1 -1
- package/src/core/memory/active-threads.js +10 -10
- package/src/core/memory/broker.js +9 -9
- package/src/core/memory/compactor.js +2 -2
- package/src/core/memory/index.js +2 -2
- package/src/core/memory/indexer.js +1 -1
- package/src/core/{code-sessions-store.js → stores/code-sessions.js} +7 -8
- package/src/core/{messages-store.js → stores/messages.js} +6 -4
- package/src/core/stores/routine-memory.js +71 -0
- package/src/core/{routines-store.js → stores/routines.js} +1 -3
- package/src/core/stores/runtime-sessions.js +99 -0
- package/src/core/{tasks-store.js → stores/tasks.js} +3 -8
- package/src/core/update-check.js +1 -1
- package/src/core/util/ids.js +14 -0
- package/src/core/util/index.js +2 -0
- package/src/core/util/time.js +9 -0
- package/src/core/voice/tts.js +1 -1
- package/src/host/daemon/api/admin-config.js +4 -3
- package/src/host/daemon/api/admin.js +1 -1
- package/src/host/daemon/api/agents.js +4 -25
- package/src/host/daemon/api/artifacts.js +26 -1
- package/src/host/daemon/api/code.js +62 -17
- package/src/host/daemon/api/confirm.js +1 -1
- package/src/host/daemon/api/connections.js +2 -2
- package/src/host/daemon/api/conversations.js +2 -2
- package/src/host/daemon/api/deck.js +1 -1
- package/src/host/daemon/api/desktop.js +1 -1
- package/src/host/daemon/api/embeddings.js +4 -4
- package/src/host/daemon/api/engines.js +2 -2
- package/src/host/daemon/api/exec.js +20 -5
- package/src/host/daemon/api/identity.js +1 -1
- package/src/host/daemon/api/mcps.js +1 -1
- package/src/host/daemon/api/messages.js +1 -1
- package/src/host/daemon/api/runtimes.js +9 -8
- package/src/host/daemon/api/sessions-search.js +1 -1
- package/src/host/daemon/api/sessions.js +2 -2
- package/src/host/daemon/api/shared.js +5 -4
- package/src/host/daemon/api/skills.js +30 -0
- package/src/host/daemon/api/super-agent.js +29 -9
- package/src/host/daemon/api/tasks.js +2 -2
- package/src/host/daemon/api/telegram.js +1 -1
- package/src/host/daemon/api/tools.js +6 -6
- package/src/host/daemon/api/tts.js +2 -2
- package/src/host/daemon/api/voice.js +14 -12
- package/src/host/daemon/api.js +2 -0
- package/src/host/daemon/compact.js +1 -1
- package/src/host/daemon/db.js +4 -4
- package/src/host/daemon/desktop-ws.js +1 -1
- package/src/host/daemon/index.js +4 -4
- package/src/host/daemon/plugins/{desktop.js → desktop/index.js} +11 -6
- package/src/host/daemon/plugins/index.js +2 -2
- package/src/host/daemon/plugins/{telegram.js → telegram/index.js} +52 -193
- package/src/host/daemon/plugins/telegram/media.js +162 -0
- package/src/host/daemon/projects-helpers.js +54 -0
- package/src/host/daemon/routines.js +28 -12
- package/src/host/daemon/smoke.js +2 -2
- package/src/host/daemon/token-store.js +1 -1
- package/src/host/daemon/transcription.js +2 -2
- package/src/host/daemon/wakeup.js +2 -2
- package/src/interfaces/cli/commands/agent.js +3 -3
- package/src/interfaces/cli/commands/command.js +1 -1
- package/src/interfaces/cli/commands/config.js +3 -2
- package/src/interfaces/cli/commands/desktop.js +1 -1
- package/src/interfaces/cli/commands/exec.js +2 -1
- package/src/interfaces/cli/commands/identity.js +2 -2
- package/src/interfaces/cli/commands/init.js +1 -1
- package/src/interfaces/cli/commands/mcp.js +1 -1
- package/src/interfaces/cli/commands/memory.js +2 -2
- package/src/interfaces/cli/commands/model.js +16 -6
- package/src/interfaces/cli/commands/project.js +1 -1
- package/src/interfaces/cli/commands/routine.js +58 -0
- package/src/interfaces/cli/commands/search.js +1 -1
- package/src/interfaces/cli/commands/session.js +4 -4
- package/src/interfaces/cli/commands/setup.js +4 -3
- package/src/interfaces/cli/commands/skills.js +25 -4
- package/src/interfaces/cli/commands/status.js +1 -1
- package/src/interfaces/cli/commands/sys.js +11 -4
- package/src/interfaces/cli/commands/update.js +1 -1
- package/src/interfaces/cli/index.js +4 -4
- package/src/interfaces/cli/postinstall.js +2 -2
- package/src/interfaces/mcp-server/index.js +1 -1
- package/src/interfaces/tui/component/prompt/index.tsx +3 -1
- package/src/interfaces/tui/context/sdk-apx.tsx +47 -7
- package/src/interfaces/tui/context/sync-apx.tsx +20 -2
- package/src/interfaces/tui/context/sync.tsx +2 -1
- package/src/interfaces/tui/routes/session/index.tsx +151 -136
- package/src/interfaces/tui/routes/session/sidebar-apx.tsx +37 -15
- package/src/interfaces/tui/run.ts +2 -0
- package/src/interfaces/web/dist/assets/index-7dVT2O1S.css +1 -0
- package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js +602 -0
- package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js.map +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/package-lock.json +6 -6
- package/src/interfaces/web/src/App.tsx +53 -32
- package/src/interfaces/web/src/components/RobyBubble.tsx +12 -6
- package/src/interfaces/web/src/components/UiSelect.tsx +13 -3
- package/src/interfaces/web/src/components/chat/SkillPicker.tsx +77 -0
- package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +253 -111
- package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +10 -8
- package/src/interfaces/web/src/components/code/CodeComposer.tsx +20 -17
- package/src/interfaces/web/src/components/code/CodeContextTab.tsx +43 -18
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +212 -0
- package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +121 -0
- package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeSessionList.tsx +30 -26
- package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +40 -21
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +140 -0
- package/src/interfaces/web/src/components/common/TabLayout.tsx +11 -7
- package/src/interfaces/web/src/components/common/TabNav.tsx +3 -3
- package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +4 -2
- package/src/interfaces/web/src/components/ui/chat-input.tsx +17 -6
- package/src/interfaces/web/src/hooks/useChat.ts +48 -2
- package/src/interfaces/web/src/hooks/useNavCollapseCtx.tsx +83 -0
- package/src/interfaces/web/src/hooks/usePersonaName.ts +11 -0
- package/src/interfaces/web/src/i18n/en.ts +7 -7
- package/src/interfaces/web/src/i18n/es.ts +8 -8
- package/src/interfaces/web/src/lib/api/agents.ts +1 -1
- package/src/interfaces/web/src/lib/api/artifacts.ts +10 -0
- package/src/interfaces/web/src/lib/api/code.ts +4 -2
- package/src/interfaces/web/src/lib/api/skills.ts +25 -0
- package/src/interfaces/web/src/lib/api.ts +1 -0
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +430 -86
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +5 -18
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -8
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +39 -40
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +16 -16
- package/src/skills/apc-context/SKILL.md +159 -0
- package/src/core/agent/ghost-guard.js +0 -24
- package/src/core/agent/prompts/channels/terminal.md +0 -16
- package/src/host/daemon/apc-runtime-context.js +0 -124
- package/src/host/daemon/super-agent-tools/helpers.js +0 -124
- package/src/host/daemon/tool-call-parser.js +0 -2
- package/src/interfaces/web/dist/assets/index-63P_ji1a.js +0 -571
- package/src/interfaces/web/dist/assets/index-63P_ji1a.js.map +0 -1
- package/src/interfaces/web/dist/assets/index-DLWy6dYz.css +0 -1
- /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/ask-questions.js +0 -0
- /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-files.js +0 -0
- /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-mcps.js +0 -0
- /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-projects.js +0 -0
- /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-file.js +0 -0
- /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-files.js +0 -0
- /package/src/core/agent/{pseudo-tools.js → tools/pseudo-tools.js} +0 -0
- /package/src/core/agent/{tool-call-parser.js → tools/tool-call-parser.js} +0 -0
- /package/src/core/{parser.js → apc/parser.js} +0 -0
- /package/src/core/{apc-skill-sync.js → apc/skill-sync.js} +0 -0
- /package/src/core/{artifacts-store.js → stores/artifacts.js} +0 -0
- /package/src/{host/daemon → core/stores}/engine-sessions.js +0 -0
- /package/src/core/{session-store.js → stores/sessions.js} +0 -0
- /package/src/host/daemon/plugins/{telegram-ask.js → telegram/ask.js} +0 -0
|
@@ -1,19 +1,47 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import useSWR from "swr";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { Bot, FolderTree, MessageSquare, PanelLeft, PanelRight, Terminal, X } from "lucide-react";
|
|
4
|
+
import { Group as PanelGroup, Panel, Separator as PanelResizeHandle } from "react-resizable-panels";
|
|
5
|
+
import { Code, Projects, Agents } from "../../lib/api";
|
|
6
|
+
import { Artifacts } from "../../lib/api/artifacts";
|
|
7
|
+
import { http } from "../../lib/http";
|
|
8
|
+
import { Empty, Loading } from "../../components/ui";
|
|
9
|
+
import { Tip } from "../../components/ui/tip";
|
|
10
|
+
import { UiSelect } from "../../components/UiSelect";
|
|
11
|
+
import { useSetPageLabel, useSetPageActions } from "../../hooks/useNavCollapseCtx";
|
|
6
12
|
import { MessageList } from "../../components/chat/MessageList";
|
|
7
13
|
import { CodeProjectPicker } from "../../components/code/CodeProjectPicker";
|
|
8
14
|
import { CodeSessionList } from "../../components/code/CodeSessionList";
|
|
9
15
|
import { CodeComposer } from "../../components/code/CodeComposer";
|
|
10
16
|
import { CodeSidePanel } from "../../components/code/CodeSidePanel";
|
|
17
|
+
import { CodeFileTree } from "../../components/code/CodeFileTree";
|
|
18
|
+
import { CodeFileViewer } from "../../components/code/CodeFileViewer";
|
|
19
|
+
import { CodeTerminal } from "../../components/code/CodeTerminal";
|
|
11
20
|
import { InlineAskPanel, pendingAskQuestions } from "../../components/chat/InlineAskPanel";
|
|
12
21
|
import { useToast } from "../../components/Toast";
|
|
13
22
|
import { t } from "../../i18n";
|
|
14
23
|
import { applyStreamEvent, textOf, type ChatMsg } from "../../hooks/useChat";
|
|
15
24
|
import type { CodeMode, CodeStreamEvent, CodeTurn } from "../../lib/api/code";
|
|
16
25
|
|
|
26
|
+
// Suppress unused import warning for textOf (kept for consumers)
|
|
27
|
+
void textOf;
|
|
28
|
+
|
|
29
|
+
const SUPER_AGENT_VALUE = "super-agent";
|
|
30
|
+
|
|
31
|
+
// Hit area is wider than the visible line so the handle is comfortable to
|
|
32
|
+
// grab — the inner ::before line is what the user sees.
|
|
33
|
+
function ResizeHandle() {
|
|
34
|
+
return (
|
|
35
|
+
<PanelResizeHandle className="relative z-10 w-px shrink-0 cursor-col-resize bg-border transition-colors hover:bg-primary/50 active:bg-primary/70" />
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ResizeHandleH() {
|
|
40
|
+
return (
|
|
41
|
+
<PanelResizeHandle className="relative z-10 h-px shrink-0 cursor-row-resize bg-border transition-colors hover:bg-primary/50 active:bg-primary/70" />
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
17
45
|
// Code module — OpenCode-style coding sessions in the APX web admin. Each
|
|
18
46
|
// project owns a list of persistent sessions; the daemon keeps the transcript
|
|
19
47
|
// server-side (api/code.js), so the UI just streams turns and renders them with
|
|
@@ -26,11 +54,34 @@ export function CodeScreen() {
|
|
|
26
54
|
|
|
27
55
|
const [pid, setPid] = useState<string>("");
|
|
28
56
|
const [sid, setSid] = useState<string | null>(null);
|
|
57
|
+
const [agentSlug, setAgentSlug] = useState<string>(SUPER_AGENT_VALUE);
|
|
29
58
|
const [msgs, setMsgs] = useState<ChatMsg[]>([]);
|
|
30
59
|
const [draft, setDraft] = useState("");
|
|
31
60
|
const [busy, setBusy] = useState(false);
|
|
61
|
+
const [leftOpen, setLeftOpen] = useState(true);
|
|
62
|
+
const [rightOpen, setRightOpen] = useState(true);
|
|
63
|
+
const [termOpen, setTermOpen] = useState(false);
|
|
64
|
+
const [termInitCmd, setTermInitCmd] = useState("");
|
|
65
|
+
const [worktreeOpen, setWorktreeOpen] = useState(false);
|
|
32
66
|
const abortRef = useRef<AbortController | null>(null);
|
|
33
67
|
|
|
68
|
+
// Open file tabs. `artifactName` marks an artifact opened for editing;
|
|
69
|
+
// saves route through Artifacts.write instead of being read-only.
|
|
70
|
+
type OpenFile = {
|
|
71
|
+
path: string;
|
|
72
|
+
content: string;
|
|
73
|
+
loading?: boolean;
|
|
74
|
+
artifactName?: string;
|
|
75
|
+
};
|
|
76
|
+
const [openFiles, setOpenFiles] = useState<OpenFile[]>([]);
|
|
77
|
+
// "chat" is the permanent tab, otherwise a file path
|
|
78
|
+
const [activeTab, setActiveTab] = useState<string>("chat");
|
|
79
|
+
|
|
80
|
+
const runInTerminal = useCallback((cmd: string) => {
|
|
81
|
+
setTermOpen(true);
|
|
82
|
+
setTermInitCmd(cmd);
|
|
83
|
+
}, []);
|
|
84
|
+
|
|
34
85
|
// Default to the first registered project once the list loads.
|
|
35
86
|
useEffect(() => {
|
|
36
87
|
if (!pid && projectList.length) setPid(String(projectList[0].id));
|
|
@@ -41,6 +92,9 @@ export function CodeScreen() {
|
|
|
41
92
|
Code.sessions.list(pid),
|
|
42
93
|
);
|
|
43
94
|
|
|
95
|
+
// Agents for the active project.
|
|
96
|
+
const agentsData = useSWR(pid ? ["agents", pid] : null, () => Agents.list(pid));
|
|
97
|
+
|
|
44
98
|
// Full transcript of the active session.
|
|
45
99
|
const session = useSWR(pid && sid ? ["code-session", pid, sid] : null, () =>
|
|
46
100
|
Code.sessions.get(pid, sid!),
|
|
@@ -58,6 +112,11 @@ export function CodeScreen() {
|
|
|
58
112
|
if (sid && list.length && !list.some((s) => s.id === sid)) setSid(list[0]?.id ?? null);
|
|
59
113
|
}, [sessions.data, sid]);
|
|
60
114
|
|
|
115
|
+
// Sync agentSlug from the active session when it loads.
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (session.data) setAgentSlug(session.data.agentSlug || SUPER_AGENT_VALUE);
|
|
118
|
+
}, [session.data]);
|
|
119
|
+
|
|
61
120
|
// Hydrate the message list whenever the active session's transcript loads.
|
|
62
121
|
// (Not while streaming — we own the array then.)
|
|
63
122
|
useEffect(() => {
|
|
@@ -89,7 +148,10 @@ export function CodeScreen() {
|
|
|
89
148
|
const onCreateSession = async () => {
|
|
90
149
|
if (!pid || busy) return;
|
|
91
150
|
try {
|
|
92
|
-
const created = await Code.sessions.create(pid, {
|
|
151
|
+
const created = await Code.sessions.create(pid, {
|
|
152
|
+
title: t("code_module.untitled"),
|
|
153
|
+
agentSlug: agentSlug !== SUPER_AGENT_VALUE ? agentSlug : null,
|
|
154
|
+
});
|
|
93
155
|
await sessions.mutate();
|
|
94
156
|
setSid(created.id);
|
|
95
157
|
setMsgs([]);
|
|
@@ -126,6 +188,19 @@ export function CodeScreen() {
|
|
|
126
188
|
};
|
|
127
189
|
|
|
128
190
|
// Persist mode / model changes to the session (PATCH) + keep SWR in sync.
|
|
191
|
+
const onAgentChange = async (slug: string) => {
|
|
192
|
+
setAgentSlug(slug);
|
|
193
|
+
if (!sid) return;
|
|
194
|
+
try {
|
|
195
|
+
await Code.sessions.update(pid, sid, {
|
|
196
|
+
agentSlug: slug !== SUPER_AGENT_VALUE ? slug : null,
|
|
197
|
+
});
|
|
198
|
+
await Promise.all([session.mutate(), sessions.mutate()]);
|
|
199
|
+
} catch (e) {
|
|
200
|
+
toast.error((e as Error).message);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
129
204
|
const patchSession = useCallback(
|
|
130
205
|
async (patch: { mode?: CodeMode; model?: string | null }) => {
|
|
131
206
|
if (!sid) return;
|
|
@@ -207,8 +282,107 @@ export function CodeScreen() {
|
|
|
207
282
|
}
|
|
208
283
|
};
|
|
209
284
|
|
|
285
|
+
const openFile = useCallback(
|
|
286
|
+
(path: string) => {
|
|
287
|
+
setActiveTab(path);
|
|
288
|
+
setOpenFiles((prev) => {
|
|
289
|
+
if (prev.some((f) => f.path === path)) return prev; // already open
|
|
290
|
+
return [...prev, { path, content: "", loading: true }];
|
|
291
|
+
});
|
|
292
|
+
// Fetch content async
|
|
293
|
+
http
|
|
294
|
+
.post<{ ok: boolean; stdout: string; stderr: string }>("/run", {
|
|
295
|
+
cmd: `cat "${path}"`,
|
|
296
|
+
project: pid,
|
|
297
|
+
})
|
|
298
|
+
.then((r) => {
|
|
299
|
+
const content = r.stdout || r.stderr || "(vacío)";
|
|
300
|
+
setOpenFiles((prev) =>
|
|
301
|
+
prev.map((f) => (f.path === path ? { ...f, content, loading: false } : f)),
|
|
302
|
+
);
|
|
303
|
+
})
|
|
304
|
+
.catch((e: Error) => {
|
|
305
|
+
setOpenFiles((prev) =>
|
|
306
|
+
prev.map((f) =>
|
|
307
|
+
f.path === path ? { ...f, content: `Error: ${e.message}`, loading: false } : f,
|
|
308
|
+
),
|
|
309
|
+
);
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
[pid],
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const closeFile = useCallback((path: string) => {
|
|
316
|
+
setOpenFiles((prev) => prev.filter((f) => f.path !== path));
|
|
317
|
+
setActiveTab((prev) => (prev === path ? "chat" : prev));
|
|
318
|
+
}, []);
|
|
319
|
+
|
|
320
|
+
// Open an artifact as an EDITABLE tab. Reuses the file-tab UI but routes
|
|
321
|
+
// saves through Artifacts.write so the daemon persists the change.
|
|
322
|
+
const openArtifact = useCallback(
|
|
323
|
+
(name: string) => {
|
|
324
|
+
const tabPath = `artifacts/${name}`;
|
|
325
|
+
setActiveTab(tabPath);
|
|
326
|
+
setOpenFiles((prev) => {
|
|
327
|
+
if (prev.some((f) => f.path === tabPath)) return prev;
|
|
328
|
+
return [...prev, { path: tabPath, content: "", loading: true, artifactName: name }];
|
|
329
|
+
});
|
|
330
|
+
Artifacts.read(pid, name)
|
|
331
|
+
.then((r) => {
|
|
332
|
+
setOpenFiles((prev) =>
|
|
333
|
+
prev.map((f) =>
|
|
334
|
+
f.path === tabPath ? { ...f, content: r.content, loading: false } : f,
|
|
335
|
+
),
|
|
336
|
+
);
|
|
337
|
+
})
|
|
338
|
+
.catch((e: Error) => {
|
|
339
|
+
setOpenFiles((prev) =>
|
|
340
|
+
prev.map((f) =>
|
|
341
|
+
f.path === tabPath ? { ...f, content: `Error: ${e.message}`, loading: false } : f,
|
|
342
|
+
),
|
|
343
|
+
);
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
[pid],
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const saveOpenFile = useCallback(
|
|
350
|
+
async (path: string, content: string) => {
|
|
351
|
+
const file = openFiles.find((f) => f.path === path);
|
|
352
|
+
if (!file?.artifactName) return;
|
|
353
|
+
try {
|
|
354
|
+
await Artifacts.write(pid, file.artifactName, content);
|
|
355
|
+
setOpenFiles((prev) =>
|
|
356
|
+
prev.map((f) => (f.path === path ? { ...f, content } : f)),
|
|
357
|
+
);
|
|
358
|
+
toast.info("Guardado.");
|
|
359
|
+
} catch (e) {
|
|
360
|
+
toast.error((e as Error).message);
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
[openFiles, pid, toast],
|
|
364
|
+
);
|
|
365
|
+
|
|
210
366
|
const hasProjects = !projects.isLoading && projectList.length > 0;
|
|
367
|
+
|
|
368
|
+
const agentOptions = useMemo(() => {
|
|
369
|
+
const base = [{ value: SUPER_AGENT_VALUE, label: "super-agent", icon: Bot, description: "Agente principal con todas las herramientas" }];
|
|
370
|
+
const project = (agentsData.data || []).map((a) => ({
|
|
371
|
+
value: a.slug,
|
|
372
|
+
label: a.slug,
|
|
373
|
+
icon: Bot,
|
|
374
|
+
description: a.description || a.role || undefined,
|
|
375
|
+
}));
|
|
376
|
+
return [...base, ...project];
|
|
377
|
+
}, [agentsData.data]);
|
|
378
|
+
|
|
211
379
|
const turns: CodeTurn[] = useMemo(() => msgs as unknown as CodeTurn[], [msgs]);
|
|
380
|
+
const activeTitle = useMemo(
|
|
381
|
+
() => sessions.data?.find((s) => s.id === sid)?.title || "",
|
|
382
|
+
[sessions.data, sid],
|
|
383
|
+
);
|
|
384
|
+
const activeProject = useMemo(() => projectList.find((p) => String(p.id) === pid), [projectList, pid]);
|
|
385
|
+
useSetPageLabel(activeTitle);
|
|
212
386
|
|
|
213
387
|
// Detect unanswered ask_questions in the last assistant turn. Local "dismissed"
|
|
214
388
|
// ref keys off the turn id so the panel re-appears for a fresh batch.
|
|
@@ -220,96 +394,266 @@ export function CodeScreen() {
|
|
|
220
394
|
void send(compiled);
|
|
221
395
|
};
|
|
222
396
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
397
|
+
// Stable toggle callbacks
|
|
398
|
+
const toggleLeft = useCallback(() => setLeftOpen((v) => !v), []);
|
|
399
|
+
const toggleTree = useCallback(() => setWorktreeOpen((v) => !v), []);
|
|
400
|
+
const toggleTerm = useCallback(() => setTermOpen((v) => !v), []);
|
|
401
|
+
const toggleRight = useCallback(() => setRightOpen((v) => !v), []);
|
|
402
|
+
|
|
403
|
+
// Inject panel toggle icons into TopBar
|
|
404
|
+
const pageActions = useMemo(
|
|
405
|
+
() =>
|
|
406
|
+
sid ? (
|
|
407
|
+
<div className="flex items-center gap-0.5">
|
|
408
|
+
{[
|
|
409
|
+
{ Icon: PanelLeft, open: leftOpen, toggle: toggleLeft, title: "Lista de sesiones" },
|
|
410
|
+
{ Icon: FolderTree, open: worktreeOpen, toggle: toggleTree, title: "Árbol de archivos" },
|
|
411
|
+
{ Icon: Terminal, open: termOpen, toggle: toggleTerm, title: "Terminal" },
|
|
412
|
+
{ Icon: PanelRight, open: rightOpen, toggle: toggleRight, title: "Panel de contexto" },
|
|
413
|
+
].map(({ Icon, open, toggle, title }) => (
|
|
414
|
+
<Tip key={title} content={title}>
|
|
415
|
+
<button
|
|
416
|
+
type="button"
|
|
417
|
+
onClick={toggle}
|
|
418
|
+
data-active={open}
|
|
419
|
+
className="rounded p-1 text-muted-fg transition-colors hover:bg-accent hover:text-accent-fg data-[active=true]:bg-accent data-[active=true]:text-accent-fg"
|
|
420
|
+
>
|
|
421
|
+
<Icon className="size-3.5" />
|
|
422
|
+
</button>
|
|
423
|
+
</Tip>
|
|
424
|
+
))}
|
|
231
425
|
</div>
|
|
232
|
-
|
|
233
|
-
|
|
426
|
+
) : null,
|
|
427
|
+
[sid, leftOpen, worktreeOpen, termOpen, rightOpen, toggleLeft, toggleTree, toggleTerm, toggleRight],
|
|
428
|
+
);
|
|
429
|
+
useSetPageActions(pageActions);
|
|
234
430
|
|
|
431
|
+
return (
|
|
432
|
+
<div className="flex h-full min-h-0 flex-col" data-testid="screen-code">
|
|
235
433
|
{projects.isLoading ? (
|
|
236
434
|
<Loading />
|
|
237
435
|
) : !hasProjects ? (
|
|
238
|
-
<
|
|
436
|
+
<div className="grid flex-1 place-items-center">
|
|
437
|
+
<Empty>{t("code_module.no_projects")}</Empty>
|
|
438
|
+
</div>
|
|
239
439
|
) : (
|
|
240
|
-
<
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
440
|
+
<PanelGroup
|
|
441
|
+
orientation="vertical"
|
|
442
|
+
id="code-layout-v"
|
|
443
|
+
className="min-h-0 flex-1"
|
|
444
|
+
>
|
|
445
|
+
{/* TOP: horizontal split across [left | tree | main | right] */}
|
|
446
|
+
<Panel id="top" defaultSize={termOpen ? "55%" : "100%"} minSize="20%">
|
|
447
|
+
<PanelGroup orientation="horizontal" id="code-layout" className="h-full">
|
|
448
|
+
{/* Left panel: session list + agent selector */}
|
|
449
|
+
{leftOpen && (
|
|
450
|
+
<>
|
|
451
|
+
<Panel id="left" defaultSize="14%" minSize="8%">
|
|
452
|
+
<aside className="flex h-full flex-col">
|
|
453
|
+
<div className="shrink-0 border-b border-border p-2">
|
|
454
|
+
<CodeProjectPicker
|
|
455
|
+
projects={projectList}
|
|
456
|
+
value={pid}
|
|
457
|
+
onChange={onPickProject}
|
|
458
|
+
disabled={busy}
|
|
459
|
+
/>
|
|
460
|
+
</div>
|
|
461
|
+
<div className="min-h-0 flex-1 overflow-hidden">
|
|
462
|
+
<CodeSessionList
|
|
463
|
+
sessions={sessions.data || []}
|
|
464
|
+
activeId={sid}
|
|
465
|
+
busy={busy}
|
|
466
|
+
onSelect={onSelectSession}
|
|
467
|
+
onCreate={onCreateSession}
|
|
468
|
+
onRename={onRenameSession}
|
|
469
|
+
onDelete={onDeleteSession}
|
|
470
|
+
/>
|
|
471
|
+
</div>
|
|
472
|
+
<div className="shrink-0 border-t border-border p-2">
|
|
473
|
+
<UiSelect
|
|
474
|
+
value={agentSlug}
|
|
475
|
+
onChange={onAgentChange}
|
|
476
|
+
options={agentOptions}
|
|
477
|
+
disabled={busy}
|
|
478
|
+
showIcon={true}
|
|
479
|
+
/>
|
|
480
|
+
</div>
|
|
481
|
+
</aside>
|
|
482
|
+
</Panel>
|
|
483
|
+
<ResizeHandle />
|
|
484
|
+
</>
|
|
485
|
+
)}
|
|
486
|
+
|
|
487
|
+
{/* File tree panel */}
|
|
488
|
+
{worktreeOpen && (
|
|
489
|
+
<>
|
|
490
|
+
<Panel id="tree" defaultSize="13%" minSize="8%">
|
|
491
|
+
<div className="h-full">
|
|
492
|
+
<CodeFileTree pid={pid} projectPath={activeProject?.path} onOpenFile={openFile} />
|
|
493
|
+
</div>
|
|
494
|
+
</Panel>
|
|
495
|
+
<ResizeHandle />
|
|
496
|
+
</>
|
|
497
|
+
)}
|
|
498
|
+
|
|
499
|
+
{/* Main panel: tab bar (only with files) + transcript/file viewer + composer */}
|
|
500
|
+
<Panel id="main" defaultSize="50%" minSize="20%">
|
|
501
|
+
<div className="flex h-full flex-col">
|
|
502
|
+
{/* Tab bar — only when files are open */}
|
|
503
|
+
{openFiles.length > 0 && (
|
|
504
|
+
<div className="flex shrink-0 items-center gap-0 overflow-x-auto border-b border-border">
|
|
505
|
+
{/* Chat tab */}
|
|
506
|
+
<button
|
|
507
|
+
type="button"
|
|
508
|
+
onClick={() => setActiveTab("chat")}
|
|
509
|
+
data-active={activeTab === "chat"}
|
|
510
|
+
className="flex shrink-0 items-center gap-1.5 border-r border-border px-3 py-2 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-accent/40 data-[active=true]:text-foreground"
|
|
511
|
+
>
|
|
512
|
+
<MessageSquare className="size-3 shrink-0" />
|
|
513
|
+
Chat
|
|
514
|
+
</button>
|
|
515
|
+
{/* File tabs */}
|
|
516
|
+
{openFiles.map((f) => {
|
|
517
|
+
const name = f.path.split("/").pop() ?? f.path;
|
|
518
|
+
const isActive = activeTab === f.path;
|
|
519
|
+
return (
|
|
520
|
+
<div
|
|
521
|
+
key={f.path}
|
|
522
|
+
data-active={isActive}
|
|
523
|
+
className="group flex shrink-0 items-center gap-1 border-r border-border px-2 py-2 text-[11px] text-muted-foreground transition-colors hover:bg-accent/40 data-[active=true]:text-foreground"
|
|
524
|
+
>
|
|
525
|
+
<Tip content={f.path}>
|
|
526
|
+
<button
|
|
527
|
+
type="button"
|
|
528
|
+
onClick={() => setActiveTab(f.path)}
|
|
529
|
+
className="min-w-0 max-w-[140px] truncate font-mono"
|
|
530
|
+
>
|
|
531
|
+
{name}
|
|
532
|
+
</button>
|
|
533
|
+
</Tip>
|
|
534
|
+
<Tip content="Cerrar">
|
|
535
|
+
<button
|
|
536
|
+
type="button"
|
|
537
|
+
onClick={() => closeFile(f.path)}
|
|
538
|
+
className="shrink-0 rounded p-0.5 opacity-60 hover:bg-accent hover:opacity-100"
|
|
539
|
+
>
|
|
540
|
+
<X className="size-2.5" />
|
|
541
|
+
</button>
|
|
542
|
+
</Tip>
|
|
543
|
+
</div>
|
|
544
|
+
);
|
|
545
|
+
})}
|
|
546
|
+
</div>
|
|
547
|
+
)}
|
|
548
|
+
|
|
549
|
+
{/* Tab content */}
|
|
550
|
+
{activeTab === "chat" ? (
|
|
551
|
+
<>
|
|
552
|
+
<div className="min-h-0 flex-1 overflow-y-auto" data-testid="code-transcript">
|
|
553
|
+
{!sid ? (
|
|
554
|
+
<div className="grid h-full place-items-center p-6">
|
|
555
|
+
<Empty>{t("code_module.pick_project")}</Empty>
|
|
556
|
+
</div>
|
|
557
|
+
) : msgs.length ? (
|
|
558
|
+
<MessageList msgs={msgs} onCopy={copyToClipboard} />
|
|
559
|
+
) : (
|
|
560
|
+
<div className="grid h-full place-items-center p-6">
|
|
561
|
+
<Empty>{t("code_module.empty_chat")}</Empty>
|
|
562
|
+
</div>
|
|
563
|
+
)}
|
|
564
|
+
</div>
|
|
565
|
+
{askVisible && pending && (
|
|
566
|
+
<InlineAskPanel
|
|
567
|
+
turnKey={pending.turnKey}
|
|
568
|
+
questions={pending.questions}
|
|
569
|
+
onSubmit={submitAnswers}
|
|
570
|
+
onDismiss={() => setDismissedKey(pending.turnKey)}
|
|
571
|
+
disabled={busy}
|
|
572
|
+
/>
|
|
573
|
+
)}
|
|
574
|
+
</>
|
|
575
|
+
) : (
|
|
576
|
+
<div className="min-h-0 flex-1 overflow-hidden">
|
|
577
|
+
{(() => {
|
|
578
|
+
const file = openFiles.find((f) => f.path === activeTab);
|
|
579
|
+
if (!file) return null;
|
|
580
|
+
return (
|
|
581
|
+
<CodeFileViewer
|
|
582
|
+
path={file.path}
|
|
583
|
+
content={file.content}
|
|
584
|
+
loading={file.loading}
|
|
585
|
+
onSave={
|
|
586
|
+
file.artifactName
|
|
587
|
+
? (content) => saveOpenFile(file.path, content)
|
|
588
|
+
: undefined
|
|
589
|
+
}
|
|
590
|
+
/>
|
|
591
|
+
);
|
|
592
|
+
})()}
|
|
593
|
+
</div>
|
|
594
|
+
)}
|
|
595
|
+
|
|
596
|
+
{/* Composer — always visible at the bottom of the main column */}
|
|
597
|
+
<div className="shrink-0 border-t border-border p-2" data-testid="code-input">
|
|
598
|
+
<CodeComposer
|
|
599
|
+
value={draft}
|
|
600
|
+
onValueChange={setDraft}
|
|
601
|
+
onSubmit={() => void send()}
|
|
602
|
+
onStop={stop}
|
|
603
|
+
busy={busy}
|
|
604
|
+
disabled={!sid}
|
|
605
|
+
mode={mode}
|
|
606
|
+
onModeChange={(m) => void patchSession({ mode: m })}
|
|
607
|
+
model={model}
|
|
608
|
+
onModelChange={(m) => void patchSession({ model: m || null })}
|
|
609
|
+
/>
|
|
610
|
+
</div>
|
|
274
611
|
</div>
|
|
612
|
+
</Panel>
|
|
613
|
+
|
|
614
|
+
{/* Right panel: context + changes + artifacts */}
|
|
615
|
+
{rightOpen && (
|
|
616
|
+
<>
|
|
617
|
+
<ResizeHandle />
|
|
618
|
+
<Panel id="right" defaultSize="22%" minSize="15%">
|
|
619
|
+
<aside className="flex h-full flex-col">
|
|
620
|
+
<CodeSidePanel
|
|
621
|
+
pid={pid}
|
|
622
|
+
turns={turns}
|
|
623
|
+
changes={changes.data}
|
|
624
|
+
changesLoading={changes.isLoading}
|
|
625
|
+
onRefreshChanges={() => void changes.mutate()}
|
|
626
|
+
session={
|
|
627
|
+
session.data
|
|
628
|
+
? {
|
|
629
|
+
title: session.data.title,
|
|
630
|
+
mode: session.data.mode,
|
|
631
|
+
createdAt: session.data.createdAt,
|
|
632
|
+
updatedAt: session.data.updatedAt,
|
|
633
|
+
agentSlug: session.data.agentSlug ?? null,
|
|
634
|
+
}
|
|
635
|
+
: null
|
|
636
|
+
}
|
|
637
|
+
onRunInTerminal={runInTerminal}
|
|
638
|
+
onEditArtifact={openArtifact}
|
|
639
|
+
/>
|
|
640
|
+
</aside>
|
|
641
|
+
</Panel>
|
|
642
|
+
</>
|
|
275
643
|
)}
|
|
276
|
-
</
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
onValueChange={setDraft}
|
|
290
|
-
onSubmit={() => void send()}
|
|
291
|
-
onStop={stop}
|
|
292
|
-
busy={busy}
|
|
293
|
-
disabled={!sid}
|
|
294
|
-
mode={mode}
|
|
295
|
-
onModeChange={(m) => void patchSession({ mode: m })}
|
|
296
|
-
model={model}
|
|
297
|
-
onModelChange={(m) => void patchSession({ model: m || null })}
|
|
298
|
-
/>
|
|
299
|
-
</div>
|
|
300
|
-
</main>
|
|
301
|
-
|
|
302
|
-
{/* Right: context + changes */}
|
|
303
|
-
<aside className="hidden w-80 shrink-0 flex-col border-l border-border lg:flex">
|
|
304
|
-
<CodeSidePanel
|
|
305
|
-
pid={pid}
|
|
306
|
-
turns={turns}
|
|
307
|
-
changes={changes.data}
|
|
308
|
-
changesLoading={changes.isLoading}
|
|
309
|
-
onRefreshChanges={() => void changes.mutate()}
|
|
310
|
-
/>
|
|
311
|
-
</aside>
|
|
312
|
-
</div>
|
|
644
|
+
</PanelGroup>
|
|
645
|
+
</Panel>
|
|
646
|
+
|
|
647
|
+
{/* BOTTOM: terminal spanning the full width below all columns */}
|
|
648
|
+
{termOpen && pid && (
|
|
649
|
+
<>
|
|
650
|
+
<ResizeHandleH />
|
|
651
|
+
<Panel id="terminal" defaultSize="45%" minSize="10%" maxSize="80%">
|
|
652
|
+
<CodeTerminal pid={pid} initCmd={termInitCmd} onClose={toggleTerm} className="h-full" />
|
|
653
|
+
</Panel>
|
|
654
|
+
</>
|
|
655
|
+
)}
|
|
656
|
+
</PanelGroup>
|
|
313
657
|
)}
|
|
314
658
|
</div>
|
|
315
659
|
);
|
|
@@ -76,24 +76,6 @@ export function DeckScreen() {
|
|
|
76
76
|
|
|
77
77
|
return (
|
|
78
78
|
<div className="mx-auto max-w-4xl space-y-6 p-6" data-testid="screen-deck">
|
|
79
|
-
<header className="flex items-start justify-between gap-4">
|
|
80
|
-
<div>
|
|
81
|
-
<h1 className="text-2xl font-bold tracking-tight">Deck</h1>
|
|
82
|
-
<p className="text-sm text-muted-fg">
|
|
83
|
-
App companion · widgets y escritorios.
|
|
84
|
-
</p>
|
|
85
|
-
</div>
|
|
86
|
-
<Button
|
|
87
|
-
size="sm"
|
|
88
|
-
variant="ghost"
|
|
89
|
-
onClick={() => mutate()}
|
|
90
|
-
disabled={isLoading}
|
|
91
|
-
title="Recargar manifest"
|
|
92
|
-
>
|
|
93
|
-
<RefreshCw size={14} className={isLoading ? "animate-spin" : ""} />
|
|
94
|
-
</Button>
|
|
95
|
-
</header>
|
|
96
|
-
|
|
97
79
|
{/* Daemon info card */}
|
|
98
80
|
{data && <DaemonCard manifest={data} />}
|
|
99
81
|
|
|
@@ -107,6 +89,11 @@ export function DeckScreen() {
|
|
|
107
89
|
? "Error al cargar el manifest."
|
|
108
90
|
: `${widgets.length} widgets · ${enabledCount} externos habilitados`
|
|
109
91
|
}
|
|
92
|
+
action={
|
|
93
|
+
<Button size="sm" variant="ghost" onClick={() => mutate()} disabled={isLoading} title="Recargar manifest">
|
|
94
|
+
<RefreshCw size={14} className={isLoading ? "animate-spin" : ""} />
|
|
95
|
+
</Button>
|
|
96
|
+
}
|
|
110
97
|
>
|
|
111
98
|
{isLoading && <Loading label="Cargando manifest del Deck…" />}
|
|
112
99
|
|
|
@@ -96,15 +96,8 @@ export function DesktopScreen() {
|
|
|
96
96
|
|
|
97
97
|
return (
|
|
98
98
|
<div className="mx-auto max-w-6xl space-y-6 p-6" data-testid="screen-desktop">
|
|
99
|
-
<header>
|
|
100
|
-
<h1 className="text-2xl font-bold tracking-tight">Escritorio</h1>
|
|
101
|
-
<p className="text-sm text-muted-fg">
|
|
102
|
-
Ventana flotante de voz (Electron): atajo global, escucha por micrófono y muestra el chat.
|
|
103
|
-
</p>
|
|
104
|
-
</header>
|
|
105
|
-
|
|
106
99
|
{/* ── Two-column layout: config on the left, last conversation on the right. ── */}
|
|
107
|
-
<div className="grid gap-6
|
|
100
|
+
<div className="grid gap-6 xl:grid-cols-[1fr_1fr]">
|
|
108
101
|
{/* ── LEFT: configuration + status ─────────────────────────────── */}
|
|
109
102
|
<div className="space-y-6">
|
|
110
103
|
<Section title="Estado" description="La ventana se lanza desde la terminal o por autostart.">
|