tuil
ReferencePackages@mwillbanks/tuil-log-viewerAPI

LogViewerModel

class exported by @mwillbanks/tuil-log-viewer.

View rawEdit

class

Public class exported by @mwillbanks/tuil-log-viewer.

export class LogViewerModel {
  readonly #pipeline: LogPipeline;
  readonly #ownsQueryEditor: boolean;
  readonly scroll: ScrollAreaState;
  readonly queryEditor: EditorSession;
  #records: readonly LogRecord[] = [];
  #selected = 0;
  #query = "";
  #theme: LogTheme;
  readonly #observers = new Set<() => void>();
  readonly #unsubscribeBuffer: () => void;
  #revision = 0;

  constructor(pipeline: LogPipeline, options: LogViewerModelOptions = {}) {
    this.#pipeline = pipeline;
    this.#records = pipeline.buffer.records();
    this.scroll = new ScrollAreaState({
      id: options.id ?? "log-viewer",
      viewport: {
        width: options.width ?? 100,
        height: options.height ?? 20,
      },
      extent: {
        width: options.width ?? 100,
        height: this.#records.length,
      },
      sticky: { bottom: true },
      followFocus: true,
    });
    if (!options.queryEditor) {
      throw new Error(
        "LogViewerModel requires a queryEditor from the application editor provider registry",
      );
    }
    if (!options.queryEditorOwnership) {
      throw new Error(
        "LogViewerModel requires explicit borrowed or owned queryEditor ownership",
      );
    }
    this.queryEditor = options.queryEditor;
    this.#ownsQueryEditor = options.queryEditorOwnership === "owned";
    this.#selected = Math.max(0, this.#records.length - 1);
    this.scroll.move("bottom");
    this.#theme = createLogTheme(options.theme ?? "comfortable");
    this.#unsubscribeBuffer = pipeline.buffer.subscribe(() => {
      if (!pipeline.buffer.statistics().paused) this.refresh();
      else this.#notify();
    });
  }

  refresh(): void {
    this.#records = this.#query
      ? this.#pipeline.filter(this.#query)
      : this.#pipeline.buffer.records();
    this.scroll.setExtent({
      width: this.scroll.snapshot().extent.width,
      height: this.#records.length,
    });
    this.#selected = Math.max(
      0,
      Math.min(this.#records.length - 1, this.#selected),
    );
    this.#notify();
  }

  setQuery(source: string): readonly string[] {
    const compiled = compileLogQuery(source);
    this.#query = source;
    this.#pipeline.query(source);
    this.queryEditor.dispatch({
      changes: [
        {
          range: {
            anchor: { line: 0, column: 0 },
            head: {
              line: 0,
              column: this.queryEditor.serialize().length,
            },
          },
          insert: source,
        },
      ],
    });
    this.refresh();
    return Object.freeze(compiled.diagnostics.map((item) => item.message));
  }

  select(index: number): void {
    this.#selected = Math.max(0, Math.min(this.#records.length - 1, index));
    this.scroll.scrollIntoView({
      x: 0,
      y: this.#selected,
      width: 1,
      height: 1,
    });
    this.#notify();
  }

  move(delta: number): void {
    this.select(this.#selected + delta);
  }

  pause(): void {
    this.#pipeline.buffer.pause();
  }

  resume(): void {
    this.#pipeline.buffer.resume();
  }

  setTheme(variant: LogThemeVariant): void {
    this.#theme = createLogTheme(variant);
    this.#notify();
  }

  subscribe(observer: () => void): () => void {
    this.#observers.add(observer);
    return () => this.#observers.delete(observer);
  }

  revision(): number {
    return this.#revision;
  }

  snapshot(): {
    readonly rows: readonly LogRowView[];
    readonly selected?: LogRecord;
    readonly live: boolean;
    readonly theme: LogTheme;
    readonly dropped: number;
    readonly sampled: number;
    readonly rateLimited: number;
    readonly total: number;
  } {
    const viewport = this.scroll.snapshot();
    const statistics = this.#pipeline.buffer.statistics();
    const start = viewport.position.y;
    const end = start + viewport.viewport.height;
    return Object.freeze({
      rows: Object.freeze(
        this.#records
          .slice(start, end)
          .map((record, offset) =>
            LogRow(record, start + offset, start + offset === this.#selected),
          ),
      ),
      selected: this.#records[this.#selected],
      live: !statistics.paused,
      theme: this.#theme,
      dropped: statistics.dropped,
      sampled: statistics.sampled,
      rateLimited: statistics.rateLimited,
      total: this.#records.length,
    });
  }

  export(format: "jsonl" | "text" = "jsonl"): string {
    return this.#pipeline.export(this.#records, format);
  }

  async copy(
    clipboard: EditorClipboardAdapter,
    format: "jsonl" | "text" = "text",
  ): Promise<string> {
    const value = this.export(format);
    await clipboard.write(value);
    return value;
  }

  dispose(): void {
    this.#unsubscribeBuffer();
    this.#observers.clear();
    if (this.#ownsQueryEditor) this.queryEditor.dispose();
  }

  #notify(): void {
    this.#revision += 1;
    for (const observer of this.#observers) observer();
  }
}

Members

MemberTypeRequiredDescriptionRelated types
#pipelineLogPipelineYesThe #pipeline member uses the LogPipeline contract.LogPipeline
#ownsQueryEditorbooleanYesThe #ownsQueryEditor member uses the boolean contract.
scrollScrollAreaStateYesThe scroll member uses the ScrollAreaState contract.ScrollAreaState
queryEditorEditorSessionYesThe queryEditor member uses the EditorSession contract.EditorSession
#recordsreadonly LogRecord[]YesThe #records member uses the readonly LogRecord[] contract.LogRecord
#selectednumberYesThe #selected member uses the number contract.
#querystringYesThe #query member uses the string contract.
#themeLogThemeYesThe #theme member uses the LogTheme contract.LogTheme
#observersSet<() => void>YesThe #observers member uses the Set<() => void> contract.
#unsubscribeBuffer() => voidYesThe #unsubscribeBuffer member uses the () => void contract.
#revisionnumberYesThe #revision member uses the number contract.
__constructoranyYesThe __constructor member uses the any contract.
refresh() => voidYesThe refresh member uses the () => void contract.
setQuery(source: string) => readonly string[]YesThe setQuery member uses the (source: string) => readonly string[] contract.
select(index: number) => voidYesThe select member uses the (index: number) => void contract.
move(delta: number) => voidYesThe move member uses the (delta: number) => void contract.
pause() => voidYesThe pause member uses the () => void contract.
resume() => voidYesThe resume member uses the () => void contract.
setTheme(variant: LogThemeVariant) => voidYesThe setTheme member uses the (variant: LogThemeVariant) => void contract.LogThemeVariant
subscribe(observer: () => void) => () => voidYesThe subscribe member uses the (observer: () => void) => () => void contract.
revision() => numberYesThe revision member uses the () => number contract.
snapshot() => &#123; readonly rows: readonly LogRowView[]; readonly selected?: LogRecord; readonly live: boolean; readonly theme: LogTheme; readonly dropped: number; readonly sampled: number; readonly rateLimited: number; readonly total: number; &#125;YesThe snapshot member uses the () => &#123; readonly rows: readonly LogRowView[]; readonly selected?: LogRecord; readonly live: boolean; readonly theme: LogTheme; readonly dropped: number; readonly sampled: number; readonly rateLimited: number; readonly total: number; &#125; contract.LogRecord, LogRowView, LogTheme
export(format?: "jsonl" | "text") => stringYesThe export member uses the (format?: "jsonl" | "text") => string contract.
copy(clipboard: EditorClipboardAdapter, format?: "jsonl" | "text") => Promise<string>YesThe copy member uses the (clipboard: EditorClipboardAdapter, format?: "jsonl" | "text") => Promise<string> contract.
dispose() => voidYesThe dispose member uses the () => void contract.
#notify() => voidYesThe #notify member uses the () => void contract.

Parameters

This declaration has no public members.

Returns

This declaration does not return a value.

Throws

No thrown errors are documented for this declaration.

Source

View the secondary source reference

Package

@mwillbanks/tuil-log-viewer

On this page