newmark-agent 0.4.0 → 0.4.3
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/config.example.json +5 -0
- package/dist/conversation-utility-host.bundle.cjs +419 -34
- package/dist/core/agent.d.ts +52 -4
- package/dist/core/agent.js +260 -23
- package/dist/core/agentKernelRunner.js +33 -16
- package/dist/core/config.d.ts +8 -0
- package/dist/core/conversationKernel.d.ts +16 -0
- package/dist/core/conversationKernel.js +34 -1
- package/dist/core/toolPolicy.d.ts +9 -0
- package/dist/core/toolPolicy.js +128 -0
- package/dist/llm/provider.d.ts +13 -1
- package/dist/llm/provider.js +42 -1
- package/dist/providers/provider-adapter.d.ts +3 -1
- package/dist/toolchain/registry-seeder.js +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +31 -1
- 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 +205 -90
- package/dist/tui/src/state.js +86 -0
- package/dist/ui/index.html +239 -71
- package/dist/ui/lucide-sprite.svg +5 -0
- package/dist/wsl-agent-host.bundle.cjs +419 -34
- package/package.json +7 -11
- 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 = []) {
|
|
@@ -394,19 +430,28 @@ function chatView(state, width, height, p) {
|
|
|
394
430
|
`${p.bold}Conversations${p.reset}`,
|
|
395
431
|
`${p.muted}N new${p.reset}`,
|
|
396
432
|
"",
|
|
397
|
-
...
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
433
|
+
...(() => {
|
|
434
|
+
const conversationRows = state.snapshot.conversations.map((item, i) => {
|
|
435
|
+
const style = state.focusRegion === "content" && i === state.selected && !state.inputMode ? `${p.selected}${p.bold}` : "";
|
|
436
|
+
const running = state.runningConversationKeys?.has(`${state.target.workspaceId}::${item.id}`);
|
|
437
|
+
const marker = running
|
|
438
|
+
? `${p.cyan}${["\\", "—", "/", "—"][state.tick % 4]}${p.reset}`
|
|
439
|
+
: item.title === state.lastConversation
|
|
440
|
+
? `${p.cyan}›${p.reset}`
|
|
441
|
+
: item.pinned
|
|
442
|
+
? `${p.amber}◆${p.reset}`
|
|
443
|
+
: "·";
|
|
444
|
+
const updated = String(item.updatedAt || "").slice(11, 16) || "—";
|
|
445
|
+
return `${style}${marker} ${pad(item.title, 19)} ${p.muted}${updated}${p.reset}`;
|
|
446
|
+
});
|
|
447
|
+
const listViewport = Math.max(1, height - 3);
|
|
448
|
+
const listMaxScroll = Math.max(0, conversationRows.length - listViewport);
|
|
449
|
+
let listScroll = Math.max(0, Math.min(listMaxScroll, Number(state.conversationListScroll) || 0));
|
|
450
|
+
if (state.selected < listScroll) listScroll = state.selected;
|
|
451
|
+
else if (state.selected >= listScroll + listViewport) listScroll = state.selected - listViewport + 1;
|
|
452
|
+
state.conversationListScroll = Math.max(0, Math.min(listMaxScroll, listScroll));
|
|
453
|
+
return conversationRows.slice(state.conversationListScroll, state.conversationListScroll + listViewport);
|
|
454
|
+
})()
|
|
410
455
|
] : [];
|
|
411
456
|
const preview = state.snapshot.conversations[state.selected] || state.snapshot.conversations[0] || {
|
|
412
457
|
id: state.target.conversationId,
|
|
@@ -424,6 +469,7 @@ function chatView(state, width, height, p) {
|
|
|
424
469
|
const messageWidth = Math.max(8, detailWidth - roleColumnWidth);
|
|
425
470
|
const historyRows = [];
|
|
426
471
|
const historyBlockRanges = [];
|
|
472
|
+
state.historyEventFocusAbsoluteLine = -1;
|
|
427
473
|
const workRuns = [...(state.snapshot.workRuns || [])]
|
|
428
474
|
.sort((leftRun, rightRun) => Number(leftRun.sequence || 0) - Number(rightRun.sequence || 0));
|
|
429
475
|
const workRunById = new Map(workRuns.map((run, index) => [String(run.runId || ""), { run, index }]));
|
|
@@ -480,6 +526,10 @@ function chatView(state, width, height, p) {
|
|
|
480
526
|
)) || finalSummaryMessage(run);
|
|
481
527
|
appendChatMessage(historyRows, assistantSummary, messageWidth, roleColumnWidth, p);
|
|
482
528
|
historyBlockRanges[index] = { runId: run.runId, start, end };
|
|
529
|
+
if (state.conversationHistoryFocus && state.historyEventFocus
|
|
530
|
+
&& Number.isInteger(state.historyEventFocusLine) && state.historyEventFocusLine >= 0) {
|
|
531
|
+
state.historyEventFocusAbsoluteLine = start + 1 + state.historyEventFocusLine;
|
|
532
|
+
}
|
|
483
533
|
renderedRunIds.add(runId);
|
|
484
534
|
};
|
|
485
535
|
for (const message of state.messages) {
|
|
@@ -513,6 +563,19 @@ function chatView(state, width, height, p) {
|
|
|
513
563
|
state.conversationScroll = Math.max(0, historyRows.length - historyEnd);
|
|
514
564
|
}
|
|
515
565
|
}
|
|
566
|
+
if (state.conversationHistoryFocus && state.historyEventFocus
|
|
567
|
+
&& Number.isInteger(state.historyEventFocusAbsoluteLine) && state.historyEventFocusAbsoluteLine >= 0) {
|
|
568
|
+
const focusLine = state.historyEventFocusAbsoluteLine;
|
|
569
|
+
if (focusLine < historyStart) {
|
|
570
|
+
historyStart = focusLine;
|
|
571
|
+
historyEnd = Math.min(historyRows.length, historyStart + historyHeight);
|
|
572
|
+
state.conversationScroll = Math.max(0, historyRows.length - historyEnd);
|
|
573
|
+
} else if (focusLine >= historyEnd) {
|
|
574
|
+
historyEnd = Math.min(historyRows.length, focusLine + 1);
|
|
575
|
+
historyStart = Math.max(0, historyEnd - historyHeight);
|
|
576
|
+
state.conversationScroll = Math.max(0, historyRows.length - historyEnd);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
516
579
|
state.historyVisibleRunIds = historyBlockRanges
|
|
517
580
|
.filter(Boolean)
|
|
518
581
|
.filter((range) => range.end >= historyStart && range.start < historyEnd)
|
|
@@ -539,18 +602,19 @@ function planView(state, width, p) {
|
|
|
539
602
|
const goal = state.snapshot.goal;
|
|
540
603
|
const lines = [
|
|
541
604
|
...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}`,
|
|
605
|
+
`${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
606
|
"",
|
|
544
607
|
...planItems.map((item, i) => {
|
|
545
608
|
const style = state.focusRegion === "content" && i === state.selected ? `${p.selected}${p.bold}` : "";
|
|
609
|
+
if (style) state.contentFocusLine = 6 + i;
|
|
546
610
|
return `${style} ${statusIcon(item.status, p)} ${pad(item.text, Math.max(18, width - 28))} ${p.muted}${item.status}${p.reset}`;
|
|
547
611
|
}),
|
|
548
612
|
"",
|
|
549
613
|
...card([
|
|
550
|
-
`${p.bold}Next handoff${p.reset}`,
|
|
614
|
+
`${p.bold}${tr(state, "Next handoff")}${p.reset}`,
|
|
551
615
|
goal?.objective || "No active goal",
|
|
552
|
-
`${p.muted}
|
|
553
|
-
], width, p, "Linked goal")
|
|
616
|
+
`${p.muted}${tr(state, "Records come from the active Newmark conversation")}${p.reset}`
|
|
617
|
+
], width, p, tr(state, "Linked goal"))
|
|
554
618
|
];
|
|
555
619
|
return lines;
|
|
556
620
|
}
|
|
@@ -559,9 +623,9 @@ function goalView(state, width, p) {
|
|
|
559
623
|
const goal = state.snapshot.goal;
|
|
560
624
|
return [
|
|
561
625
|
...conversationContext(state, p, "Conversation goal"),
|
|
562
|
-
`${p.bold}Goal${p.reset} ${goal?.paused ? `${p.amber}paused${p.reset}` : `${p.green}active${p.reset}`}`,
|
|
626
|
+
`${p.bold}${tr(state, "Goal")}${p.reset} ${goal?.paused ? `${p.amber}paused${p.reset}` : `${p.green}active${p.reset}`}`,
|
|
563
627
|
"",
|
|
564
|
-
`${p.brand}${p.bold}${goal?.objective || "No active goal"}${p.reset}`,
|
|
628
|
+
`${p.brand}${p.bold}${goal?.objective || tr(state, "No active goal")}${p.reset}`,
|
|
565
629
|
"",
|
|
566
630
|
`Rounds ${goal?.goalRounds || 0}`,
|
|
567
631
|
`Verified ${goal?.verified ? "yes" : "no"}`,
|
|
@@ -576,11 +640,12 @@ function agentsView(state, width, p) {
|
|
|
576
640
|
const messageWidth = Math.max(8, width - roleColumnWidth - 4);
|
|
577
641
|
const rows = [
|
|
578
642
|
...conversationContext(state, p, "Conversation subagents"),
|
|
579
|
-
`${p.bold}Subagents${p.reset} ${p.muted}Parallel work with isolated context${p.reset}`,
|
|
643
|
+
`${p.bold}${tr(state, "Subagents")}${p.reset} ${p.muted}${tr(state, "Parallel work with isolated context")}${p.reset}`,
|
|
580
644
|
""
|
|
581
645
|
];
|
|
582
646
|
state.snapshot.subagents.forEach((agent, i) => {
|
|
583
647
|
const style = state.focusRegion === "content" && i === state.selected ? `${p.selected}` : "";
|
|
648
|
+
if (style) state.contentFocusLine = rows.length;
|
|
584
649
|
const expanded = state.agentHistoryExpandedId === String(agent.id || agent.displayName || "");
|
|
585
650
|
rows.push(
|
|
586
651
|
`${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 +662,11 @@ function agentsView(state, width, p) {
|
|
|
597
662
|
}, messageWidth, roleColumnWidth, p);
|
|
598
663
|
});
|
|
599
664
|
if (!messages.length) {
|
|
600
|
-
historyRows.push(`${" ".repeat(roleColumnWidth)}${p.muted}No messages recorded yet
|
|
665
|
+
historyRows.push(`${" ".repeat(roleColumnWidth)}${p.muted}${tr(state, "No messages recorded yet.")}${p.reset}`);
|
|
601
666
|
historyRows.push("");
|
|
602
667
|
}
|
|
603
668
|
if (agent.result) {
|
|
604
|
-
historyRows.push(`${p.brand}${p.bold}RESULT${p.reset}`);
|
|
669
|
+
historyRows.push(`${p.brand}${p.bold}${tr(state, "RESULT")}${p.reset}`);
|
|
605
670
|
wrapText(String(agent.result), messageWidth).forEach((row) => {
|
|
606
671
|
historyRows.push(`${" ".repeat(roleColumnWidth)}${row}`);
|
|
607
672
|
});
|
|
@@ -626,16 +691,18 @@ function modelView(state, width, p) {
|
|
|
626
691
|
|| (selection.providerId === current.providerId && selection.modelId === current.modelId));
|
|
627
692
|
return [
|
|
628
693
|
...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}`,
|
|
694
|
+
`${p.bold}${tr(state, "Reasoning effort")}${p.reset} ${p.muted}${tr(state, "Shared GUI/TUI request tier · ←/→ changes section")}${p.reset}`,
|
|
630
695
|
...INTELLIGENCE_TIERS.map((tier, index) => {
|
|
631
696
|
const style = state.focusRegion === "content" && state.contentColumn === 0 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
697
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
632
698
|
return `${style} ${tier === currentTier ? `${p.cyan}●${p.reset}` : "○"} ${tier}${p.reset}`;
|
|
633
699
|
}),
|
|
634
700
|
"",
|
|
635
|
-
`${p.bold}Deployment${p.reset} ${p.muted}Used by this conversation, including its Plan and Subagents${p.reset}`,
|
|
701
|
+
`${p.bold}${tr(state, "Deployment")}${p.reset} ${p.muted}${tr(state, "Used by this conversation, including its Plan and Subagents")}${p.reset}`,
|
|
636
702
|
"",
|
|
637
703
|
...options.flatMap((option, index) => {
|
|
638
704
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
705
|
+
if (style) state.contentFocusLine = 14 + index * 2;
|
|
639
706
|
const marker = isCurrent(option.selection) ? `${p.cyan}●${p.reset}` : "○";
|
|
640
707
|
return [
|
|
641
708
|
`${style} ${marker} ${pad(option.label, Math.max(18, Math.min(32, width - 24)))} ${p.muted}${option.provider}${p.reset}`,
|
|
@@ -643,7 +710,7 @@ function modelView(state, width, p) {
|
|
|
643
710
|
];
|
|
644
711
|
}),
|
|
645
712
|
"",
|
|
646
|
-
`${p.muted}Enter applies the focused tier or deployment. Effort persists globally; deployments remain per conversation
|
|
713
|
+
`${p.muted}${tr(state, "Enter applies the focused tier or deployment. Effort persists globally; deployments remain per conversation.")}${p.reset}`
|
|
647
714
|
];
|
|
648
715
|
}
|
|
649
716
|
|
|
@@ -651,7 +718,7 @@ function flowBarView(state, width, p) {
|
|
|
651
718
|
const workflow = state.currentFlow;
|
|
652
719
|
return [
|
|
653
720
|
...conversationContext(state, p, "Flow bar"),
|
|
654
|
-
`${p.bold}Flow Bar${p.reset} ${p.green}● tracked${p.reset}`,
|
|
721
|
+
`${p.bold}${tr(state, "Flow Bar")}${p.reset} ${p.green}● tracked${p.reset}`,
|
|
655
722
|
"",
|
|
656
723
|
`${p.brand}${p.bold}${workflow?.name || "No workflow selected"}${p.reset}`,
|
|
657
724
|
`${p.muted}Mode ${state.snapshot.mode} · current task ${Number(workflow?.pc || 0) + 1} / ${workflow?.components?.length || 0}${p.reset}`,
|
|
@@ -665,11 +732,12 @@ function flowBarView(state, width, p) {
|
|
|
665
732
|
function flowListView(state, width, p) {
|
|
666
733
|
return [
|
|
667
734
|
...conversationContext(state, p, "Flow list"),
|
|
668
|
-
`${p.bold}Flow List${p.reset} ${p.muted}Available workflows for this tracked conversation${p.reset}`,
|
|
735
|
+
`${p.bold}${tr(state, "Flow List")}${p.reset} ${p.muted}Available workflows for this tracked conversation${p.reset}`,
|
|
669
736
|
"",
|
|
670
737
|
...state.flows.map((name, index) => {
|
|
671
738
|
const selected = state.currentFlow?.name === name;
|
|
672
739
|
const style = state.focusRegion === "content" && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
740
|
+
if (style) state.contentFocusLine = 6 + index;
|
|
673
741
|
return `${style} ${selected ? `${p.cyan}●${p.reset}` : "○"} ${pad(name, Math.max(18, width - 8))}`;
|
|
674
742
|
})
|
|
675
743
|
];
|
|
@@ -677,29 +745,30 @@ function flowListView(state, width, p) {
|
|
|
677
745
|
|
|
678
746
|
function flowTaskView(state, width, p) {
|
|
679
747
|
const components = state.currentFlow?.components || [];
|
|
680
|
-
|
|
748
|
+
const rows = [
|
|
681
749
|
...conversationContext(state, p, "Flow task"),
|
|
682
|
-
`${p.bold}Flow Task${p.reset} ${p.muted}${state.currentFlow?.name || "No workflow"}${p.reset}`,
|
|
683
|
-
""
|
|
684
|
-
...components.flatMap((component, index) => {
|
|
685
|
-
const style = state.focusRegion === "content" && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
686
|
-
const mode = component.type === "logic" ? "LOGIC" : String(component.mode || "BUILD").toUpperCase();
|
|
687
|
-
return [
|
|
688
|
-
`${style} ${String(component.id).padStart(2)} ${pad(mode, 7)} ${truncate(component.prompt, Math.max(18, width - 16))}`,
|
|
689
|
-
component.type === "logic" ? ` ${p.muted}true → ${component.goto_true} · false → ${component.goto_false}${p.reset}` : ""
|
|
690
|
-
].filter(Boolean);
|
|
691
|
-
})
|
|
750
|
+
`${p.bold}${tr(state, "Flow Task")}${p.reset} ${p.muted}${state.currentFlow?.name || "No workflow"}${p.reset}`,
|
|
751
|
+
""
|
|
692
752
|
];
|
|
753
|
+
components.forEach((component, index) => {
|
|
754
|
+
const style = state.focusRegion === "content" && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
755
|
+
if (style) state.contentFocusLine = rows.length;
|
|
756
|
+
const mode = component.type === "logic" ? "LOGIC" : String(component.mode || "BUILD").toUpperCase();
|
|
757
|
+
rows.push(`${style} ${String(component.id).padStart(2)} ${pad(mode, 7)} ${truncate(component.prompt, Math.max(18, width - 16))}`);
|
|
758
|
+
if (component.type === "logic") rows.push(` ${p.muted}true → ${component.goto_true} · false → ${component.goto_false}${p.reset}`);
|
|
759
|
+
});
|
|
760
|
+
return rows;
|
|
693
761
|
}
|
|
694
762
|
|
|
695
763
|
function toolsView(state, width, p) {
|
|
696
764
|
const real = state.adapterKind !== "mock";
|
|
697
765
|
return [
|
|
698
|
-
`${p.bold}Tools & connectors${p.reset} ${p.muted}Enter toggles ${real ? "Newmark native-tool config" : "access in this demo"}${p.reset}`,
|
|
766
|
+
`${p.bold}${tr(state, "Tools & connectors")}${p.reset} ${p.muted}Enter toggles ${real ? "Newmark native-tool config" : "access in this demo"}${p.reset}`,
|
|
699
767
|
"",
|
|
700
768
|
`${p.muted} STATE TOOL SOURCE CALLS${p.reset}`,
|
|
701
769
|
...state.tools.map((tool, i) => {
|
|
702
770
|
const style = state.focusRegion === "content" && i === state.selected ? `${p.selected}${p.bold}` : "";
|
|
771
|
+
if (style) state.contentFocusLine = 3 + i;
|
|
703
772
|
const stateLabel = tool.enabled ? `${p.green} ON ${p.reset}` : `${p.muted}OFF ${p.reset}`;
|
|
704
773
|
return `${style} ${stateLabel} ${pad(tool.name, 20)} ${pad(tool.group, Math.max(12, width - 48))} ${String(tool.calls).padStart(5)}${p.reset}`;
|
|
705
774
|
}),
|
|
@@ -707,18 +776,20 @@ function toolsView(state, width, p) {
|
|
|
707
776
|
...card([
|
|
708
777
|
real ? "Tool availability is loaded from the active Newmark config." : "Tool calls are represented for interaction design only.",
|
|
709
778
|
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")
|
|
779
|
+
], width, p, real ? tr(state, "Live runtime") : tr(state, "Safety boundary"))
|
|
711
780
|
];
|
|
712
781
|
}
|
|
713
782
|
|
|
714
783
|
function automationView(state, width, p) {
|
|
784
|
+
if (state.focusRegion === "content" && state.selected === 0) state.contentFocusLine = 2;
|
|
715
785
|
return [
|
|
716
|
-
`${p.bold}Automations${p.reset} ${p.muted}Schedules bound to explicit workspaces${p.reset}`,
|
|
786
|
+
`${p.bold}${tr(state, "Automations")}${p.reset} ${p.muted}Schedules bound to explicit workspaces${p.reset}`,
|
|
717
787
|
"",
|
|
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}`,
|
|
788
|
+
`${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
789
|
"",
|
|
720
790
|
...state.automations.flatMap((job, i) => {
|
|
721
791
|
const style = state.focusRegion === "content" && i + 1 === state.selected ? `${p.selected}${p.bold}` : "";
|
|
792
|
+
if (style) state.contentFocusLine = 4 + i * 2;
|
|
722
793
|
return [
|
|
723
794
|
`${style} ${job.enabled ? `${p.green}● ACTIVE${p.reset}` : `${p.muted}○ PAUSED${p.reset}`} ${pad(job.name, 24)} ${p.muted}${job.schedule}${p.reset}`,
|
|
724
795
|
` Last run: ${job.last}`
|
|
@@ -730,17 +801,19 @@ function automationView(state, width, p) {
|
|
|
730
801
|
}
|
|
731
802
|
|
|
732
803
|
function workflowView(state, width, p) {
|
|
804
|
+
if (state.focusRegion === "content" && state.selected === 0) state.contentFocusLine = 2;
|
|
733
805
|
const rows = [
|
|
734
|
-
`${p.bold}WorkFlow${p.reset} ${p.muted}Manage Flow definitions stored under ~/.Newmark/Flow${p.reset}`,
|
|
806
|
+
`${p.bold}${tr(state, "WorkFlow")}${p.reset} ${p.muted}Manage Flow definitions stored under ~/.Newmark/Flow${p.reset}`,
|
|
735
807
|
"",
|
|
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}`,
|
|
808
|
+
`${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
809
|
""
|
|
738
810
|
];
|
|
739
811
|
if (!state.flows.length) {
|
|
740
|
-
rows.push(`${p.muted}No workflows configured
|
|
812
|
+
rows.push(`${p.muted}${tr(state, "No workflows configured.")}${p.reset}`);
|
|
741
813
|
}
|
|
742
814
|
state.flows.forEach((name, index) => {
|
|
743
815
|
const selected = state.focusRegion === "content" && state.selected === index + 1;
|
|
816
|
+
if (selected) state.contentFocusLine = rows.length;
|
|
744
817
|
const workflow = state.workflowDetails[name] || { components: [] };
|
|
745
818
|
const expanded = state.workflowExpandedName === name;
|
|
746
819
|
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 +850,9 @@ function memoryView(state, width, p) {
|
|
|
777
850
|
: [`${p.muted}— none —${p.reset}`]
|
|
778
851
|
)
|
|
779
852
|
];
|
|
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);
|
|
853
|
+
const tagRows = relationColumn(tr(state, "Tags"), tags, 0);
|
|
854
|
+
const selectedRows = relationColumn(tr(state, "Selected tag"), detail.tag ? [detail.tag] : [], 1);
|
|
855
|
+
const childRows = relationColumn(tr(state, "Child tags"), children, 2);
|
|
783
856
|
const relationRows = Array.from(
|
|
784
857
|
{ length: Math.max(tagRows.length, selectedRows.length, childRows.length) },
|
|
785
858
|
(_, row) => `${pad(tagRows[row] || "", relationWidth)} ${p.muted}│${p.reset} ${pad(selectedRows[row] || "", relationWidth)} ${p.muted}│${p.reset} ${childRows[row] || ""}`
|
|
@@ -795,12 +868,12 @@ function memoryView(state, width, p) {
|
|
|
795
868
|
});
|
|
796
869
|
const contentLines = detail.content.split(/\r?\n/).filter(Boolean).slice(0, 7);
|
|
797
870
|
const components = [
|
|
798
|
-
`${p.bold}Memory components${p.reset}`,
|
|
799
|
-
...(componentNames.length ? componentNames : [`${p.muted}No memory components${p.reset}`])
|
|
871
|
+
`${p.bold}${tr(state, "Memory components")}${p.reset}`,
|
|
872
|
+
...(componentNames.length ? componentNames : [`${p.muted}${tr(state, "No memory components")}${p.reset}`])
|
|
800
873
|
];
|
|
801
874
|
const preview = [
|
|
802
|
-
`${p.bold}Core memory${p.reset}`,
|
|
803
|
-
`${p.bold}${detail.component?.name || "No component selected"}${p.reset}`,
|
|
875
|
+
`${p.bold}${tr(state, "Core memory")}${p.reset}`,
|
|
876
|
+
`${p.bold}${detail.component?.name || tr(state, "No component selected")}${p.reset}`,
|
|
804
877
|
`${p.muted}${detail.component?.description || ""}${p.reset}`,
|
|
805
878
|
`${p.muted}Parents: ${detail.node.parents.join(", ") || "none"} · Children: ${children.join(", ") || "none"}${p.reset}`,
|
|
806
879
|
...contentLines,
|
|
@@ -810,9 +883,16 @@ function memoryView(state, width, p) {
|
|
|
810
883
|
{ length: Math.max(components.length, preview.length) },
|
|
811
884
|
(_, row) => `${pad(components[row] || "", componentWidth)} ${p.muted}│${p.reset} ${preview[row] || ""}`
|
|
812
885
|
);
|
|
886
|
+
if (state.focusRegion === "content") {
|
|
887
|
+
const focusColumn = Math.min(3, Math.max(0, state.contentColumn));
|
|
888
|
+
const focusIndex = state.memoryColumnIndices[focusColumn] || 0;
|
|
889
|
+
state.contentFocusLine = focusColumn === 3
|
|
890
|
+
? 3 + relationRows.length + 2 + focusIndex
|
|
891
|
+
: 4 + focusIndex;
|
|
892
|
+
}
|
|
813
893
|
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
|
|
894
|
+
`${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}` : ""}`,
|
|
895
|
+
`${p.muted}/ search · O overview · Esc clear · ←/→ columns · ↑/↓ select · Enter follow/select${p.reset}`,
|
|
816
896
|
`${p.muted}${"─".repeat(Math.max(8, width - 1))}${p.reset}`,
|
|
817
897
|
...relationRows,
|
|
818
898
|
"",
|
|
@@ -826,13 +906,14 @@ function settingsView(state, width, p) {
|
|
|
826
906
|
const categories = SETTINGS_CATEGORIES;
|
|
827
907
|
const leftWidth = Math.min(22, Math.max(18, Math.floor(width * 0.24)));
|
|
828
908
|
const categoryRows = [
|
|
829
|
-
`${p.bold}Settings${p.reset}`,
|
|
830
|
-
`${p.muted}Categories${p.reset}`,
|
|
909
|
+
`${p.bold}${tr(state, "Settings")}${p.reset}`,
|
|
910
|
+
`${p.muted}${tr(state, "Categories")}${p.reset}`,
|
|
831
911
|
"",
|
|
832
912
|
...categories.map(({ id, icon, label }, index) => {
|
|
833
913
|
const focused = state.focusRegion === "content" && state.contentColumn === 0 && state.settingsCategoryIndex === index;
|
|
914
|
+
if (focused) state.contentFocusLine = 3 + index;
|
|
834
915
|
const active = state.settingsTab === id;
|
|
835
|
-
return `${focused ? `${p.selected}${p.bold}` : active ? p.bold : ""}${active ? `${p.cyan}>${p.reset}` : " "} ${icon} ${label}${p.reset}`;
|
|
916
|
+
return `${focused ? `${p.selected}${p.bold}` : active ? p.bold : ""}${active ? `${p.cyan}>${p.reset}` : " "} ${icon} ${tr(state, label)}${p.reset}`;
|
|
836
917
|
}),
|
|
837
918
|
"",
|
|
838
919
|
`${p.muted}→ enter section${p.reset}`,
|
|
@@ -851,6 +932,7 @@ function settingsView(state, width, p) {
|
|
|
851
932
|
`${p.muted} STATE PROVIDER PROTOCOL KEY MODELS${p.reset}`,
|
|
852
933
|
...state.providers.map((provider, index) => {
|
|
853
934
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
935
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
854
936
|
const enabled = provider.enabled ? `${p.green} ON ${p.reset}` : `${p.muted}OFF ${p.reset}`;
|
|
855
937
|
const key = provider.has_api_key ? `${p.green}set${p.reset}` : `${p.amber}none${p.reset}`;
|
|
856
938
|
return `${style} ${enabled} ${pad(provider.name, 22)} ${pad(provider.protocol, 14)} ${key} ${String(provider.models.length).padStart(3)}${p.reset}`;
|
|
@@ -866,6 +948,7 @@ function settingsView(state, width, p) {
|
|
|
866
948
|
`${p.muted} STATE MODEL PROVIDER VALIDATION CTX${p.reset}`,
|
|
867
949
|
...models.map((entry, index) => {
|
|
868
950
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
951
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
869
952
|
const enabled = entry.model.enabled !== false && entry.provider.enabled;
|
|
870
953
|
const stateLabel = enabled ? `${p.green} ON ${p.reset}` : `${p.muted}OFF ${p.reset}`;
|
|
871
954
|
const validation = entry.model.validation || {};
|
|
@@ -881,6 +964,7 @@ function settingsView(state, width, p) {
|
|
|
881
964
|
`${p.muted} STATE TOOL GROUP CALLS${p.reset}`,
|
|
882
965
|
...state.tools.map((tool, index) => {
|
|
883
966
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && index === state.selected ? `${p.selected}${p.bold}` : "";
|
|
967
|
+
if (style) state.contentFocusLine = 5 + index;
|
|
884
968
|
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
969
|
}),
|
|
886
970
|
"",
|
|
@@ -892,6 +976,7 @@ function settingsView(state, width, p) {
|
|
|
892
976
|
...header,
|
|
893
977
|
...rows.flatMap((row, i) => {
|
|
894
978
|
const style = state.focusRegion === "content" && state.contentColumn === 1 && i === state.selected ? `${p.selected}` : "";
|
|
979
|
+
if (style) state.contentFocusLine = 4 + i * 2;
|
|
895
980
|
return [
|
|
896
981
|
`${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
982
|
` ${p.muted}${settingDescription(state.settingsTab, row.key)}${p.reset}`
|
|
@@ -988,16 +1073,16 @@ function settingsActionHint(tab) {
|
|
|
988
1073
|
|
|
989
1074
|
function helpView(state, width, p) {
|
|
990
1075
|
return [
|
|
991
|
-
`${p.brand}${p.bold}SHORTCUT GUIDE${p.reset} ${p.muted}Context-aware TUI controls${p.reset}`,
|
|
1076
|
+
`${p.brand}${p.bold}${tr(state, "SHORTCUT GUIDE")}${p.reset} ${p.muted}Context-aware TUI controls${p.reset}`,
|
|
992
1077
|
`${p.muted}${"─".repeat(Math.max(20, width - 1))}${p.reset}`,
|
|
993
1078
|
"",
|
|
994
|
-
`${p.bold}Navigation${p.reset}`,
|
|
1079
|
+
`${p.bold}${tr(state, "Navigation")}${p.reset}`,
|
|
995
1080
|
" ↑ / ↓ Move within lists; long menus auto-scroll with the cursor",
|
|
996
1081
|
" ← / → Move between menu levels and content columns",
|
|
997
1082
|
" Enter Expand workspace, open view, select conversation, or confirm",
|
|
998
1083
|
" Tab From editor: conversation selection; from content: left menu",
|
|
999
1084
|
"",
|
|
1000
|
-
`${p.bold}Conversation editing${p.reset}`,
|
|
1085
|
+
`${p.bold}${tr(state, "Conversation editing")}${p.reset}`,
|
|
1001
1086
|
" Enter Edit selected conversation / send current draft",
|
|
1002
1087
|
" Enter mode Settings → General → Input mode chooses default Guide or Next",
|
|
1003
1088
|
" Shift+Enter Insert a newline; the input viewport always reserves two rows",
|
|
@@ -1008,17 +1093,17 @@ function helpView(state, width, p) {
|
|
|
1008
1093
|
" Flow Requires ↑ / ↓ + Enter workflow selection before editing",
|
|
1009
1094
|
" T Pin / unpin selected conversation; cursor follows its ID",
|
|
1010
1095
|
"",
|
|
1011
|
-
`${p.bold}Running work${p.reset}`,
|
|
1096
|
+
`${p.bold}${tr(state, "Running work")}${p.reset}`,
|
|
1012
1097
|
" \\ — / — Rotating left marker means that conversation is running",
|
|
1013
1098
|
" Esc Request graceful stop for the focused running conversation",
|
|
1014
1099
|
" Esc again Force-stop that same conversation",
|
|
1015
1100
|
" Switching focus never interrupts background conversations",
|
|
1016
1101
|
"",
|
|
1017
|
-
`${p.bold}Operation content${p.reset}`,
|
|
1102
|
+
`${p.bold}${tr(state, "Operation content")}${p.reset}`,
|
|
1018
1103
|
" Automations First row creates a persisted workspace-bound automation",
|
|
1019
1104
|
" WorkFlow Flat Operation item; content manages and creates Flow files",
|
|
1020
1105
|
"",
|
|
1021
|
-
`${p.bold}Global${p.reset}`,
|
|
1106
|
+
`${p.bold}${tr(state, "Global")}${p.reset}`,
|
|
1022
1107
|
" Ctrl+K Command palette",
|
|
1023
1108
|
" N New conversation",
|
|
1024
1109
|
" T Toggle theme outside conversation selection",
|
|
@@ -1044,6 +1129,7 @@ function renderContent(state, width, height, p) {
|
|
|
1044
1129
|
settings: settingsView,
|
|
1045
1130
|
help: helpView
|
|
1046
1131
|
};
|
|
1132
|
+
state.contentFocusLine = -1;
|
|
1047
1133
|
return views[state.view](state, width, p);
|
|
1048
1134
|
}
|
|
1049
1135
|
|
|
@@ -1065,11 +1151,19 @@ function overlayLines(state, width, p) {
|
|
|
1065
1151
|
}
|
|
1066
1152
|
if (state.overlay === "palette") {
|
|
1067
1153
|
const commands = filteredCommands(state);
|
|
1154
|
+
const paletteViewport = Math.min(7, Math.max(1, commands.length));
|
|
1155
|
+
const paletteMaxScroll = Math.max(0, commands.length - paletteViewport);
|
|
1156
|
+
let paletteScroll = Math.max(0, Math.min(paletteMaxScroll, Number(state.paletteScroll) || 0));
|
|
1157
|
+
if (state.paletteIndex < paletteScroll) paletteScroll = state.paletteIndex;
|
|
1158
|
+
else if (state.paletteIndex >= paletteScroll + paletteViewport) paletteScroll = state.paletteIndex - paletteViewport + 1;
|
|
1159
|
+
state.paletteScroll = Math.max(0, Math.min(paletteMaxScroll, paletteScroll));
|
|
1160
|
+
const visibleCommands = commands.slice(state.paletteScroll, state.paletteScroll + paletteViewport);
|
|
1068
1161
|
const rows = [
|
|
1069
1162
|
`${p.bold}>${p.reset} ${state.paletteQuery}${p.cyan}▏${p.reset}`,
|
|
1070
1163
|
`${p.muted}${"─".repeat(Math.max(8, Math.min(64, width - 10)))}${p.reset}`,
|
|
1071
|
-
...(
|
|
1072
|
-
const
|
|
1164
|
+
...(visibleCommands.length ? visibleCommands.map((command, i) => {
|
|
1165
|
+
const commandIndex = state.paletteScroll + i;
|
|
1166
|
+
const style = commandIndex === state.paletteIndex ? `${p.selected}${p.bold}` : "";
|
|
1073
1167
|
return `${style} ${pad(command.label, Math.max(18, Math.min(52, width - 18)))} ${p.muted}${command.hint}${p.reset}`;
|
|
1074
1168
|
}) : [`${p.muted} No matching commands${p.reset}`]),
|
|
1075
1169
|
"",
|
|
@@ -1078,14 +1172,22 @@ function overlayLines(state, width, p) {
|
|
|
1078
1172
|
return card(rows, Math.min(72, width - 4), p, "Command palette");
|
|
1079
1173
|
}
|
|
1080
1174
|
if (state.overlay === "flow-select") {
|
|
1175
|
+
const flowViewport = Math.min(7, Math.max(1, state.flows.length));
|
|
1176
|
+
const flowMaxScroll = Math.max(0, state.flows.length - flowViewport);
|
|
1177
|
+
let flowScroll = Math.max(0, Math.min(flowMaxScroll, Number(state.flowSelectionScroll) || 0));
|
|
1178
|
+
if (state.flowSelectionIndex < flowScroll) flowScroll = state.flowSelectionIndex;
|
|
1179
|
+
else if (state.flowSelectionIndex >= flowScroll + flowViewport) flowScroll = state.flowSelectionIndex - flowViewport + 1;
|
|
1180
|
+
state.flowSelectionScroll = Math.max(0, Math.min(flowMaxScroll, flowScroll));
|
|
1181
|
+
const visibleFlows = state.flows.slice(state.flowSelectionScroll, state.flowSelectionScroll + flowViewport);
|
|
1081
1182
|
return card([
|
|
1082
1183
|
`${p.bold}Flow mode requires a workflow${p.reset}`,
|
|
1083
1184
|
`${p.muted}This selection is bound to ${state.lastConversation}.${p.reset}`,
|
|
1084
1185
|
"",
|
|
1085
|
-
...(
|
|
1086
|
-
?
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1186
|
+
...(visibleFlows.length
|
|
1187
|
+
? visibleFlows.map((name, i) => {
|
|
1188
|
+
const flowIndex = state.flowSelectionScroll + i;
|
|
1189
|
+
const style = flowIndex === state.flowSelectionIndex ? `${p.selected}${p.bold}` : "";
|
|
1190
|
+
return `${style} ${flowIndex === state.flowSelectionIndex ? "›" : " "} ${name}${p.reset}`;
|
|
1089
1191
|
})
|
|
1090
1192
|
: [`${p.amber}No workflows are configured in ~/.Newmark/Flow.${p.reset}`]),
|
|
1091
1193
|
"",
|
|
@@ -1158,9 +1260,22 @@ function render(state, columns = process.stdout.columns || 100, rows = process.s
|
|
|
1158
1260
|
const bodyHeight = height - 3;
|
|
1159
1261
|
const sidebar = sidebarWidth ? renderSidebar(state, bodyHeight, sidebarWidth, p) : [];
|
|
1160
1262
|
const content = renderContent(state, contentWidth, bodyHeight, p);
|
|
1263
|
+
let contentLines = content;
|
|
1264
|
+
if (state.view !== "chat") {
|
|
1265
|
+
const maximumScroll = Math.max(0, content.length - bodyHeight);
|
|
1266
|
+
let scroll = Math.max(0, Math.min(maximumScroll, Number(state.contentScroll) || 0));
|
|
1267
|
+
const focusLine = Number(state.contentFocusLine) || -1;
|
|
1268
|
+
if (focusLine >= 0) {
|
|
1269
|
+
if (focusLine < scroll) scroll = focusLine;
|
|
1270
|
+
else if (focusLine >= scroll + bodyHeight) scroll = focusLine - bodyHeight + 1;
|
|
1271
|
+
}
|
|
1272
|
+
scroll = Math.max(0, Math.min(maximumScroll, scroll));
|
|
1273
|
+
state.contentScroll = scroll;
|
|
1274
|
+
contentLines = content.slice(scroll, scroll + bodyHeight);
|
|
1275
|
+
}
|
|
1161
1276
|
const body = Array.from({ length: bodyHeight }, (_, i) => {
|
|
1162
1277
|
const left = sidebarWidth ? `${pad(sidebar[i] || "", sidebarWidth)}${p.muted}│${p.reset} ` : "";
|
|
1163
|
-
return `${left}${pad(
|
|
1278
|
+
return `${left}${pad(contentLines[i] || "", contentWidth)}`;
|
|
1164
1279
|
});
|
|
1165
1280
|
const activeNav = data.navigation.find((item) => item.id === state.view);
|
|
1166
1281
|
const compactContext = ["chat", "plan", "agents"].includes(state.view)
|