refactor frontend

This commit is contained in:
Julian Freeman
2026-04-16 16:26:21 -04:00
parent 6cc694754f
commit dabd8789f4
14 changed files with 1574 additions and 1357 deletions

View File

@@ -0,0 +1,337 @@
import { computed, onMounted, ref, watch } from "vue";
import { invoke } from "@tauri-apps/api/core";
import { open } from "@tauri-apps/plugin-dialog";
import { sortBookmarks, sortExtensions, sortProfiles } from "../utils/sort";
import type {
ActiveSection,
AppPage,
BookmarkSortKey,
BrowserConfigEntry,
BrowserConfigListResponse,
BrowserView,
CreateCustomBrowserConfigInput,
ExtensionSortKey,
ProfileSortKey,
ScanResponse,
} from "../types/browser";
export function useBrowserManager() {
const page = ref<AppPage>("browserData");
const loading = ref(true);
const error = ref("");
const openProfileError = ref("");
const openingProfileKey = ref("");
const response = ref<ScanResponse>({ browsers: [] });
const browserConfigs = ref<BrowserConfigEntry[]>([]);
const configsLoading = ref(true);
const configError = ref("");
const savingConfig = ref(false);
const deletingConfigId = ref("");
const createConfigForm = ref<CreateCustomBrowserConfigInput>({
name: "",
iconKey: "chrome",
executablePath: "",
userDataPath: "",
});
const selectedBrowserId = ref("");
const activeSection = ref<ActiveSection>("profiles");
const expandedExtensionIds = ref<string[]>([]);
const expandedBookmarkUrls = ref<string[]>([]);
const profileSortKey = ref<ProfileSortKey>("name");
const extensionSortKey = ref<ExtensionSortKey>("name");
const bookmarkSortKey = ref<BookmarkSortKey>("title");
const browsers = computed(() => response.value.browsers);
const currentBrowser = computed<BrowserView | null>(
() =>
browsers.value.find((browser) => browser.browserId === selectedBrowserId.value) ??
browsers.value[0] ??
null,
);
const sortedProfiles = computed(() =>
sortProfiles(currentBrowser.value?.profiles ?? [], profileSortKey.value),
);
const sortedExtensions = computed(() =>
sortExtensions(currentBrowser.value?.extensions ?? [], extensionSortKey.value),
);
const sortedBookmarks = computed(() =>
sortBookmarks(currentBrowser.value?.bookmarks ?? [], bookmarkSortKey.value),
);
watch(
browsers,
(items) => {
if (!items.length) {
selectedBrowserId.value = "";
return;
}
const hasSelected = items.some(
(browser) => browser.browserId === selectedBrowserId.value,
);
if (!hasSelected) {
selectedBrowserId.value = items[0].browserId;
}
},
{ immediate: true },
);
watch(selectedBrowserId, () => {
expandedExtensionIds.value = [];
expandedBookmarkUrls.value = [];
openProfileError.value = "";
});
async function loadBrowserConfigs() {
configsLoading.value = true;
configError.value = "";
try {
const result = await invoke<BrowserConfigListResponse>("list_browser_configs");
browserConfigs.value = result.configs;
} catch (loadError) {
configError.value =
loadError instanceof Error ? loadError.message : "Failed to load browser configs.";
} finally {
configsLoading.value = false;
}
}
async function scanBrowsers() {
loading.value = true;
error.value = "";
try {
response.value = await invoke<ScanResponse>("scan_browsers");
} catch (scanError) {
error.value =
scanError instanceof Error
? scanError.message
: "Failed to scan browser data.";
} finally {
loading.value = false;
}
}
async function refreshAll() {
await Promise.all([loadBrowserConfigs(), scanBrowsers()]);
}
async function openBrowserProfile(browserId: string, profileId: string) {
const profileKey = `${browserId}:${profileId}`;
openingProfileKey.value = profileKey;
openProfileError.value = "";
try {
await invoke("open_browser_profile", {
browserId,
profileId,
});
} catch (openError) {
openProfileError.value =
openError instanceof Error
? openError.message
: "Failed to open the selected browser profile.";
} finally {
openingProfileKey.value = "";
}
}
async function createCustomBrowserConfig() {
savingConfig.value = true;
configError.value = "";
try {
const result = await invoke<BrowserConfigListResponse>("create_custom_browser_config", {
input: createConfigForm.value,
});
browserConfigs.value = result.configs;
createConfigForm.value = {
name: "",
iconKey: "chrome",
executablePath: "",
userDataPath: "",
};
await scanBrowsers();
} catch (saveError) {
configError.value =
saveError instanceof Error ? saveError.message : "Failed to create browser config.";
} finally {
savingConfig.value = false;
}
}
async function deleteCustomBrowserConfig(configId: string) {
deletingConfigId.value = configId;
configError.value = "";
try {
const result = await invoke<BrowserConfigListResponse>("delete_custom_browser_config", {
configId,
});
browserConfigs.value = result.configs;
await scanBrowsers();
} catch (deleteError) {
configError.value =
deleteError instanceof Error ? deleteError.message : "Failed to delete browser config.";
} finally {
deletingConfigId.value = "";
}
}
async function pickExecutablePath() {
const selected = await open({
multiple: false,
directory: false,
filters: [
{
name: "Executable",
extensions: ["exe"],
},
],
});
if (typeof selected === "string") {
createConfigForm.value.executablePath = selected;
}
}
async function pickUserDataPath() {
const selected = await open({
multiple: false,
directory: true,
});
if (typeof selected === "string") {
createConfigForm.value.userDataPath = selected;
}
}
function isDeletingConfig(configId: string) {
return deletingConfigId.value === configId;
}
function isOpeningProfile(browserId: string, profileId: string) {
return openingProfileKey.value === `${browserId}:${profileId}`;
}
function browserMonogram(browserId: string) {
const current = browsers.value.find((browser) => browser.browserId === browserId);
const iconKey = current?.iconKey ?? current?.browserFamilyId;
if (iconKey === "chrome") return "CH";
if (iconKey === "edge") return "ED";
if (iconKey === "brave") return "BR";
const name = current?.browserName?.trim() ?? "";
if (name) {
const letters = name
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map((part) => part[0]);
if (letters.length) return letters.join("").toUpperCase();
}
return browserId.slice(0, 2).toUpperCase();
}
function configMonogram(config: BrowserConfigEntry) {
const iconKey = config.iconKey ?? config.browserFamilyId;
if (iconKey === "chrome") return "CH";
if (iconKey === "edge") return "ED";
if (iconKey === "brave") return "BR";
const letters = config.name
.trim()
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map((part) => part[0]);
return (letters.join("") || config.id.slice(0, 2)).toUpperCase();
}
function extensionMonogram(name: string) {
return name.trim().slice(0, 1).toUpperCase() || "?";
}
function domainFromUrl(url: string) {
try {
return new URL(url).hostname;
} catch {
return url;
}
}
function sectionCount(section: ActiveSection) {
if (!currentBrowser.value) return 0;
if (section === "profiles") return currentBrowser.value.profiles.length;
if (section === "extensions") return currentBrowser.value.extensions.length;
return currentBrowser.value.bookmarks.length;
}
function toggleExtensionProfiles(extensionId: string) {
expandedExtensionIds.value = expandedExtensionIds.value.includes(extensionId)
? expandedExtensionIds.value.filter((id) => id !== extensionId)
: [...expandedExtensionIds.value, extensionId];
}
function toggleBookmarkProfiles(url: string) {
expandedBookmarkUrls.value = expandedBookmarkUrls.value.includes(url)
? expandedBookmarkUrls.value.filter((value) => value !== url)
: [...expandedBookmarkUrls.value, url];
}
function extensionProfilesExpanded(extensionId: string) {
return expandedExtensionIds.value.includes(extensionId);
}
function bookmarkProfilesExpanded(url: string) {
return expandedBookmarkUrls.value.includes(url);
}
onMounted(() => {
void refreshAll();
});
return {
activeSection,
bookmarkProfilesExpanded,
bookmarkSortKey,
browserConfigs,
browserMonogram,
browsers,
configError,
configMonogram,
configsLoading,
createConfigForm,
createCustomBrowserConfig,
currentBrowser,
deleteCustomBrowserConfig,
domainFromUrl,
error,
extensionMonogram,
extensionProfilesExpanded,
extensionSortKey,
isDeletingConfig,
isOpeningProfile,
loading,
openProfileError,
openBrowserProfile,
page,
pickExecutablePath,
pickUserDataPath,
profileSortKey,
refreshAll,
savingConfig,
sectionCount,
selectedBrowserId,
sortedBookmarks,
sortedExtensions,
sortedProfiles,
toggleBookmarkProfiles,
toggleExtensionProfiles,
};
}