90 lines · 3.0 KB · TypeScript
RawDownload
import { EDITORS, EditorId, type EnvironmentId } from "@t3tools/contracts";import {  mapAtomCommandResult,  type AtomCommandFailure,  type AtomCommandResult,} from "@t3tools/client-runtime/state/runtime";import * as Cause from "effect/Cause";import * as Data from "effect/Data";import { AsyncResult } from "effect/unstable/reactivity";import { getLocalStorageItem, setLocalStorageItem, useLocalStorage } from "./hooks/useLocalStorage";import { useCallback, useMemo } from "react";import { shellEnvironment } from "./state/shell";import { useAtomCommand } from "./state/use-atom-command"; const LAST_EDITOR_KEY = "t3code:last-editor"; export class PreferredEditorUnavailableError extends Data.TaggedError(  "PreferredEditorUnavailableError",)<{  readonly message: string;}> {} export function usePreferredEditor(availableEditors: ReadonlyArray<EditorId>) {  const [lastEditor, setLastEditor] = useLocalStorage(LAST_EDITOR_KEY, null, EditorId);   const effectiveEditor = useMemo(() => {    if (lastEditor && availableEditors.includes(lastEditor)) return lastEditor;    return EDITORS.find((editor) => availableEditors.includes(editor.id))?.id ?? null;  }, [lastEditor, availableEditors]);   return [effectiveEditor, setLastEditor] as const;} export function resolveAndPersistPreferredEditor(  availableEditors: readonly EditorId[],): EditorId | null {  const availableEditorIds = new Set(availableEditors);  const stored = getLocalStorageItem(LAST_EDITOR_KEY, EditorId);  if (stored && availableEditorIds.has(stored)) return stored;  const editor = EDITORS.find((editor) => availableEditorIds.has(editor.id))?.id ?? null;  if (editor) setLocalStorageItem(LAST_EDITOR_KEY, editor, EditorId);  return editor ?? null;} export function useOpenInPreferredEditor(  environmentId: EnvironmentId | null,  availableEditors: readonly EditorId[],) {  const openInEditor = useAtomCommand(shellEnvironment.openInEditor, {    reportFailure: false,  });  type OpenInEditorError = AtomCommandFailure<Awaited<ReturnType<typeof openInEditor>>>;   return useCallback(    async (      targetPath: string,    ): Promise<      AtomCommandResult<EditorId, OpenInEditorError | PreferredEditorUnavailableError>    > => {      if (environmentId === null) {        return AsyncResult.failure(          Cause.fail(            new PreferredEditorUnavailableError({              message: "No environment is selected.",            }),          ),        );      }      const editor = resolveAndPersistPreferredEditor(availableEditors);      if (!editor) {        return AsyncResult.failure(          Cause.fail(            new PreferredEditorUnavailableError({              message: "No available editors found.",            }),          ),        );      }      const result = await openInEditor({        environmentId,        input: {          cwd: targetPath,          editor,        },      });      return mapAtomCommandResult(result, () => editor);    },    [availableEditors, environmentId, openInEditor],  );}