@teammates/consolonia 0.4.0 → 0.5.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.
|
@@ -33,7 +33,7 @@ import type { DrawingContext, TextStyle } from "../drawing/context.js";
|
|
|
33
33
|
import type { InputEvent } from "../input/events.js";
|
|
34
34
|
import { Control } from "../layout/control.js";
|
|
35
35
|
import type { Constraint, Rect, Size } from "../layout/types.js";
|
|
36
|
-
import type
|
|
36
|
+
import { type StyledSpan } from "../styled.js";
|
|
37
37
|
import { type StyledLine } from "./styled-text.js";
|
|
38
38
|
import { type DeleteSizer, type InputColorizer, TextInput } from "./text-input.js";
|
|
39
39
|
export interface DropdownItem {
|
|
@@ -93,8 +93,10 @@ export interface ChatViewOptions {
|
|
|
93
93
|
dropdownLabelStyle?: TextStyle;
|
|
94
94
|
/** Maximum number of lines the input box can grow to (default 1). */
|
|
95
95
|
maxInputHeight?: number;
|
|
96
|
-
/** Footer content shown below the input (StyledLine for mixed colors). */
|
|
96
|
+
/** Footer content shown below the input (StyledLine for mixed colors). Sets the left side. */
|
|
97
97
|
footer?: StyledLine;
|
|
98
|
+
/** Right-aligned footer content. */
|
|
99
|
+
footerRight?: StyledLine;
|
|
98
100
|
/** Style for footer text (used as default when footer is a plain string). */
|
|
99
101
|
footerStyle?: TextStyle;
|
|
100
102
|
/** Command history entries. */
|
|
@@ -117,6 +119,7 @@ export declare class ChatView extends Control {
|
|
|
117
119
|
private _input;
|
|
118
120
|
private _inputSeparator;
|
|
119
121
|
private _footer;
|
|
122
|
+
private _footerRight;
|
|
120
123
|
private _dropdownItems;
|
|
121
124
|
private _dropdownIndex;
|
|
122
125
|
private _feedStyle;
|
|
@@ -166,8 +169,10 @@ export declare class ChatView extends Control {
|
|
|
166
169
|
set bannerWidget(widget: Control);
|
|
167
170
|
/** Get the current banner widget. */
|
|
168
171
|
get bannerWidget(): Control;
|
|
169
|
-
/** Set footer content (plain string or StyledSpan for mixed colors). */
|
|
172
|
+
/** Set left-side footer content (plain string or StyledSpan for mixed colors). */
|
|
170
173
|
setFooter(content: StyledLine): void;
|
|
174
|
+
/** Set right-side footer content (plain string or StyledSpan for mixed colors). */
|
|
175
|
+
setFooterRight(content: StyledLine): void;
|
|
171
176
|
/** Append a line of plain text to the feed. Auto-scrolls to bottom. */
|
|
172
177
|
appendToFeed(text: string, style?: TextStyle): void;
|
|
173
178
|
/** Append a styled line (StyledSpan) to the feed. */
|
|
@@ -57,6 +57,7 @@ export class ChatView extends Control {
|
|
|
57
57
|
_input;
|
|
58
58
|
_inputSeparator;
|
|
59
59
|
_footer;
|
|
60
|
+
_footerRight;
|
|
60
61
|
_dropdownItems = [];
|
|
61
62
|
_dropdownIndex = -1;
|
|
62
63
|
// ── Configuration ──────────────────────────────────────────────
|
|
@@ -163,6 +164,13 @@ export class ChatView extends Control {
|
|
|
163
164
|
wrap: false,
|
|
164
165
|
});
|
|
165
166
|
this.addChild(this._footer);
|
|
167
|
+
const footerRightLine = options.footerRight ?? "";
|
|
168
|
+
this._footerRight = new StyledText({
|
|
169
|
+
lines: [footerRightLine],
|
|
170
|
+
defaultStyle: this._footerStyle,
|
|
171
|
+
wrap: false,
|
|
172
|
+
});
|
|
173
|
+
this.addChild(this._footerRight);
|
|
166
174
|
// Wire input events to ChatView events
|
|
167
175
|
this._input.on("submit", (text) => this.emit("submit", text));
|
|
168
176
|
this._input.on("change", (text) => this.emit("change", text));
|
|
@@ -221,11 +229,16 @@ export class ChatView extends Control {
|
|
|
221
229
|
return this._banner;
|
|
222
230
|
}
|
|
223
231
|
// ── Public API: Footer ─────────────────────────────────────────
|
|
224
|
-
/** Set footer content (plain string or StyledSpan for mixed colors). */
|
|
232
|
+
/** Set left-side footer content (plain string or StyledSpan for mixed colors). */
|
|
225
233
|
setFooter(content) {
|
|
226
234
|
this._footer.lines = [content];
|
|
227
235
|
this.invalidate();
|
|
228
236
|
}
|
|
237
|
+
/** Set right-side footer content (plain string or StyledSpan for mixed colors). */
|
|
238
|
+
setFooterRight(content) {
|
|
239
|
+
this._footerRight.lines = [content];
|
|
240
|
+
this.invalidate();
|
|
241
|
+
}
|
|
229
242
|
// ── Public API: Feed ───────────────────────────────────────────
|
|
230
243
|
/** Append a line of plain text to the feed. Auto-scrolls to bottom. */
|
|
231
244
|
appendToFeed(text, style) {
|
|
@@ -335,6 +348,10 @@ export class ChatView extends Control {
|
|
|
335
348
|
/** Scroll the feed by a delta (positive = down, negative = up). */
|
|
336
349
|
scrollFeed(delta) {
|
|
337
350
|
this._feedScrollOffset = Math.max(0, this._feedScrollOffset + delta);
|
|
351
|
+
// Clear selection when scrolling (unless actively drag-selecting)
|
|
352
|
+
if (!this._selecting && this._hasSelection()) {
|
|
353
|
+
this.clearSelection();
|
|
354
|
+
}
|
|
338
355
|
this.invalidate();
|
|
339
356
|
}
|
|
340
357
|
// ── Public API: Input ──────────────────────────────────────────
|
|
@@ -446,6 +463,7 @@ export class ChatView extends Control {
|
|
|
446
463
|
this._input.focusable = false;
|
|
447
464
|
this._inputSeparator.visible = false;
|
|
448
465
|
this._footer.visible = false;
|
|
466
|
+
this._footerRight.visible = false;
|
|
449
467
|
}
|
|
450
468
|
else {
|
|
451
469
|
// Restore normal input chrome
|
|
@@ -454,6 +472,7 @@ export class ChatView extends Control {
|
|
|
454
472
|
this._input.onFocus();
|
|
455
473
|
this._inputSeparator.visible = true;
|
|
456
474
|
this._footer.visible = true;
|
|
475
|
+
this._footerRight.visible = true;
|
|
457
476
|
}
|
|
458
477
|
this.invalidate();
|
|
459
478
|
}
|
|
@@ -547,6 +566,8 @@ export class ChatView extends Control {
|
|
|
547
566
|
const ratio = relY / this._feedH;
|
|
548
567
|
this._feedScrollOffset = Math.round(ratio * this._maxScroll);
|
|
549
568
|
this._feedScrollOffset = Math.max(0, Math.min(this._feedScrollOffset, this._maxScroll));
|
|
569
|
+
if (this._hasSelection())
|
|
570
|
+
this.clearSelection();
|
|
550
571
|
this.invalidate();
|
|
551
572
|
}
|
|
552
573
|
return true;
|
|
@@ -558,6 +579,8 @@ export class ChatView extends Control {
|
|
|
558
579
|
const clampedPos = Math.max(0, Math.min(newThumbPos, maxThumbPos));
|
|
559
580
|
const ratio = maxThumbPos > 0 ? clampedPos / maxThumbPos : 0;
|
|
560
581
|
this._feedScrollOffset = Math.round(ratio * this._maxScroll);
|
|
582
|
+
if (this._hasSelection())
|
|
583
|
+
this.clearSelection();
|
|
561
584
|
this.invalidate();
|
|
562
585
|
return true;
|
|
563
586
|
}
|
|
@@ -631,6 +654,7 @@ export class ChatView extends Control {
|
|
|
631
654
|
if (me.type === "release" && this._selecting) {
|
|
632
655
|
this._selecting = false;
|
|
633
656
|
this._stopSelScroll();
|
|
657
|
+
this._selEnd = { x: me.x, y: me.y };
|
|
634
658
|
// If anchor == end (just a click, no drag), clear selection
|
|
635
659
|
if (this._selAnchor &&
|
|
636
660
|
this._selEnd &&
|
|
@@ -881,6 +905,7 @@ export class ChatView extends Control {
|
|
|
881
905
|
this._renderDropdown(ctx, b.x, y, W, totalDropdownH);
|
|
882
906
|
}
|
|
883
907
|
else {
|
|
908
|
+
// Left footer
|
|
884
909
|
this._footer.measure({
|
|
885
910
|
minWidth: 0,
|
|
886
911
|
maxWidth: W,
|
|
@@ -889,6 +914,23 @@ export class ChatView extends Control {
|
|
|
889
914
|
});
|
|
890
915
|
this._footer.arrange({ x: b.x, y, width: W, height: footerH });
|
|
891
916
|
this._footer.render(ctx);
|
|
917
|
+
// Right footer (right-aligned on the same row)
|
|
918
|
+
const rightSize = this._footerRight.measure({
|
|
919
|
+
minWidth: 0,
|
|
920
|
+
maxWidth: W,
|
|
921
|
+
minHeight: 0,
|
|
922
|
+
maxHeight: 1,
|
|
923
|
+
});
|
|
924
|
+
if (rightSize.width > 0) {
|
|
925
|
+
const rightX = b.x + W - rightSize.width;
|
|
926
|
+
this._footerRight.arrange({
|
|
927
|
+
x: rightX,
|
|
928
|
+
y,
|
|
929
|
+
width: rightSize.width,
|
|
930
|
+
height: footerH,
|
|
931
|
+
});
|
|
932
|
+
this._footerRight.render(ctx);
|
|
933
|
+
}
|
|
892
934
|
}
|
|
893
935
|
}
|
|
894
936
|
// ── Feed rendering ─────────────────────────────────────────────
|
|
@@ -983,6 +1025,9 @@ export class ChatView extends Control {
|
|
|
983
1025
|
}
|
|
984
1026
|
cy += item.height;
|
|
985
1027
|
}
|
|
1028
|
+
// Always cache feed geometry for selection edge-detection
|
|
1029
|
+
this._feedY = y;
|
|
1030
|
+
this._feedH = height;
|
|
986
1031
|
// Render scrollbar and cache geometry for hit-testing
|
|
987
1032
|
if (height > 0 && totalContentH > height) {
|
|
988
1033
|
const scrollX = x + width - 1;
|
|
@@ -994,8 +1039,6 @@ export class ChatView extends Control {
|
|
|
994
1039
|
const thumbStyle = this._feedStyle;
|
|
995
1040
|
// Cache for mouse interaction
|
|
996
1041
|
this._scrollbarX = scrollX;
|
|
997
|
-
this._feedY = y;
|
|
998
|
-
this._feedH = height;
|
|
999
1042
|
this._thumbPos = thumbPos;
|
|
1000
1043
|
this._thumbSize = thumbSize;
|
|
1001
1044
|
this._maxScroll = maxScroll;
|
package/package.json
CHANGED