import { StatusBarAlignment, ThemeColor, window, workspace } from "vscode"; import { createMachine, interpret } from "@xstate/fsm"; import { agent } from "./agent"; import { notifications } from "./notifications"; const label = "Tabby"; const iconLoading = "$(loading~spin)"; const iconReady = "$(check)"; const iconDisconnected = "$(plug)"; const iconUnauthorized = "$(key)"; const iconDisabled = "$(x)"; const colorNormal = new ThemeColor("statusBar.foreground"); const colorWarning = new ThemeColor("statusBarItem.warningForeground"); const backgroundColorNormal = new ThemeColor("statusBar.background"); const backgroundColorWarning = new ThemeColor("statusBarItem.warningBackground"); const item = window.createStatusBarItem(StatusBarAlignment.Right); const fsm = createMachine({ id: "statusBarItem", initial: "loading", states: { loading: { on: { ready: "ready", disconnected: "disconnected", unauthorized: "unauthorized", disabled: "disabled" }, entry: () => toLoading(), }, ready: { on: { disconnected: "disconnected", unauthorized: "unauthorized", disabled: "disabled" }, entry: () => toReady(), }, disconnected: { on: { ready: "ready", unauthorized: "unauthorized", disabled: "disabled" }, entry: () => toDisconnected(), }, unauthorized: { on: { ready: "ready", disconnected: "disconnected", disabled: "disabled", authStart: "unauthorizedAndAuthInProgress", }, entry: () => toUnauthorized(), }, unauthorizedAndAuthInProgress: { // if auth succeeds, we will get `ready` before `authEnd` event on: { ready: "ready", disconnected: "disconnected", disabled: "disabled", authEnd: "unauthorized" }, entry: () => toUnauthorizedAndAuthInProgress(), }, disabled: { on: { loading: "loading", ready: "ready", disconnected: "disconnected", unauthorized: "unauthorized" }, entry: () => toDisabled(), }, }, }); const fsmService = interpret(fsm); function toLoading() { item.color = colorNormal; item.backgroundColor = backgroundColorNormal; item.text = `${iconLoading} ${label}`; item.tooltip = "Tabby is initializing."; item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["loading"] }; } function toReady() { item.color = colorNormal; item.backgroundColor = backgroundColorNormal; item.text = `${iconReady} ${label}`; item.tooltip = "Tabby is providing code suggestions for you."; item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["ready"] }; } function toDisconnected() { item.color = colorWarning; item.backgroundColor = backgroundColorWarning; item.text = `${iconDisconnected} ${label}`; item.tooltip = "Cannot connect to Tabby Server. Click to open settings."; item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["disconnected"] }; } function toUnauthorized() { item.color = colorWarning; item.backgroundColor = backgroundColorWarning; item.text = `${iconUnauthorized} ${label}`; item.tooltip = "Tabby Server requires authorization. Click to continue."; item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["unauthorized"] }; } function toUnauthorizedAndAuthInProgress() { item.color = colorWarning; item.backgroundColor = backgroundColorWarning; item.text = `${iconUnauthorized} ${label}`; item.tooltip = "Waiting for authorization."; item.command = undefined; } function toDisabled() { item.color = colorWarning; item.backgroundColor = backgroundColorWarning; item.text = `${iconDisabled} ${label}`; item.tooltip = "Tabby is disabled."; item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["disabled"] }; } function updateStatusBarItem() { const enabled = workspace.getConfiguration("tabby").get("codeCompletion", true); if (!enabled) { fsmService.send("disabled"); } else { const status = agent().getStatus(); switch (status) { case "notInitialized": fsmService.send("loading"); break; case "ready": case "disconnected": case "unauthorized": fsmService.send(status); break; } } } export const tabbyStatusBarItem = () => { fsmService.start(); updateStatusBarItem(); workspace.onDidChangeConfiguration((event) => { if (event.affectsConfiguration("tabby")) { updateStatusBarItem(); } }); agent().on("statusChanged", updateStatusBarItem); agent().on("authRequired", () => { notifications.showInformationStartAuth({ onAuthStart: () => { fsmService.send("authStart"); }, onAuthEnd: () => { fsmService.send("authEnd"); }, }); }); item.show(); return item; };