diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 861133b..b072cf6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,11 +45,3 @@ mise generate git-pre-commit --write --task=check ```shell mise tasks # list all available tasks ``` - -## Roadmap - -- [ ] Automatically setup SDKs based on mise tools -- [ ] Improve UI to install tools -- [ ] Manage mise settings -- [ ] Upgrade mise tools -- ... \ No newline at end of file diff --git a/package.json b/package.json index db08c33..0158fad 100644 --- a/package.json +++ b/package.json @@ -636,6 +636,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "react-error-boundary": "^4.1.2", - "toml-v1": "^1.0.0" + "toml-v1": "^1.0.0", + "zustand": "^5.0.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39c5cf3..6697aec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: toml-v1: specifier: ^1.0.0 version: 1.0.0 + zustand: + specifier: ^5.0.2 + version: 5.0.2(@types/react@18.3.12)(react@18.3.1) devDependencies: '@biomejs/biome': specifier: 1.9.4 @@ -2808,6 +2811,24 @@ packages: zod@3.24.0: resolution: {integrity: sha512-Hz+wiY8yD0VLA2k/+nsg2Abez674dDGTai33SwNvMPuf9uIrBC9eFgIMQxBBbHFxVXi8W+5nX9DcAh9YNSQm/w==} + zustand@5.0.2: + resolution: {integrity: sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -6177,4 +6198,9 @@ snapshots: zod@3.24.0: {} + zustand@5.0.2(@types/react@18.3.12)(react@18.3.1): + optionalDependencies: + '@types/react': 18.3.12 + react: 18.3.1 + zwitch@2.0.4: {} diff --git a/src/webviewPanel.ts b/src/webviewPanel.ts index 958ab39..6e2ba6b 100644 --- a/src/webviewPanel.ts +++ b/src/webviewPanel.ts @@ -1,5 +1,4 @@ import { readFileSync, writeFileSync } from "node:fs"; -import { homedir } from "node:os"; import * as os from "node:os"; import path from "node:path"; import * as cheerio from "cheerio"; @@ -19,7 +18,7 @@ export default class WebViewPanel { private readonly _extContext: vscode.ExtensionContext; private _disposables: vscode.Disposable[] = []; private readonly miseService: MiseService; - private view: PanelView = "TOOLS"; + private readonly view: PanelView = "TOOLS"; public static createOrShow( extContext: vscode.ExtensionContext, diff --git a/src/webviews/App.tsx b/src/webviews/App.tsx index 5f73d96..5a6b5ab 100644 --- a/src/webviews/App.tsx +++ b/src/webviews/App.tsx @@ -1,7 +1,6 @@ import { Settings } from "./Settings"; import { Tools } from "./Tools"; import { TrackedConfigs } from "./TrackedConfigs"; -import { Tabs } from "./components/Tabs"; export function App() { const view = document diff --git a/src/webviews/Settings.tsx b/src/webviews/Settings.tsx index 9c3ddfd..5a08b87 100644 --- a/src/webviews/Settings.tsx +++ b/src/webviews/Settings.tsx @@ -1,9 +1,9 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; import { VscodeCheckbox } from "@vscode-elements/react-elements"; -import React, { useState } from "react"; import { type FlattenedProperty, getDefaultForType } from "../utils/miseUtilts"; import CustomTable from "./components/CustomTable"; import { IconButton } from "./components/IconButton"; +import { useWebviewStore } from "./store"; import { toDisplayPath, useEditSettingMutation, @@ -12,7 +12,12 @@ import { } from "./webviewVsCodeApi"; export const Settings = () => { - const [showModifiedOnly, setShowModifiedOnly] = useState(true); + const showModifiedOnly = useWebviewStore( + (state) => state.showModifiedSettingsOnly, + ); + const setShowModifiedOnly = useWebviewStore( + (state) => state.setShowModifiedSettingsOnly, + ); const openFileMutation = useOpenFileMutation(); const queryClient = useQueryClient(); diff --git a/src/webviews/Tools.tsx b/src/webviews/Tools.tsx index 10cbd90..216ff0b 100644 --- a/src/webviews/Tools.tsx +++ b/src/webviews/Tools.tsx @@ -1,8 +1,8 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { VscodeButton, VscodeCheckbox } from "@vscode-elements/react-elements"; -import { useState } from "react"; import CustomTable from "./components/CustomTable"; import { IconButton } from "./components/IconButton"; +import { useWebviewStore } from "./store"; import { toDisplayPath, trackedConfigsQueryOptions, @@ -223,9 +223,17 @@ const ActionCell = ({ export const Tools = () => { const queryClient = useQueryClient(); - const [selectedTool, setSelectedTool] = useState(null); - const [showOutdatedOnly, setShowOutdatedOnly] = useState(false); - const [activeOnly, setActiveOnly] = useState(true); + const selectedTool = useWebviewStore((state) => state.selectedTool); + const setSelectedTool = useWebviewStore((state) => state.setSelectedTool); + + const showOutdatedOnly = useWebviewStore((state) => state.showOutdatedOnly); + const setShowOutdatedOnly = useWebviewStore( + (state) => state.setShowOutdatedOnly, + ); + const activeOnly = useWebviewStore((state) => state.showActiveToolsOnly); + const setActiveOnly = useWebviewStore( + (state) => state.setShowActiveToolsOnly, + ); const toolsQuery = useQuery({ queryKey: ["tools"], diff --git a/src/webviews/components/Tabs.tsx b/src/webviews/components/Tabs.tsx deleted file mode 100644 index 2d7860b..0000000 --- a/src/webviews/components/Tabs.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { type ReactNode, useState } from "react"; -import { vscode } from "../webviewVsCodeApi"; - -export function Tabs({ - tabs, - initialTab, -}: { - initialTab?: string; - tabs: Array<{ name: string; icon?: string; content: ReactNode }>; -}) { - const [selectedTab, setSelectedTab] = useState( - initialTab ?? tabs[0]?.name ?? "", - ); - - return ( -
- - - {tabs.find((tab) => tab.name === selectedTab)?.content} -
- ); -} diff --git a/src/webviews/store.ts b/src/webviews/store.ts new file mode 100644 index 0000000..355e9db --- /dev/null +++ b/src/webviews/store.ts @@ -0,0 +1,54 @@ +import { create } from "zustand/index"; +import { type StorageValue, persist } from "zustand/middleware"; +import { vscode } from "./webviewVsCodeApi"; + +interface WebviewStore { + showModifiedSettingsOnly: boolean; + setShowModifiedSettingsOnly: (value: boolean) => void; + + selectedTool: MiseTool | null; + setSelectedTool: (value: MiseTool | null) => void; + + showOutdatedOnly: boolean; + setShowOutdatedOnly: (value: boolean) => void; + + showActiveToolsOnly: boolean; + setShowActiveToolsOnly: (value: boolean) => void; +} + +export const useWebviewStore = create()( + persist( + (set) => ({ + showModifiedSettingsOnly: true, + setShowModifiedSettingsOnly: (value: boolean) => + set({ showModifiedSettingsOnly: value }), + + selectedTool: null, + setSelectedTool: (value: MiseTool | null) => set({ selectedTool: value }), + + showOutdatedOnly: false, + setShowOutdatedOnly: (value: boolean) => set({ showOutdatedOnly: value }), + + showActiveToolsOnly: true, + setShowActiveToolsOnly: (value: boolean) => + set({ showActiveToolsOnly: value }), + }), + { + name: "webview-store", + storage: { + getItem: async (name) => { + const state = await vscode.getState(); + return state[name] as StorageValue; + }, + setItem: async (name, value) => { + return vscode.setState({ [name]: value }); + }, + removeItem: async (name) => { + const state = await vscode.getState(); + delete state[name]; + vscode.setState(state); + }, + }, + }, + ), +); diff --git a/src/webviews/webviewVsCodeApi.ts b/src/webviews/webviewVsCodeApi.ts index 868850c..23e6e93 100644 --- a/src/webviews/webviewVsCodeApi.ts +++ b/src/webviews/webviewVsCodeApi.ts @@ -3,16 +3,14 @@ import { queryOptions, useMutation } from "@tanstack/react-query"; declare global { interface Window { acquireVsCodeApi(): { + getState(): Promise>; + setState(state: Record): void; postMessage(message: unknown): void; }; } } -export function getVsCode() { - return window.acquireVsCodeApi(); -} - -export const vscode = getVsCode(); +export const vscode = window.acquireVsCodeApi(); export const vscodeClient = { async request({