poly-weaver 0.12.2 → 0.12.4
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/ansi.d.ts +12 -0
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +334 -9
- package/dist/ansi.js.map +1 -1
- package/dist/offscreen/artifact-pane.d.ts +5 -0
- package/dist/offscreen/artifact-pane.d.ts.map +1 -1
- package/dist/offscreen/artifact-pane.js +20 -1
- package/dist/offscreen/artifact-pane.js.map +1 -1
- package/dist/offscreen/controller.d.ts +1 -0
- package/dist/offscreen/controller.d.ts.map +1 -1
- package/dist/offscreen/controller.js +105 -0
- package/dist/offscreen/controller.js.map +1 -1
- package/dist/offscreen/inquiry-pane.d.ts +2 -0
- package/dist/offscreen/inquiry-pane.d.ts.map +1 -1
- package/dist/offscreen/inquiry-pane.js +19 -0
- package/dist/offscreen/inquiry-pane.js.map +1 -1
- package/dist/offscreen/inquiry-sender.js +1 -1
- package/dist/offscreen/inquiry-sender.js.map +1 -1
- package/dist/offscreen/panes.d.ts +2 -0
- package/dist/offscreen/panes.d.ts.map +1 -1
- package/dist/offscreen/split-inquiry.d.ts +17 -0
- package/dist/offscreen/split-inquiry.d.ts.map +1 -1
- package/dist/offscreen/split-inquiry.js +107 -1
- package/dist/offscreen/split-inquiry.js.map +1 -1
- package/dist/providers/codex/plan-mode-activator.js +0 -3
- package/dist/providers/codex/plan-mode-activator.js.map +1 -1
- package/dist/providers/option-catalog.d.ts.map +1 -1
- package/dist/providers/option-catalog.js +23 -0
- package/dist/providers/option-catalog.js.map +1 -1
- package/dist/session/locator.d.ts +2 -2
- package/dist/session/locator.d.ts.map +1 -1
- package/dist/session/locator.js +2 -2
- package/dist/session/locator.js.map +1 -1
- package/dist/status-bar.d.ts +5 -2
- package/dist/status-bar.d.ts.map +1 -1
- package/dist/status-bar.js +18 -20
- package/dist/status-bar.js.map +1 -1
- package/dist/terminal/clipboard-write.d.ts +17 -0
- package/dist/terminal/clipboard-write.d.ts.map +1 -0
- package/dist/terminal/clipboard-write.js +58 -0
- package/dist/terminal/clipboard-write.js.map +1 -0
- package/dist/terminal/host.d.ts +90 -0
- package/dist/terminal/host.d.ts.map +1 -1
- package/dist/terminal/host.js +858 -17
- package/dist/terminal/host.js.map +1 -1
- package/dist/terminal/input-router.d.ts +21 -6
- package/dist/terminal/input-router.d.ts.map +1 -1
- package/dist/terminal/input-router.js +30 -8
- package/dist/terminal/input-router.js.map +1 -1
- package/dist/terminal/mouse-mode-tracker.d.ts +1 -0
- package/dist/terminal/mouse-mode-tracker.d.ts.map +1 -1
- package/dist/terminal/mouse-mode-tracker.js +3 -0
- package/dist/terminal/mouse-mode-tracker.js.map +1 -1
- package/dist/terminal/osc52.d.ts +1 -0
- package/dist/terminal/osc52.d.ts.map +1 -1
- package/dist/terminal/osc52.js +3 -0
- package/dist/terminal/osc52.js.map +1 -1
- package/dist/terminal/paste-submit.d.ts +1 -1
- package/dist/terminal/paste-submit.d.ts.map +1 -1
- package/dist/terminal/paste-submit.js +2 -2
- package/dist/terminal/paste-submit.js.map +1 -1
- package/dist/terminal/render.d.ts +5 -0
- package/dist/terminal/render.d.ts.map +1 -1
- package/dist/terminal/render.js +14 -4
- package/dist/terminal/render.js.map +1 -1
- package/dist/terminal/selection.d.ts +101 -0
- package/dist/terminal/selection.d.ts.map +1 -0
- package/dist/terminal/selection.js +517 -0
- package/dist/terminal/selection.js.map +1 -0
- package/dist/terminal/virtual-terminal.d.ts +5 -1
- package/dist/terminal/virtual-terminal.d.ts.map +1 -1
- package/dist/terminal/virtual-terminal.js +10 -0
- package/dist/terminal/virtual-terminal.js.map +1 -1
- package/dist/user/host-curate-prompt.d.ts +2 -0
- package/dist/user/host-curate-prompt.d.ts.map +1 -1
- package/dist/user/host-curate-prompt.js +239 -43
- package/dist/user/host-curate-prompt.js.map +1 -1
- package/dist/user/host-prompt.d.ts.map +1 -1
- package/dist/user/host-prompt.js +257 -14
- package/dist/user/host-prompt.js.map +1 -1
- package/dist/user/selectable-doc-pane.d.ts +13 -0
- package/dist/user/selectable-doc-pane.d.ts.map +1 -0
- package/dist/user/selectable-doc-pane.js +34 -0
- package/dist/user/selectable-doc-pane.js.map +1 -0
- package/dist/user/split-surface.d.ts +2 -0
- package/dist/user/split-surface.d.ts.map +1 -1
- package/dist/user/split-surface.js +17 -0
- package/dist/user/split-surface.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,10 +7,11 @@ import { noteFocus } from "../text-editing/kill-ring.js";
|
|
|
7
7
|
import { clipToWidth, tagFor, userAddedTag, wrapToWidth, } from "./curate-prompt.js";
|
|
8
8
|
import { buildPlanPane } from "./plan-pane.js";
|
|
9
9
|
import { buildIssuesArtifactPane, issueMetaToken } from "./issues-pane.js";
|
|
10
|
-
import { buildSplitSurface, computeSplitMetrics, SPLIT_COLLAPSE_AT_COLS, } from "./split-surface.js";
|
|
10
|
+
import { buildSplitSurface, computeSplitMetrics, sideBySidePaneRegion, SPLIT_COLLAPSE_AT_COLS, } from "./split-surface.js";
|
|
11
11
|
import { renderShortcutHints } from "./shortcut-hints.js";
|
|
12
12
|
import { OffscreenController, } from "../offscreen/controller.js";
|
|
13
13
|
import { composeIssueInquiry } from "./issue-inquiry.js";
|
|
14
|
+
import { createSelectableDocumentPane, } from "./selectable-doc-pane.js";
|
|
14
15
|
import { AttachmentManager } from "../paste-image/attachment-manager.js";
|
|
15
16
|
import { ALT_V_PASTE_IMAGE_HINT, handleBracketedPaste, handleImagePasteIntent, PASTING_IMAGE_HINT, } from "../paste-image/intents.js";
|
|
16
17
|
import { applyEditingBufferEvent, applyEditInsertedText, bufferFromText, emptyBuffer, finalizeEditText, renderMultilineEditor, } from "./feedback-editing.js";
|
|
@@ -64,6 +65,8 @@ export class HostBackedCuratePrompt {
|
|
|
64
65
|
userAdded: [],
|
|
65
66
|
editing: undefined,
|
|
66
67
|
planScrollTop: 0,
|
|
68
|
+
issueScrollTop: 0,
|
|
69
|
+
issueScrollPinnedToCursor: true,
|
|
67
70
|
nextCycle: issues.map(() => 0),
|
|
68
71
|
};
|
|
69
72
|
const parser = new EmacsInputParser();
|
|
@@ -114,6 +117,7 @@ export class HostBackedCuratePrompt {
|
|
|
114
117
|
if (!editing)
|
|
115
118
|
return;
|
|
116
119
|
state.editing = undefined;
|
|
120
|
+
state.issueScrollPinnedToCursor = true;
|
|
117
121
|
if (editing.target.kind === "reviewer-revise" &&
|
|
118
122
|
!state.comments[editing.target.index]) {
|
|
119
123
|
state.decisions[editing.target.index] = "keep";
|
|
@@ -127,6 +131,7 @@ export class HostBackedCuratePrompt {
|
|
|
127
131
|
state.comments[i] = text.length > 0 ? text : undefined;
|
|
128
132
|
if (!state.comments[i])
|
|
129
133
|
state.decisions[i] = "keep";
|
|
134
|
+
state.issueScrollPinnedToCursor = true;
|
|
130
135
|
return;
|
|
131
136
|
}
|
|
132
137
|
if (editing.target.kind === "user-edit") {
|
|
@@ -138,12 +143,14 @@ export class HostBackedCuratePrompt {
|
|
|
138
143
|
state.userAdded.splice(i, 1);
|
|
139
144
|
clampCursor(issues.length, state);
|
|
140
145
|
}
|
|
146
|
+
state.issueScrollPinnedToCursor = true;
|
|
141
147
|
return;
|
|
142
148
|
}
|
|
143
149
|
if (text.length > 0) {
|
|
144
150
|
state.userAdded.push(text);
|
|
145
151
|
state.cursor = issues.length + state.userAdded.length - 1;
|
|
146
152
|
}
|
|
153
|
+
state.issueScrollPinnedToCursor = true;
|
|
147
154
|
clampCursor(issues.length, state);
|
|
148
155
|
};
|
|
149
156
|
const submitEdit = () => {
|
|
@@ -170,6 +177,7 @@ export class HostBackedCuratePrompt {
|
|
|
170
177
|
});
|
|
171
178
|
};
|
|
172
179
|
const openReviewerEditor = (index, text = "") => {
|
|
180
|
+
state.issueScrollPinnedToCursor = true;
|
|
173
181
|
state.editing = {
|
|
174
182
|
target: { kind: "reviewer-revise", index },
|
|
175
183
|
buffer: bufferFromText(text),
|
|
@@ -178,6 +186,7 @@ export class HostBackedCuratePrompt {
|
|
|
178
186
|
};
|
|
179
187
|
};
|
|
180
188
|
const openUserAddEditor = () => {
|
|
189
|
+
state.issueScrollPinnedToCursor = true;
|
|
181
190
|
state.editing = {
|
|
182
191
|
target: { kind: "user-add" },
|
|
183
192
|
buffer: emptyBuffer(),
|
|
@@ -191,6 +200,7 @@ export class HostBackedCuratePrompt {
|
|
|
191
200
|
editing.transientHint = outcome.hint ?? null;
|
|
192
201
|
if (outcome.inserted !== undefined)
|
|
193
202
|
applyEditInsertedText(editing, outcome.inserted);
|
|
203
|
+
state.issueScrollPinnedToCursor = true;
|
|
194
204
|
this.host.scheduleParentUiRender();
|
|
195
205
|
};
|
|
196
206
|
const launchEditPasteIntent = (editing, task) => {
|
|
@@ -248,6 +258,7 @@ export class HostBackedCuratePrompt {
|
|
|
248
258
|
if (event.type === "ctrl-j") {
|
|
249
259
|
dirty = applyEditingBufferEvent(state.editing, { type: "newline" }) || dirty;
|
|
250
260
|
dirty = true;
|
|
261
|
+
state.issueScrollPinnedToCursor = true;
|
|
251
262
|
continue;
|
|
252
263
|
}
|
|
253
264
|
if (event.type === "arrow") {
|
|
@@ -261,10 +272,14 @@ export class HostBackedCuratePrompt {
|
|
|
261
272
|
type: event.direction === "up" ? "ctrl-p" : "ctrl-n",
|
|
262
273
|
}) || dirty;
|
|
263
274
|
}
|
|
275
|
+
if (dirty)
|
|
276
|
+
state.issueScrollPinnedToCursor = true;
|
|
264
277
|
continue;
|
|
265
278
|
}
|
|
266
279
|
if (isTextBufferEvent(event)) {
|
|
267
280
|
dirty = applyEditingBufferEvent(state.editing, event) || dirty;
|
|
281
|
+
if (dirty)
|
|
282
|
+
state.issueScrollPinnedToCursor = true;
|
|
268
283
|
continue;
|
|
269
284
|
}
|
|
270
285
|
continue;
|
|
@@ -285,12 +300,14 @@ export class HostBackedCuratePrompt {
|
|
|
285
300
|
state.cursor = (state.cursor - 1 + total) % total;
|
|
286
301
|
if (state.cursor < issues.length)
|
|
287
302
|
state.nextCycle[state.cursor] = 0;
|
|
303
|
+
state.issueScrollPinnedToCursor = true;
|
|
288
304
|
dirty = true;
|
|
289
305
|
}
|
|
290
306
|
else if (event.direction === "down") {
|
|
291
307
|
state.cursor = (state.cursor + 1) % total;
|
|
292
308
|
if (state.cursor < issues.length)
|
|
293
309
|
state.nextCycle[state.cursor] = 0;
|
|
310
|
+
state.issueScrollPinnedToCursor = true;
|
|
294
311
|
dirty = true;
|
|
295
312
|
}
|
|
296
313
|
continue;
|
|
@@ -341,6 +358,7 @@ export class HostBackedCuratePrompt {
|
|
|
341
358
|
attachments: new AttachmentManager(),
|
|
342
359
|
transientHint: null,
|
|
343
360
|
};
|
|
361
|
+
state.issueScrollPinnedToCursor = true;
|
|
344
362
|
dirty = true;
|
|
345
363
|
}
|
|
346
364
|
else {
|
|
@@ -358,6 +376,7 @@ export class HostBackedCuratePrompt {
|
|
|
358
376
|
if (next === "revise") {
|
|
359
377
|
openReviewerEditor(state.cursor);
|
|
360
378
|
}
|
|
379
|
+
state.issueScrollPinnedToCursor = true;
|
|
361
380
|
dirty = true;
|
|
362
381
|
continue;
|
|
363
382
|
}
|
|
@@ -398,6 +417,7 @@ export class HostBackedCuratePrompt {
|
|
|
398
417
|
if (event.text.toLowerCase() === "d") {
|
|
399
418
|
state.userAdded.splice(state.cursor - issues.length, 1);
|
|
400
419
|
clampCursor(issues.length, state);
|
|
420
|
+
state.issueScrollPinnedToCursor = true;
|
|
401
421
|
dirty = true;
|
|
402
422
|
}
|
|
403
423
|
continue;
|
|
@@ -416,6 +436,7 @@ export class HostBackedCuratePrompt {
|
|
|
416
436
|
if (openedEditor) {
|
|
417
437
|
openReviewerEditor(state.cursor);
|
|
418
438
|
}
|
|
439
|
+
state.issueScrollPinnedToCursor = true;
|
|
419
440
|
if (state.decisions[state.cursor] !== prev || openedEditor)
|
|
420
441
|
dirty = true;
|
|
421
442
|
}
|
|
@@ -424,6 +445,7 @@ export class HostBackedCuratePrompt {
|
|
|
424
445
|
this.host.scheduleParentUiRender();
|
|
425
446
|
return finished;
|
|
426
447
|
};
|
|
448
|
+
const selectionPanes = createCurateSelectionPanes(verdict, plan, state, context, () => this.host.getLayout(), () => offscreenController);
|
|
427
449
|
const baseMode = {
|
|
428
450
|
getSurface: () => {
|
|
429
451
|
const layout = this.host.getLayout();
|
|
@@ -431,6 +453,15 @@ export class HostBackedCuratePrompt {
|
|
|
431
453
|
const rows = Math.max(1, layout.childRows);
|
|
432
454
|
return buildSurface(verdict, plan, state, cols, rows, context, offscreenController);
|
|
433
455
|
},
|
|
456
|
+
selectionRegionAt: (column, row) => {
|
|
457
|
+
const layout = this.host.getLayout();
|
|
458
|
+
const cols = Math.max(20, layout.columns);
|
|
459
|
+
const rows = Math.max(1, layout.childRows);
|
|
460
|
+
return curateSelectionRegionAt(column, row, cols, rows);
|
|
461
|
+
},
|
|
462
|
+
selectionTargetAt: (column, row) => selectionPanes.selectionTargetAt(column, row),
|
|
463
|
+
docSurfaceSelectionPlacementAt: (column, row) => selectionPanes.placementAt(column, row),
|
|
464
|
+
docSurfaceSelectionPlacements: () => selectionPanes.placements(),
|
|
434
465
|
onEvents: (keys) => {
|
|
435
466
|
processEvents(parser.mapKeys(keys));
|
|
436
467
|
return true;
|
|
@@ -488,15 +519,7 @@ function navigableCount(issueCount, state) {
|
|
|
488
519
|
function clampCursor(issueCount, state) {
|
|
489
520
|
state.cursor = Math.max(0, Math.min(state.cursor, navigableCount(issueCount, state) - 1));
|
|
490
521
|
}
|
|
491
|
-
function
|
|
492
|
-
const pinnedHint = clipToWidth(globalHintText, width);
|
|
493
|
-
const scrollRows = Math.max(0, rows - 1);
|
|
494
|
-
if (scrollRows === 0) {
|
|
495
|
-
return {
|
|
496
|
-
lines: [pinnedHint],
|
|
497
|
-
cursor: { row: 0, col: 0 },
|
|
498
|
-
};
|
|
499
|
-
}
|
|
522
|
+
function buildIssueListRows(verdict, state, width, citationLive, issueInquiry) {
|
|
500
523
|
const issues = verdict.issues;
|
|
501
524
|
const lines = [];
|
|
502
525
|
const n = issues.length;
|
|
@@ -508,9 +531,6 @@ function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive
|
|
|
508
531
|
}
|
|
509
532
|
}
|
|
510
533
|
lines.push("");
|
|
511
|
-
// Every navigable row gets an explicit anchor so scrolling and prompt
|
|
512
|
-
// cursor placement work for reviewer issues, user-added rows, and the
|
|
513
|
-
// trailing add row uniformly.
|
|
514
534
|
const rowStarts = [];
|
|
515
535
|
const rowEnds = [];
|
|
516
536
|
let editorRow;
|
|
@@ -543,8 +563,6 @@ function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive
|
|
|
543
563
|
if (metaToken) {
|
|
544
564
|
const lastIdx = lines.length - 1;
|
|
545
565
|
const lastVis = visibleLength(lines[lastIdx]);
|
|
546
|
-
// Append meta to the last rendered row if it fits with a separating
|
|
547
|
-
// space; otherwise put it on a new indented row.
|
|
548
566
|
if (lastVis + 1 + metaWidth <= width) {
|
|
549
567
|
lines[lastIdx] = `${lines[lastIdx]} ${metaToken}`;
|
|
550
568
|
}
|
|
@@ -553,7 +571,6 @@ function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive
|
|
|
553
571
|
}
|
|
554
572
|
}
|
|
555
573
|
}
|
|
556
|
-
// Inline revise row(s).
|
|
557
574
|
if (state.decisions[i] === "revise") {
|
|
558
575
|
const prefix = " └─ Revise: ";
|
|
559
576
|
const reviseAvail = Math.max(1, width - visibleLength(prefix));
|
|
@@ -631,29 +648,43 @@ function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive
|
|
|
631
648
|
for (const l of wrapToWidth(renderShortcutHints(curateHelpTokens(state, issues.length, citationLive, issueInquiry)), width)) {
|
|
632
649
|
lines.push(l);
|
|
633
650
|
}
|
|
651
|
+
return { lines, rowStarts, rowEnds, editorRow, editorCol };
|
|
652
|
+
}
|
|
653
|
+
function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive, issueInquiry) {
|
|
654
|
+
const pinnedHint = clipToWidth(globalHintText, width);
|
|
655
|
+
const scrollRows = Math.max(0, rows - 1);
|
|
656
|
+
if (scrollRows === 0) {
|
|
657
|
+
return {
|
|
658
|
+
lines: [pinnedHint],
|
|
659
|
+
cursor: { row: 0, col: 0 },
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
const { lines, rowStarts, rowEnds, editorRow, editorCol } = buildIssueListRows(verdict, state, width, citationLive, issueInquiry);
|
|
634
663
|
const activeStart = rowStarts[state.cursor] ?? 0;
|
|
635
664
|
const activeEndExclusive = rowEnds[state.cursor] ?? lines.length;
|
|
636
665
|
// Prefer to show the entire active block; if it is taller than `rows`,
|
|
637
666
|
// fall back to the editor row (when editing) or the header row.
|
|
638
|
-
let scrollTop = 0;
|
|
639
667
|
const total = lines.length;
|
|
668
|
+
let scrollTop = Math.min(Math.max(0, state.issueScrollTop), Math.max(0, total - scrollRows));
|
|
640
669
|
if (total > scrollRows) {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
scrollTop
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
if (editorRow
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
670
|
+
if (state.issueScrollPinnedToCursor) {
|
|
671
|
+
// Default: anchor to the start of the active issue.
|
|
672
|
+
scrollTop = activeStart;
|
|
673
|
+
// If the block fits and there is room above, pull the window up so we
|
|
674
|
+
// also include some preceding context rather than leaving blank rows
|
|
675
|
+
// below.
|
|
676
|
+
if (activeEndExclusive - scrollTop < scrollRows) {
|
|
677
|
+
scrollTop = Math.max(0, total - scrollRows);
|
|
678
|
+
if (scrollTop > activeStart)
|
|
679
|
+
scrollTop = activeStart;
|
|
680
|
+
}
|
|
681
|
+
// If editing, prefer keeping the editor row visible.
|
|
682
|
+
if (editorRow !== undefined) {
|
|
683
|
+
if (editorRow < scrollTop)
|
|
684
|
+
scrollTop = editorRow;
|
|
685
|
+
if (editorRow >= scrollTop + scrollRows)
|
|
686
|
+
scrollTop = editorRow - scrollRows + 1;
|
|
687
|
+
}
|
|
657
688
|
}
|
|
658
689
|
// Clamp.
|
|
659
690
|
if (scrollTop < 0)
|
|
@@ -661,6 +692,7 @@ function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive
|
|
|
661
692
|
if (scrollTop + scrollRows > total)
|
|
662
693
|
scrollTop = total - scrollRows;
|
|
663
694
|
}
|
|
695
|
+
state.issueScrollTop = scrollTop;
|
|
664
696
|
let cursorRow;
|
|
665
697
|
let cursorCol;
|
|
666
698
|
if (editorRow !== undefined) {
|
|
@@ -674,13 +706,9 @@ function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive
|
|
|
674
706
|
}
|
|
675
707
|
// Slice / pad to exactly `scrollRows`; the global hint row is pinned below
|
|
676
708
|
// this scrollable issue window.
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
while (lines.length < scrollRows)
|
|
681
|
-
lines.push("");
|
|
682
|
-
if (lines.length > scrollRows)
|
|
683
|
-
lines.splice(scrollRows, lines.length - scrollRows);
|
|
709
|
+
const visibleLines = lines.slice(scrollTop, scrollTop + scrollRows);
|
|
710
|
+
while (visibleLines.length < scrollRows)
|
|
711
|
+
visibleLines.push("");
|
|
684
712
|
if (cursorRow < 0)
|
|
685
713
|
cursorRow = 0;
|
|
686
714
|
if (cursorRow >= scrollRows)
|
|
@@ -689,9 +717,9 @@ function buildLeftPane(verdict, state, width, rows, globalHintText, citationLive
|
|
|
689
717
|
cursorCol = 0;
|
|
690
718
|
if (cursorCol >= width)
|
|
691
719
|
cursorCol = Math.max(0, width - 1);
|
|
692
|
-
|
|
720
|
+
visibleLines.push(pinnedHint);
|
|
693
721
|
return {
|
|
694
|
-
lines:
|
|
722
|
+
lines: visibleLines.map((l) => clipToWidth(l, width)),
|
|
695
723
|
cursor: { row: cursorRow, col: cursorCol },
|
|
696
724
|
};
|
|
697
725
|
}
|
|
@@ -719,6 +747,174 @@ function computeNarrowPlanRows(rows) {
|
|
|
719
747
|
// visible.
|
|
720
748
|
return Math.max(3, Math.floor((rows - 1) / 2));
|
|
721
749
|
}
|
|
750
|
+
function computeCurateCollapsedLayout(rows) {
|
|
751
|
+
const visibleRows = Math.max(1, rows);
|
|
752
|
+
const planRows = computeNarrowPlanRows(visibleRows);
|
|
753
|
+
return {
|
|
754
|
+
planRows,
|
|
755
|
+
planEnd: Math.min(planRows, visibleRows),
|
|
756
|
+
separatorRow: planRows + 1,
|
|
757
|
+
bodyStart: planRows + 2,
|
|
758
|
+
bodyEnd: visibleRows,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
function curateSelectionRegionAt(column, row, cols, rows) {
|
|
762
|
+
if (column < 1 || column > cols || row < 1 || row > rows)
|
|
763
|
+
return null;
|
|
764
|
+
const geometry = curateSelectionGeometry(cols, rows);
|
|
765
|
+
if (pointInRegion(column, row, geometry.plan?.region))
|
|
766
|
+
return geometry.plan.region;
|
|
767
|
+
if (pointInRegion(column, row, geometry.issueList?.region)) {
|
|
768
|
+
return geometry.issueList.region;
|
|
769
|
+
}
|
|
770
|
+
if (pointInRegion(column, row, geometry.issueChrome))
|
|
771
|
+
return geometry.issueChrome;
|
|
772
|
+
const split = computeSplitMetrics({ cols, rows, collapseAt: COLLAPSE_AT_COLS });
|
|
773
|
+
if (!split.collapsed && geometry.issueChrome === undefined) {
|
|
774
|
+
return sideBySidePaneRegion(column, rows, split);
|
|
775
|
+
}
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
function curateSelectionGeometry(cols, rows) {
|
|
779
|
+
const split = computeSplitMetrics({ cols, rows, collapseAt: COLLAPSE_AT_COLS });
|
|
780
|
+
if (!split.collapsed) {
|
|
781
|
+
const issueRows = Math.max(0, rows - 1);
|
|
782
|
+
return {
|
|
783
|
+
plan: {
|
|
784
|
+
region: {
|
|
785
|
+
rowStart: 1,
|
|
786
|
+
rowEnd: rows,
|
|
787
|
+
colStart: split.leftWidth + 2,
|
|
788
|
+
colEnd: cols,
|
|
789
|
+
},
|
|
790
|
+
width: split.rightWidth,
|
|
791
|
+
visibleRows: rows,
|
|
792
|
+
},
|
|
793
|
+
issueList: issueRows > 0
|
|
794
|
+
? {
|
|
795
|
+
region: { rowStart: 1, rowEnd: issueRows, colStart: 1, colEnd: split.leftWidth },
|
|
796
|
+
width: split.leftWidth,
|
|
797
|
+
visibleRows: issueRows,
|
|
798
|
+
}
|
|
799
|
+
: undefined,
|
|
800
|
+
issueChrome: issueRows < rows
|
|
801
|
+
? { rowStart: issueRows + 1, rowEnd: rows, colStart: 1, colEnd: split.leftWidth }
|
|
802
|
+
: undefined,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
const layout = computeCurateCollapsedLayout(rows);
|
|
806
|
+
const issueRows = Math.max(0, layout.bodyEnd - layout.bodyStart + 1);
|
|
807
|
+
const issueListRows = Math.max(0, issueRows - 1);
|
|
808
|
+
return {
|
|
809
|
+
plan: layout.planEnd >= 1
|
|
810
|
+
? {
|
|
811
|
+
region: { rowStart: 1, rowEnd: layout.planEnd, colStart: 1, colEnd: cols },
|
|
812
|
+
width: cols,
|
|
813
|
+
visibleRows: layout.planEnd,
|
|
814
|
+
}
|
|
815
|
+
: undefined,
|
|
816
|
+
issueList: issueListRows > 0
|
|
817
|
+
? {
|
|
818
|
+
region: {
|
|
819
|
+
rowStart: layout.bodyStart,
|
|
820
|
+
rowEnd: layout.bodyStart + issueListRows - 1,
|
|
821
|
+
colStart: 1,
|
|
822
|
+
colEnd: cols,
|
|
823
|
+
},
|
|
824
|
+
width: cols,
|
|
825
|
+
visibleRows: issueListRows,
|
|
826
|
+
}
|
|
827
|
+
: undefined,
|
|
828
|
+
issueChrome: issueRows > issueListRows
|
|
829
|
+
? { rowStart: layout.bodyStart + issueListRows, rowEnd: layout.bodyEnd, colStart: 1, colEnd: cols }
|
|
830
|
+
: undefined,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function createCurateSelectionPanes(verdict, plan, state, context, getLayout, getController) {
|
|
834
|
+
const current = () => {
|
|
835
|
+
const layout = getLayout();
|
|
836
|
+
const cols = Math.max(20, layout.columns);
|
|
837
|
+
const rows = Math.max(1, layout.childRows);
|
|
838
|
+
return { cols, rows, geometry: curateSelectionGeometry(cols, rows) };
|
|
839
|
+
};
|
|
840
|
+
const issueLines = (width, planWidth) => {
|
|
841
|
+
const controller = getController();
|
|
842
|
+
const issueInquiry = (context.offscreen?.slots ?? []).map((slot) => ({
|
|
843
|
+
binding: slot.binding,
|
|
844
|
+
ready: controller?.isIssueInquiryReady(slot.binding) ?? true,
|
|
845
|
+
}));
|
|
846
|
+
const citationLive = !state.editing &&
|
|
847
|
+
state.cursor < verdict.issues.length &&
|
|
848
|
+
findCitationTargets(verdict.issues[state.cursor]?.citations, buildCuratePlanPane(plan, planWidth).sources).length > 0;
|
|
849
|
+
return buildIssueListRows(verdict, state, width, citationLive, issueInquiry).lines;
|
|
850
|
+
};
|
|
851
|
+
const planPane = createSelectableDocumentPane({
|
|
852
|
+
lines: () => {
|
|
853
|
+
const { geometry } = current();
|
|
854
|
+
return geometry.plan ? buildCuratePlanPane(plan, geometry.plan.width).lines : [];
|
|
855
|
+
},
|
|
856
|
+
getScrollTop: () => state.planScrollTop,
|
|
857
|
+
setScrollTop: (value) => {
|
|
858
|
+
const { geometry } = current();
|
|
859
|
+
const pane = geometry.plan;
|
|
860
|
+
if (!pane)
|
|
861
|
+
return;
|
|
862
|
+
const max = Math.max(0, buildCuratePlanPane(plan, pane.width).lines.length - pane.visibleRows);
|
|
863
|
+
state.planScrollTop = Math.min(max, Math.max(0, Math.trunc(value)));
|
|
864
|
+
},
|
|
865
|
+
getViewportRows: () => current().geometry.plan?.visibleRows ?? 1,
|
|
866
|
+
});
|
|
867
|
+
const issuePane = createSelectableDocumentPane({
|
|
868
|
+
lines: () => {
|
|
869
|
+
const { geometry } = current();
|
|
870
|
+
const planWidth = geometry.plan?.width ?? geometry.issueList?.width ?? 1;
|
|
871
|
+
return geometry.issueList ? issueLines(geometry.issueList.width, planWidth) : [];
|
|
872
|
+
},
|
|
873
|
+
getScrollTop: () => state.issueScrollTop,
|
|
874
|
+
setScrollTop: (value) => {
|
|
875
|
+
const { geometry } = current();
|
|
876
|
+
const pane = geometry.issueList;
|
|
877
|
+
if (!pane)
|
|
878
|
+
return;
|
|
879
|
+
const planWidth = geometry.plan?.width ?? pane.width;
|
|
880
|
+
const max = Math.max(0, issueLines(pane.width, planWidth).length - pane.visibleRows);
|
|
881
|
+
state.issueScrollTop = Math.min(max, Math.max(0, Math.trunc(value)));
|
|
882
|
+
},
|
|
883
|
+
getViewportRows: () => current().geometry.issueList?.visibleRows ?? 1,
|
|
884
|
+
onManualScroll: () => {
|
|
885
|
+
state.issueScrollPinnedToCursor = false;
|
|
886
|
+
},
|
|
887
|
+
});
|
|
888
|
+
const placements = () => {
|
|
889
|
+
const { geometry } = current();
|
|
890
|
+
const result = [];
|
|
891
|
+
if (geometry.plan &&
|
|
892
|
+
buildCuratePlanPane(plan, geometry.plan.width).lines.length > geometry.plan.visibleRows) {
|
|
893
|
+
result.push({ region: geometry.plan.region, pane: planPane });
|
|
894
|
+
}
|
|
895
|
+
if (geometry.issueList) {
|
|
896
|
+
const planWidth = geometry.plan?.width ?? geometry.issueList.width;
|
|
897
|
+
if (issueLines(geometry.issueList.width, planWidth).length > geometry.issueList.visibleRows) {
|
|
898
|
+
result.push({ region: geometry.issueList.region, pane: issuePane });
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return result;
|
|
902
|
+
};
|
|
903
|
+
return {
|
|
904
|
+
selectionTargetAt: (column, row) => placements().some((placement) => pointInRegion(column, row, placement.region))
|
|
905
|
+
? "parent-doc-surface"
|
|
906
|
+
: null,
|
|
907
|
+
placementAt: (column, row) => placements().find((placement) => pointInRegion(column, row, placement.region)),
|
|
908
|
+
placements,
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function pointInRegion(column, row, region) {
|
|
912
|
+
return !!region &&
|
|
913
|
+
column >= region.colStart &&
|
|
914
|
+
column <= region.colEnd &&
|
|
915
|
+
row >= region.rowStart &&
|
|
916
|
+
row <= region.rowEnd;
|
|
917
|
+
}
|
|
722
918
|
function buildSurface(verdict, plan, state, cols, rows, context, offscreenController) {
|
|
723
919
|
const split = computeSplitMetrics({ cols, rows, collapseAt: COLLAPSE_AT_COLS });
|
|
724
920
|
const primaryReady = offscreenController?.isIssueInquiryReady("primary") ?? true;
|
|
@@ -735,7 +931,7 @@ function buildSurface(verdict, plan, state, cols, rows, context, offscreenContro
|
|
|
735
931
|
// Narrow stack: plan on top, divider rule, issues below. Both panes are
|
|
736
932
|
// independently scrollable (plan via PageUp/PageDown/Home/End/wheel,
|
|
737
933
|
// issues via Up/Down cursor movement).
|
|
738
|
-
const planRows =
|
|
934
|
+
const { planRows } = computeCurateCollapsedLayout(rows);
|
|
739
935
|
const issueRows = Math.max(1, rows - 1 - planRows);
|
|
740
936
|
const right = buildRightPane(verdict, plan, state, cols, planRows);
|
|
741
937
|
const left = buildLeftPane(verdict, state, cols, issueRows, hint, citationLive, issueInquiry);
|