diff options
| author | Ellie Huxtable <ellie@atuin.sh> | 2024-07-30 16:54:10 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-30 16:54:10 +0100 |
| commit | 808138de633e410c1d3867d4fb7cb74967647605 (patch) | |
| tree | f180b7066b91d8d8d8006219a118439be1621d74 /ui/src/state | |
| parent | chore(deps): bump debian (#2320) (diff) | |
| download | atuin-808138de633e410c1d3867d4fb7cb74967647605.zip | |
chore: remove ui directory (#2329)
This is still in development, but rather than clutter the commit history
and issues with an unreleased project I've split the UI into its own
repo.
Once ready for release, I'll either merge the ui code back in, or just
make the repo public.
Diffstat (limited to 'ui/src/state')
| -rw-r--r-- | ui/src/state/client.ts | 33 | ||||
| -rw-r--r-- | ui/src/state/models.ts | 177 | ||||
| -rw-r--r-- | ui/src/state/runbooks/runbook.ts | 124 | ||||
| -rw-r--r-- | ui/src/state/store.ts | 289 |
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)), - ), - }), -); |
