123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import { VDomModel, VDomRenderer } from '@jupyterlab/apputils';
- import {
- ILogger,
- ILoggerRegistry,
- IContentChange
- } from '@jupyterlab/logconsole';
- import { GroupItem, TextItem, interactiveItem } from '@jupyterlab/statusbar';
- import { listIcon } from '@jupyterlab/ui-components';
- import { Signal } from '@lumino/signaling';
- import React from 'react';
- /**
- * A pure functional component for a Log Console status item.
- *
- * @param props - the props for the component.
- *
- * @returns a tsx component for rendering the Log Console status.
- */
- function LogConsoleStatusComponent(
- props: LogConsoleStatusComponent.IProps
- ): React.ReactElement<LogConsoleStatusComponent.IProps> {
- let title = '';
- if (props.newMessages > 0) {
- title = `${props.newMessages} new messages, `;
- }
- title += `${props.logEntries} log entries for ${props.source}`;
- return (
- <GroupItem spacing={0} onClick={props.handleClick} title={title}>
- <listIcon.react top={'2px'} kind={'statusBar'} />
- {props.newMessages > 0 ? <TextItem source={props.newMessages} /> : <></>}
- </GroupItem>
- );
- }
- /*
- * A namespace for LogConsoleStatusComponent.
- */
- namespace LogConsoleStatusComponent {
- /**
- * The props for the LogConsoleStatusComponent.
- */
- export interface IProps {
- /**
- * A click handler for the item. By default
- * Log Console panel is launched.
- */
- handleClick: () => void;
- /**
- * Number of log entries.
- */
- logEntries: number;
- /**
- * Number of new log messages.
- */
- newMessages: number;
- /**
- * Log source name
- */
- source: string | null;
- }
- }
- /**
- * A VDomRenderer widget for displaying the status of Log Console logs.
- */
- export class LogConsoleStatus extends VDomRenderer<LogConsoleStatus.Model> {
- /**
- * Construct the log console status widget.
- *
- * @param options - The status widget initialization options.
- */
- constructor(options: LogConsoleStatus.IOptions) {
- super(new LogConsoleStatus.Model(options.loggerRegistry));
- this._handleClick = options.handleClick;
- this.addClass(interactiveItem);
- this.addClass('jp-LogConsoleStatusItem');
- }
- /**
- * Render the log console status item.
- */
- render() {
- if (this.model === null || this.model.version === 0) {
- this.hide();
- return null;
- }
- this.show();
- let {
- flashEnabled,
- messages,
- source,
- version,
- versionDisplayed,
- versionNotified
- } = this.model;
- if (source !== null && flashEnabled && version > versionNotified) {
- this._flashHighlight();
- this.model.sourceNotified(source, version);
- } else if (source !== null && flashEnabled && version > versionDisplayed) {
- this._showHighlighted();
- } else {
- this._clearHighlight();
- }
- return (
- <LogConsoleStatusComponent
- handleClick={this._handleClick}
- logEntries={messages}
- newMessages={version - versionDisplayed}
- source={this.model.source}
- />
- );
- }
- private _flashHighlight() {
- this._showHighlighted();
- // To make sure the browser triggers the animation, we remove the class,
- // wait for an animation frame, then add it back
- this.removeClass('jp-LogConsole-flash');
- requestAnimationFrame(() => {
- this.addClass('jp-LogConsole-flash');
- });
- }
- private _showHighlighted() {
- this.addClass('jp-mod-selected');
- }
- private _clearHighlight() {
- this.removeClass('jp-LogConsole-flash');
- this.removeClass('jp-mod-selected');
- }
- private _handleClick: () => void;
- }
- /**
- * A namespace for Log Console log status.
- */
- export namespace LogConsoleStatus {
- /**
- * A VDomModel for the LogConsoleStatus item.
- */
- export class Model extends VDomModel {
- /**
- * Create a new LogConsoleStatus model.
- *
- * @param loggerRegistry - The logger registry providing the logs.
- */
- constructor(loggerRegistry: ILoggerRegistry) {
- super();
- this._loggerRegistry = loggerRegistry;
- this._loggerRegistry.registryChanged.connect(
- this._handleLogRegistryChange,
- this
- );
- this._handleLogRegistryChange();
- }
- /**
- * Number of messages currently in the current source.
- */
- get messages(): number {
- if (this._source === null) {
- return 0;
- }
- const logger = this._loggerRegistry.getLogger(this._source);
- return logger.length;
- }
- /**
- * The number of messages ever stored by the current source.
- */
- get version(): number {
- if (this._source === null) {
- return 0;
- }
- const logger = this._loggerRegistry.getLogger(this._source);
- return logger.version;
- }
- /**
- * The name of the active log source
- */
- get source(): string | null {
- return this._source;
- }
- set source(name: string | null) {
- if (this._source === name) {
- return;
- }
- this._source = name;
- // refresh rendering
- this.stateChanged.emit();
- }
- /**
- * The last source version that was displayed.
- */
- get versionDisplayed(): number {
- if (this._source === null) {
- return 0;
- }
- return this._sourceVersion.get(this._source)?.lastDisplayed ?? 0;
- }
- /**
- * The last source version we notified the user about.
- */
- get versionNotified(): number {
- if (this._source === null) {
- return 0;
- }
- return this._sourceVersion.get(this._source)?.lastNotified ?? 0;
- }
- /**
- * Flag to toggle flashing when new logs added.
- */
- get flashEnabled(): boolean {
- return this._flashEnabled;
- }
- set flashEnabled(enabled: boolean) {
- if (this._flashEnabled === enabled) {
- return;
- }
- this._flashEnabled = enabled;
- this.flashEnabledChanged.emit();
- // refresh rendering
- this.stateChanged.emit();
- }
- /**
- * Record the last source version displayed to the user.
- *
- * @param source - The name of the log source.
- * @param version - The version of the log that was displayed.
- *
- * #### Notes
- * This will also update the last notified version so that the last
- * notified version is always at least the last displayed version.
- */
- sourceDisplayed(source: string | null, version: number | null) {
- if (source === null || version === null) {
- return;
- }
- const versions = this._sourceVersion.get(source)!;
- let change = false;
- if (versions.lastDisplayed < version) {
- versions.lastDisplayed = version;
- change = true;
- }
- if (versions.lastNotified < version) {
- versions.lastNotified = version;
- change = true;
- }
- if (change && source === this._source) {
- this.stateChanged.emit();
- }
- }
- /**
- * Record a source version we notified the user about.
- *
- * @param source - The name of the log source.
- * @param version - The version of the log.
- */
- sourceNotified(source: string | null, version: number) {
- if (source === null) {
- return;
- }
- const versions = this._sourceVersion.get(source);
- if (versions!.lastNotified < version) {
- versions!.lastNotified = version;
- if (source === this._source) {
- this.stateChanged.emit();
- }
- }
- }
- private _handleLogRegistryChange() {
- const loggers = this._loggerRegistry.getLoggers();
- for (let logger of loggers) {
- if (!this._sourceVersion.has(logger.source)) {
- logger.contentChanged.connect(this._handleLogContentChange, this);
- this._sourceVersion.set(logger.source, {
- lastDisplayed: 0,
- lastNotified: 0
- });
- }
- }
- }
- private _handleLogContentChange(
- { source }: ILogger,
- change: IContentChange
- ) {
- if (source === this._source) {
- this.stateChanged.emit();
- }
- }
- /**
- * A signal emitted when the flash enablement changes.
- */
- public flashEnabledChanged = new Signal<this, void>(this);
- private _flashEnabled: boolean = true;
- private _loggerRegistry: ILoggerRegistry;
- private _source: string | null = null;
- /**
- * The view status of each source.
- *
- * #### Notes
- * Keys are source names, value is a list of two numbers. The first
- * represents the version of the messages that was last displayed to the
- * user, the second represents the version that we last notified the user
- * about.
- */
- private _sourceVersion: Map<string, IVersionInfo> = new Map();
- }
- interface IVersionInfo {
- lastDisplayed: number;
- lastNotified: number;
- }
- /**
- * Options for creating a new LogConsoleStatus item
- */
- export interface IOptions {
- /**
- * The logger registry providing the logs.
- */
- loggerRegistry: ILoggerRegistry;
- /**
- * A click handler for the item. By default
- * Log Console panel is launched.
- */
- handleClick: () => void;
- }
- }
|