praxis-agent 0.35.0 → 0.37.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/README.md +20 -2
- package/dist/application/background-agent-manager.d.ts +16 -6
- package/dist/application/background-agent-manager.js +139 -97
- package/dist/application/context-engine.d.ts +56 -0
- package/dist/application/context-engine.js +129 -0
- package/dist/application/durable-follow-up.d.ts +14 -0
- package/dist/application/durable-follow-up.js +52 -0
- package/dist/application/native-session-transcript.d.ts +72 -0
- package/dist/application/native-session-transcript.js +351 -0
- package/dist/application/native-sidechain-transcript.d.ts +34 -0
- package/dist/application/native-sidechain-transcript.js +156 -0
- package/dist/application/session-service.d.ts +23 -4
- package/dist/application/session-service.js +1130 -494
- package/dist/application/subagent-service.d.ts +5 -1
- package/dist/application/subagent-service.js +749 -320
- package/dist/application/team-agent-runtime.d.ts +29 -0
- package/dist/application/team-agent-runtime.js +169 -0
- package/dist/application/team-capability.d.ts +27 -0
- package/dist/application/team-capability.js +41 -0
- package/dist/application/team-lead-operations.d.ts +38 -0
- package/dist/application/team-lead-operations.js +155 -0
- package/dist/application/team-mailbox.d.ts +54 -0
- package/dist/application/team-mailbox.js +196 -0
- package/dist/application/team-manager.d.ts +113 -0
- package/dist/application/team-manager.js +681 -0
- package/dist/application/team-workspace.d.ts +29 -0
- package/dist/application/team-workspace.js +197 -0
- package/dist/application/top-level-agent-manager.d.ts +1 -0
- package/dist/application/top-level-agent-manager.js +1025 -403
- package/dist/application/transcript-projection.d.ts +35 -0
- package/dist/application/transcript-projection.js +139 -0
- package/dist/application/turn-lifecycle.d.ts +2 -0
- package/dist/application/turn-memory-coordinator.d.ts +43 -0
- package/dist/application/turn-memory-coordinator.js +98 -0
- package/dist/cli/agents-dashboard.js +10 -5
- package/dist/cli/interactive.d.ts +7 -0
- package/dist/cli/interactive.js +645 -334
- package/dist/cli/tui/claude-style.d.ts +8 -25
- package/dist/cli/tui/claude-style.js +299 -277
- package/dist/cli/tui/decision-surface-model.d.ts +71 -0
- package/dist/cli/tui/decision-surface-model.js +131 -0
- package/dist/cli/tui/decision-surface.d.ts +7 -0
- package/dist/cli/tui/decision-surface.js +41 -0
- package/dist/cli/tui/help-surface-model.d.ts +68 -0
- package/dist/cli/tui/help-surface-model.js +131 -0
- package/dist/cli/tui/mcp-panel.js +1 -2
- package/dist/cli/tui/permission-surface-model.d.ts +195 -0
- package/dist/cli/tui/permission-surface-model.js +360 -0
- package/dist/cli/tui/permission-surface.d.ts +7 -0
- package/dist/cli/tui/permission-surface.js +69 -0
- package/dist/cli/tui/presentation-environment.d.ts +32 -0
- package/dist/cli/tui/presentation-environment.js +149 -0
- package/dist/cli/tui/task-panel.js +10 -1
- package/dist/cli/tui/theme.d.ts +46 -3
- package/dist/cli/tui/theme.js +414 -56
- package/dist/cli/tui/tool-permission.js +16 -8
- package/dist/cli/tui/transcript-presentation.d.ts +19 -1
- package/dist/cli/tui/transcript-presentation.js +26 -6
- package/dist/cli/tui/transcript-viewport.d.ts +18 -0
- package/dist/cli/tui/transcript-viewport.js +517 -111
- package/dist/cli/tui/transcript-window-model.d.ts +108 -0
- package/dist/cli/tui/transcript-window-model.js +772 -0
- package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
- package/dist/cli/tui/tui-interaction-router.js +127 -0
- package/dist/cli/tui/tui-screen-model.d.ts +114 -0
- package/dist/cli/tui/tui-screen-model.js +210 -0
- package/dist/cli/tui/tui-view-model.d.ts +2 -49
- package/dist/cli/tui/tui-view-model.js +1 -68
- package/dist/cli-runtime.d.ts +4 -2
- package/dist/cli-runtime.js +397 -32
- package/dist/compatibility/claude/transcript-codec.js +6 -0
- package/dist/core/agent-orchestration.d.ts +31 -0
- package/dist/core/agent-orchestration.js +235 -0
- package/dist/core/permission-policy.d.ts +4 -0
- package/dist/core/permission-policy.js +21 -0
- package/dist/core/runtime.d.ts +6 -0
- package/dist/core/runtime.js +20 -2
- package/dist/core/team-mailbox.d.ts +54 -0
- package/dist/core/team-mailbox.js +212 -0
- package/dist/core/team-ownership.d.ts +84 -0
- package/dist/core/team-ownership.js +505 -0
- package/dist/core/tool-execution-scheduler.js +6 -1
- package/dist/core/transcript-event.d.ts +5 -1
- package/dist/core/transcript-event.js +12 -0
- package/dist/persistence/claude-job-store.d.ts +58 -0
- package/dist/persistence/claude-job-store.js +483 -1
- package/dist/persistence/claude-transcript-store.d.ts +2 -1
- package/dist/persistence/claude-transcript-store.js +2 -10
- package/dist/persistence/native-team-mailbox.d.ts +68 -0
- package/dist/persistence/native-team-mailbox.js +542 -0
- package/dist/persistence/native-team-store.d.ts +34 -0
- package/dist/persistence/native-team-store.js +257 -0
- package/dist/persistence/native-transcript-reader.d.ts +53 -0
- package/dist/persistence/native-transcript-reader.js +283 -0
- package/dist/persistence/native-transcript-store.d.ts +69 -0
- package/dist/persistence/native-transcript-store.js +321 -0
- package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
- package/dist/persistence/subagent-lifecycle-store.js +373 -146
- package/dist/persistence/transcript-file-append.d.ts +2 -0
- package/dist/persistence/transcript-file-append.js +10 -0
- package/dist/platform/exclusive-file-lease.d.ts +3 -0
- package/dist/platform/exclusive-file-lease.js +3 -0
- package/dist/tools/claude-capabilities.d.ts +4 -0
- package/dist/tools/claude-capabilities.js +18 -2
- package/dist/tools/team-lead-tools.d.ts +19 -0
- package/dist/tools/team-lead-tools.js +479 -0
- package/dist/tools/team-member-tools.d.ts +21 -0
- package/dist/tools/team-member-tools.js +259 -0
- package/package.json +1 -1
- package/dist/cli/tui/fullscreen-renderer.d.ts +0 -14
- package/dist/cli/tui/fullscreen-renderer.js +0 -13
|
@@ -128,47 +128,77 @@ function hardWrappedRows(word, width, startColumn) {
|
|
|
128
128
|
}
|
|
129
129
|
return { rows, column };
|
|
130
130
|
}
|
|
131
|
-
function
|
|
131
|
+
function wrappedLineLayout(line, width, output) {
|
|
132
132
|
const expanded = expandTabs(line);
|
|
133
|
-
if (isPrintableAscii(expanded) && expanded.length <= width)
|
|
133
|
+
if (isPrintableAscii(expanded) && expanded.length <= width) {
|
|
134
|
+
output?.push(expanded);
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
if (expanded.length === 0) {
|
|
138
|
+
output?.push('');
|
|
134
139
|
return 1;
|
|
140
|
+
}
|
|
135
141
|
const words = expanded.split(' ');
|
|
136
|
-
let
|
|
142
|
+
let completedRows = 0;
|
|
143
|
+
let row = '';
|
|
137
144
|
let column = 0;
|
|
145
|
+
const pushRow = () => {
|
|
146
|
+
output?.push(row);
|
|
147
|
+
row = '';
|
|
148
|
+
column = 0;
|
|
149
|
+
completedRows += 1;
|
|
150
|
+
};
|
|
151
|
+
const appendHardWrappedWord = (word) => {
|
|
152
|
+
if (!output) {
|
|
153
|
+
const wrapped = hardWrappedRows(word, width, column);
|
|
154
|
+
completedRows += wrapped.rows;
|
|
155
|
+
column = wrapped.column;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const clusters = graphemes(word);
|
|
159
|
+
for (const [index, cluster] of clusters.entries()) {
|
|
160
|
+
const clusterWidth = graphemeWidth(cluster);
|
|
161
|
+
if (column + clusterWidth > width)
|
|
162
|
+
pushRow();
|
|
163
|
+
row += cluster;
|
|
164
|
+
column += clusterWidth;
|
|
165
|
+
if (column >= width && index < clusters.length - 1)
|
|
166
|
+
pushRow();
|
|
167
|
+
}
|
|
168
|
+
};
|
|
138
169
|
for (let index = 0; index < words.length; index += 1) {
|
|
139
170
|
const word = words[index] ?? '';
|
|
140
171
|
const wordWidth = isPrintableAscii(word)
|
|
141
172
|
? word.length
|
|
142
173
|
: terminalStringWidth(word);
|
|
143
174
|
if (index > 0) {
|
|
144
|
-
if (column >= width)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
175
|
+
if (column >= width)
|
|
176
|
+
pushRow();
|
|
177
|
+
if (output)
|
|
178
|
+
row += ' ';
|
|
148
179
|
column += 1;
|
|
149
180
|
}
|
|
150
181
|
if (wordWidth > width) {
|
|
151
182
|
const current = hardWrappedRows(word, width, column);
|
|
152
183
|
const fresh = hardWrappedRows(word, width, 0);
|
|
153
184
|
if (column > 0 && current.rows > fresh.rows) {
|
|
154
|
-
|
|
155
|
-
column = 0;
|
|
156
|
-
rows += fresh.rows;
|
|
157
|
-
column = fresh.column;
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
rows += current.rows;
|
|
161
|
-
column = current.column;
|
|
185
|
+
pushRow();
|
|
162
186
|
}
|
|
187
|
+
appendHardWrappedWord(word);
|
|
163
188
|
continue;
|
|
164
189
|
}
|
|
165
190
|
if (column + wordWidth > width && column > 0 && word.length > 0) {
|
|
166
|
-
|
|
167
|
-
column = 0;
|
|
191
|
+
pushRow();
|
|
168
192
|
}
|
|
193
|
+
if (output)
|
|
194
|
+
row += word;
|
|
169
195
|
column += wordWidth;
|
|
170
196
|
}
|
|
171
|
-
|
|
197
|
+
pushRow();
|
|
198
|
+
return completedRows;
|
|
199
|
+
}
|
|
200
|
+
function wrappedLineRows(line, width) {
|
|
201
|
+
return wrappedLineLayout(line, width);
|
|
172
202
|
}
|
|
173
203
|
function wrappedLineCount(text, width) {
|
|
174
204
|
const usable = Math.max(1, width);
|
|
@@ -315,7 +345,76 @@ function textTail(text, budget, width) {
|
|
|
315
345
|
}
|
|
316
346
|
return `${TRANSCRIPT_TRUNCATION_MARKER}${kept.join('\n')}`;
|
|
317
347
|
}
|
|
318
|
-
|
|
348
|
+
function textHead(text, budget, width) {
|
|
349
|
+
const usableBudget = Math.max(1, budget);
|
|
350
|
+
const usableWidth = Math.max(1, width);
|
|
351
|
+
if (wrappedLineCount(text, usableWidth) <= usableBudget)
|
|
352
|
+
return text;
|
|
353
|
+
const marker = `${TRANSCRIPT_TRUNCATION_MARKER}`;
|
|
354
|
+
const lines = text.split('\n');
|
|
355
|
+
const kept = [];
|
|
356
|
+
for (const line of lines) {
|
|
357
|
+
const candidate = [...kept, line, marker].join('\n');
|
|
358
|
+
if (wrappedLineCount(candidate, usableWidth) > usableBudget)
|
|
359
|
+
break;
|
|
360
|
+
kept.push(line);
|
|
361
|
+
}
|
|
362
|
+
let candidate = [...kept, marker].join('\n');
|
|
363
|
+
if (wrappedLineCount(candidate, usableWidth) <= usableBudget)
|
|
364
|
+
return candidate;
|
|
365
|
+
const remaining = Math.max(0, usableBudget - wrappedLineCount(marker, usableWidth));
|
|
366
|
+
const prefix = kept.join('\n');
|
|
367
|
+
const clusters = graphemes(prefix);
|
|
368
|
+
let low = 0;
|
|
369
|
+
let high = clusters.length;
|
|
370
|
+
let best = '';
|
|
371
|
+
while (low <= high) {
|
|
372
|
+
const middle = Math.floor((low + high) / 2);
|
|
373
|
+
const next = clusters.slice(0, middle).join('');
|
|
374
|
+
if (wrappedLineCount(`${next}${next ? '\n' : ''}${marker}`, usableWidth) <=
|
|
375
|
+
usableBudget) {
|
|
376
|
+
best = next;
|
|
377
|
+
low = middle + 1;
|
|
378
|
+
}
|
|
379
|
+
else
|
|
380
|
+
high = middle - 1;
|
|
381
|
+
}
|
|
382
|
+
candidate = `${best}${best ? '\n' : ''}${marker}`;
|
|
383
|
+
return wrappedLineCount(candidate, usableWidth) <= usableBudget
|
|
384
|
+
? candidate
|
|
385
|
+
: marker.slice(0, Math.max(1, remaining));
|
|
386
|
+
}
|
|
387
|
+
function textVisualRows(text, width) {
|
|
388
|
+
const usableWidth = Math.max(1, width);
|
|
389
|
+
const visualRows = [];
|
|
390
|
+
const visible = text.includes('\u001b') || text.includes('\u009b')
|
|
391
|
+
? stripVTControlCharacters(text)
|
|
392
|
+
: text;
|
|
393
|
+
for (const logicalLine of visible.replace(/\r\n?/gu, '\n').split('\n'))
|
|
394
|
+
wrappedLineLayout(logicalLine, usableWidth, visualRows);
|
|
395
|
+
return visualRows;
|
|
396
|
+
}
|
|
397
|
+
function textRange(text, startRow, budget, width) {
|
|
398
|
+
const visualRows = textVisualRows(text, width);
|
|
399
|
+
const limit = Math.max(1, budget);
|
|
400
|
+
const start = Math.min(Math.max(0, startRow), visualRows.length);
|
|
401
|
+
const clippedBefore = start > 0 && limit >= 2;
|
|
402
|
+
const available = Math.max(1, limit - (clippedBefore ? 1 : 0));
|
|
403
|
+
let contentBudget = available;
|
|
404
|
+
let end = Math.min(visualRows.length, start + contentBudget);
|
|
405
|
+
const clippedAfter = end < visualRows.length && available >= 2;
|
|
406
|
+
if (clippedAfter) {
|
|
407
|
+
contentBudget = available - 1;
|
|
408
|
+
end = Math.min(visualRows.length, start + contentBudget);
|
|
409
|
+
}
|
|
410
|
+
const selected = visualRows.slice(start, end);
|
|
411
|
+
return [
|
|
412
|
+
...(clippedBefore ? [TRANSCRIPT_TRUNCATION_MARKER] : []),
|
|
413
|
+
...selected,
|
|
414
|
+
...(clippedAfter ? [TRANSCRIPT_TRUNCATION_MARKER] : []),
|
|
415
|
+
].join('\n');
|
|
416
|
+
}
|
|
417
|
+
function estimateRenderedTranscriptEntryLines(entry, width, mode) {
|
|
319
418
|
if (entry.kind === 'read-summary')
|
|
320
419
|
return wrappedLineCount(` Read ${entry.count} file${entry.count === 1 ? '' : 's'} (ctrl+o to expand)`, width);
|
|
321
420
|
if (entry.kind === 'item') {
|
|
@@ -347,7 +446,9 @@ export function estimateTranscriptEntryLines(entry, width, mode) {
|
|
|
347
446
|
}
|
|
348
447
|
if (item.kind === 'compact') {
|
|
349
448
|
if (mode === 'screen-reader')
|
|
350
|
-
return
|
|
449
|
+
return (1 +
|
|
450
|
+
wrappedLineCount('Conversation compacted', width) +
|
|
451
|
+
wrappedLineCount(item.summary, markdownContentWidth(width)));
|
|
351
452
|
return (1 +
|
|
352
453
|
wrappedLineCount('✻ Conversation compacted (ctrl+o for history)', width) +
|
|
353
454
|
(mode === 'audit'
|
|
@@ -411,38 +512,292 @@ export function estimateTranscriptEntryLines(entry, width, mode) {
|
|
|
411
512
|
rows += visibleOutputLineCount(output, width, mode);
|
|
412
513
|
return rows;
|
|
413
514
|
}
|
|
515
|
+
export function estimateTranscriptEntryLines(entry, width, mode) {
|
|
516
|
+
if (entry.viewportSlice)
|
|
517
|
+
return entry.viewportSlice.rows;
|
|
518
|
+
const rendered = estimateRenderedTranscriptEntryLines(entry, width, mode);
|
|
519
|
+
const hasBoundedAuthoritativeText = (entry.kind === 'orphan-tool-result' && entry.item.text.length > 500) ||
|
|
520
|
+
(entry.kind === 'tool' &&
|
|
521
|
+
entry.result?.isError === true &&
|
|
522
|
+
entry.result.text.length > 500);
|
|
523
|
+
if (!hasBoundedAuthoritativeText)
|
|
524
|
+
return rendered;
|
|
525
|
+
return entryViewportRows(entry, width, mode)?.length ?? rendered;
|
|
526
|
+
}
|
|
414
527
|
export function transcriptPresentationLineCount(entries, width, mode) {
|
|
415
528
|
return entries.reduce((n, entry) => n + estimateTranscriptEntryLines(entry, width, mode), 0);
|
|
416
529
|
}
|
|
530
|
+
function viewportOutputRows(text, width, mode) {
|
|
531
|
+
const lines = contentLines(text);
|
|
532
|
+
const visible = mode === 'normal' ? lines.slice(0, 3) : lines;
|
|
533
|
+
const outputWidth = Math.max(1, width - 2);
|
|
534
|
+
const rows = visible.flatMap((line, index) => textVisualRows(`${index === 0 ? '⎿ ' : ' '}${line || ' '}`, outputWidth));
|
|
535
|
+
if (mode === 'normal' && lines.length > visible.length) {
|
|
536
|
+
rows.push(...textVisualRows(` ${TRANSCRIPT_TRUNCATION_MARKER} +${lines.length - visible.length} lines (ctrl+o to expand)`, outputWidth));
|
|
537
|
+
}
|
|
538
|
+
return rows;
|
|
539
|
+
}
|
|
540
|
+
function entryViewportRows(entry, width, mode) {
|
|
541
|
+
if (entry.kind === 'read-summary')
|
|
542
|
+
return textVisualRows(` Read ${entry.count} file${entry.count === 1 ? '' : 's'} (ctrl+o to expand)`, width);
|
|
543
|
+
if (entry.kind === 'item') {
|
|
544
|
+
const item = entry.item;
|
|
545
|
+
if (item.kind === 'context')
|
|
546
|
+
return undefined;
|
|
547
|
+
if (item.kind === 'user')
|
|
548
|
+
return [
|
|
549
|
+
'',
|
|
550
|
+
...(mode === 'screen-reader' ? [''] : []),
|
|
551
|
+
...textVisualRows(`${mode === 'screen-reader' ? 'You: ' : '❯ '}${item.text}`, width),
|
|
552
|
+
];
|
|
553
|
+
if (item.kind === 'assistant')
|
|
554
|
+
return [
|
|
555
|
+
'',
|
|
556
|
+
...(mode === 'screen-reader' ? ['Praxis:'] : []),
|
|
557
|
+
...textVisualRows(item.text, mode === 'screen-reader'
|
|
558
|
+
? Math.max(1, width - 7)
|
|
559
|
+
: markdownContentWidth(width)),
|
|
560
|
+
];
|
|
561
|
+
if (item.kind === 'thinking') {
|
|
562
|
+
const summary = item.text.replace(/\s+/gu, ' ').trim();
|
|
563
|
+
const heading = mode === 'screen-reader'
|
|
564
|
+
? 'Thinking:Thought for a moment'
|
|
565
|
+
: `✻ Thought for a moment${summary ? ` · ${summary.slice(0, 160)}` : ''}`;
|
|
566
|
+
if (mode === 'normal')
|
|
567
|
+
return [
|
|
568
|
+
'',
|
|
569
|
+
...textVisualRows(heading, width),
|
|
570
|
+
...(summary.length > 160
|
|
571
|
+
? textVisualRows(' ctrl+o to expand thinking', width)
|
|
572
|
+
: []),
|
|
573
|
+
];
|
|
574
|
+
return [
|
|
575
|
+
'',
|
|
576
|
+
...textVisualRows(heading, width),
|
|
577
|
+
...textVisualRows(item.text, mode === 'screen-reader' ? width : markdownContentWidth(width)),
|
|
578
|
+
];
|
|
579
|
+
}
|
|
580
|
+
if (item.kind === 'compact') {
|
|
581
|
+
const heading = mode === 'screen-reader'
|
|
582
|
+
? 'Conversation compacted'
|
|
583
|
+
: '✻ Conversation compacted (ctrl+o for history)';
|
|
584
|
+
return [
|
|
585
|
+
'',
|
|
586
|
+
...textVisualRows(heading, width),
|
|
587
|
+
...(mode === 'normal'
|
|
588
|
+
? []
|
|
589
|
+
: textVisualRows(item.summary, markdownContentWidth(width))),
|
|
590
|
+
];
|
|
591
|
+
}
|
|
592
|
+
if (item.kind === 'local-result')
|
|
593
|
+
return textVisualRows(`⎿ ${item.text}`, Math.max(1, width - 2));
|
|
594
|
+
if (item.kind === 'warning')
|
|
595
|
+
return textVisualRows(`⚠ ${item.text}`, width);
|
|
596
|
+
if (item.kind === 'notice')
|
|
597
|
+
return textVisualRows(`· ${item.text}`, width);
|
|
598
|
+
return textVisualRows(item.text, width);
|
|
599
|
+
}
|
|
600
|
+
if (entry.kind === 'orphan-tool-result')
|
|
601
|
+
return [
|
|
602
|
+
entry.item.isError ? '└ Error' : '└ Result',
|
|
603
|
+
...textVisualRows(entry.item.text, Math.max(1, width - 2)),
|
|
604
|
+
];
|
|
605
|
+
if (entry.kind === 'orphan-shell-result')
|
|
606
|
+
return textVisualRows(`⎿ ${orphanShellOutput(entry.item.stdout, entry.item.stderr)}`, width);
|
|
607
|
+
if (entry.kind === 'shell') {
|
|
608
|
+
const rows = [
|
|
609
|
+
'',
|
|
610
|
+
...textVisualRows(`${mode === 'screen-reader' ? 'Shell command: ' : '! '}${entry.item.command}`, width),
|
|
611
|
+
];
|
|
612
|
+
if (!entry.result)
|
|
613
|
+
return rows;
|
|
614
|
+
const output = combinedShellOutput(entry.result.stdout, entry.result.stderr);
|
|
615
|
+
return [
|
|
616
|
+
...rows,
|
|
617
|
+
...(output
|
|
618
|
+
? viewportOutputRows(output, width, mode)
|
|
619
|
+
: textVisualRows('⎿ ', Math.max(1, width - 2))),
|
|
620
|
+
];
|
|
621
|
+
}
|
|
622
|
+
if (mode === 'normal' &&
|
|
623
|
+
entry.item.call.name === 'Read' &&
|
|
624
|
+
entry.result &&
|
|
625
|
+
!entry.result.isError)
|
|
626
|
+
return textVisualRows(' Read 1 file (ctrl+o to expand)', width);
|
|
627
|
+
const rows = ['', ...textVisualRows(`⏺ ${toolHeading(entry)}`, width)];
|
|
628
|
+
if (!['Bash', 'Read', 'Edit'].includes(entry.item.call.name) &&
|
|
629
|
+
entry.item.detail)
|
|
630
|
+
rows.push(...textVisualRows(` ${entry.item.detail}`, width));
|
|
631
|
+
if (!entry.result)
|
|
632
|
+
return rows;
|
|
633
|
+
if (entry.result.isError) {
|
|
634
|
+
rows.push(...textVisualRows(`⎿ Error: ${entry.result.text}`, Math.max(1, width - 2)));
|
|
635
|
+
return rows;
|
|
636
|
+
}
|
|
637
|
+
if (entry.item.call.name === 'Edit') {
|
|
638
|
+
const oldLines = contentLines(inputString(entry, 'old_string'));
|
|
639
|
+
const newLines = contentLines(inputString(entry, 'new_string'));
|
|
640
|
+
rows.push(...textVisualRows(`⎿ Added ${newLines.length} line${newLines.length === 1 ? '' : 's'}, removed ${oldLines.length} line${oldLines.length === 1 ? '' : 's'}`, Math.max(1, width - 2)));
|
|
641
|
+
if (mode !== 'normal') {
|
|
642
|
+
for (const [index, line] of oldLines.entries())
|
|
643
|
+
rows.push(...textVisualRows(` ${index + 1} -${line}`, Math.max(1, width - 2)));
|
|
644
|
+
for (const [index, line] of newLines.entries())
|
|
645
|
+
rows.push(...textVisualRows(` ${index + 1} +${line}`, Math.max(1, width - 2)));
|
|
646
|
+
}
|
|
647
|
+
return rows;
|
|
648
|
+
}
|
|
649
|
+
rows.push(...viewportOutputRows(entry.result.text, width, mode));
|
|
650
|
+
return rows;
|
|
651
|
+
}
|
|
652
|
+
function markdownViewportRows(text, width) {
|
|
653
|
+
const rows = [];
|
|
654
|
+
let insideFence = false;
|
|
655
|
+
let fenceLabel = 'code';
|
|
656
|
+
for (const logicalLine of text.replace(/\r\n?/gu, '\n').split('\n')) {
|
|
657
|
+
if (logicalLine.startsWith('```')) {
|
|
658
|
+
const before = insideFence;
|
|
659
|
+
if (!insideFence)
|
|
660
|
+
fenceLabel = logicalLine.slice(3) || 'code';
|
|
661
|
+
insideFence = !insideFence;
|
|
662
|
+
rows.push({
|
|
663
|
+
text: logicalLine,
|
|
664
|
+
fenceBefore: before,
|
|
665
|
+
fenceAfter: insideFence,
|
|
666
|
+
fenceLabel,
|
|
667
|
+
});
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
for (const row of textVisualRows(logicalLine, width))
|
|
671
|
+
rows.push({
|
|
672
|
+
text: row,
|
|
673
|
+
fenceBefore: insideFence,
|
|
674
|
+
fenceAfter: insideFence,
|
|
675
|
+
fenceLabel,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
return rows;
|
|
679
|
+
}
|
|
680
|
+
export function createTranscriptEntryViewportIndex(entry, width, mode) {
|
|
681
|
+
if (entry.kind === 'item' && entry.item.kind === 'assistant') {
|
|
682
|
+
const contentWidth = mode === 'screen-reader'
|
|
683
|
+
? Math.max(1, width - 7)
|
|
684
|
+
: markdownContentWidth(width);
|
|
685
|
+
const assistantMarkdownRows = markdownViewportRows(entry.item.text, contentWidth);
|
|
686
|
+
return {
|
|
687
|
+
entry,
|
|
688
|
+
width,
|
|
689
|
+
mode,
|
|
690
|
+
rows: [
|
|
691
|
+
'',
|
|
692
|
+
...(mode === 'screen-reader' ? ['Praxis:'] : []),
|
|
693
|
+
...assistantMarkdownRows.map((row) => row.text),
|
|
694
|
+
],
|
|
695
|
+
assistantMarkdownRows,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
const rows = entryViewportRows(entry, width, mode);
|
|
699
|
+
return rows ? { entry, width, mode, rows } : undefined;
|
|
700
|
+
}
|
|
701
|
+
function matchingViewportIndex(index, entry, width, mode) {
|
|
702
|
+
return index?.entry === entry && index.width === width && index.mode === mode
|
|
703
|
+
? index
|
|
704
|
+
: undefined;
|
|
705
|
+
}
|
|
706
|
+
function projectAssistantMarkdownSlice(source, sourceText, startRow, budget, width, mode, viewportIndex) {
|
|
707
|
+
const limit = Math.max(1, budget);
|
|
708
|
+
const sourceChromeRows = 1 + (mode === 'screen-reader' ? 1 : 0);
|
|
709
|
+
const marginTop = startRow <= 0 ? 1 : 0;
|
|
710
|
+
const projectedChromeRows = marginTop + (mode === 'screen-reader' ? 1 : 0);
|
|
711
|
+
const contentBudget = Math.max(1, limit - projectedChromeRows);
|
|
712
|
+
const contentWidth = mode === 'screen-reader'
|
|
713
|
+
? Math.max(1, width - 7)
|
|
714
|
+
: markdownContentWidth(width);
|
|
715
|
+
const sourceRows = matchingViewportIndex(viewportIndex, source, width, mode)
|
|
716
|
+
?.assistantMarkdownRows ??
|
|
717
|
+
markdownViewportRows(sourceText ?? '', contentWidth);
|
|
718
|
+
const contentStart = Math.min(Math.max(0, startRow - sourceChromeRows + (startRow > 0 ? 1 : 0)), sourceRows.length);
|
|
719
|
+
const clippedBefore = contentStart > 0 && contentBudget > 1;
|
|
720
|
+
let remaining = Math.max(1, contentBudget - (clippedBefore ? 1 : 0));
|
|
721
|
+
let contentEnd = Math.min(sourceRows.length, contentStart + remaining);
|
|
722
|
+
const clippedAfter = contentEnd < sourceRows.length && remaining > 1;
|
|
723
|
+
if (clippedAfter) {
|
|
724
|
+
remaining -= 1;
|
|
725
|
+
contentEnd = Math.min(sourceRows.length, contentStart + remaining);
|
|
726
|
+
}
|
|
727
|
+
const selected = sourceRows.slice(contentStart, contentEnd);
|
|
728
|
+
const first = selected[0];
|
|
729
|
+
const last = selected.at(-1);
|
|
730
|
+
const needsSyntheticOpen = clippedBefore && first?.fenceBefore === true;
|
|
731
|
+
const needsSyntheticClose = clippedAfter && last?.fenceAfter === true;
|
|
732
|
+
const lines = [
|
|
733
|
+
...(clippedBefore
|
|
734
|
+
? [
|
|
735
|
+
needsSyntheticOpen
|
|
736
|
+
? `\`\`\`${first?.fenceLabel || 'code'}`
|
|
737
|
+
: TRANSCRIPT_TRUNCATION_MARKER,
|
|
738
|
+
]
|
|
739
|
+
: []),
|
|
740
|
+
...selected.map((row) => row.text),
|
|
741
|
+
...(clippedAfter
|
|
742
|
+
? [needsSyntheticClose ? '```' : TRANSCRIPT_TRUNCATION_MARKER]
|
|
743
|
+
: []),
|
|
744
|
+
];
|
|
745
|
+
const text = lines.join('\n');
|
|
746
|
+
return {
|
|
747
|
+
...source,
|
|
748
|
+
item: { kind: 'assistant', text },
|
|
749
|
+
viewportSlice: {
|
|
750
|
+
text,
|
|
751
|
+
rows: projectedChromeRows + lines.length,
|
|
752
|
+
assistantMarkdown: { marginTop },
|
|
753
|
+
},
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function withViewportSlice(projected, source, startRow, budget, width, mode, viewportIndex) {
|
|
757
|
+
const retainedIndex = matchingViewportIndex(viewportIndex, source, width, mode);
|
|
758
|
+
if (source.kind === 'item' &&
|
|
759
|
+
source.item.kind === 'assistant' &&
|
|
760
|
+
(retainedIndex?.rows.length ??
|
|
761
|
+
estimateRenderedTranscriptEntryLines(source, width, mode)) > budget)
|
|
762
|
+
return projectAssistantMarkdownSlice(source, retainedIndex ? undefined : source.item.text, startRow, budget, width, mode, retainedIndex);
|
|
763
|
+
const sourceRows = retainedIndex?.rows ?? entryViewportRows(source, width, mode);
|
|
764
|
+
if (!sourceRows || sourceRows.length <= budget)
|
|
765
|
+
return projected;
|
|
766
|
+
const limit = Math.max(1, budget);
|
|
767
|
+
const start = Math.min(Math.max(0, startRow + (startRow > 0 ? 1 : 0)), Math.max(0, sourceRows.length - 1));
|
|
768
|
+
const clippedBefore = start > 0 && limit > 1;
|
|
769
|
+
let remaining = Math.max(1, limit - (clippedBefore ? 1 : 0));
|
|
770
|
+
let end = Math.min(sourceRows.length, start + remaining);
|
|
771
|
+
const clippedAfter = end < sourceRows.length && remaining > 1;
|
|
772
|
+
if (clippedAfter) {
|
|
773
|
+
remaining -= 1;
|
|
774
|
+
end = Math.min(sourceRows.length, start + remaining);
|
|
775
|
+
}
|
|
776
|
+
const rows = [
|
|
777
|
+
...(clippedBefore ? [TRANSCRIPT_TRUNCATION_MARKER] : []),
|
|
778
|
+
...sourceRows.slice(start, end),
|
|
779
|
+
...(clippedAfter ? [TRANSCRIPT_TRUNCATION_MARKER] : []),
|
|
780
|
+
];
|
|
781
|
+
return {
|
|
782
|
+
...projected,
|
|
783
|
+
viewportSlice: { text: rows.join('\n'), rows: rows.length },
|
|
784
|
+
};
|
|
785
|
+
}
|
|
417
786
|
function fitEntryText(entry, value, replace, budget, viewportWidth, contentWidth, mode) {
|
|
418
787
|
if (estimateTranscriptEntryLines(entry, viewportWidth, mode) <= budget ||
|
|
419
788
|
!value)
|
|
420
789
|
return entry;
|
|
421
790
|
const withoutValue = replace(entry, '');
|
|
422
|
-
const remaining = budget -
|
|
791
|
+
const remaining = budget -
|
|
792
|
+
Math.max(0, estimateTranscriptEntryLines(withoutValue, viewportWidth, mode) - 1);
|
|
423
793
|
if (remaining <= 0)
|
|
424
794
|
return withoutValue;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const clusters = graphemes(candidate);
|
|
430
|
-
let low = 0;
|
|
431
|
-
let high = clusters.length;
|
|
432
|
-
let suffix = '';
|
|
433
|
-
while (low <= high) {
|
|
434
|
-
const middle = Math.floor((low + high) / 2);
|
|
435
|
-
const next = clusters.slice(clusters.length - middle).join('');
|
|
436
|
-
const nextEntry = replace(entry, next);
|
|
437
|
-
if (estimateTranscriptEntryLines(nextEntry, viewportWidth, mode) <= budget) {
|
|
438
|
-
suffix = next;
|
|
439
|
-
low = middle + 1;
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
high = middle - 1;
|
|
443
|
-
}
|
|
795
|
+
for (let allowed = remaining; allowed >= 1; allowed -= 1) {
|
|
796
|
+
const projected = replace(entry, textTail(value, allowed, contentWidth));
|
|
797
|
+
if (estimateTranscriptEntryLines(projected, viewportWidth, mode) <= budget)
|
|
798
|
+
return projected;
|
|
444
799
|
}
|
|
445
|
-
return
|
|
800
|
+
return withoutValue;
|
|
446
801
|
}
|
|
447
802
|
function replaceToolInput(entry, key, value) {
|
|
448
803
|
return {
|
|
@@ -456,7 +811,23 @@ function replaceToolInput(entry, key, value) {
|
|
|
456
811
|
},
|
|
457
812
|
};
|
|
458
813
|
}
|
|
459
|
-
function
|
|
814
|
+
function rangeEntryText(entry, value, replace, startRow, budget, viewportWidth, contentWidth, mode) {
|
|
815
|
+
if (!value)
|
|
816
|
+
return entry;
|
|
817
|
+
const withoutValue = replace(entry, '');
|
|
818
|
+
// Empty renderer fields still account for one placeholder row. The source
|
|
819
|
+
// content begins before that placeholder, while the two clipping markers
|
|
820
|
+
// occupy the boundary rows of a middle projection.
|
|
821
|
+
const fixedRows = Math.max(0, estimateTranscriptEntryLines(withoutValue, viewportWidth, mode) - 1);
|
|
822
|
+
const contentStart = Math.max(0, startRow - fixedRows + (startRow > 0 ? 2 : 0));
|
|
823
|
+
for (let contentBudget = Math.max(1, budget - fixedRows); contentBudget >= 1; contentBudget -= 1) {
|
|
824
|
+
const projected = replace(entry, textRange(value, contentStart, contentBudget, contentWidth));
|
|
825
|
+
if (estimateTranscriptEntryLines(projected, viewportWidth, mode) <= budget)
|
|
826
|
+
return projected;
|
|
827
|
+
}
|
|
828
|
+
return withoutValue;
|
|
829
|
+
}
|
|
830
|
+
function projectOversizedEntryLegacy(entry, budget, width, mode, fromStart = false) {
|
|
460
831
|
if (entry.kind === 'item') {
|
|
461
832
|
const item = entry.item;
|
|
462
833
|
if (item.kind === 'thinking')
|
|
@@ -464,7 +835,7 @@ function projectOversizedEntry(entry, budget, width, mode) {
|
|
|
464
835
|
...entry,
|
|
465
836
|
item: {
|
|
466
837
|
...item,
|
|
467
|
-
text: textTail(item.text, Math.max(1, budget - 2), markdownContentWidth(width)),
|
|
838
|
+
text: (fromStart ? textHead : textTail)(item.text, Math.max(1, budget - 2), markdownContentWidth(width)),
|
|
468
839
|
},
|
|
469
840
|
};
|
|
470
841
|
if (item.kind === 'compact')
|
|
@@ -472,7 +843,7 @@ function projectOversizedEntry(entry, budget, width, mode) {
|
|
|
472
843
|
...entry,
|
|
473
844
|
item: {
|
|
474
845
|
...item,
|
|
475
|
-
summary: textTail(item.summary, Math.max(1, budget - 2), markdownContentWidth(width)),
|
|
846
|
+
summary: (fromStart ? textHead : textTail)(item.summary, Math.max(1, budget - 2), markdownContentWidth(width)),
|
|
476
847
|
},
|
|
477
848
|
};
|
|
478
849
|
if ('text' in item)
|
|
@@ -480,7 +851,7 @@ function projectOversizedEntry(entry, budget, width, mode) {
|
|
|
480
851
|
...entry,
|
|
481
852
|
item: {
|
|
482
853
|
...item,
|
|
483
|
-
text: textTail(item.text, Math.max(1, budget -
|
|
854
|
+
text: (fromStart ? textHead : textTail)(item.text, Math.max(1, budget -
|
|
484
855
|
(item.kind === 'user'
|
|
485
856
|
? 1
|
|
486
857
|
: item.kind === 'assistant'
|
|
@@ -561,6 +932,85 @@ function projectOversizedEntry(entry, budget, width, mode) {
|
|
|
561
932
|
}
|
|
562
933
|
return entry;
|
|
563
934
|
}
|
|
935
|
+
function projectOversizedEntry(entry, budget, width, mode, fromStart = false) {
|
|
936
|
+
const projected = projectOversizedEntryLegacy(entry, budget, width, mode, fromStart);
|
|
937
|
+
const rows = entryViewportRows(entry, width, mode);
|
|
938
|
+
if (!rows || rows.length <= budget)
|
|
939
|
+
return projected;
|
|
940
|
+
const visibleRows = Math.max(1, budget);
|
|
941
|
+
const start = fromStart ? 0 : Math.max(0, rows.length - visibleRows);
|
|
942
|
+
return withViewportSlice(projected, entry, start, budget, width, mode);
|
|
943
|
+
}
|
|
944
|
+
function projectTranscriptEntryRangeLegacy(entry, startRow, budget, width, mode) {
|
|
945
|
+
if (entry.kind === 'item' && 'text' in entry.item) {
|
|
946
|
+
const item = entry.item;
|
|
947
|
+
return rangeEntryText(entry, item.text, (source, text) => ({
|
|
948
|
+
...source,
|
|
949
|
+
item: { ...source.item, text },
|
|
950
|
+
}), startRow, budget, width, item.kind === 'assistant'
|
|
951
|
+
? mode === 'screen-reader'
|
|
952
|
+
? Math.max(1, width - 7)
|
|
953
|
+
: markdownContentWidth(width)
|
|
954
|
+
: item.kind === 'user'
|
|
955
|
+
? Math.max(1, width - (mode === 'screen-reader' ? 5 : 2))
|
|
956
|
+
: Math.max(1, width - (item.kind === 'local-result' ? 4 : 2)), mode);
|
|
957
|
+
}
|
|
958
|
+
if (entry.kind === 'item' && entry.item.kind === 'compact')
|
|
959
|
+
return rangeEntryText(entry, entry.item.summary, (source, summary) => ({
|
|
960
|
+
...source,
|
|
961
|
+
item: { ...source.item, summary },
|
|
962
|
+
}), startRow, budget, width, markdownContentWidth(width), mode);
|
|
963
|
+
if (entry.kind === 'tool') {
|
|
964
|
+
if (entry.result && entry.item.call.name !== 'Edit') {
|
|
965
|
+
const result = entry.result;
|
|
966
|
+
return rangeEntryText(entry, result.text, (source, text) => ({
|
|
967
|
+
...source,
|
|
968
|
+
result: { ...result, text },
|
|
969
|
+
}), startRow, budget, width, Math.max(1, width - 5), mode);
|
|
970
|
+
}
|
|
971
|
+
const inputKey = ['command', 'new_string', 'old_string', 'file_path'].find((key) => inputString(entry, key).length > 0);
|
|
972
|
+
if (inputKey)
|
|
973
|
+
return rangeEntryText(entry, inputString(entry, inputKey), (source, value) => replaceToolInput(source, inputKey, value), startRow, budget, width, Math.max(1, width - 8), mode);
|
|
974
|
+
if (entry.item.detail)
|
|
975
|
+
return rangeEntryText(entry, entry.item.detail, (source, detail) => ({
|
|
976
|
+
...source,
|
|
977
|
+
item: { ...source.item, detail },
|
|
978
|
+
}), startRow, budget, width, Math.max(1, width - 1), mode);
|
|
979
|
+
}
|
|
980
|
+
if (entry.kind === 'shell') {
|
|
981
|
+
if (entry.result) {
|
|
982
|
+
const result = entry.result;
|
|
983
|
+
const combined = combinedShellOutput(result.stdout, result.stderr);
|
|
984
|
+
return rangeEntryText(entry, combined, (source, stdout) => ({
|
|
985
|
+
...source,
|
|
986
|
+
result: { ...result, stdout, stderr: '' },
|
|
987
|
+
}), startRow, budget, width, Math.max(1, width - 5), mode);
|
|
988
|
+
}
|
|
989
|
+
return rangeEntryText(entry, entry.item.command, (source, command) => ({
|
|
990
|
+
...source,
|
|
991
|
+
item: { ...source.item, command },
|
|
992
|
+
}), startRow, budget, width, Math.max(1, width - 4), mode);
|
|
993
|
+
}
|
|
994
|
+
if (entry.kind === 'orphan-tool-result')
|
|
995
|
+
return rangeEntryText(entry, entry.item.text, (source, text) => ({
|
|
996
|
+
...source,
|
|
997
|
+
item: { ...source.item, text },
|
|
998
|
+
}), startRow, budget, width, Math.max(1, width - 2), mode);
|
|
999
|
+
if (entry.kind === 'orphan-shell-result') {
|
|
1000
|
+
const combined = orphanShellOutput(entry.item.stdout, entry.item.stderr);
|
|
1001
|
+
return rangeEntryText(entry, combined, (source, stdout) => ({
|
|
1002
|
+
...source,
|
|
1003
|
+
item: { ...source.item, stdout, stderr: '' },
|
|
1004
|
+
}), startRow, budget, width, Math.max(1, width - 2), mode);
|
|
1005
|
+
}
|
|
1006
|
+
return projectOversizedEntry(entry, budget, width, mode);
|
|
1007
|
+
}
|
|
1008
|
+
export function projectTranscriptEntryRange(entry, startRow, budget, width, mode, viewportIndex) {
|
|
1009
|
+
const retainedIndex = matchingViewportIndex(viewportIndex, entry, width, mode);
|
|
1010
|
+
return withViewportSlice(retainedIndex
|
|
1011
|
+
? entry
|
|
1012
|
+
: projectTranscriptEntryRangeLegacy(entry, startRow, budget, width, mode), entry, startRow, budget, width, mode, retainedIndex);
|
|
1013
|
+
}
|
|
564
1014
|
export function projectTranscriptPresentationTail(entries, budget, width, mode) {
|
|
565
1015
|
if (!entries.length)
|
|
566
1016
|
return [];
|
|
@@ -583,24 +1033,32 @@ export function projectTranscriptPresentationTail(entries, budget, width, mode)
|
|
|
583
1033
|
}
|
|
584
1034
|
return entries;
|
|
585
1035
|
}
|
|
586
|
-
function
|
|
1036
|
+
export function projectTranscriptPresentationRange(entries, budget, width, startRow, mode) {
|
|
587
1037
|
const limit = Math.max(1, budget);
|
|
588
|
-
|
|
589
|
-
|
|
1038
|
+
const totalRows = transcriptPresentationLineCount(entries, width, mode);
|
|
1039
|
+
const rangeStart = Math.min(Math.max(0, startRow), totalRows);
|
|
1040
|
+
const rangeEnd = Math.min(totalRows, rangeStart + limit);
|
|
1041
|
+
const selected = [];
|
|
1042
|
+
let cursor = 0;
|
|
590
1043
|
for (const entry of entries) {
|
|
591
1044
|
const count = estimateTranscriptEntryLines(entry, width, mode);
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
1045
|
+
const entryEnd = cursor + count;
|
|
1046
|
+
const overlapStart = Math.max(rangeStart, cursor);
|
|
1047
|
+
const overlapEnd = Math.min(rangeEnd, entryEnd);
|
|
1048
|
+
if (overlapStart < overlapEnd) {
|
|
1049
|
+
const localStart = overlapStart - cursor;
|
|
1050
|
+
const overlapRows = overlapEnd - overlapStart;
|
|
1051
|
+
const projected = localStart === 0 && overlapRows === count
|
|
1052
|
+
? entry
|
|
1053
|
+
: projectTranscriptEntryRange(entry, localStart, overlapRows, width, mode);
|
|
1054
|
+
if (estimateTranscriptEntryLines(projected, width, mode) <= overlapRows)
|
|
1055
|
+
selected.push(projected);
|
|
599
1056
|
}
|
|
600
|
-
|
|
601
|
-
|
|
1057
|
+
cursor = entryEnd;
|
|
1058
|
+
if (cursor >= rangeEnd)
|
|
1059
|
+
break;
|
|
602
1060
|
}
|
|
603
|
-
return
|
|
1061
|
+
return selected;
|
|
604
1062
|
}
|
|
605
1063
|
export function projectTranscriptPresentationWindow(entries, budget, width, scrollOffset, mode) {
|
|
606
1064
|
if (scrollOffset <= 0)
|
|
@@ -608,58 +1066,6 @@ export function projectTranscriptPresentationWindow(entries, budget, width, scro
|
|
|
608
1066
|
const limit = Math.max(1, budget);
|
|
609
1067
|
const totalRows = transcriptPresentationLineCount(entries, width, mode);
|
|
610
1068
|
const clampedOffset = Math.min(Math.max(0, scrollOffset), Math.max(0, totalRows - limit));
|
|
611
|
-
|
|
612
|
-
const endRows = totalRows - clampedOffset;
|
|
613
|
-
let rows = 0;
|
|
614
|
-
let start = entries.length;
|
|
615
|
-
let startBeforeRows = 0;
|
|
616
|
-
let startEntryRows = 0;
|
|
617
|
-
for (let i = 0; i < entries.length; i += 1) {
|
|
618
|
-
const entry = entries[i];
|
|
619
|
-
if (!entry)
|
|
620
|
-
continue;
|
|
621
|
-
const count = estimateTranscriptEntryLines(entry, width, mode);
|
|
622
|
-
const next = rows + count;
|
|
623
|
-
if (start === entries.length && next > startRows) {
|
|
624
|
-
start = i;
|
|
625
|
-
startBeforeRows = rows;
|
|
626
|
-
startEntryRows = count;
|
|
627
|
-
}
|
|
628
|
-
rows = next;
|
|
629
|
-
}
|
|
630
|
-
if (start === entries.length)
|
|
631
|
-
return [];
|
|
632
|
-
let completeEnd = start - 1;
|
|
633
|
-
rows = 0;
|
|
634
|
-
for (let i = 0; i < entries.length; i += 1) {
|
|
635
|
-
const entry = entries[i];
|
|
636
|
-
if (!entry)
|
|
637
|
-
continue;
|
|
638
|
-
rows += estimateTranscriptEntryLines(entry, width, mode);
|
|
639
|
-
if (rows <= endRows)
|
|
640
|
-
completeEnd = i;
|
|
641
|
-
if (rows >= endRows)
|
|
642
|
-
break;
|
|
643
|
-
}
|
|
644
|
-
const offsetWithinStart = startRows - startBeforeRows;
|
|
645
|
-
const remainingStartRows = startEntryRows - offsetWithinStart;
|
|
646
|
-
if (completeEnd < start) {
|
|
647
|
-
const entry = entries[start];
|
|
648
|
-
if (!entry)
|
|
649
|
-
return [];
|
|
650
|
-
return projectTranscriptPresentationStart([entry], limit, width, mode);
|
|
651
|
-
}
|
|
652
|
-
let candidates = entries.slice(start, completeEnd + 1);
|
|
653
|
-
if (offsetWithinStart > 0) {
|
|
654
|
-
const entry = candidates[0];
|
|
655
|
-
if (entry) {
|
|
656
|
-
const allowance = Math.min(limit, Math.max(1, remainingStartRows));
|
|
657
|
-
candidates = [
|
|
658
|
-
projectOversizedEntry(entry, allowance, width, mode),
|
|
659
|
-
...candidates.slice(1),
|
|
660
|
-
];
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return projectTranscriptPresentationStart(candidates, limit, width, mode);
|
|
1069
|
+
return projectTranscriptPresentationRange(entries, limit, width, Math.max(0, totalRows - clampedOffset - limit), mode);
|
|
664
1070
|
}
|
|
665
1071
|
//# sourceMappingURL=transcript-viewport.js.map
|