@theia/core 1.62.1 → 1.63.0-next.24

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.
Files changed (28) hide show
  1. package/README.md +6 -6
  2. package/lib/browser/secondary-window-handler.d.ts +1 -0
  3. package/lib/browser/secondary-window-handler.d.ts.map +1 -1
  4. package/lib/browser/secondary-window-handler.js +4 -1
  5. package/lib/browser/secondary-window-handler.js.map +1 -1
  6. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  7. package/lib/browser/shell/application-shell.js +5 -1
  8. package/lib/browser/shell/application-shell.js.map +1 -1
  9. package/lib/browser/tree/tree-widget.d.ts +38 -1
  10. package/lib/browser/tree/tree-widget.d.ts.map +1 -1
  11. package/lib/browser/tree/tree-widget.js +62 -5
  12. package/lib/browser/tree/tree-widget.js.map +1 -1
  13. package/lib/browser/window/default-secondary-window-service.d.ts +1 -0
  14. package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
  15. package/lib/browser/window/default-secondary-window-service.js +3 -0
  16. package/lib/browser/window/default-secondary-window-service.js.map +1 -1
  17. package/lib/browser/window/secondary-window-service.d.ts +1 -0
  18. package/lib/browser/window/secondary-window-service.d.ts.map +1 -1
  19. package/lib/node/logger-cli-contribution.d.ts.map +1 -1
  20. package/lib/node/logger-cli-contribution.js +4 -0
  21. package/lib/node/logger-cli-contribution.js.map +1 -1
  22. package/package.json +6 -6
  23. package/src/browser/secondary-window-handler.ts +5 -1
  24. package/src/browser/shell/application-shell.ts +5 -1
  25. package/src/browser/tree/tree-widget.tsx +85 -4
  26. package/src/browser/window/default-secondary-window-service.ts +4 -0
  27. package/src/browser/window/secondary-window-service.ts +1 -0
  28. package/src/node/logger-cli-contribution.ts +5 -0
@@ -16,7 +16,7 @@
16
16
 
17
17
  import { injectable, inject, postConstruct } from 'inversify';
18
18
  import { Message } from '@lumino/messaging';
19
- import { Disposable, MenuPath, SelectionService } from '../../common';
19
+ import { Disposable, MenuPath, SelectionService, Event as TheiaEvent, Emitter } from '../../common';
20
20
  import { Key, KeyCode, KeyModifier } from '../keyboard/keys';
21
21
  import { ContextMenuRenderer } from '../context-menu-renderer';
22
22
  import { StatefulWidget } from '../shell';
@@ -62,6 +62,27 @@ export const COMPOSITE_TREE_NODE_CLASS = 'theia-CompositeTreeNode';
62
62
  export const TREE_NODE_CAPTION_CLASS = 'theia-TreeNodeCaption';
63
63
  export const TREE_NODE_INDENT_GUIDE_CLASS = 'theia-tree-node-indent';
64
64
 
65
+ /**
66
+ * Threshold in pixels to consider the view as being scrolled to the bottom
67
+ */
68
+ export const SCROLL_BOTTOM_THRESHOLD = 30;
69
+
70
+ /**
71
+ * Tree scroll event data.
72
+ */
73
+ export interface TreeScrollEvent {
74
+ readonly scrollTop: number;
75
+ readonly scrollLeft: number;
76
+ }
77
+
78
+ /**
79
+ * Tree scroll state data.
80
+ */
81
+ export interface TreeScrollState {
82
+ readonly scrollTop: number;
83
+ readonly isAtBottom: boolean;
84
+ }
85
+
65
86
  export const TreeProps = Symbol('TreeProps');
66
87
 
67
88
  /**
@@ -165,6 +186,9 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
165
186
  protected searchBox: SearchBox;
166
187
  protected searchHighlights: Map<string, TreeDecoration.CaptionHighlight>;
167
188
 
189
+ protected readonly onScrollEmitter = new Emitter<TreeScrollEvent>();
190
+ readonly onScroll: TheiaEvent<TreeScrollEvent> = this.onScrollEmitter.event;
191
+
168
192
  @inject(TreeDecoratorService)
169
193
  protected readonly decoratorService: TreeDecoratorService;
170
194
  @inject(TreeSearch)
@@ -258,6 +282,7 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
258
282
  this.node.addEventListener('mouseup', this.handleMiddleClickEvent.bind(this));
259
283
  this.node.addEventListener('auxclick', this.handleMiddleClickEvent.bind(this));
260
284
  this.toDispose.pushAll([
285
+ this.onScrollEmitter,
261
286
  this.model,
262
287
  this.model.onChanged(() => this.updateRows()),
263
288
  this.model.onSelectionChanged(() => this.scheduleUpdateScrollToRow({ resize: false })),
@@ -509,6 +534,7 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
509
534
  rows={rows}
510
535
  renderNodeRow={this.renderNodeRow}
511
536
  scrollToRow={this.scrollToRow}
537
+ onScrollEmitter={this.onScrollEmitter}
512
538
  {...this.props.viewProps}
513
539
  />;
514
540
  }
@@ -1157,6 +1183,39 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
1157
1183
  return this.node;
1158
1184
  }
1159
1185
 
1186
+ /**
1187
+ * Get the current scroll state from the virtualized view.
1188
+ * This should be used instead of accessing the DOM scroll properties directly
1189
+ * when the tree is virtualized.
1190
+ */
1191
+ protected getVirtualizedScrollState(): TreeScrollState | undefined {
1192
+ return this.view?.getScrollState();
1193
+ }
1194
+
1195
+ /**
1196
+ * Check if the tree is scrolled to the bottom.
1197
+ * Works with both virtualized and non-virtualized trees.
1198
+ */
1199
+ isScrolledToBottom(): boolean {
1200
+ if (this.props.virtualized !== false && this.view) {
1201
+ // Use virtualized scroll state
1202
+ const scrollState = this.getVirtualizedScrollState();
1203
+ return scrollState?.isAtBottom ?? true;
1204
+ } else {
1205
+ // Fallback to DOM-based calculation for non-virtualized trees
1206
+ const scrollContainer = this.node;
1207
+ const scrollHeight = scrollContainer.scrollHeight;
1208
+ const scrollTop = scrollContainer.scrollTop;
1209
+ const clientHeight = scrollContainer.clientHeight;
1210
+
1211
+ if (scrollHeight <= clientHeight) {
1212
+ return true;
1213
+ }
1214
+
1215
+ return scrollHeight - scrollTop - clientHeight <= SCROLL_BOTTOM_THRESHOLD;
1216
+ }
1217
+ }
1218
+
1160
1219
  protected override onAfterAttach(msg: Message): void {
1161
1220
  const up = [
1162
1221
  Key.ARROW_UP,
@@ -1577,11 +1636,17 @@ export namespace TreeWidget {
1577
1636
  */
1578
1637
  rows: NodeRow[]
1579
1638
  renderNodeRow: (row: NodeRow) => React.ReactNode
1639
+ /**
1640
+ * Optional scroll event emitter.
1641
+ */
1642
+ onScrollEmitter?: Emitter<TreeScrollEvent>
1580
1643
  }
1581
1644
  export class View extends React.Component<ViewProps> {
1582
1645
  list: VirtuosoHandle | undefined;
1646
+ private lastScrollState: TreeScrollState = { scrollTop: 0, isAtBottom: true };
1647
+
1583
1648
  override render(): React.ReactNode {
1584
- const { rows, width, height, scrollToRow, renderNodeRow, ...other } = this.props;
1649
+ const { rows, width, height, scrollToRow, renderNodeRow, onScrollEmitter, ...other } = this.props;
1585
1650
  return <Virtuoso
1586
1651
  ref={list => {
1587
1652
  this.list = (list || undefined);
@@ -1592,14 +1657,30 @@ export namespace TreeWidget {
1592
1657
  });
1593
1658
  }
1594
1659
  }}
1660
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1661
+ onScroll={(e: any) => {
1662
+ const scrollTop = e.target.scrollTop;
1663
+ const scrollHeight = e.target.scrollHeight;
1664
+ const clientHeight = e.target.clientHeight;
1665
+ const isAtBottom = scrollHeight - scrollTop - clientHeight <= SCROLL_BOTTOM_THRESHOLD;
1666
+
1667
+ // Store scroll state before firing the event to prevent jitter during inference and scrolling
1668
+ this.lastScrollState = { scrollTop, isAtBottom };
1669
+ onScrollEmitter?.fire({ scrollTop, scrollLeft: e.target.scrollLeft || 0 });
1670
+ }}
1595
1671
  totalCount={rows.length}
1596
1672
  itemContent={index => renderNodeRow(rows[index])}
1597
1673
  width={width}
1598
1674
  height={height}
1599
- // This is a pixel value, it will scan 200px to the top and bottom of the current view
1600
- overscan={500}
1675
+ // This is a pixel value that determines how many pixels to render outside the visible area
1676
+ // Higher value provides smoother scrolling experience especially during inference, but uses more memory
1677
+ overscan={800}
1601
1678
  {...other}
1602
1679
  />;
1603
1680
  }
1681
+
1682
+ getScrollState(): TreeScrollState {
1683
+ return { ...this.lastScrollState };
1684
+ }
1604
1685
  }
1605
1686
  }
@@ -189,6 +189,10 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService {
189
189
  return [height, width, left, top];
190
190
  }
191
191
 
192
+ getWindows(): Window[] {
193
+ return this.secondaryWindows;
194
+ }
195
+
192
196
  focus(win: Window): void {
193
197
  win.focus();
194
198
  }
@@ -39,4 +39,5 @@ export interface SecondaryWindowService {
39
39
 
40
40
  /** Handles focussing the given secondary window in the browser and on Electron. */
41
41
  focus(win: Window): void;
42
+ getWindows(): Window[];
42
43
  }
@@ -120,6 +120,11 @@ export class LogLevelCliContribution implements CliContribution {
120
120
  console.error(`Error creating log file ${filename}: ${e}`);
121
121
  }
122
122
  }
123
+
124
+ // some initial loggers have already been constructed. Fire the event to notify them.
125
+ if (args['log-level'] || args['log-config'] || args['log-file']) {
126
+ this.logConfigChangedEvent.fire();
127
+ }
123
128
  }
124
129
 
125
130
  protected async watchLogConfigFile(filename: string): Promise<void> {