142 lines · 4.4 KB · TypeScript
RawDownload
import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Schema from "effect/Schema"; import * as DesktopEnvironment from "./DesktopEnvironment.ts"; export interface DesktopIconPaths {  readonly ico: Option.Option<string>;  readonly icns: Option.Option<string>;  readonly png: Option.Option<string>;} export class DesktopAssetProbeError extends Schema.TaggedErrorClass<DesktopAssetProbeError>()(  "DesktopAssetProbeError",  {    fileName: Schema.String,    candidatePath: Schema.String,    cause: Schema.Defect(),  },) {  override get message(): string {    return `Failed to probe desktop asset "${this.fileName}" at ${this.candidatePath}.`;  }} export class DesktopAssets extends Context.Service<  DesktopAssets,  {    readonly iconPaths: Effect.Effect<DesktopIconPaths>;    readonly resolveResourcePath: (      fileName: string,    ) => Effect.Effect<Option.Option<string>, DesktopAssetProbeError>;  }>()("@t3tools/desktop/app/DesktopAssets") {} const resolveResourcePath = Effect.fn("desktop.assets.resolveResourcePath")(function* (  fileName: string,): Effect.fn.Return<  Option.Option<string>,  DesktopAssetProbeError,  FileSystem.FileSystem | DesktopEnvironment.DesktopEnvironment> {  const fileSystem = yield* FileSystem.FileSystem;  const environment = yield* DesktopEnvironment.DesktopEnvironment;  const candidates = environment.resolveResourcePathCandidates(fileName);  for (const candidate of candidates) {    const exists = yield* fileSystem      .exists(candidate)      .pipe(        Effect.mapError(          (cause) => new DesktopAssetProbeError({ fileName, candidatePath: candidate, cause }),        ),      );    if (exists) {      return Option.some(candidate);    }  }  return Option.none<string>();}); const sourceTreeIconFileNames = {  dev: {    ico: "blueprint-windows.ico",    macPng: "blueprint-macos-1024.png",    universalPng: "blueprint-universal-1024.png",  },  prod: {    ico: "t3-black-windows.ico",    macPng: "black-macos-1024.png",    universalPng: "black-universal-1024.png",  },} as const; function resolveSourceTreeIconPath(  environment: DesktopEnvironment.DesktopEnvironment["Service"],  ext: keyof DesktopIconPaths,): string | undefined {  if (environment.isPackaged || ext === "icns") return undefined;  const brand = environment.isDevelopment ? "dev" : "prod";  const fileNames = sourceTreeIconFileNames[brand];  const fileName =    ext === "ico"      ? fileNames.ico      : environment.platform === "darwin"        ? fileNames.macPng        : fileNames.universalPng;  return environment.path.join(environment.rootDir, "assets", brand, fileName);} const resolveIconPath = Effect.fn("desktop.assets.resolveIconPath")(function* (  ext: keyof DesktopIconPaths,): Effect.fn.Return<  Option.Option<string>,  DesktopAssetProbeError,  FileSystem.FileSystem | DesktopEnvironment.DesktopEnvironment> {  const fileSystem = yield* FileSystem.FileSystem;  const environment = yield* DesktopEnvironment.DesktopEnvironment;  const sourceTreeIconPath = resolveSourceTreeIconPath(environment, ext);  if (sourceTreeIconPath !== undefined) {    const sourceTreeIconExists = yield* fileSystem.exists(sourceTreeIconPath).pipe(      Effect.mapError(        (cause) =>          new DesktopAssetProbeError({            fileName: `icon.${ext}`,            candidatePath: sourceTreeIconPath,            cause,          }),      ),    );    if (sourceTreeIconExists) {      return Option.some(sourceTreeIconPath);    }  }   return yield* resolveResourcePath(`icon.${ext}`);}); export const make = Effect.gen(function* () {  const context = yield* Effect.context<    FileSystem.FileSystem | DesktopEnvironment.DesktopEnvironment  >();  const [ico, icns, png] = yield* Effect.all(    [resolveIconPath("ico"), resolveIconPath("icns"), resolveIconPath("png")] as const,    { concurrency: "unbounded" },  );  const iconPaths = { ico, icns, png } satisfies DesktopIconPaths;   return DesktopAssets.of({    iconPaths: Effect.succeed(iconPaths),    resolveResourcePath: Effect.fn("desktop.assets.resolveResourcePath.scoped")(      function* (fileName) {        return yield* resolveResourcePath(fileName).pipe(Effect.provide(context));      },    ),  });}); export const layer = Layer.effect(DesktopAssets, make);