All files / src/views status-modal.ts

100% Statements 63/63
100% Branches 22/22
100% Functions 10/10
100% Lines 62/62

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132                    13x 13x 13x       13x 13x 13x 13x 13x       13x 13x 12x 1x 1x 1x   11x 11x 11x 11x   1x 1x         11x 11x   11x 11x 11x       11x 11x   11x   11x 11x   11x 10x     11x   11x         11x       10x 10x 9x 1x   9x 1x               11x   2x 2x   2x 2x         11x 11x 11x               43x 43x 43x       11x 11x 11x 11x 1x 1x   10x 10x         10x 10x    
import { App, Modal, Notice } from "obsidian";
import type LilbeePlugin from "../main";
import type { ModelShowResponse, StatusResponse } from "../types";
import { MESSAGES } from "../locales/en";
import { bindEscapeToClose, noticeForResultError } from "../utils";
 
export class StatusModal extends Modal {
    private plugin: LilbeePlugin;
 
    constructor(app: App, plugin: LilbeePlugin) {
        super(app);
        this.plugin = plugin;
        bindEscapeToClose(this);
    }
 
    onOpen(): void {
        const { contentEl } = this;
        contentEl.empty();
        contentEl.addClass("lilbee-status-modal");
        contentEl.createEl("h2", { text: MESSAGES.TITLE_STATUS });
        void this.loadStatus(contentEl);
    }
 
    private async loadStatus(contentEl: HTMLElement): Promise<void> {
        try {
            const statusResult = await this.plugin.api.status();
            if (statusResult.isErr()) {
                new Notice(noticeForResultError(statusResult.error, MESSAGES.ERROR_COULD_NOT_CONNECT));
                this.close();
                return;
            }
            const status = statusResult.value;
            this.renderDocuments(contentEl, status);
            await this.renderModels(contentEl, status);
            this.renderWiki(contentEl, status);
        } catch {
            new Notice(MESSAGES.ERROR_COULD_NOT_CONNECT);
            this.close();
        }
    }
 
    private renderDocuments(container: HTMLElement, status: StatusResponse): void {
        const section = container.createEl("details", { attr: { open: "" } });
        section.createEl("summary", { text: MESSAGES.LABEL_STATUS_DOCUMENTS });
 
        const table = section.createEl("table", { cls: "lilbee-status-table" });
        this.addRow(table, MESSAGES.LABEL_STATUS_DOCUMENTS, String(status.sources.length));
        this.addRow(table, MESSAGES.LABEL_STATUS_CHUNKS, String(status.total_chunks));
    }
 
    private async renderModels(container: HTMLElement, status: StatusResponse): Promise<void> {
        const section = container.createEl("details", { attr: { open: "" } });
        section.createEl("summary", { text: MESSAGES.LABEL_MODELS });
 
        const table = section.createEl("table", { cls: "lilbee-status-table" });
 
        const chatModel = status.config.chat_model;
        this.addModelRow(table, MESSAGES.LABEL_STATUS_CHAT_MODEL, chatModel);
 
        if (chatModel) {
            await this.renderModelDetails(table, chatModel);
        }
 
        const ocrValue = status.config.enable_ocr;
        const ocrLabel =
            ocrValue === "true"
                ? MESSAGES.STATUS_VALUE_OCR_ON
                : ocrValue === "false"
                  ? MESSAGES.STATUS_VALUE_OCR_OFF
                  : MESSAGES.STATUS_VALUE_OCR_AUTO;
        this.addRow(table, MESSAGES.LABEL_STATUS_OCR, ocrLabel);
    }
 
    private async renderModelDetails(table: HTMLTableElement, model: string): Promise<void> {
        try {
            const info: ModelShowResponse = await this.plugin.api.showModel(model);
            if (info.architecture) {
                this.addRow(table, MESSAGES.LABEL_STATUS_ARCHITECTURE, info.architecture);
            }
            if (info.context_length) {
                this.addRow(table, MESSAGES.LABEL_STATUS_CONTEXT_LENGTH, info.context_length);
            }
        } catch {
            // Model details not available — not critical
        }
    }
 
    private renderWiki(container: HTMLElement, status: StatusResponse): void {
        if (!status.wiki) return;
 
        const section = container.createEl("details", { attr: { open: "" } });
        section.createEl("summary", { text: MESSAGES.LABEL_STATUS_WIKI });
 
        const table = section.createEl("table", { cls: "lilbee-status-table" });
        this.addRow(
            table,
            MESSAGES.LABEL_STATUS_WIKI,
            status.wiki.enabled ? MESSAGES.LABEL_STATUS_ENABLED : MESSAGES.LABEL_STATUS_DISABLED,
        );
        this.addRow(table, MESSAGES.LABEL_STATUS_WIKI_PAGES, String(status.wiki.page_count));
        this.addRow(table, MESSAGES.LABEL_STATUS_WIKI_DRAFTS, String(status.wiki.draft_count));
        this.addRow(
            table,
            MESSAGES.LABEL_STATUS_WIKI_LAST_LINT,
            status.wiki.last_lint ?? MESSAGES.LABEL_STATUS_NOT_AVAILABLE,
        );
    }
 
    private addRow(table: HTMLTableElement, label: string, value: string): void {
        const row = table.createEl("tr");
        row.createEl("td", { text: label, cls: "lilbee-status-label" });
        row.createEl("td", { text: value, cls: "lilbee-status-value" });
    }
 
    private addModelRow(table: HTMLTableElement, label: string, model: string): void {
        const row = table.createEl("tr");
        row.createEl("td", { text: label, cls: "lilbee-status-label" });
        const cell = row.createEl("td", { cls: "lilbee-status-value" });
        if (!model) {
            cell.setText(MESSAGES.LABEL_STATUS_NONE);
            return;
        }
        cell.setText(shortModelLabel(model));
        cell.setAttribute("title", model);
    }
}
 
function shortModelLabel(model: string): string {
    const slash = model.lastIndexOf("/");
    return slash === -1 ? model : model.slice(slash + 1);
}