aboutsummaryrefslogtreecommitdiffstats
path: root/ui/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/state')
-rw-r--r--ui/src/state/client.ts33
-rw-r--r--ui/src/state/models.ts177
-rw-r--r--ui/src/state/runbooks/runbook.ts124
-rw-r--r--ui/src/state/store.ts289
4 files changed, 0 insertions, 623 deletions
diff --git a/ui/src/state/client.ts b/ui/src/state/client.ts
deleted file mode 100644
index c46fc4e6..00000000
--- a/ui/src/state/client.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// At some point, I'd like to replace some of the Atuin calls
-// with separate state handling here
-
-import { invoke } from "@tauri-apps/api/core";
-import { Settings } from "@/state/models";
-
-export async function sessionToken(): Promise<String> {
- return await invoke("session");
-}
-
-export async function settings(): Promise<Settings> {
- return await invoke("config");
-}
-
-export async function login(
- username: string,
- password: string,
- key: string,
-): Promise<string> {
- return await invoke("login", { username, password, key });
-}
-
-export async function logout(): Promise<string> {
- return await invoke("logout");
-}
-
-export async function register(
- username: string,
- email: string,
- password: string,
-): Promise<string> {
- return await invoke("register", { username, email, password });
-}
diff --git a/ui/src/state/models.ts b/ui/src/state/models.ts
deleted file mode 100644
index 891f7a55..00000000
--- a/ui/src/state/models.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import { invoke } from "@tauri-apps/api/core";
-import Database from "@tauri-apps/plugin-sql";
-
-export class User {
- username: string | null;
-
- constructor(username: string) {
- this.username = username;
- }
-
- isLoggedIn(): boolean {
- return this.username !== "" && this.username !== null;
- }
-}
-
-export const DefaultUser: User = new User("");
-
-export interface HomeInfo {
- historyCount: number;
- recordCount: number;
- lastSyncTime: Date | null;
- recentCommands: ShellHistory[];
- topCommands: ShellHistory[];
-}
-
-export const DefaultHomeInfo: HomeInfo = {
- historyCount: 0,
- recordCount: 0,
- lastSyncTime: new Date(),
- recentCommands: [],
- topCommands: [],
-};
-
-export class ShellHistory {
- id: string;
- timestamp: number;
- command: string;
- user: string;
- host: string;
- cwd: string;
- duration: number;
- exit: number;
-
- /// Pass a row straight from the database to this
- constructor(
- id: string,
- timestamp: number,
- command: string,
- hostuser: string,
- cwd: string,
- duration: number,
- exit: number,
- ) {
- this.id = id;
- this.timestamp = timestamp;
- this.command = command;
-
- let [host, user] = hostuser.split(":");
- this.user = user;
- this.host = host;
-
- this.cwd = cwd;
- this.duration = duration;
- this.exit = exit;
- }
-}
-
-export interface Alias {
- name: string;
- value: string;
-}
-
-export interface Var {
- name: string;
- value: string;
- export: boolean;
-}
-
-export interface InspectHistory {
- other: ShellHistory[];
-}
-
-// Not yet complete. Not all types are defined here.
-// Gonna hold off until the settings refactoring.
-export interface Settings {
- auto_sync: boolean;
- update_check: boolean;
- sync_address: string;
- sync_frequency: string;
- db_path: string;
- record_store_path: string;
- key_path: string;
- session_path: string;
- shell_up_key_binding: boolean;
- inline_height: number;
- invert: boolean;
- show_preview: boolean;
- max_preview_height: number;
- show_help: boolean;
- show_tabs: boolean;
- word_chars: string;
- scroll_context_lines: number;
- history_format: string;
- prefers_reduced_motion: boolean;
- store_failed: boolean;
- secrets_filter: boolean;
- workspaces: boolean;
- ctrl_n_shortcuts: boolean;
- network_connect_timeout: number;
- network_timeout: number;
- local_timeout: number;
- enter_accept: boolean;
- smart_sort: boolean;
- sync: Sync;
-}
-
-interface Sync {
- records: boolean;
-}
-
-// Define other interfaces (Dialect, Timezone, Style, SearchMode, FilterMode, ExitMode, KeymapMode, CursorStyle, WordJumpMode, RegexSet, Stats) accordingly.
-
-export async function inspectCommandHistory(
- h: ShellHistory,
-): Promise<InspectHistory> {
- const settings: Settings = await invoke("cli_settings");
- const db = await Database.load("sqlite:" + settings.db_path);
-
- let other: any[] = await db.select(
- "select * from history where command=?1 order by timestamp desc",
- [h.command],
- );
- console.log(other);
-
- return {
- other: other.map(
- (i) =>
- new ShellHistory(
- i.id,
- i.timestamp,
- i.command,
- i.hostname,
- i.cwd,
- i.duration,
- i.exit,
- ),
- ),
- };
-}
-
-export async function inspectDirectoryHistory(
- h: ShellHistory,
-): Promise<InspectHistory> {
- const settings: Settings = await invoke("cli_settings");
- const db = await Database.load("sqlite:" + settings.db_path);
-
- let other: any[] = await db.select(
- "select * from history where cwd=?1 order by timestamp desc",
- [h.cwd],
- );
- console.log(other);
-
- return {
- other: other.map(
- (i) =>
- new ShellHistory(
- i.id,
- i.timestamp,
- i.command,
- i.hostname,
- i.cwd,
- i.duration,
- i.exit,
- ),
- ),
- };
-}
diff --git a/ui/src/state/runbooks/runbook.ts b/ui/src/state/runbooks/runbook.ts
deleted file mode 100644
index 8555f4ea..00000000
--- a/ui/src/state/runbooks/runbook.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import Database from "@tauri-apps/plugin-sql";
-import { uuidv7 } from "uuidv7";
-
-export default class Runbook {
- id: string;
- created: Date;
- updated: Date;
-
- private _name: string;
- private _content: string;
-
- set name(value: string) {
- this.updated = new Date();
- this._name = value;
- }
-
- set content(value: string) {
- this.updated = new Date();
- this._content = value;
- }
-
- get content() {
- return this._content;
- }
-
- get name() {
- return this._name;
- }
-
- constructor(
- id: string,
- name: string,
- content: string,
- created: Date,
- updated: Date,
- ) {
- this.id = id;
- this._name = name;
- this._content = content;
- this.created = created;
- this.updated = updated;
- }
-
- /// Create a new Runbook, and automatically generate an ID.
- public static async create(): Promise<Runbook> {
- let now = new Date();
-
- // Initialize with the same value for created/updated, to avoid needing null.
- let runbook = new Runbook(uuidv7(), "", "", now, now);
- await runbook.save();
-
- return runbook;
- }
-
- public static async load(id: String): Promise<Runbook | null> {
- const db = await Database.load("sqlite:runbooks.db");
-
- let res = await db.select<any[]>("select * from runbooks where id = $1", [
- id,
- ]);
-
- if (res.length == 0) return null;
-
- let rb = res[0];
-
- return new Runbook(
- rb.id,
- rb.name,
- rb.content,
- new Date(rb.created / 1000000),
- new Date(rb.updated / 1000000),
- );
- }
-
- static async all(): Promise<Runbook[]> {
- const db = await Database.load("sqlite:runbooks.db");
-
- let res = await db.select<any[]>(
- "select * from runbooks order by updated desc",
- );
-
- return res.map((i) => {
- return new Runbook(
- i.id,
- i.name,
- i.content,
- new Date(i.created / 1000000),
- new Date(i.updated / 1000000),
- );
- });
- }
-
- public async save() {
- const db = await Database.load("sqlite:runbooks.db");
-
- await db.execute(
- `insert into runbooks(id, name, content, created, updated)
- values ($1, $2, $3, $4, $5)
-
- on conflict(id) do update
- set
- name=$2,
- content=$3,
- updated=$5`,
-
- // getTime returns a timestamp as unix milliseconds
- // we won't need or use the resolution here, but elsewhere Atuin stores timestamps in sqlite as nanoseconds since epoch
- // let's do that across the board to avoid mistakes
- [
- this.id,
- this._name,
- this._content,
- this.created.getTime() * 1000000,
- this.updated.getTime() * 1000000,
- ],
- );
- }
-
- public static async delete(id: string) {
- const db = await Database.load("sqlite:runbooks.db");
-
- await db.execute("delete from runbooks where id=$1", [id]);
- }
-}
diff --git a/ui/src/state/store.ts b/ui/src/state/store.ts
deleted file mode 100644
index 39ee0096..00000000
--- a/ui/src/state/store.ts
+++ /dev/null
@@ -1,289 +0,0 @@
-import { create } from "zustand";
-import { persist } from "zustand/middleware";
-
-import { parseISO } from "date-fns";
-
-import { fetch } from "@tauri-apps/plugin-http";
-
-import {
- User,
- DefaultUser,
- HomeInfo,
- DefaultHomeInfo,
- Alias,
- ShellHistory,
- Var,
-} from "./models";
-
-import { invoke } from "@tauri-apps/api/core";
-import { sessionToken, settings } from "./client";
-import { getWeekInfo } from "@/lib/utils";
-import Runbook from "./runbooks/runbook";
-import { Terminal } from "@xterm/xterm";
-import { FitAddon } from "@xterm/addon-fit";
-import { WebglAddon } from "@xterm/addon-webgl";
-
-export class TerminalData {
- terminal: Terminal;
- fitAddon: FitAddon;
-
- constructor(terminal: Terminal, fit: FitAddon) {
- this.terminal = terminal;
- this.fitAddon = fit;
- }
-}
-
-// I'll probs want to slice this up at some point, but for now a
-// big blobby lump of state is fine.
-// Totally just hoping that structure will be emergent in the future.
-export interface AtuinState {
- user: User;
- homeInfo: HomeInfo;
- aliases: Alias[];
- vars: Var[];
- shellHistory: ShellHistory[];
- calendar: any[];
- weekStart: number;
- runbooks: Runbook[];
- currentRunbook: string | null;
-
- refreshHomeInfo: () => void;
- refreshCalendar: () => void;
- refreshAliases: () => void;
- refreshVars: () => void;
- refreshUser: () => void;
- refreshRunbooks: () => void;
- refreshShellHistory: (query?: string) => void;
- historyNextPage: (query?: string) => void;
-
- setCurrentRunbook: (id: String) => void;
- setPtyTerm: (pty: string, terminal: any) => void;
- newPtyTerm: (pty: string) => TerminalData;
- cleanupPtyTerm: (pty: string) => void;
-
- terminals: { [pty: string]: TerminalData };
-
- // Store ephemeral state for runbooks, that is not persisted to the database
- runbookInfo: { [runbook: string]: { ptys: number } };
- incRunbookPty: (runbook: string) => void;
- decRunbookPty: (runbook: string) => void;
-}
-
-let state = (set: any, get: any): AtuinState => ({
- user: DefaultUser,
- homeInfo: DefaultHomeInfo,
- aliases: [],
- vars: [],
- shellHistory: [],
- calendar: [],
- runbooks: [],
- currentRunbook: "",
- terminals: {},
- runbookInfo: {},
-
- weekStart: getWeekInfo().firstDay,
-
- refreshAliases: () => {
- invoke("aliases").then((aliases: any) => {
- set({ aliases: aliases });
- });
- },
-
- refreshCalendar: () => {
- invoke("history_calendar").then((calendar: any) => {
- set({ calendar: calendar });
- });
- },
-
- refreshVars: () => {
- invoke("vars").then((vars: any) => {
- set({ vars: vars });
- });
- },
-
- refreshRunbooks: async () => {
- let runbooks = await Runbook.all();
- set({ runbooks });
- },
-
- refreshShellHistory: (query?: string) => {
- if (query) {
- invoke("search", { query: query })
- .then((res: any) => {
- set({ shellHistory: res });
- })
- .catch((e) => {
- console.log(e);
- });
- } else {
- invoke("list").then((res: any) => {
- set({ shellHistory: res });
- });
- }
- },
-
- refreshHomeInfo: () => {
- invoke("home_info")
- .then((res: any) => {
- console.log(res);
- set({
- homeInfo: {
- historyCount: res.history_count,
- recordCount: res.record_count,
- lastSyncTime: (res.last_sync && parseISO(res.last_sync)) || null,
- recentCommands: res.recent_commands,
- topCommands: res.top_commands.map((top: any) => ({
- command: top[0],
- count: top[1],
- })),
- },
- });
- })
- .catch((e) => {
- console.log(e);
- });
- },
-
- refreshUser: async () => {
- let config = await settings();
- let session;
-
- try {
- session = await sessionToken();
- } catch (e) {
- console.log("Not logged in, so not refreshing user");
- set({ user: DefaultUser });
- return;
- }
- let url = config.sync_address + "/api/v0/me";
-
- let res = await fetch(url, {
- headers: {
- Authorization: `Token ${session}`,
- },
- });
- let me = await res.json();
-
- set({ user: new User(me.username) });
- },
-
- historyNextPage: (query?: string) => {
- let history = get().shellHistory;
- let offset = history.length - 1;
-
- if (query) {
- invoke("search", { query: query, offset: offset })
- .then((res: any) => {
- set({ shellHistory: [...history, ...res] });
- })
- .catch((e) => {
- console.log(e);
- });
- } else {
- invoke("list", { offset: offset }).then((res: any) => {
- set({ shellHistory: [...history, ...res] });
- });
- }
- },
-
- setCurrentRunbook: (id: String) => {
- set({ currentRunbook: id });
- },
-
- setPtyTerm: (pty: string, terminal: TerminalData) => {
- set({
- terminals: { ...get().terminals, [pty]: terminal },
- });
- },
-
- cleanupPtyTerm: (pty: string) => {
- set((state: AtuinState) => {
- const terminals = Object.keys(state.terminals).reduce(
- (terms: { [pty: string]: TerminalData }, key) => {
- if (key !== pty) {
- terms[key] = state.terminals[key];
- }
- return terms;
- },
- {},
- );
-
- return { terminals };
- });
- },
-
- newPtyTerm: (pty: string) => {
- let terminal = new Terminal();
-
- // TODO: fallback to canvas, also some sort of setting to allow disabling webgl usage
- // probs fine for now though, it's widely supported. maybe issues on linux.
- terminal.loadAddon(new WebglAddon());
-
- let fitAddon = new FitAddon();
- terminal.loadAddon(fitAddon);
-
- const onResize = (size: { cols: number; rows: number }) => {
- invoke("pty_resize", {
- pid: pty,
- cols: size.cols,
- rows: size.rows,
- });
- };
-
- terminal.onResize(onResize);
-
- let td = new TerminalData(terminal, fitAddon);
-
- set({
- terminals: { ...get().terminals, [pty]: td },
- });
-
- return td;
- },
-
- incRunbookPty: (runbook: string) => {
- set((state: AtuinState) => {
- let oldVal = state.runbookInfo[runbook] || { ptys: 0 };
- let newVal = { ptys: oldVal.ptys + 1 };
- console.log(newVal);
-
- return {
- runbookInfo: {
- ...state.runbookInfo,
- [runbook]: newVal,
- },
- };
- });
- },
-
- decRunbookPty: (runbook: string) => {
- set((state: AtuinState) => {
- let newVal = state.runbookInfo[runbook];
- if (!newVal) {
- return;
- }
-
- newVal.ptys--;
-
- return {
- runbookInfo: {
- ...state.runbookInfo,
- [runbook]: newVal,
- },
- };
- });
- },
-});
-
-export const useStore = create<AtuinState>()(
- persist(state, {
- name: "atuin-storage",
-
- // don't serialize the terminals map
- // it won't work as JSON. too cyclical
- partialize: (state) =>
- Object.fromEntries(
- Object.entries(state).filter(([key]) => !["terminals"].includes(key)),
- ),
- }),
-);