From 7d961b13cff5190c9a2f036c3d8816bd0db1baf4 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:43:32 +0800 Subject: [PATCH 1/4] feat: add AMap and Baidu map providers --- src/app.test.tsx | 2 + src/components/settings-menu.tsx | 11 ++-- src/lib/external-map.test.ts | 19 +++++- src/lib/external-map.ts | 105 ++++++++++++++++++++++++++----- src/lib/plugin-settings.test.ts | 11 ++++ src/lib/plugin-settings.ts | 10 +-- 6 files changed, 127 insertions(+), 31 deletions(-) diff --git a/src/app.test.tsx b/src/app.test.tsx index 5bdb108..19c0d6e 100644 --- a/src/app.test.tsx +++ b/src/app.test.tsx @@ -101,6 +101,8 @@ describe("App settings access", () => { "OpenStreetMap", "Google Maps", "Apple Maps", + "AMap", + "Baidu Maps", ]); }); }); diff --git a/src/components/settings-menu.tsx b/src/components/settings-menu.tsx index eab1997..4e5ddd1 100644 --- a/src/components/settings-menu.tsx +++ b/src/components/settings-menu.tsx @@ -9,8 +9,11 @@ import { } from "react"; import { useIsDarkTheme } from "../hooks/use-is-dark-theme"; import { usePluginSettings } from "../hooks/use-plugin-settings"; +import { + EXTERNAL_MAP_PROVIDER_OPTIONS, + type ExternalMapProvider, +} from "../lib/external-map"; import type { - ExternalMapProvider, MapStylePreference, ThemePreference, } from "../lib/plugin-settings"; @@ -40,12 +43,6 @@ const MAP_STYLE_OPTIONS = [ { value: "dark", label: "Dark" }, ] as const satisfies readonly SettingOption[]; -const EXTERNAL_MAP_PROVIDER_OPTIONS = [ - { value: "openstreetmap", label: "OpenStreetMap" }, - { value: "google", label: "Google Maps" }, - { value: "apple", label: "Apple Maps" }, -] as const satisfies readonly SettingOption[]; - const SETTINGS_DIALOG_ID = "settings-dialog"; function useDismissibleLayer( diff --git a/src/lib/external-map.test.ts b/src/lib/external-map.test.ts index d7a7bcf..774e591 100644 --- a/src/lib/external-map.test.ts +++ b/src/lib/external-map.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; -import type { ExternalMapProvider } from "./plugin-settings"; -import { getExternalMap } from "./external-map"; +import { getExternalMap, type ExternalMapProvider } from "./external-map"; import type { Coordinates } from "../types"; interface ExternalMapTestCase { @@ -37,6 +36,22 @@ const testCases: ExternalMapTestCase[] = [ url: "https://maps.apple.com/?ll=-33.8688%2C151.2093&z=16&q=Location", }, }, + { + provider: "amap", + coordinates: { latitude: -33.8688, longitude: 151.2093 }, + expected: { + label: "AMap", + url: "https://uri.amap.com/marker?position=151.2093%2C-33.8688&name=Location&src=eagle-plugin-mini-map&coordinate=wgs84&callnative=0", + }, + }, + { + provider: "baidu", + coordinates: { latitude: -33.8688, longitude: 151.2093 }, + expected: { + label: "Baidu Maps", + url: "https://api.map.baidu.com/marker?location=-33.8688%2C151.2093&title=Location&content=Location&output=html&coord_type=wgs84&src=webapp.eagle.mini-map", + }, + }, ]; describe("getExternalMap", () => { diff --git a/src/lib/external-map.ts b/src/lib/external-map.ts index d1d3096..c6ed4c6 100644 --- a/src/lib/external-map.ts +++ b/src/lib/external-map.ts @@ -1,4 +1,3 @@ -import type { ExternalMapProvider } from "./plugin-settings"; import type { Coordinates } from "../types"; interface ExternalMap { @@ -6,32 +5,104 @@ interface ExternalMap { url: string; } -export function getExternalMap( - provider: ExternalMapProvider, - coordinates: Coordinates, -): ExternalMap { - const { latitude, longitude } = coordinates; +interface ExternalMapProviderDefinition { + label: string; + buildUrl: (coordinates: Coordinates) => string; +} - switch (provider) { - case "openstreetmap": { +const EXTERNAL_MAP_PROVIDERS = { + openstreetmap: { + label: "OpenStreetMap", + buildUrl: ({ latitude, longitude }) => { const url = new URL("https://www.openstreetmap.org/"); url.searchParams.set("mlat", String(latitude)); url.searchParams.set("mlon", String(longitude)); url.searchParams.set("zoom", "16"); - return { label: "OpenStreetMap", url: url.toString() }; - } - case "google": { + return url.toString(); + }, + }, + google: { + label: "Google Maps", + buildUrl: ({ latitude, longitude }) => { const url = new URL("https://www.google.com/maps/search/"); url.searchParams.set("api", "1"); url.searchParams.set("query", `${latitude},${longitude}`); - return { label: "Google Maps", url: url.toString() }; - } - case "apple": { + return url.toString(); + }, + }, + apple: { + label: "Apple Maps", + buildUrl: ({ latitude, longitude }) => { const url = new URL("https://maps.apple.com/"); url.searchParams.set("ll", `${latitude},${longitude}`); url.searchParams.set("z", "16"); url.searchParams.set("q", "Location"); - return { label: "Apple Maps", url: url.toString() }; - } - } + return url.toString(); + }, + }, + amap: { + label: "AMap", + buildUrl: ({ latitude, longitude }) => { + const url = new URL("https://uri.amap.com/marker"); + url.searchParams.set("position", `${longitude},${latitude}`); + url.searchParams.set("name", "Location"); + url.searchParams.set("src", "eagle-plugin-mini-map"); + // Treat the plugin's EXIF GPS coordinates as WGS-84; AMap otherwise + // defaults to GCJ-02. + // See: https://developer.amap.com/api/uri-api/gettingstarted + url.searchParams.set("coordinate", "wgs84"); + url.searchParams.set("callnative", "0"); + return url.toString(); + }, + }, + baidu: { + label: "Baidu Maps", + buildUrl: ({ latitude, longitude }) => { + const url = new URL("https://api.map.baidu.com/marker"); + url.searchParams.set("location", `${latitude},${longitude}`); + url.searchParams.set("title", "Location"); + url.searchParams.set("content", "Location"); + url.searchParams.set("output", "html"); + // Treat the plugin's EXIF GPS coordinates as WGS-84; Baidu otherwise + // defaults to BD-09. + // See: https://lbsyun.baidu.com/faq/api?title=webapi%2Furi%2Fweb + url.searchParams.set("coord_type", "wgs84"); + url.searchParams.set("src", "webapp.eagle.mini-map"); + return url.toString(); + }, + }, +} satisfies Record; + +export type ExternalMapProvider = keyof typeof EXTERNAL_MAP_PROVIDERS; + +interface ExternalMapProviderOption { + readonly value: ExternalMapProvider; + readonly label: string; +} + +export const EXTERNAL_MAP_PROVIDER_OPTIONS: readonly ExternalMapProviderOption[] = + Object.entries(EXTERNAL_MAP_PROVIDERS).map(([value, { label }]) => ({ + value: value as ExternalMapProvider, + label, + })); + +export function isExternalMapProvider( + value: unknown, +): value is ExternalMapProvider { + return ( + typeof value === "string" && + EXTERNAL_MAP_PROVIDER_OPTIONS.some((option) => option.value === value) + ); +} + +export function getExternalMap( + provider: ExternalMapProvider, + coordinates: Coordinates, +): ExternalMap { + const definition = EXTERNAL_MAP_PROVIDERS[provider]; + + return { + label: definition.label, + url: definition.buildUrl(coordinates), + }; } diff --git a/src/lib/plugin-settings.test.ts b/src/lib/plugin-settings.test.ts index 7faa54c..e7af8df 100644 --- a/src/lib/plugin-settings.test.ts +++ b/src/lib/plugin-settings.test.ts @@ -67,6 +67,17 @@ describe("readPluginSettings", () => { }); }); + it.each(["amap", "baidu"] as const)( + "accepts the %s external map provider", + (externalMapProvider) => { + const storage = readableStorage(JSON.stringify({ externalMapProvider })); + + expect(readPluginSettings(storage).externalMapProvider).toBe( + externalMapProvider, + ); + }, + ); + it("keeps valid fields when other fields are missing or invalid", () => { const storage = readableStorage( JSON.stringify({ diff --git a/src/lib/plugin-settings.ts b/src/lib/plugin-settings.ts index 2e1339b..93e39bd 100644 --- a/src/lib/plugin-settings.ts +++ b/src/lib/plugin-settings.ts @@ -1,6 +1,10 @@ +import { + isExternalMapProvider, + type ExternalMapProvider, +} from "./external-map"; + export type ThemePreference = "eagle" | "light" | "dark"; export type MapStylePreference = "auto" | "light" | "dark"; -export type ExternalMapProvider = "openstreetmap" | "google" | "apple"; export interface PluginSettings { theme: ThemePreference; @@ -28,10 +32,6 @@ function isMapStylePreference(value: unknown): value is MapStylePreference { return value === "auto" || value === "light" || value === "dark"; } -function isExternalMapProvider(value: unknown): value is ExternalMapProvider { - return value === "openstreetmap" || value === "google" || value === "apple"; -} - export function readPluginSettings( storage?: Pick, ): PluginSettings { From ce8df021afe17738e654866b046552957adee716 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:58:54 +0800 Subject: [PATCH 2/4] feat: persist map zoom setting --- package.json | 3 +- pnpm-lock.yaml | 3 ++ src/app.test.tsx | 1 + src/components/external-map-link.test.tsx | 1 + src/components/mini-map.test.tsx | 22 ++++++-- src/components/mini-map.tsx | 27 ++++------ src/components/settings-menu.test.tsx | 1 + src/hooks/use-plugin-settings.test.tsx | 3 ++ src/hooks/use-plugin-theme.test.tsx | 1 + src/lib/plugin-settings.test.ts | 5 ++ src/lib/plugin-settings.ts | 66 +++++++++++------------ 11 files changed, 78 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index b4e407d..0f57caf 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "react": "^19.2.7", "react-dom": "^19.2.7", "react-map-gl": "^8.1.1", - "tailwindcss": "^4.3.1" + "tailwindcss": "^4.3.1", + "zod": "^4.4.3" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ba8014..58e51d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: tailwindcss: specifier: ^4.3.1 version: 4.3.1 + zod: + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@eslint/js': specifier: ^10.0.1 diff --git a/src/app.test.tsx b/src/app.test.tsx index 19c0d6e..5f1e265 100644 --- a/src/app.test.tsx +++ b/src/app.test.tsx @@ -22,6 +22,7 @@ const mocks = vi.hoisted(() => { theme: "dark", mapStyle: "light", externalMapProvider: "apple", + zoom: 13, }; return { diff --git a/src/components/external-map-link.test.tsx b/src/components/external-map-link.test.tsx index be3374a..e6b3208 100644 --- a/src/components/external-map-link.test.tsx +++ b/src/components/external-map-link.test.tsx @@ -17,6 +17,7 @@ const mocks = vi.hoisted(() => ({ theme: "eagle", mapStyle: "auto", externalMapProvider: "openstreetmap", + zoom: 13, }, })); diff --git a/src/components/mini-map.test.tsx b/src/components/mini-map.test.tsx index 12d9b4d..b609fcb 100644 --- a/src/components/mini-map.test.tsx +++ b/src/components/mini-map.test.tsx @@ -22,12 +22,16 @@ const mocks = vi.hoisted(() => { theme: "eagle", mapStyle: "auto", externalMapProvider: "openstreetmap", + zoom: 13, }; return { settings, isDark: false, recordMapProps: vi.fn<(props: MockMapProps) => void>(), + updateSettings: vi.fn((patch: Partial) => { + Object.assign(settings, patch); + }), }; }); @@ -38,7 +42,7 @@ vi.mock("../hooks/use-is-dark-theme", () => ({ vi.mock("../hooks/use-plugin-settings", () => ({ usePluginSettings: () => ({ settings: mocks.settings, - updateSettings: vi.fn(), + updateSettings: mocks.updateSettings, }), })); @@ -50,11 +54,12 @@ vi.mock("react-map-gl/maplibre", () => ({ })); beforeEach(() => { - mocks.settings = { + Object.assign(mocks.settings, { theme: "eagle", mapStyle: "auto", externalMapProvider: "openstreetmap", - }; + zoom: 13, + }); mocks.isDark = false; }); @@ -63,7 +68,15 @@ afterEach(() => { vi.clearAllMocks(); }); -describe("MiniMap map style", () => { +describe("MiniMap", () => { + it("uses the persisted zoom level", () => { + mocks.settings.zoom = 16; + + render(); + + expect(mocks.recordMapProps.mock.lastCall?.[0].zoom).toBe(16); + }); + it.each([ ["auto in a light plugin theme", "auto", false, MAP_STYLE_LIGHT], ["auto in a dark plugin theme", "auto", true, MAP_STYLE_DARK], @@ -95,6 +108,7 @@ describe("MiniMap map style", () => { mocks.settings.mapStyle = "light"; rerender(); + expect(mocks.updateSettings).toHaveBeenCalledWith({ zoom: 14 }); expect(screen.getByTestId("map")).toBe(mapElement); expect(mocks.recordMapProps.mock.lastCall?.[0]).toMatchObject({ latitude: 1.3521, diff --git a/src/components/mini-map.tsx b/src/components/mini-map.tsx index 446325a..4ca5000 100644 --- a/src/components/mini-map.tsx +++ b/src/components/mini-map.tsx @@ -3,7 +3,7 @@ import { useCallback, useState } from "react"; import Map from "react-map-gl/maplibre"; import { useIsDarkTheme } from "../hooks/use-is-dark-theme"; import { usePluginSettings } from "../hooks/use-plugin-settings"; -import type { MapStylePreference } from "../lib/plugin-settings"; +import { MAP_ZOOM, type MapStylePreference } from "../lib/plugin-settings"; import type { Coordinates } from "../types"; import { ZoomControls } from "./zoom-controls"; @@ -15,9 +15,6 @@ const MAP_STYLE_LIGHT = "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"; const MAP_STYLE_DARK = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json"; -const MIN_ZOOM = 2; -const MAX_ZOOM = 18; -const DEFAULT_ZOOM = 13; function resolveMapStyle( preference: MapStylePreference, @@ -30,10 +27,10 @@ function resolveMapStyle( export function MiniMap({ latitude, longitude, label }: MiniMapProps) { const isDark = useIsDarkTheme(); - const { settings } = usePluginSettings(); + const { settings, updateSettings } = usePluginSettings(); const mapStyle = resolveMapStyle(settings.mapStyle, isDark); const [isLoaded, setIsLoaded] = useState(false); - const [zoom, setZoom] = useState(DEFAULT_ZOOM); + const zoom = settings.zoom; const handleLoad = useCallback( (event: { target: { resize: () => void } }) => { @@ -44,19 +41,17 @@ export function MiniMap({ latitude, longitude, label }: MiniMapProps) { [], ); - const clampZoom = useCallback((value: number) => { - return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, value)); - }, []); - const handleZoomChange = useCallback( (delta: number) => { - setZoom((prev) => clampZoom(prev + delta)); + updateSettings({ + zoom: Math.min(MAP_ZOOM.max, Math.max(MAP_ZOOM.min, zoom + delta)), + }); }, - [clampZoom], + [updateSettings, zoom], ); - const canZoomIn = zoom < MAX_ZOOM; - const canZoomOut = zoom > MIN_ZOOM; + const canZoomIn = zoom < MAP_ZOOM.max; + const canZoomOut = zoom > MAP_ZOOM.min; return (
{ theme: "dark", mapStyle: "light", externalMapProvider: "apple", + zoom: 13, }; return { settings, updateSettings: vi.fn() }; diff --git a/src/hooks/use-plugin-settings.test.tsx b/src/hooks/use-plugin-settings.test.tsx index a9f1487..4874d81 100644 --- a/src/hooks/use-plugin-settings.test.tsx +++ b/src/hooks/use-plugin-settings.test.tsx @@ -34,6 +34,7 @@ describe("PluginSettingsProvider", () => { theme: "dark", mapStyle: "light", externalMapProvider: "apple", + zoom: 16, }), ); const getItem = vi.spyOn(Storage.prototype, "getItem"); @@ -49,6 +50,7 @@ describe("PluginSettingsProvider", () => { theme: "dark", mapStyle: "light", externalMapProvider: "apple", + zoom: 16, }); }); @@ -87,6 +89,7 @@ describe("PluginSettingsProvider", () => { theme: "eagle", mapStyle: "dark", externalMapProvider: "google", + zoom: 13, }), ); }); diff --git a/src/hooks/use-plugin-theme.test.tsx b/src/hooks/use-plugin-theme.test.tsx index 29162c0..8c1b0f1 100644 --- a/src/hooks/use-plugin-theme.test.tsx +++ b/src/hooks/use-plugin-theme.test.tsx @@ -88,6 +88,7 @@ function storeTheme(theme: ThemePreference): void { theme, mapStyle: "auto", externalMapProvider: "openstreetmap", + zoom: 13, }), ); } diff --git a/src/lib/plugin-settings.test.ts b/src/lib/plugin-settings.test.ts index e7af8df..bbabf6f 100644 --- a/src/lib/plugin-settings.test.ts +++ b/src/lib/plugin-settings.test.ts @@ -56,6 +56,7 @@ describe("readPluginSettings", () => { theme: "dark", mapStyle: "light", externalMapProvider: "apple", + zoom: 16, unknown: "ignored", }), ); @@ -64,6 +65,7 @@ describe("readPluginSettings", () => { theme: "dark", mapStyle: "light", externalMapProvider: "apple", + zoom: 16, }); }); @@ -83,6 +85,7 @@ describe("readPluginSettings", () => { JSON.stringify({ theme: "light", mapStyle: "satellite", + zoom: 19, }), ); @@ -90,6 +93,7 @@ describe("readPluginSettings", () => { theme: "light", mapStyle: "auto", externalMapProvider: "openstreetmap", + zoom: 13, }); }); @@ -125,6 +129,7 @@ describe("writePluginSettings", () => { theme: "dark", mapStyle: "dark", externalMapProvider: "google", + zoom: 15, }; writePluginSettings(settings, storage); diff --git a/src/lib/plugin-settings.ts b/src/lib/plugin-settings.ts index 93e39bd..a3dcc1b 100644 --- a/src/lib/plugin-settings.ts +++ b/src/lib/plugin-settings.ts @@ -1,36 +1,46 @@ +import { z } from "zod"; import { isExternalMapProvider, type ExternalMapProvider, } from "./external-map"; -export type ThemePreference = "eagle" | "light" | "dark"; -export type MapStylePreference = "auto" | "light" | "dark"; +export const MAP_ZOOM = { + min: 2, + max: 18, + default: 13, +} as const; -export interface PluginSettings { - theme: ThemePreference; - mapStyle: MapStylePreference; - externalMapProvider: ExternalMapProvider; -} - -export const DEFAULT_PLUGIN_SETTINGS: Readonly = Object.freeze({ +export const DEFAULT_PLUGIN_SETTINGS = Object.freeze({ theme: "eagle", mapStyle: "auto", externalMapProvider: "openstreetmap", -}); + zoom: MAP_ZOOM.default, +} as const); -export const PLUGIN_SETTINGS_STORAGE_KEY = "eagle-plugin-mini-map:settings:v1"; +const pluginSettingsSchema = z + .object({ + theme: z + .enum(["eagle", "light", "dark"]) + .catch(DEFAULT_PLUGIN_SETTINGS.theme), + mapStyle: z + .enum(["auto", "light", "dark"]) + .catch(DEFAULT_PLUGIN_SETTINGS.mapStyle), + externalMapProvider: z + .custom(isExternalMapProvider) + .catch(DEFAULT_PLUGIN_SETTINGS.externalMapProvider), + zoom: z + .number() + .min(MAP_ZOOM.min) + .max(MAP_ZOOM.max) + .catch(DEFAULT_PLUGIN_SETTINGS.zoom), + }) + .catch(DEFAULT_PLUGIN_SETTINGS); -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} +export type PluginSettings = z.infer; +export type ThemePreference = PluginSettings["theme"]; +export type MapStylePreference = PluginSettings["mapStyle"]; -function isThemePreference(value: unknown): value is ThemePreference { - return value === "eagle" || value === "light" || value === "dark"; -} - -function isMapStylePreference(value: unknown): value is MapStylePreference { - return value === "auto" || value === "light" || value === "dark"; -} +export const PLUGIN_SETTINGS_STORAGE_KEY = "eagle-plugin-mini-map:settings:v1"; export function readPluginSettings( storage?: Pick, @@ -42,19 +52,7 @@ export function readPluginSettings( if (serialized === null) return { ...DEFAULT_PLUGIN_SETTINGS }; const stored: unknown = JSON.parse(serialized); - if (!isRecord(stored)) return { ...DEFAULT_PLUGIN_SETTINGS }; - - return { - theme: isThemePreference(stored.theme) - ? stored.theme - : DEFAULT_PLUGIN_SETTINGS.theme, - mapStyle: isMapStylePreference(stored.mapStyle) - ? stored.mapStyle - : DEFAULT_PLUGIN_SETTINGS.mapStyle, - externalMapProvider: isExternalMapProvider(stored.externalMapProvider) - ? stored.externalMapProvider - : DEFAULT_PLUGIN_SETTINGS.externalMapProvider, - }; + return { ...pluginSettingsSchema.parse(stored) }; } catch { return { ...DEFAULT_PLUGIN_SETTINGS }; } From 1f3f55d5becac8a5b37275afe5164b4b5b79526f Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:13:36 +0800 Subject: [PATCH 3/4] refactor: derive external map provider schema --- src/lib/external-map.test.ts | 20 +++++++++++++++++++- src/lib/external-map.ts | 12 ++++-------- src/lib/plugin-settings.ts | 11 ++++------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/lib/external-map.test.ts b/src/lib/external-map.test.ts index 774e591..f9ab7f6 100644 --- a/src/lib/external-map.test.ts +++ b/src/lib/external-map.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from "vitest"; -import { getExternalMap, type ExternalMapProvider } from "./external-map"; +import { + externalMapProviderSchema, + getExternalMap, + type ExternalMapProvider, +} from "./external-map"; import type { Coordinates } from "../types"; interface ExternalMapTestCase { @@ -62,3 +66,17 @@ describe("getExternalMap", () => { }, ); }); + +describe("externalMapProviderSchema", () => { + it("accepts every registered provider", () => { + const providers = testCases.map(({ provider }) => provider); + + expect( + providers.map((provider) => externalMapProviderSchema.parse(provider)), + ).toEqual(providers); + }); + + it("rejects unknown providers", () => { + expect(externalMapProviderSchema.safeParse("unknown").success).toBe(false); + }); +}); diff --git a/src/lib/external-map.ts b/src/lib/external-map.ts index c6ed4c6..3a83518 100644 --- a/src/lib/external-map.ts +++ b/src/lib/external-map.ts @@ -1,3 +1,4 @@ +import { z } from "zod"; import type { Coordinates } from "../types"; interface ExternalMap { @@ -86,14 +87,9 @@ export const EXTERNAL_MAP_PROVIDER_OPTIONS: readonly ExternalMapProviderOption[] label, })); -export function isExternalMapProvider( - value: unknown, -): value is ExternalMapProvider { - return ( - typeof value === "string" && - EXTERNAL_MAP_PROVIDER_OPTIONS.some((option) => option.value === value) - ); -} +export const externalMapProviderSchema = z.enum( + EXTERNAL_MAP_PROVIDER_OPTIONS.map(({ value }) => value), +); export function getExternalMap( provider: ExternalMapProvider, diff --git a/src/lib/plugin-settings.ts b/src/lib/plugin-settings.ts index a3dcc1b..ef6c536 100644 --- a/src/lib/plugin-settings.ts +++ b/src/lib/plugin-settings.ts @@ -1,8 +1,5 @@ import { z } from "zod"; -import { - isExternalMapProvider, - type ExternalMapProvider, -} from "./external-map"; +import { externalMapProviderSchema } from "./external-map"; export const MAP_ZOOM = { min: 2, @@ -25,9 +22,9 @@ const pluginSettingsSchema = z mapStyle: z .enum(["auto", "light", "dark"]) .catch(DEFAULT_PLUGIN_SETTINGS.mapStyle), - externalMapProvider: z - .custom(isExternalMapProvider) - .catch(DEFAULT_PLUGIN_SETTINGS.externalMapProvider), + externalMapProvider: externalMapProviderSchema.catch( + DEFAULT_PLUGIN_SETTINGS.externalMapProvider, + ), zoom: z .number() .min(MAP_ZOOM.min) From 16d47c9b21ff58bffaa0e38e0a1fd2eccd67f5d5 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:50:30 +0800 Subject: [PATCH 4/4] test: remove redundant provider schema coverage --- src/lib/external-map.test.ts | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/lib/external-map.test.ts b/src/lib/external-map.test.ts index f9ab7f6..774e591 100644 --- a/src/lib/external-map.test.ts +++ b/src/lib/external-map.test.ts @@ -1,9 +1,5 @@ import { describe, expect, it } from "vitest"; -import { - externalMapProviderSchema, - getExternalMap, - type ExternalMapProvider, -} from "./external-map"; +import { getExternalMap, type ExternalMapProvider } from "./external-map"; import type { Coordinates } from "../types"; interface ExternalMapTestCase { @@ -66,17 +62,3 @@ describe("getExternalMap", () => { }, ); }); - -describe("externalMapProviderSchema", () => { - it("accepts every registered provider", () => { - const providers = testCases.map(({ provider }) => provider); - - expect( - providers.map((provider) => externalMapProviderSchema.parse(provider)), - ).toEqual(providers); - }); - - it("rejects unknown providers", () => { - expect(externalMapProviderSchema.safeParse("unknown").success).toBe(false); - }); -});