newmark-agent 0.4.0 → 0.4.2
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/dist/conversation-utility-host.bundle.cjs +112 -1
- package/dist/core/agent.js +2 -0
- package/dist/core/toolPolicy.d.ts +9 -0
- package/dist/core/toolPolicy.js +125 -0
- package/dist/toolchain/registry-seeder.js +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +28 -0
- package/dist/tools/nativeTools.js +1 -0
- package/dist/tui/src/app.js +24 -0
- package/dist/tui/src/i18n.js +151 -0
- package/dist/tui/src/render.js +152 -61
- package/dist/tui/src/state.js +83 -0
- package/dist/ui/index.html +172 -62
- package/dist/ui/lucide-sprite.svg +5 -0
- package/dist/wsl-agent-host.bundle.cjs +112 -1
- package/package.json +4 -10
- package/Flow/Electron-Debug-Release.Flow.json +0 -43
- package/Flow/Flow.md +0 -9
- package/Flow/UI-Feature-Integration.Flow.json +0 -96
package/dist/tui/src/render.js
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
const data = require("./data");
|
|
4
4
|
const {
|
|
5
5
|
activeConversationModelLabel,
|
|
6
|
+
buildEventKey,
|
|
6
7
|
filteredCommands,
|
|
8
|
+
focusableEventsForRun,
|
|
7
9
|
memoryTagOptions,
|
|
8
10
|
resolveThemeAppearance,
|
|
9
11
|
selectedMemoryDetail
|
|
10
12
|
} = require("./state");
|
|
11
13
|
const { SETTINGS_CATEGORIES, displaySettingValue, settingsRows } = require("./settings-schema");
|
|
14
|
+
const { tr, resolveLanguage } = require("./i18n");
|
|
12
15
|
|
|
13
16
|
const ESC = "\u001b[";
|
|
14
17
|
const ANSI_PATTERN = /\u001b\[[0-9;?]*[A-Za-z]/g;
|
|
@@ -143,7 +146,7 @@ function card(lines, width, p, title) {
|
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
function renderSidebar(state, height, width, p) {
|
|
146
|
-
const header = [`${p.brand}${p.bold} NEWMARK${p.reset}`, `${p.muted} Agent TUI${p.reset}`, ""];
|
|
149
|
+
const header = [`${p.brand}${p.bold} NEWMARK${p.reset}`, `${p.muted} ${tr(state, "Agent TUI")}${p.reset}`, ""];
|
|
147
150
|
const menuLines = [];
|
|
148
151
|
let focusedLine = 0;
|
|
149
152
|
const isFocused = (level, index) => state.focusRegion === "menu"
|
|
@@ -155,7 +158,7 @@ function renderSidebar(state, height, width, p) {
|
|
|
155
158
|
const style = isFocused(level, index) ? `${p.selected}${p.bold}` : active ? p.bold : "";
|
|
156
159
|
const prefix = " ".repeat(indent);
|
|
157
160
|
if (isFocused(level, index)) focusedLine = menuLines.length;
|
|
158
|
-
menuLines.push(`${marker}${style} ${prefix}${item.icon} ${pad(item.label, width - 7 - indent)}${p.reset}`);
|
|
161
|
+
menuLines.push(`${marker}${style} ${prefix}${item.icon} ${pad(tr(state, item.label), width - 7 - indent)}${p.reset}`);
|
|
159
162
|
};
|
|
160
163
|
const addGroup = (label, level, rootIndex, expanded, active, icon = "") => {
|
|
161
164
|
const focused = isFocused("root", rootIndex);
|
|
@@ -165,7 +168,7 @@ function renderSidebar(state, height, width, p) {
|
|
|
165
168
|
if (focused) focusedLine = menuLines.length;
|
|
166
169
|
menuLines.push(`${marker}${style} ${chevron} ${icon ? `${icon} ` : ""}${pad(label, width - 6 - (icon ? 2 : 0))}${p.reset}`);
|
|
167
170
|
};
|
|
168
|
-
menuLines.push(`${p.muted} WORKSPACES${p.reset}`);
|
|
171
|
+
menuLines.push(`${p.muted} ${tr(state, "WORKSPACES")}${p.reset}`);
|
|
169
172
|
state.workspaces.forEach((workspace, workspaceIndex) => {
|
|
170
173
|
const rootIndex = workspaceIndex;
|
|
171
174
|
const level = `workspace:${workspace.id}`;
|
|
@@ -178,7 +181,7 @@ function renderSidebar(state, height, width, p) {
|
|
|
178
181
|
}
|
|
179
182
|
});
|
|
180
183
|
menuLines.push("");
|
|
181
|
-
menuLines.push(`${p.muted} OPERATIONS${p.reset}`);
|
|
184
|
+
menuLines.push(`${p.muted} ${tr(state, "OPERATIONS")}${p.reset}`);
|
|
182
185
|
const operations = data.navigation.filter((item) => item.section === "operations");
|
|
183
186
|
operations.forEach((item, index) => {
|
|
184
187
|
const rootIndex = state.workspaces.length + index;
|
|
@@ -186,9 +189,9 @@ function renderSidebar(state, height, width, p) {
|
|
|
186
189
|
});
|
|
187
190
|
const activeWorkspace = state.workspaces.find((item) => item.id === state.target.workspaceId);
|
|
188
191
|
const footer = [
|
|
189
|
-
`${p.muted} ACTIVE TARGET${p.reset}`,
|
|
192
|
+
`${p.muted} ${tr(state, "ACTIVE TARGET")}${p.reset}`,
|
|
190
193
|
` ${p.brand}${activeWorkspace?.icon || "◆"}${p.reset} ${truncate(activeWorkspace?.name || state.target.workspaceId, width - 5)}`,
|
|
191
|
-
`${p.muted} ACTIVE CONVERSATION${p.reset}`,
|
|
194
|
+
`${p.muted} ${tr(state, "ACTIVE CONVERSATION")}${p.reset}`,
|
|
192
195
|
` ${p.cyan}↳${p.reset} ${truncate(state.lastConversation, width - 5)}`,
|
|
193
196
|
`${p.green} ● local · ${state.adapterKind === "mock" ? "demo" : "Newmark core"}${p.reset}`
|
|
194
197
|
];
|
|
@@ -207,7 +210,7 @@ function conversationContext(state, p, scope) {
|
|
|
207
210
|
const workspace = state.workspaces.find((item) => item.id === state.target.workspaceId);
|
|
208
211
|
return [
|
|
209
212
|
`${p.muted}Workspace / ${workspace?.name || state.target.workspaceId} / Conversation${p.reset}`,
|
|
210
|
-
`${p.cyan}↳${p.reset} ${p.bold}${state.lastConversation}${p.reset} ${p.muted}· ${scope}${p.reset}`,
|
|
213
|
+
`${p.cyan}↳${p.reset} ${p.bold}${state.lastConversation}${p.reset} ${p.muted}· ${tr(state, scope)}${p.reset}`,
|
|
211
214
|
`${p.muted}${"─".repeat(42)}${p.reset}`,
|
|
212
215
|
""
|
|
213
216
|
];
|
|
@@ -305,33 +308,66 @@ function buildEventRows(run, width, p, extraGuides = [], state = {}) {
|
|
|
305
308
|
const rows = [];
|
|
306
309
|
const ordered = orderedWorkEvents(run, extraGuides);
|
|
307
310
|
const displayImages = runDisplayImages(run);
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
const focusables = focusableEventsForRun(run);
|
|
312
|
+
const focusByEvent = new Map();
|
|
313
|
+
focusables.forEach((event, index) => focusByEvent.set(event, { key: buildEventKey(event, index), index }));
|
|
314
|
+
const eventFocus = !!(state.conversationHistoryFocus && state.historyEventFocus);
|
|
315
|
+
const focusedIndex = Number(state.historyEventIndex) || 0;
|
|
316
|
+
state.historyEventFocusLine = -1;
|
|
317
|
+
const zh = resolveLanguage(state) === "zh";
|
|
318
|
+
const labelFor = (event) => {
|
|
319
|
+
const type = String(event?.type || "").toLowerCase();
|
|
320
|
+
if (type === "tool_call") return "TOOL";
|
|
321
|
+
if (type === "tool_result") return "RESULT";
|
|
322
|
+
if (type === "thought" || type === "thought_result") return zh ? "思考" : "THOUGHT";
|
|
323
|
+
return "PROGRESS";
|
|
324
|
+
};
|
|
325
|
+
const nameFor = (event) => String(event?.toolName || "");
|
|
326
|
+
const contentFor = (event) => {
|
|
327
|
+
const type = String(event?.type || "").toLowerCase();
|
|
328
|
+
const content = String(event?.content || event?.toolArgs || "").trim();
|
|
329
|
+
if ((type === "thought" || type === "thought_result") && !content) return zh ? "正在思考…" : "Thinking…";
|
|
330
|
+
return content;
|
|
313
331
|
};
|
|
332
|
+
let focusCursor = 0;
|
|
314
333
|
for (const event of ordered) {
|
|
315
334
|
if (isGuideEvent(event)) {
|
|
316
335
|
rows.push(...guideEventRows(event, width, p));
|
|
317
336
|
continue;
|
|
318
337
|
}
|
|
319
338
|
if (event.type === "final_response") continue;
|
|
320
|
-
const
|
|
321
|
-
|
|
339
|
+
const meta = focusByEvent.get(event);
|
|
340
|
+
const focusIndex = meta ? meta.index : focusCursor;
|
|
341
|
+
const key = meta ? meta.key : buildEventKey(event, focusIndex);
|
|
342
|
+
const focused = eventFocus && focusIndex === focusedIndex;
|
|
343
|
+
if (focused) state.historyEventFocusLine = rows.length;
|
|
344
|
+
const collapsed = !!(state.collapsedBuildEvents && state.collapsedBuildEvents.has(key));
|
|
345
|
+
const label = labelFor(event);
|
|
346
|
+
const name = nameFor(event);
|
|
347
|
+
const content = contentFor(event);
|
|
348
|
+
const chevron = collapsed ? "▸" : "▾";
|
|
349
|
+
const marker = focused ? "›" : " ";
|
|
350
|
+
if (collapsed || !content) {
|
|
351
|
+
const head = ` ${marker} ${chevron} ${label}${name ? ` ${name}` : ""}`;
|
|
352
|
+
rows.push(focused ? `${p.selected}${p.bold}${head}${p.reset}` : `${p.muted}${head}${p.reset}`);
|
|
353
|
+
} else {
|
|
354
|
+
const head = ` ${marker} ${chevron} ${label}${name ? ` ${name}` : ""} `;
|
|
355
|
+
const prefixWidth = visibleLength(head);
|
|
356
|
+
wrapText(content, Math.max(1, width - prefixWidth)).forEach((row, index) => {
|
|
357
|
+
const line = `${index === 0 ? head : " ".repeat(prefixWidth)}${row}`;
|
|
358
|
+
rows.push(focused ? `${p.selected}${p.bold}${line}${p.reset}` : `${p.muted}${line}${p.reset}`);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
322
361
|
if (event.type === "tool_call") {
|
|
323
|
-
append(` TOOL ${event.toolName || "tool"} `, content);
|
|
324
362
|
const result = ordered.find((candidate) => candidate.type === "tool_result" && candidate.displayImage && (
|
|
325
363
|
event.toolCallId && candidate.toolCallId ? String(event.toolCallId) === String(candidate.toolCallId) : candidate.toolName === event.toolName
|
|
326
364
|
));
|
|
327
365
|
if (result?.displayImage) rows.push(displayImageRow(result.displayImage, displayImages.indexOf(result.displayImage), state, p, width));
|
|
328
|
-
} else if (event.type === "tool_result") {
|
|
329
|
-
append(` RESULT ${event.toolName || "tool"} `, content);
|
|
330
|
-
} else if (["text", "status", "response"].includes(event.type)) {
|
|
331
|
-
append(" PROGRESS ", content);
|
|
332
366
|
}
|
|
367
|
+
focusCursor = focusIndex + 1;
|
|
333
368
|
}
|
|
334
|
-
|
|
369
|
+
state.historyEventFocusCount = focusCursor;
|
|
370
|
+
return rows;
|
|
335
371
|
}
|
|
336
372
|
|
|
337
373
|
function collapsedGuideRows(run, width, p, extraGuides = []) {
|
|
@@ -424,6 +460,7 @@ function chatView(state, width, height, p) {
|
|
|
424
460
|
const messageWidth = Math.max(8, detailWidth - roleColumnWidth);
|
|
425
461
|
const historyRows = [];
|
|
426
462
|
const historyBlockRanges = [];
|
|
463
|
+
state.historyEventFocusAbsoluteLine = -1;
|
|
427
464
|
const workRuns = [...(state.snapshot.workRuns || [])]
|
|
428
465
|
.sort((leftRun, rightRun) => Number(leftRun.sequence || 0) - Number(rightRun.sequence || 0));
|
|
429
466
|
const workRunById = new Map(workRuns.map((run, index) => [String(run.runId || ""), { run, index }]));
|
|
@@ -480,6 +517,10 @@ function chatView(state, width, height, p) {
|
|
|
480
517
|
)) || finalSummaryMessage(run);
|
|
481
518
|
appendChatMessage(historyRows, assistantSummary, messageWidth, roleColumnWidth, p);
|
|
482
519
|
historyBlockRanges[index] = { runId: run.runId, start, end };
|
|
520
|
+
if (state.conversationHistoryFocus && state.historyEventFocus
|
|
521
|
+
&& Number.isInteger(state.historyEventFocusLine) && state.historyEventFocusLine >= 0) {
|
|
522
|
+
state.historyEventFocusAbsoluteLine = start + 1 + state.historyEventFocusLine;
|
|
523
|
+
}
|
|
483
524
|
renderedRunIds.add(runId);
|
|
484
525
|
};
|
|
485
526
|
for (const message of state.messages) {
|
|
@@ -513,6 +554,19 @@ function chatView(state, width, height, p) {
|
|
|
513
554
|
state.conversationScroll = Math.max(0, historyRows.length - historyEnd);
|
|
514
555
|
}
|
|
515
556
|
}
|
|
557
|
+
if (state.conversationHistoryFocus && state.historyEventFocus
|
|
558
|
+
&& Number.isInteger(state.historyEventFocusAbsoluteLine) && state.historyEventFocusAbsoluteLine >= 0) {
|
|
559
|
+
const focusLine = state.historyEventFocusAbsoluteLine;
|
|
560
|
+
if (focusLine < historyStart) {
|
|
561
|
+
historyStart = focusLine;
|
|
562
|
+
historyEnd = Math.min(historyRows.length, historyStart + historyHeight);
|
|
563
|
+
state.conversationScroll = Math.max(0, historyRows.length - historyEnd);
|
|
564
|
+
} else if (focusLine >= historyEnd) {
|
|
565
|
+
historyEnd = Math.min(historyRows.length, focusLine + 1);
|
|
566
|
+
historyStart = Math.max(0, historyEnd - historyHeight);
|
|
567
|
+
state.conversationScroll = Math.max(0, historyRows.length - historyEnd);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
516
570
|
state.historyVisibleRunIds = historyBlockRanges
|
|
517
571
|
.filter(Boolean)
|
|
518
572
|
.filter((range) => range.end >= historyStart && range.start < historyEnd)
|
|
@@ -539,18 +593,19 @@ function planView(state, width, p) {
|
|
|
539
593
|
const goal = state.snapshot.goal;
|
|
540
594
|
const lines = [
|
|
541
595
|
...conversationContext(state, p, "Conversation plan"),
|
|
542
|
-
`${p.bold}Linked Plan · rev ${state.snapshot.linkedPlan.revision}${p.reset} ${p.cyan}${planItems.filter((item) => item.status === "done").length} / ${planItems.length} steps${p.reset}`,
|
|
596
|
+
`${p.bold}${tr(state, "Linked Plan")} · rev ${state.snapshot.linkedPlan.revision}${p.reset} ${p.cyan}${planItems.filter((item) => item.status === "done").length} / ${planItems.length} steps${p.reset}`,
|
|
543
597
|
"",
|
|
544
598
|
...planItems.map((item, i) => {
|
|
545
599
|
const style = state.focusRegion === "content" && i === state.selected ? `${p.selected}${p.bold}` : "";
|
|
600
|
+
if (style) state.contentFocusLine = 6 + i;
|
|
546
601
|
return `${style} ${statusIcon(item.status, p)} ${pad(item.text, Math.max(18, width - 28))} ${p.muted}${item.status}${p.reset}`;
|
|
547
602
|
}),
|
|
548
603
|
"",
|
|
549
604
|
...card([
|
|
550
|
-
`${p.bold}Next handoff${p.reset}`,
|
|
605
|
+
`${p.bold}${tr(state, "Next handoff")}${p.reset}`,
|
|
551
606
|
goal?.objective || "No active goal",
|
|
552
|
-
`${p.muted}
|
|
553
|
-
], width, p, "Linked goal")
|
|
607
|
+
`${p.muted}${tr(state, "Records come from the active Newmark conversation")}${p.reset}`
|
|
608
|
+
], width, p, tr(state, "Linked goal"))
|
|
554
609
|
];
|
|
555
610
|
return lines;
|
|
556
611
|
}
|
|
@@ -559,9 +614,9 @@ function goalView(state, width, p) {
|
|
|
559
614
|
const goal = state.snapshot.goal;
|
|
560
615
|
return [
|
|
561
616
|
...conversationContext(state, p, "Conversation goal"),
|
|
562
|
-
`${p.bold}Goal${p.reset} ${goal?.paused ? `${p.amber}paused${p.reset}` : `${p.green}active${p.reset}`}`,
|
|
617
|
+
`${p.bold}${tr(state, "Goal")}${p.reset} ${goal?.paused ? `${p.amber}paused${p.reset}` : `${p.green}active${p.reset}`}`,
|
|
563
618
|
"",
|
|
564
|
-
`${p.brand}${p.bold}${goal?.objective || "No active goal"}${p.reset}`,
|
|
619
|
+
`${p.brand}${p.bold}${goal?.objective || tr(state, "No active goal")}${p.reset}`,
|
|
565
620
|
"",
|
|
566
621
|
`Rounds ${goal?.goalRounds || 0}`,
|
|
567
622
|
`Verified ${goal?.verified ? "yes" : "no"}`,
|
|
@@ -576,11 +631,12 @@ function agentsView(state, width, p) {
|
|
|
576
631
|
const messageWidth = Math.max(8, width - roleColumnWidth - 4);
|
|
577
632
|
const rows = [
|
|
578
633
|
...conversationContext(state, p, "Conversation subagents"),
|
|
579
|
-
`${p.bold}Subagents${p.reset} ${p.muted}Parallel work with isolated context${p.reset}`,
|
|
634
|
+
`${p.bold}${tr(state, "Subagents")}${p.reset} ${p.muted}${tr(state, "Parallel work with isolated context")}${p.reset}`,
|
|
580
635
|
""
|
|
581
636
|
];
|
|
582
637
|
state.snapshot.subagents.forEach((agent, i) => {
|
|
583
638
|
const style = state.focusRegion === "content" && i === state.selected ? `${p.selected}` : "";
|
|
639
|
+
if (style) state.contentFocusLine = rows.length;
|
|
584
640
|
const expanded = state.agentHistoryExpandedId === String(agent.id || agent.displayName || "");
|
|
585
641
|
rows.push(
|
|
586
642
|
`${style} ${expanded ? "▾" : "▸"} ${statusIcon(agent.status, p)} ${p.bold}${pad(agent.displayName, 18)}${p.reset} ${pad(agent.task, Math.max(12, width - 49))} ${p.muted}${agent.status}${p.reset}`,
|
|
@@ -597,11 +653,11 @@ function agentsView(state, width, p) {
|
|
|
597
653
|
}, messageWidth, roleColumnWidth, p);
|
|
598
654
|
});
|
|
599
655
|
if (!messages.length) {
|
|
600
|
-
historyRows.push(`${" ".repeat(roleColumnWidth)}${p.muted}No messages recorded yet
|
|
656
|
+
historyRows.push(`${" ".repeat(roleColumnWidth)}${p.muted}${tr(state, "No messages recorded yet.")}${p.reset}`);
|
|
601
657
|
historyRows.push("");
|
|
602
658
|
}
|
|
603
659
|
if (agent.result) {
|
|
604
|
-
historyRows.push(`${p.brand}${p.bold}RESULT${p.reset}`);
|
|
660
|
+
historyRows.push(`${p.brand}${p.bold}${tr(state, "RESULT")}${p.reset}`);
|
|
605
661
|
wrapText(String(agent.result), messageWidth).forEach((row) => {
|
|
606
662
|
historyRows.push(`${" ".repeat(roleColumnWidth)}${row}`);
|
|
607
663
|
});
|
|
@@ -626,16 +682,18 @@ function modelView(state, width, p) {
|
|
|
626
682
|
|| (selection.providerId === current.providerId && selection.modelId === current.modelId));
|
|
627
683
|
return [
|
|
628
684
|
...conversationContext(state, p, "Model and reasoning effort"),
|
|
629
|
-
`${p.bold}Reasoning effort${p.reset} ${p.muted}Shared GUI/TUI request tier · ←/→ changes section${p.reset}`,
|
|
685
|
+
`${p.bold}${tr(state, "Reasoning effort")}${p.reset} ${p.muted}${tr(state, "Shared GUI/TUI request tier · ←/→ changes section")}${p.reset}`,
|
|
630
686
|
...INTELLIGENCE_TIERS.map((tier, index) => {
|
|
631
687
|
const style = state.focusRegion === "content" && state.contentColumn === 0 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
688
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
632
689
|
return `${style} ${tier === currentTier ? `${p.cyan}●${p.reset}` : "○"} ${tier}${p.reset}`;
|
|
633
690
|
}),
|
|
634
691
|
"",
|
|
635
|
-
`${p.bold}Deployment${p.reset} ${p.muted}Used by this conversation, including its Plan and Subagents${p.reset}`,
|
|
692
|
+
`${p.bold}${tr(state, "Deployment")}${p.reset} ${p.muted}${tr(state, "Used by this conversation, including its Plan and Subagents")}${p.reset}`,
|
|
636
693
|
"",
|
|
637
694
|
...options.flatMap((option, index) => {
|
|
638
695
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
696
|
+
if (style) state.contentFocusLine = 14 + index * 2;
|
|
639
697
|
const marker = isCurrent(option.selection) ? `${p.cyan}●${p.reset}` : "○";
|
|
640
698
|
return [
|
|
641
699
|
`${style} ${marker} ${pad(option.label, Math.max(18, Math.min(32, width - 24)))} ${p.muted}${option.provider}${p.reset}`,
|
|
@@ -643,7 +701,7 @@ function modelView(state, width, p) {
|
|
|
643
701
|
];
|
|
644
702
|
}),
|
|
645
703
|
"",
|
|
646
|
-
`${p.muted}Enter applies the focused tier or deployment. Effort persists globally; deployments remain per conversation
|
|
704
|
+
`${p.muted}${tr(state, "Enter applies the focused tier or deployment. Effort persists globally; deployments remain per conversation.")}${p.reset}`
|
|
647
705
|
];
|
|
648
706
|
}
|
|
649
707
|
|
|
@@ -651,7 +709,7 @@ function flowBarView(state, width, p) {
|
|
|
651
709
|
const workflow = state.currentFlow;
|
|
652
710
|
return [
|
|
653
711
|
...conversationContext(state, p, "Flow bar"),
|
|
654
|
-
`${p.bold}Flow Bar${p.reset} ${p.green}● tracked${p.reset}`,
|
|
712
|
+
`${p.bold}${tr(state, "Flow Bar")}${p.reset} ${p.green}● tracked${p.reset}`,
|
|
655
713
|
"",
|
|
656
714
|
`${p.brand}${p.bold}${workflow?.name || "No workflow selected"}${p.reset}`,
|
|
657
715
|
`${p.muted}Mode ${state.snapshot.mode} · current task ${Number(workflow?.pc || 0) + 1} / ${workflow?.components?.length || 0}${p.reset}`,
|
|
@@ -665,11 +723,12 @@ function flowBarView(state, width, p) {
|
|
|
665
723
|
function flowListView(state, width, p) {
|
|
666
724
|
return [
|
|
667
725
|
...conversationContext(state, p, "Flow list"),
|
|
668
|
-
`${p.bold}Flow List${p.reset} ${p.muted}Available workflows for this tracked conversation${p.reset}`,
|
|
726
|
+
`${p.bold}${tr(state, "Flow List")}${p.reset} ${p.muted}Available workflows for this tracked conversation${p.reset}`,
|
|
669
727
|
"",
|
|
670
728
|
...state.flows.map((name, index) => {
|
|
671
729
|
const selected = state.currentFlow?.name === name;
|
|
672
730
|
const style = state.focusRegion === "content" && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
731
|
+
if (style) state.contentFocusLine = 6 + index;
|
|
673
732
|
return `${style} ${selected ? `${p.cyan}●${p.reset}` : "○"} ${pad(name, Math.max(18, width - 8))}`;
|
|
674
733
|
})
|
|
675
734
|
];
|
|
@@ -679,10 +738,11 @@ function flowTaskView(state, width, p) {
|
|
|
679
738
|
const components = state.currentFlow?.components || [];
|
|
680
739
|
return [
|
|
681
740
|
...conversationContext(state, p, "Flow task"),
|
|
682
|
-
`${p.bold}Flow Task${p.reset} ${p.muted}${state.currentFlow?.name || "No workflow"}${p.reset}`,
|
|
741
|
+
`${p.bold}${tr(state, "Flow Task")}${p.reset} ${p.muted}${state.currentFlow?.name || "No workflow"}${p.reset}`,
|
|
683
742
|
"",
|
|
684
743
|
...components.flatMap((component, index) => {
|
|
685
744
|
const style = state.focusRegion === "content" && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
745
|
+
if (style) state.contentFocusLine = 6 + index * 2;
|
|
686
746
|
const mode = component.type === "logic" ? "LOGIC" : String(component.mode || "BUILD").toUpperCase();
|
|
687
747
|
return [
|
|
688
748
|
`${style} ${String(component.id).padStart(2)} ${pad(mode, 7)} ${truncate(component.prompt, Math.max(18, width - 16))}`,
|
|
@@ -695,11 +755,12 @@ function flowTaskView(state, width, p) {
|
|
|
695
755
|
function toolsView(state, width, p) {
|
|
696
756
|
const real = state.adapterKind !== "mock";
|
|
697
757
|
return [
|
|
698
|
-
`${p.bold}Tools & connectors${p.reset} ${p.muted}Enter toggles ${real ? "Newmark native-tool config" : "access in this demo"}${p.reset}`,
|
|
758
|
+
`${p.bold}${tr(state, "Tools & connectors")}${p.reset} ${p.muted}Enter toggles ${real ? "Newmark native-tool config" : "access in this demo"}${p.reset}`,
|
|
699
759
|
"",
|
|
700
760
|
`${p.muted} STATE TOOL SOURCE CALLS${p.reset}`,
|
|
701
761
|
...state.tools.map((tool, i) => {
|
|
702
762
|
const style = state.focusRegion === "content" && i === state.selected ? `${p.selected}${p.bold}` : "";
|
|
763
|
+
if (style) state.contentFocusLine = 3 + i;
|
|
703
764
|
const stateLabel = tool.enabled ? `${p.green} ON ${p.reset}` : `${p.muted}OFF ${p.reset}`;
|
|
704
765
|
return `${style} ${stateLabel} ${pad(tool.name, 20)} ${pad(tool.group, Math.max(12, width - 48))} ${String(tool.calls).padStart(5)}${p.reset}`;
|
|
705
766
|
}),
|
|
@@ -707,18 +768,20 @@ function toolsView(state, width, p) {
|
|
|
707
768
|
...card([
|
|
708
769
|
real ? "Tool availability is loaded from the active Newmark config." : "Tool calls are represented for interaction design only.",
|
|
709
770
|
real ? "Agent prompts may execute enabled tools under Newmark's existing approval policy." : "No shell, file, browser, network, or connector action is available here."
|
|
710
|
-
], width, p, real ? "Live runtime" : "Safety boundary")
|
|
771
|
+
], width, p, real ? tr(state, "Live runtime") : tr(state, "Safety boundary"))
|
|
711
772
|
];
|
|
712
773
|
}
|
|
713
774
|
|
|
714
775
|
function automationView(state, width, p) {
|
|
776
|
+
if (state.focusRegion === "content" && state.selected === 0) state.contentFocusLine = 2;
|
|
715
777
|
return [
|
|
716
|
-
`${p.bold}Automations${p.reset} ${p.muted}Schedules bound to explicit workspaces${p.reset}`,
|
|
778
|
+
`${p.bold}${tr(state, "Automations")}${p.reset} ${p.muted}Schedules bound to explicit workspaces${p.reset}`,
|
|
717
779
|
"",
|
|
718
|
-
`${state.focusRegion === "content" && state.selected === 0 ? `${p.selected}${p.bold}` : ""} ${p.cyan}[+] New automation${p.reset} ${p.muted}Create a schedule for this workspace${p.reset}`,
|
|
780
|
+
`${state.focusRegion === "content" && state.selected === 0 ? `${p.selected}${p.bold}` : ""} ${p.cyan}${tr(state, "[+] New automation")}${p.reset} ${p.muted}Create a schedule for this workspace${p.reset}`,
|
|
719
781
|
"",
|
|
720
782
|
...state.automations.flatMap((job, i) => {
|
|
721
783
|
const style = state.focusRegion === "content" && i + 1 === state.selected ? `${p.selected}${p.bold}` : "";
|
|
784
|
+
if (style) state.contentFocusLine = 4 + i * 2;
|
|
722
785
|
return [
|
|
723
786
|
`${style} ${job.enabled ? `${p.green}● ACTIVE${p.reset}` : `${p.muted}○ PAUSED${p.reset}`} ${pad(job.name, 24)} ${p.muted}${job.schedule}${p.reset}`,
|
|
724
787
|
` Last run: ${job.last}`
|
|
@@ -730,17 +793,19 @@ function automationView(state, width, p) {
|
|
|
730
793
|
}
|
|
731
794
|
|
|
732
795
|
function workflowView(state, width, p) {
|
|
796
|
+
if (state.focusRegion === "content" && state.selected === 0) state.contentFocusLine = 2;
|
|
733
797
|
const rows = [
|
|
734
|
-
`${p.bold}WorkFlow${p.reset} ${p.muted}Manage Flow definitions stored under ~/.Newmark/Flow${p.reset}`,
|
|
798
|
+
`${p.bold}${tr(state, "WorkFlow")}${p.reset} ${p.muted}Manage Flow definitions stored under ~/.Newmark/Flow${p.reset}`,
|
|
735
799
|
"",
|
|
736
|
-
`${state.focusRegion === "content" && state.selected === 0 ? `${p.selected}${p.bold}` : ""} ${p.cyan}[+] New workflow${p.reset} ${p.muted}Create a valid first dialog component${p.reset}`,
|
|
800
|
+
`${state.focusRegion === "content" && state.selected === 0 ? `${p.selected}${p.bold}` : ""} ${p.cyan}${tr(state, "[+] New workflow")}${p.reset} ${p.muted}Create a valid first dialog component${p.reset}`,
|
|
737
801
|
""
|
|
738
802
|
];
|
|
739
803
|
if (!state.flows.length) {
|
|
740
|
-
rows.push(`${p.muted}No workflows configured
|
|
804
|
+
rows.push(`${p.muted}${tr(state, "No workflows configured.")}${p.reset}`);
|
|
741
805
|
}
|
|
742
806
|
state.flows.forEach((name, index) => {
|
|
743
807
|
const selected = state.focusRegion === "content" && state.selected === index + 1;
|
|
808
|
+
if (selected) state.contentFocusLine = rows.length;
|
|
744
809
|
const workflow = state.workflowDetails[name] || { components: [] };
|
|
745
810
|
const expanded = state.workflowExpandedName === name;
|
|
746
811
|
rows.push(`${selected ? `${p.selected}${p.bold}` : ""} ${expanded ? "▾" : "▸"} ${pad(name, Math.max(18, width - 28))} ${p.muted}${workflow.components.length} component(s)${p.reset}`);
|
|
@@ -777,9 +842,9 @@ function memoryView(state, width, p) {
|
|
|
777
842
|
: [`${p.muted}— none —${p.reset}`]
|
|
778
843
|
)
|
|
779
844
|
];
|
|
780
|
-
const tagRows = relationColumn("Tags", tags, 0);
|
|
781
|
-
const selectedRows = relationColumn("Selected tag", detail.tag ? [detail.tag] : [], 1);
|
|
782
|
-
const childRows = relationColumn("Child tags", children, 2);
|
|
845
|
+
const tagRows = relationColumn(tr(state, "Tags"), tags, 0);
|
|
846
|
+
const selectedRows = relationColumn(tr(state, "Selected tag"), detail.tag ? [detail.tag] : [], 1);
|
|
847
|
+
const childRows = relationColumn(tr(state, "Child tags"), children, 2);
|
|
783
848
|
const relationRows = Array.from(
|
|
784
849
|
{ length: Math.max(tagRows.length, selectedRows.length, childRows.length) },
|
|
785
850
|
(_, row) => `${pad(tagRows[row] || "", relationWidth)} ${p.muted}│${p.reset} ${pad(selectedRows[row] || "", relationWidth)} ${p.muted}│${p.reset} ${childRows[row] || ""}`
|
|
@@ -795,12 +860,12 @@ function memoryView(state, width, p) {
|
|
|
795
860
|
});
|
|
796
861
|
const contentLines = detail.content.split(/\r?\n/).filter(Boolean).slice(0, 7);
|
|
797
862
|
const components = [
|
|
798
|
-
`${p.bold}Memory components${p.reset}`,
|
|
799
|
-
...(componentNames.length ? componentNames : [`${p.muted}No memory components${p.reset}`])
|
|
863
|
+
`${p.bold}${tr(state, "Memory components")}${p.reset}`,
|
|
864
|
+
...(componentNames.length ? componentNames : [`${p.muted}${tr(state, "No memory components")}${p.reset}`])
|
|
800
865
|
];
|
|
801
866
|
const preview = [
|
|
802
|
-
`${p.bold}Core memory${p.reset}`,
|
|
803
|
-
`${p.bold}${detail.component?.name || "No component selected"}${p.reset}`,
|
|
867
|
+
`${p.bold}${tr(state, "Core memory")}${p.reset}`,
|
|
868
|
+
`${p.bold}${detail.component?.name || tr(state, "No component selected")}${p.reset}`,
|
|
804
869
|
`${p.muted}${detail.component?.description || ""}${p.reset}`,
|
|
805
870
|
`${p.muted}Parents: ${detail.node.parents.join(", ") || "none"} · Children: ${children.join(", ") || "none"}${p.reset}`,
|
|
806
871
|
...contentLines,
|
|
@@ -810,9 +875,16 @@ function memoryView(state, width, p) {
|
|
|
810
875
|
{ length: Math.max(components.length, preview.length) },
|
|
811
876
|
(_, row) => `${pad(components[row] || "", componentWidth)} ${p.muted}│${p.reset} ${preview[row] || ""}`
|
|
812
877
|
);
|
|
878
|
+
if (state.focusRegion === "content") {
|
|
879
|
+
const focusColumn = Math.min(3, Math.max(0, state.contentColumn));
|
|
880
|
+
const focusIndex = state.memoryColumnIndices[focusColumn] || 0;
|
|
881
|
+
state.contentFocusLine = focusColumn === 3
|
|
882
|
+
? 3 + relationRows.length + 2 + focusIndex
|
|
883
|
+
: 4 + focusIndex;
|
|
884
|
+
}
|
|
813
885
|
return [
|
|
814
|
-
`${p.bold}Memory Lab${p.reset} ${state.memorySearchActive ? p.selected : p.muted}/${p.reset} ${state.memorySearchQuery || `${p.muted}search tags${p.reset}`}${state.memorySearchActive ? `${p.cyan}▏${p.reset}` : ""}`,
|
|
815
|
-
`${p.muted}/ search · Esc clear · ←/→ columns · ↑/↓ select · Enter follow/select
|
|
886
|
+
`${p.bold}${tr(state, "Memory Lab")}${p.reset} ${state.memorySearchActive ? p.selected : p.muted}/${p.reset} ${state.memorySearchQuery || `${p.muted}search tags${p.reset}`}${state.memorySearchActive ? `${p.cyan}▏${p.reset}` : ""}`,
|
|
887
|
+
`${p.muted}/ search · O overview · Esc clear · ←/→ columns · ↑/↓ select · Enter follow/select${p.reset}`,
|
|
816
888
|
`${p.muted}${"─".repeat(Math.max(8, width - 1))}${p.reset}`,
|
|
817
889
|
...relationRows,
|
|
818
890
|
"",
|
|
@@ -826,13 +898,14 @@ function settingsView(state, width, p) {
|
|
|
826
898
|
const categories = SETTINGS_CATEGORIES;
|
|
827
899
|
const leftWidth = Math.min(22, Math.max(18, Math.floor(width * 0.24)));
|
|
828
900
|
const categoryRows = [
|
|
829
|
-
`${p.bold}Settings${p.reset}`,
|
|
830
|
-
`${p.muted}Categories${p.reset}`,
|
|
901
|
+
`${p.bold}${tr(state, "Settings")}${p.reset}`,
|
|
902
|
+
`${p.muted}${tr(state, "Categories")}${p.reset}`,
|
|
831
903
|
"",
|
|
832
904
|
...categories.map(({ id, icon, label }, index) => {
|
|
833
905
|
const focused = state.focusRegion === "content" && state.contentColumn === 0 && state.settingsCategoryIndex === index;
|
|
906
|
+
if (focused) state.contentFocusLine = 3 + index;
|
|
834
907
|
const active = state.settingsTab === id;
|
|
835
|
-
return `${focused ? `${p.selected}${p.bold}` : active ? p.bold : ""}${active ? `${p.cyan}>${p.reset}` : " "} ${icon} ${label}${p.reset}`;
|
|
908
|
+
return `${focused ? `${p.selected}${p.bold}` : active ? p.bold : ""}${active ? `${p.cyan}>${p.reset}` : " "} ${icon} ${tr(state, label)}${p.reset}`;
|
|
836
909
|
}),
|
|
837
910
|
"",
|
|
838
911
|
`${p.muted}→ enter section${p.reset}`,
|
|
@@ -851,6 +924,7 @@ function settingsView(state, width, p) {
|
|
|
851
924
|
`${p.muted} STATE PROVIDER PROTOCOL KEY MODELS${p.reset}`,
|
|
852
925
|
...state.providers.map((provider, index) => {
|
|
853
926
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
927
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
854
928
|
const enabled = provider.enabled ? `${p.green} ON ${p.reset}` : `${p.muted}OFF ${p.reset}`;
|
|
855
929
|
const key = provider.has_api_key ? `${p.green}set${p.reset}` : `${p.amber}none${p.reset}`;
|
|
856
930
|
return `${style} ${enabled} ${pad(provider.name, 22)} ${pad(provider.protocol, 14)} ${key} ${String(provider.models.length).padStart(3)}${p.reset}`;
|
|
@@ -866,6 +940,7 @@ function settingsView(state, width, p) {
|
|
|
866
940
|
`${p.muted} STATE MODEL PROVIDER VALIDATION CTX${p.reset}`,
|
|
867
941
|
...models.map((entry, index) => {
|
|
868
942
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
943
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
869
944
|
const enabled = entry.model.enabled !== false && entry.provider.enabled;
|
|
870
945
|
const stateLabel = enabled ? `${p.green} ON ${p.reset}` : `${p.muted}OFF ${p.reset}`;
|
|
871
946
|
const validation = entry.model.validation || {};
|
|
@@ -881,6 +956,7 @@ function settingsView(state, width, p) {
|
|
|
881
956
|
`${p.muted} STATE TOOL GROUP CALLS${p.reset}`,
|
|
882
957
|
...state.tools.map((tool, index) => {
|
|
883
958
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
959
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
884
960
|
return `${style} ${tool.enabled ? `${p.green} ON ${p.reset}` : `${p.muted}OFF ${p.reset}`} ${pad(tool.name, 23)} ${pad(tool.group, 17)} ${String(tool.calls).padStart(5)}${p.reset}`;
|
|
885
961
|
}),
|
|
886
962
|
"",
|
|
@@ -892,6 +968,7 @@ function settingsView(state, width, p) {
|
|
|
892
968
|
...header,
|
|
893
969
|
...rows.flatMap((row, i) => {
|
|
894
970
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && i === state.selected ? `${p.selected}` : "";
|
|
971
|
+
if (style) state.contentFocusLine = 4 + i * 2;
|
|
895
972
|
return [
|
|
896
973
|
`${style} ${p.bold}${pad(row.label, 27)}${p.reset} ${p.cyan}${displaySettingValue(row.value)}${p.reset}${row.extension ? ` ${p.amber}TUI EXTENSION${p.reset}` : ""}`,
|
|
897
974
|
` ${p.muted}${settingDescription(state.settingsTab, row.key)}${p.reset}`
|
|
@@ -988,16 +1065,16 @@ function settingsActionHint(tab) {
|
|
|
988
1065
|
|
|
989
1066
|
function helpView(state, width, p) {
|
|
990
1067
|
return [
|
|
991
|
-
`${p.brand}${p.bold}SHORTCUT GUIDE${p.reset} ${p.muted}Context-aware TUI controls${p.reset}`,
|
|
1068
|
+
`${p.brand}${p.bold}${tr(state, "SHORTCUT GUIDE")}${p.reset} ${p.muted}Context-aware TUI controls${p.reset}`,
|
|
992
1069
|
`${p.muted}${"─".repeat(Math.max(20, width - 1))}${p.reset}`,
|
|
993
1070
|
"",
|
|
994
|
-
`${p.bold}Navigation${p.reset}`,
|
|
1071
|
+
`${p.bold}${tr(state, "Navigation")}${p.reset}`,
|
|
995
1072
|
" ↑ / ↓ Move within lists; long menus auto-scroll with the cursor",
|
|
996
1073
|
" ← / → Move between menu levels and content columns",
|
|
997
1074
|
" Enter Expand workspace, open view, select conversation, or confirm",
|
|
998
1075
|
" Tab From editor: conversation selection; from content: left menu",
|
|
999
1076
|
"",
|
|
1000
|
-
`${p.bold}Conversation editing${p.reset}`,
|
|
1077
|
+
`${p.bold}${tr(state, "Conversation editing")}${p.reset}`,
|
|
1001
1078
|
" Enter Edit selected conversation / send current draft",
|
|
1002
1079
|
" Enter mode Settings → General → Input mode chooses default Guide or Next",
|
|
1003
1080
|
" Shift+Enter Insert a newline; the input viewport always reserves two rows",
|
|
@@ -1008,17 +1085,17 @@ function helpView(state, width, p) {
|
|
|
1008
1085
|
" Flow Requires ↑ / ↓ + Enter workflow selection before editing",
|
|
1009
1086
|
" T Pin / unpin selected conversation; cursor follows its ID",
|
|
1010
1087
|
"",
|
|
1011
|
-
`${p.bold}Running work${p.reset}`,
|
|
1088
|
+
`${p.bold}${tr(state, "Running work")}${p.reset}`,
|
|
1012
1089
|
" \\ — / — Rotating left marker means that conversation is running",
|
|
1013
1090
|
" Esc Request graceful stop for the focused running conversation",
|
|
1014
1091
|
" Esc again Force-stop that same conversation",
|
|
1015
1092
|
" Switching focus never interrupts background conversations",
|
|
1016
1093
|
"",
|
|
1017
|
-
`${p.bold}Operation content${p.reset}`,
|
|
1094
|
+
`${p.bold}${tr(state, "Operation content")}${p.reset}`,
|
|
1018
1095
|
" Automations First row creates a persisted workspace-bound automation",
|
|
1019
1096
|
" WorkFlow Flat Operation item; content manages and creates Flow files",
|
|
1020
1097
|
"",
|
|
1021
|
-
`${p.bold}Global${p.reset}`,
|
|
1098
|
+
`${p.bold}${tr(state, "Global")}${p.reset}`,
|
|
1022
1099
|
" Ctrl+K Command palette",
|
|
1023
1100
|
" N New conversation",
|
|
1024
1101
|
" T Toggle theme outside conversation selection",
|
|
@@ -1044,6 +1121,7 @@ function renderContent(state, width, height, p) {
|
|
|
1044
1121
|
settings: settingsView,
|
|
1045
1122
|
help: helpView
|
|
1046
1123
|
};
|
|
1124
|
+
state.contentFocusLine = -1;
|
|
1047
1125
|
return views[state.view](state, width, p);
|
|
1048
1126
|
}
|
|
1049
1127
|
|
|
@@ -1158,9 +1236,22 @@ function render(state, columns = process.stdout.columns || 100, rows = process.s
|
|
|
1158
1236
|
const bodyHeight = height - 3;
|
|
1159
1237
|
const sidebar = sidebarWidth ? renderSidebar(state, bodyHeight, sidebarWidth, p) : [];
|
|
1160
1238
|
const content = renderContent(state, contentWidth, bodyHeight, p);
|
|
1239
|
+
let contentLines = content;
|
|
1240
|
+
if (state.view !== "chat") {
|
|
1241
|
+
const maximumScroll = Math.max(0, content.length - bodyHeight);
|
|
1242
|
+
let scroll = Math.max(0, Math.min(maximumScroll, Number(state.contentScroll) || 0));
|
|
1243
|
+
const focusLine = Number(state.contentFocusLine) || -1;
|
|
1244
|
+
if (focusLine >= 0) {
|
|
1245
|
+
if (focusLine < scroll) scroll = focusLine;
|
|
1246
|
+
else if (focusLine >= scroll + bodyHeight) scroll = focusLine - bodyHeight + 1;
|
|
1247
|
+
}
|
|
1248
|
+
scroll = Math.max(0, Math.min(maximumScroll, scroll));
|
|
1249
|
+
state.contentScroll = scroll;
|
|
1250
|
+
contentLines = content.slice(scroll, scroll + bodyHeight);
|
|
1251
|
+
}
|
|
1161
1252
|
const body = Array.from({ length: bodyHeight }, (_, i) => {
|
|
1162
1253
|
const left = sidebarWidth ? `${pad(sidebar[i] || "", sidebarWidth)}${p.muted}│${p.reset} ` : "";
|
|
1163
|
-
return `${left}${pad(
|
|
1254
|
+
return `${left}${pad(contentLines[i] || "", contentWidth)}`;
|
|
1164
1255
|
});
|
|
1165
1256
|
const activeNav = data.navigation.find((item) => item.id === state.view);
|
|
1166
1257
|
const compactContext = ["chat", "plan", "agents"].includes(state.view)
|