support custom userdata

This commit is contained in:
Julian Freeman
2026-04-16 14:50:38 -04:00
parent 436797abfa
commit a8ad89074f
9 changed files with 748 additions and 76 deletions

View File

@@ -6,20 +6,30 @@ const {
activeSection,
bookmarkProfilesExpanded,
bookmarkSortKey,
browserConfigs,
browserMonogram,
browsers,
configError,
configMonogram,
configsLoading,
createConfigForm,
createCustomBrowserConfig,
currentBrowser,
deleteCustomBrowserConfig,
domainFromUrl,
error,
extensionMonogram,
extensionProfilesExpanded,
extensionSortKey,
isDeletingConfig,
isOpeningProfile,
loading,
openProfileError,
openBrowserProfile,
page,
profileSortKey,
scanBrowsers,
refreshAll,
savingConfig,
sectionCount,
selectedBrowserId,
sortedBookmarks,
@@ -45,9 +55,9 @@ const {
v-for="browser in browsers"
:key="browser.browserId"
class="browser-nav-item"
:class="[browser.browserId, { active: browser.browserId === currentBrowser?.browserId }]"
:class="[browser.browserFamilyId ?? browser.browserId, { active: browser.browserId === currentBrowser?.browserId }]"
type="button"
@click="selectedBrowserId = browser.browserId"
@click="selectedBrowserId = browser.browserId; page = 'browserData'"
>
<div class="browser-nav-icon">{{ browserMonogram(browser.browserId) }}</div>
<div class="browser-nav-body">
@@ -61,23 +71,140 @@ const {
<p>No supported Chromium browser data was found yet.</p>
</div>
<button class="refresh-button sidebar-refresh" type="button" @click="scanBrowsers">
{{ loading ? "Scanning..." : "Refresh" }}
<button class="refresh-button sidebar-refresh" type="button" @click="refreshAll">
{{ loading || configsLoading ? "Refreshing..." : "Refresh" }}
</button>
</aside>
<main class="content-panel">
<section v-if="loading" class="state-panel">
<p class="eyebrow">Scanning</p>
<h2>Reading local browser data</h2>
<p>Profiles, installed extensions, and bookmarks are being collected.</p>
<section class="page-tabs">
<button
class="page-tab"
:class="{ active: page === 'browserData' }"
type="button"
@click="page = 'browserData'"
>
Browser Data
</button>
<button
class="page-tab"
:class="{ active: page === 'configuration' }"
type="button"
@click="page = 'configuration'"
>
Configuration
</button>
</section>
<section v-else-if="error" class="state-panel error">
<p class="eyebrow">Error</p>
<h2>Scan failed</h2>
<p>{{ error }}</p>
</section>
<template v-if="page === 'configuration'">
<section class="content-scroll-area">
<section class="content-section">
<div v-if="configError" class="inline-error">
{{ configError }}
</div>
<div class="config-form-card">
<div class="config-form-header">
<h3>Add Custom Browser</h3>
<p>Provide a name, executable path, and Chromium user data path.</p>
</div>
<div class="config-form-grid">
<label class="field-group">
<span>Name</span>
<input v-model="createConfigForm.name" placeholder="Work Chrome" />
</label>
<label class="field-group">
<span>Executable Path</span>
<input
v-model="createConfigForm.executablePath"
placeholder="C:\Program Files\...\chrome.exe"
/>
</label>
<label class="field-group field-span">
<span>User Data Path</span>
<input
v-model="createConfigForm.userDataPath"
placeholder="C:\Users\...\User Data"
/>
</label>
</div>
<div class="config-form-actions">
<button
class="primary-button"
type="button"
:disabled="savingConfig"
@click="createCustomBrowserConfig"
>
{{ savingConfig ? "Saving..." : "Add Config" }}
</button>
</div>
</div>
<div v-if="configsLoading" class="empty-card">
<p>Loading browser configs...</p>
</div>
<div v-else class="stack-list">
<article
v-for="config in browserConfigs"
:key="config.id"
class="config-card"
>
<div class="config-card-header">
<div class="config-card-lead">
<div class="browser-nav-icon config-icon">
{{ configMonogram(config) }}
</div>
<div>
<div class="config-title-row">
<h4>{{ config.name }}</h4>
<span class="badge neutral">
{{ config.source === "default" ? "Default" : "Custom" }}
</span>
</div>
<p class="config-id">{{ config.id }}</p>
</div>
</div>
<button
v-if="config.deletable"
class="danger-button"
type="button"
:disabled="isDeletingConfig(config.id)"
@click="deleteCustomBrowserConfig(config.id)"
>
{{ isDeletingConfig(config.id) ? "Deleting..." : "Delete" }}
</button>
</div>
<div class="config-meta">
<div class="config-meta-row">
<span class="config-label">Executable</span>
<p :title="config.executablePath">{{ config.executablePath || "Not resolved" }}</p>
</div>
<div class="config-meta-row">
<span class="config-label">User Data</span>
<p :title="config.userDataPath">{{ config.userDataPath }}</p>
</div>
</div>
</article>
</div>
</section>
</section>
</template>
<template v-else-if="loading">
<section class="state-panel">
<p class="eyebrow">Scanning</p>
<h2>Reading local browser data</h2>
<p>Profiles, installed extensions, and bookmarks are being collected.</p>
</section>
</template>
<template v-else-if="error">
<section class="state-panel error">
<p class="eyebrow">Error</p>
<h2>Scan failed</h2>
<p>{{ error }}</p>
</section>
</template>
<template v-else-if="currentBrowser">
<section class="section-tabs">
@@ -129,11 +256,7 @@ const {
</div>
<div v-if="sortedProfiles.length" class="stack-list">
<article
v-for="profile in sortedProfiles"
:key="profile.id"
class="profile-card"
>
<article v-for="profile in sortedProfiles" :key="profile.id" class="profile-card">
<div class="profile-avatar">
<img
v-if="profile.avatarDataUrl"
@@ -247,11 +370,7 @@ const {
</div>
<div v-if="sortedBookmarks.length" class="bookmark-list">
<article
v-for="bookmark in sortedBookmarks"
:key="bookmark.url"
class="bookmark-card"
>
<article v-for="bookmark in sortedBookmarks" :key="bookmark.url" class="bookmark-card">
<div class="bookmark-body">
<div class="bookmark-topline">
<h4>{{ bookmark.title }}</h4>
@@ -290,11 +409,13 @@ const {
</div>
</template>
<section v-else class="state-panel">
<p class="eyebrow">No Data</p>
<h2>No supported browser was detected</h2>
<p>Install or sign in to Chrome, Edge, or Brave and refresh the scan.</p>
</section>
<template v-else>
<section class="state-panel">
<p class="eyebrow">No Data</p>
<h2>No supported browser was detected</h2>
<p>Install or sign in to Chrome, Edge, or Brave and refresh the scan.</p>
</section>
</template>
</main>
</div>
</template>