From 76f2a43bd092c0cfad83a12b4beb5984c793705a Mon Sep 17 00:00:00 2001 From: Max Koon <22125083+k2on@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:05:23 -0500 Subject: [PATCH] refactor: better shortcut hook --- packages/react-native-opentui/index.tsx | 5 + packages/shared/src/db/schema/public.ts | 23 +++ packages/shared/src/mutators.ts | 13 +- packages/shared/src/queries.ts | 16 ++ packages/shared/src/zero-schema.gen.ts | 174 +++++++++++++++++++++ packages/ui/components/Button.tsx | 19 ++- packages/ui/components/Dialog.tsx | 7 - packages/ui/components/Table.tsx | 44 ++---- packages/ui/lib/shortcuts/Debug.tsx | 30 ++++ packages/ui/lib/shortcuts/Provider.tsx | 12 ++ packages/ui/lib/shortcuts/Provider.web.tsx | 13 ++ packages/ui/lib/shortcuts/hooks.ts | 14 ++ packages/ui/lib/shortcuts/index.ts | 3 + packages/ui/lib/shortcuts/store.ts | 40 +++++ packages/ui/src/budget.tsx | 72 +++++++++ packages/ui/src/index.tsx | 33 ++-- packages/ui/src/settings.tsx | 45 +++--- packages/ui/src/settings/accounts.tsx | 1 - packages/ui/src/useKeyboard.ts | 8 - packages/ui/src/useKeyboard.web.ts | 47 ------ packages/ui/tsconfig.json | 5 +- 21 files changed, 481 insertions(+), 143 deletions(-) create mode 100644 packages/ui/lib/shortcuts/Debug.tsx create mode 100644 packages/ui/lib/shortcuts/Provider.tsx create mode 100644 packages/ui/lib/shortcuts/Provider.web.tsx create mode 100644 packages/ui/lib/shortcuts/hooks.ts create mode 100644 packages/ui/lib/shortcuts/index.ts create mode 100644 packages/ui/lib/shortcuts/store.ts create mode 100644 packages/ui/src/budget.tsx delete mode 100644 packages/ui/src/useKeyboard.ts delete mode 100644 packages/ui/src/useKeyboard.web.ts diff --git a/packages/react-native-opentui/index.tsx b/packages/react-native-opentui/index.tsx index d8812d2..9ba7d33 100644 --- a/packages/react-native-opentui/index.tsx +++ b/packages/react-native-opentui/index.tsx @@ -74,6 +74,11 @@ export function View({ children, style }: ViewProps) { justifyContent: attr(style, "justifyContent", "string"), flexShrink: attr(style, "flexShrink", "number"), flexDirection: attr(style, "flexDirection", "string"), + top: attr(style, "top", "number"), + zIndex: attr(style, "zIndex", "number"), + left: attr(style, "left", "number"), + right: attr(style, "right", "number"), + bottom: attr(style, "bottom", "number"), flexGrow: attr(style, "flex", "number") || attr(style, "flexGrow", "number"), }; diff --git a/packages/shared/src/db/schema/public.ts b/packages/shared/src/db/schema/public.ts index a327bec..87ae188 100644 --- a/packages/shared/src/db/schema/public.ts +++ b/packages/shared/src/db/schema/public.ts @@ -6,6 +6,7 @@ import { timestamp, pgEnum, uniqueIndex, + numeric, } from "drizzle-orm/pg-core"; export const users = pgTable( @@ -65,3 +66,25 @@ export const plaidAccessTokens = pgTable("plaidAccessToken", { token: text("token").notNull(), createdAt: timestamp("created_at").notNull().defaultNow(), }); + +export const budget = pgTable("budget", { + id: text("id").primaryKey(), + orgId: text("org_id").notNull(), + label: text("label").notNull(), + createdBy: text("created_by").notNull(), + createdAt: timestamp("created_at").notNull().defaultNow(), + updatedAt: timestamp("updated_at").notNull().defaultNow(), +}); + +export const category = pgTable("category", { + id: text("id").primaryKey(), + budgetId: text("budget_id").notNull(), + amount: decimal("amount").notNull(), + every: text("every", { enum: ["year", "month", "week"] }).notNull(), + order: numeric("order").notNull(), + label: text("label").notNull(), + color: text("color").notNull(), + createdBy: text("created_by").notNull(), + createdAt: timestamp("created_at").notNull().defaultNow(), + updatedAt: timestamp("updated_at").notNull().defaultNow(), +}); diff --git a/packages/shared/src/mutators.ts b/packages/shared/src/mutators.ts index d87ef25..8f3cff6 100644 --- a/packages/shared/src/mutators.ts +++ b/packages/shared/src/mutators.ts @@ -1,5 +1,5 @@ import type { Transaction } from "@rocicorp/zero"; -import type { AuthData } from "./auth"; +import { authDataSchema, type AuthData } from "./auth"; import { type Schema } from "./zero-schema.gen"; import { isLoggedIn } from "./zql"; @@ -39,6 +39,17 @@ export function createMutators(authData: AuthData | null) { } }, }, + budget: { + async create(tx: Tx, { id }: { id: string }) { + isLoggedIn(authData); + await tx.mutate.budget.insert({ + id, + orgId: authData.user.id, + label: "New Budget", + createdBy: authData.user.id, + }); + }, + }, } as const; } diff --git a/packages/shared/src/queries.ts b/packages/shared/src/queries.ts index d095806..99e2b2c 100644 --- a/packages/shared/src/queries.ts +++ b/packages/shared/src/queries.ts @@ -60,4 +60,20 @@ export const queries = { .orderBy("createdAt", "desc"); }, ), + getBudgets: syncedQueryWithContext( + "getBudgets", + z.tuple([]), + (authData: AuthData | null) => { + isLoggedIn(authData); + return builder.budget.limit(10); + }, + ), + getBudgetCategories: syncedQueryWithContext( + "getBudgetCategories", + z.tuple([]), + (authData: AuthData | null) => { + isLoggedIn(authData); + return builder.category.orderBy("order", "desc"); + }, + ), }; diff --git a/packages/shared/src/zero-schema.gen.ts b/packages/shared/src/zero-schema.gen.ts index 7591763..d98eaad 100644 --- a/packages/shared/src/zero-schema.gen.ts +++ b/packages/shared/src/zero-schema.gen.ts @@ -112,6 +112,170 @@ export const schema = { }, primaryKey: ["id"], }, + budget: { + name: "budget", + columns: { + id: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "budget", + "id" + >, + }, + orgId: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "budget", + "orgId" + >, + serverName: "org_id", + }, + label: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "budget", + "label" + >, + }, + createdBy: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "budget", + "createdBy" + >, + serverName: "created_by", + }, + createdAt: { + type: "number", + optional: true, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "budget", + "createdAt" + >, + serverName: "created_at", + }, + updatedAt: { + type: "number", + optional: true, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "budget", + "updatedAt" + >, + serverName: "updated_at", + }, + }, + primaryKey: ["id"], + }, + category: { + name: "category", + columns: { + id: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "id" + >, + }, + budgetId: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "budgetId" + >, + serverName: "budget_id", + }, + amount: { + type: "number", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "amount" + >, + }, + every: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "every" + >, + }, + order: { + type: "number", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "order" + >, + }, + label: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "label" + >, + }, + color: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "color" + >, + }, + createdBy: { + type: "string", + optional: false, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "createdBy" + >, + serverName: "created_by", + }, + createdAt: { + type: "number", + optional: true, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "createdAt" + >, + serverName: "created_at", + }, + updatedAt: { + type: "number", + optional: true, + customType: null as unknown as ZeroCustomType< + ZeroSchema, + "category", + "updatedAt" + >, + serverName: "updated_at", + }, + }, + primaryKey: ["id"], + }, plaidAccessTokens: { name: "plaidAccessTokens", columns: { @@ -433,6 +597,16 @@ export type Schema = typeof schema; * This type is auto-generated from your Drizzle schema definition. */ export type Balance = Row; +/** + * Represents a row from the "budget" table. + * This type is auto-generated from your Drizzle schema definition. + */ +export type Budget = Row; +/** + * Represents a row from the "category" table. + * This type is auto-generated from your Drizzle schema definition. + */ +export type Category = Row; /** * Represents a row from the "plaidAccessTokens" table. * This type is auto-generated from your Drizzle schema definition. diff --git a/packages/ui/components/Button.tsx b/packages/ui/components/Button.tsx index 269eb3c..8196ff5 100644 --- a/packages/ui/components/Button.tsx +++ b/packages/ui/components/Button.tsx @@ -1,7 +1,8 @@ -import { useKeyboard } from "../src/useKeyboard"; -import type { ReactNode } from "react"; +import { useEffect, type ReactNode } from "react"; import { Text, Pressable } from "react-native"; +type WithRequired = T & { [P in K]-?: T[P] }; + export interface ButtonProps { children: ReactNode; onPress?: () => void; @@ -21,10 +22,16 @@ const STYLES: Record< export function Button({ children, variant, onPress, shortcut }: ButtonProps) { const { backgroundColor, color } = STYLES[variant || "default"]; - useKeyboard((key) => { - if (!shortcut || !onPress) return; - if (key.name == shortcut) onPress(); - }); + // if (shortcut) { + // useKeys((key) => { + // if ( + // typeof shortcut == "object" + // ? key.name == shortcut.name + // : key.name == shortcut + // ) + // return onPress; + // }); + // } return ( diff --git a/packages/ui/components/Dialog.tsx b/packages/ui/components/Dialog.tsx index 7608811..746097a 100644 --- a/packages/ui/components/Dialog.tsx +++ b/packages/ui/components/Dialog.tsx @@ -1,6 +1,5 @@ import { createContext, type ReactNode } from "react"; import { Modal, View, Text } from "react-native"; -import { useKeyboard } from "../src/useKeyboard"; export interface DialogState { close?: () => void; @@ -15,12 +14,6 @@ interface ProviderProps { close?: () => void; } export function Provider({ children, visible, close }: ProviderProps) { - useKeyboard((key) => { - if (key.name == "escape") { - if (close) close(); - } - }, []); - return ( diff --git a/packages/ui/components/Table.tsx b/packages/ui/components/Table.tsx index 5254bcc..f263a42 100644 --- a/packages/ui/components/Table.tsx +++ b/packages/ui/components/Table.tsx @@ -1,7 +1,14 @@ -import { createContext, use, useState, type ReactNode } from "react"; +import { + createContext, + use, + useEffect, + useRef, + useState, + type ReactNode, +} from "react"; import { View, Text } from "react-native"; -import { useKeyboard } from "../src/useKeyboard"; import type { KeyEvent } from "@opentui/core"; +import { useShortcut } from "../lib/shortcuts/hooks"; const HEADER_COLOR = "#7158e2"; const TABLE_COLORS = ["#ddd", "#eee"]; @@ -58,33 +65,12 @@ export function Provider({ const [idx, setIdx] = useState(0); const [selectedFrom, setSelectedFrom] = useState(); - useKeyboard( - (key) => { - if (key.name == "j" || key.name == "down") { - if (key.shift && selectedFrom == undefined) { - setSelectedFrom(idx); - } - setIdx((prev) => Math.min(prev + 1, data.length - 1)); - } else if (key.name == "k" || key.name == "up") { - if (key.shift && selectedFrom == undefined) { - setSelectedFrom(idx); - } - setIdx((prev) => Math.max(prev - 1, 0)); - } else if (key.name == "g" && key.shift) { - setIdx(data.length - 1); - } else if (key.name == "v") { - setSelectedFrom(idx); - } else if (key.name == "escape") { - setSelectedFrom(undefined); - } else { - const from = selectedFrom ? Math.min(idx, selectedFrom) : idx; - const to = selectedFrom ? Math.max(idx, selectedFrom) : idx; - const selected = data.slice(from, to + 1); - if (onKey) onKey(key, selected); - } - }, - [data, idx, selectedFrom], - ); + useShortcut("j", () => { + setIdx((prev) => Math.min(prev + 1, data.length - 1)); + }); + useShortcut("k", () => { + setIdx((prev) => Math.max(prev - 1, 0)); + }); const columnMap = new Map( columns.map((col) => { diff --git a/packages/ui/lib/shortcuts/Debug.tsx b/packages/ui/lib/shortcuts/Debug.tsx new file mode 100644 index 0000000..717ef77 --- /dev/null +++ b/packages/ui/lib/shortcuts/Debug.tsx @@ -0,0 +1,30 @@ +import { useSyncExternalStore } from "react"; +import { View, Text } from "react-native"; +import { keysStore } from "./store"; + +export function ShortcutDebug() { + const entries = useSyncExternalStore( + keysStore.subscribe, + keysStore.getSnapshot, + ); + + return ( + + + {entries + .values() + .map(([key, _]) => key) + .toArray() + .join(",")} + + + ); +} diff --git a/packages/ui/lib/shortcuts/Provider.tsx b/packages/ui/lib/shortcuts/Provider.tsx new file mode 100644 index 0000000..0d9919a --- /dev/null +++ b/packages/ui/lib/shortcuts/Provider.tsx @@ -0,0 +1,12 @@ +import type { ReactNode } from "react"; +import { useKeyboard } from "@opentui/react"; +import { keysStore } from "./store"; + +export function ShortcutProvider({ children }: { children: ReactNode }) { + useKeyboard((e) => { + const fn = keysStore.getHandler(e.name); + fn?.(); + }); + + return children; +} diff --git a/packages/ui/lib/shortcuts/Provider.web.tsx b/packages/ui/lib/shortcuts/Provider.web.tsx new file mode 100644 index 0000000..a384a9b --- /dev/null +++ b/packages/ui/lib/shortcuts/Provider.web.tsx @@ -0,0 +1,13 @@ +import type { ReactNode } from "react"; +import { keysStore } from "./store"; + +if (typeof window !== "undefined") { + window.addEventListener("keydown", (e) => { + const fn = keysStore.getHandler(e.key); + fn?.(); + }); +} + +export function ShortcutProvider({ children }: { children: ReactNode }) { + return children; +} diff --git a/packages/ui/lib/shortcuts/hooks.ts b/packages/ui/lib/shortcuts/hooks.ts new file mode 100644 index 0000000..38454c0 --- /dev/null +++ b/packages/ui/lib/shortcuts/hooks.ts @@ -0,0 +1,14 @@ +import { useEffect, useRef } from "react"; +import { keysStore } from "./store"; + +export const useShortcut = (key: string, handler: () => void) => { + const ref = useRef(handler); + ref.current = handler; + + useEffect(() => { + keysStore.register(key, ref); + return () => { + keysStore.deregister(key); + }; + }, []); +}; diff --git a/packages/ui/lib/shortcuts/index.ts b/packages/ui/lib/shortcuts/index.ts new file mode 100644 index 0000000..eb0b8b4 --- /dev/null +++ b/packages/ui/lib/shortcuts/index.ts @@ -0,0 +1,3 @@ +export * from "./Debug"; +export * from "./Provider"; +export * from "./store"; diff --git a/packages/ui/lib/shortcuts/store.ts b/packages/ui/lib/shortcuts/store.ts new file mode 100644 index 0000000..126c27c --- /dev/null +++ b/packages/ui/lib/shortcuts/store.ts @@ -0,0 +1,40 @@ +import { type RefObject } from "react"; + +// internal map +const keys = new Map void>>(); + +// cached snapshot (stable reference) +let snapshot: [string, RefObject<() => void>][] = []; + +let listeners = new Set<() => void>(); + +function emit() { + // refresh snapshot ONLY when keys actually change + snapshot = Array.from(keys.entries()); + for (const fn of listeners) fn(); +} + +export const keysStore = { + subscribe(fn: () => void) { + listeners.add(fn); + return () => listeners.delete(fn); + }, + + getSnapshot() { + return snapshot; // stable unless emit() ran + }, + + register(key: string, ref: RefObject<() => void>) { + keys.set(key, ref); + emit(); + }, + + deregister(key: string) { + keys.delete(key); + emit(); + }, + + getHandler(key: string) { + return keys.get(key)?.current; + }, +}; diff --git a/packages/ui/src/budget.tsx b/packages/ui/src/budget.tsx new file mode 100644 index 0000000..67ae010 --- /dev/null +++ b/packages/ui/src/budget.tsx @@ -0,0 +1,72 @@ +import { use } from "react"; +import { View, Text } from "react-native"; +import { RouterContext } from "."; +import { queries, type Mutators, type Schema } from "@money/shared"; +import { useQuery, useZero } from "@rocicorp/zero/react"; +import * as Table from "../components/Table"; +import { Button } from "../components/Button"; + +const COLUMNS: Table.Column[] = [{ name: "label", label: "Name" }]; + +export function Budget() { + const { auth } = use(RouterContext); + const [budgets] = useQuery(queries.getBudgets(auth)); + // const [items] = useQuery(queries.getBudgetCategories(auth)); + + const items: any[] = []; + + const z = useZero(); + + const newBudget = () => { + const id = new Date().getTime().toString(); + z.mutate.budget.create({ + id, + }); + }; + + if (budgets.length == 0) + return ( + + + No budgets, please create a new budget + + + + ); + + const budget = budgets[0]!; + + return ( + <> + + + Selected Budget: {budget.label} + + + { + if (event.name == "n" && event.shift) { + newBudget(); + } + }} + > + + + + + + + + ); +} diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx index 6129f8e..e7ae4c5 100644 --- a/packages/ui/src/index.tsx +++ b/packages/ui/src/index.tsx @@ -1,18 +1,24 @@ -import { createContext, use } from "react"; +import { createContext, use, useEffect, useRef } from "react"; import { Transactions } from "./transactions"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import { Settings } from "./settings"; -import { useKeyboard } from "./useKeyboard"; import type { AuthData } from "@money/shared/auth"; +import { Budget } from "./budget"; +import { ShortcutProvider, ShortcutDebug, keysStore } from "../lib/shortcuts"; +import { useShortcut } from "../lib/shortcuts/hooks"; const PAGES = { "/": { screen: , key: "1", }, + "/budget": { + screen: , + key: "2", + }, "/settings": { screen: , - key: "2", + key: "3", children: { "/accounts": {}, "/family": {}, @@ -59,7 +65,10 @@ type AppProps = { export function App({ auth, route, setRoute }: AppProps) { return ( -
+ + +
+ ); } @@ -67,17 +76,9 @@ export function App({ auth, route, setRoute }: AppProps) { function Main() { const { route, setRoute } = use(RouterContext); - useKeyboard((key) => { - const screen = Object.entries(PAGES).find( - ([, screen]) => screen.key == key.name, - ); - - if (!screen) return; - - const [route] = screen as [Route, never]; - - setRoute(route); - }); + for (const [route, page] of Object.entries(PAGES)) { + useShortcut(page.key, () => setRoute(route as Route)); + } const match = route in PAGES diff --git a/packages/ui/src/settings.tsx b/packages/ui/src/settings.tsx index dc3da50..de3842c 100644 --- a/packages/ui/src/settings.tsx +++ b/packages/ui/src/settings.tsx @@ -4,8 +4,6 @@ import { RouterContext, type Route } from "."; import { General } from "./settings/general"; import { Accounts } from "./settings/accounts"; import { Family } from "./settings/family"; -import { useKeyboard } from "./useKeyboard"; -import { Modal } from "react-native-opentui"; type SettingsRoute = Extract; @@ -32,28 +30,27 @@ type Tab = keyof typeof TABS; export function Settings() { const { route, setRoute } = use(RouterContext); - useKeyboard( - (key) => { - if (key.name == "h") { - const currentIdx = Object.entries(TABS).findIndex( - ([tabRoute, _]) => tabRoute == route, - ); - const routes = Object.keys(TABS) as SettingsRoute[]; - const last = routes[currentIdx - 1]; - if (!last) return; - setRoute(last); - } else if (key.name == "l") { - const currentIdx = Object.entries(TABS).findIndex( - ([tabRoute, _]) => tabRoute == route, - ); - const routes = Object.keys(TABS) as SettingsRoute[]; - const next = routes[currentIdx + 1]; - if (!next) return; - setRoute(next); - } - }, - [route], - ); + // useKeyboard( + // (key) => { + // if (key.name == "h") { + // const currentIdx = Object.entries(TABS).findIndex( + // ([tabRoute, _]) => tabRoute == route, + // ); + // const routes = Object.keys(TABS) as SettingsRoute[]; + // const last = routes[currentIdx - 1]; + // if (!last) return; + // setRoute(last); + // } else if (key.name == "l") { + // const currentIdx = Object.entries(TABS).findIndex( + // ([tabRoute, _]) => tabRoute == route, + // ); + // const routes = Object.keys(TABS) as SettingsRoute[]; + // const next = routes[currentIdx + 1]; + // if (!next) return; + // setRoute(next); + // } + // }, + // ); return ( diff --git a/packages/ui/src/settings/accounts.tsx b/packages/ui/src/settings/accounts.tsx index ce4a939..1690218 100644 --- a/packages/ui/src/settings/accounts.tsx +++ b/packages/ui/src/settings/accounts.tsx @@ -3,7 +3,6 @@ import { queries, type Mutators, type Schema } from "@money/shared"; import { use, useEffect, useState } from "react"; import { RouterContext } from ".."; import { View, Text, Linking } from "react-native"; -import { useKeyboard } from "../useKeyboard"; import { Button } from "../../components/Button"; import * as Table from "../../components/Table"; import * as Dialog from "../../components/Dialog"; diff --git a/packages/ui/src/useKeyboard.ts b/packages/ui/src/useKeyboard.ts deleted file mode 100644 index b157c50..0000000 --- a/packages/ui/src/useKeyboard.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useKeyboard as useOpentuiKeyboard } from "@opentui/react"; - -export function useKeyboard( - handler: Parameters[0], - _deps: any[] = [], -) { - return useOpentuiKeyboard(handler); -} diff --git a/packages/ui/src/useKeyboard.web.ts b/packages/ui/src/useKeyboard.web.ts deleted file mode 100644 index 2dea73a..0000000 --- a/packages/ui/src/useKeyboard.web.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { useEffect } from "react"; -import type { KeyboardEvent } from "react"; -import type { KeyEvent } from "@opentui/core"; - -function convertName(keyName: string): string { - const result = keyName.toLowerCase(); - if (result == "arrowdown") return "down"; - if (result == "arrowup") return "up"; - return result; -} - -export function useKeyboard( - handler: (key: KeyEvent) => void, - deps: any[] = [], -) { - useEffect(() => { - const handlerWeb = (event: KeyboardEvent) => { - // @ts-ignore - handler({ - name: convertName(event.key), - ctrl: event.ctrlKey, - meta: event.metaKey, - shift: event.shiftKey, - option: event.metaKey, - sequence: "", - number: false, - raw: "", - eventType: "press", - source: "raw", - code: event.code, - super: false, - hyper: false, - capsLock: false, - numLock: false, - baseCode: event.keyCode, - preventDefault: () => event.preventDefault(), - }); - }; - - // @ts-ignore - window.addEventListener("keydown", handlerWeb); - return () => { - // @ts-ignore - window.removeEventListener("keydown", handlerWeb); - }; - }, deps); -} diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 2aea777..1f3e2e3 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -1,10 +1,7 @@ { "compilerOptions": { - "paths": { - "@/*": ["./*"] - }, // Environment setup & latest features - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "ESNext", "moduleDetection": "force",