@teammates/consolonia 0.3.4 → 0.4.1

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 { StyledSpan } from "../styled.js";
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) {
@@ -446,6 +459,7 @@ export class ChatView extends Control {
446
459
  this._input.focusable = false;
447
460
  this._inputSeparator.visible = false;
448
461
  this._footer.visible = false;
462
+ this._footerRight.visible = false;
449
463
  }
450
464
  else {
451
465
  // Restore normal input chrome
@@ -454,6 +468,7 @@ export class ChatView extends Control {
454
468
  this._input.onFocus();
455
469
  this._inputSeparator.visible = true;
456
470
  this._footer.visible = true;
471
+ this._footerRight.visible = true;
457
472
  }
458
473
  this.invalidate();
459
474
  }
@@ -631,6 +646,7 @@ export class ChatView extends Control {
631
646
  if (me.type === "release" && this._selecting) {
632
647
  this._selecting = false;
633
648
  this._stopSelScroll();
649
+ this._selEnd = { x: me.x, y: me.y };
634
650
  // If anchor == end (just a click, no drag), clear selection
635
651
  if (this._selAnchor &&
636
652
  this._selEnd &&
@@ -881,6 +897,7 @@ export class ChatView extends Control {
881
897
  this._renderDropdown(ctx, b.x, y, W, totalDropdownH);
882
898
  }
883
899
  else {
900
+ // Left footer
884
901
  this._footer.measure({
885
902
  minWidth: 0,
886
903
  maxWidth: W,
@@ -889,6 +906,23 @@ export class ChatView extends Control {
889
906
  });
890
907
  this._footer.arrange({ x: b.x, y, width: W, height: footerH });
891
908
  this._footer.render(ctx);
909
+ // Right footer (right-aligned on the same row)
910
+ const rightSize = this._footerRight.measure({
911
+ minWidth: 0,
912
+ maxWidth: W,
913
+ minHeight: 0,
914
+ maxHeight: 1,
915
+ });
916
+ if (rightSize.width > 0) {
917
+ const rightX = b.x + W - rightSize.width;
918
+ this._footerRight.arrange({
919
+ x: rightX,
920
+ y,
921
+ width: rightSize.width,
922
+ height: footerH,
923
+ });
924
+ this._footerRight.render(ctx);
925
+ }
892
926
  }
893
927
  }
894
928
  // ── Feed rendering ─────────────────────────────────────────────
@@ -983,6 +1017,9 @@ export class ChatView extends Control {
983
1017
  }
984
1018
  cy += item.height;
985
1019
  }
1020
+ // Always cache feed geometry for selection edge-detection
1021
+ this._feedY = y;
1022
+ this._feedH = height;
986
1023
  // Render scrollbar and cache geometry for hit-testing
987
1024
  if (height > 0 && totalContentH > height) {
988
1025
  const scrollX = x + width - 1;
@@ -994,8 +1031,6 @@ export class ChatView extends Control {
994
1031
  const thumbStyle = this._feedStyle;
995
1032
  // Cache for mouse interaction
996
1033
  this._scrollbarX = scrollX;
997
- this._feedY = y;
998
- this._feedH = height;
999
1034
  this._thumbPos = thumbPos;
1000
1035
  this._thumbSize = thumbSize;
1001
1036
  this._maxScroll = maxScroll;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teammates/consolonia",
3
- "version": "0.3.4",
3
+ "version": "0.4.1",
4
4
  "description": "Terminal UI rendering engine inspired by Consolonia. Pixel-level compositing with ANSI output.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",