diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt
index 59731197a..6d068e88e 100644
--- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt
+++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/autofill/AutofillService.kt
@@ -384,8 +384,9 @@ class AutofillService : AutofillService() {
val appInfo = fieldFinder.getAppInfo()
val encodedUrl = appInfo?.let { java.net.URLEncoder.encode(it, "UTF-8") } ?: ""
- // Create deep link URL
- val deepLinkUrl = "aliasvault://items/add-edit-page?itemUrl=$encodedUrl"
+ // Open the action picker so the user can choose between linking this app
+ // to an existing credential or creating a new one.
+ val deepLinkUrl = "aliasvault://items/autofill-open-app?itemUrl=$encodedUrl"
// Add a click listener to open AliasVault app with deep link
val intent = Intent(Intent.ACTION_VIEW).apply {
@@ -553,10 +554,11 @@ class AutofillService : AutofillService() {
val dataSetBuilder = Dataset.Builder(presentation)
- // Create deep link URL to open the items page
+ // Open the action picker so the user can choose between linking this app
+ // to an existing credential or creating a new one.
val appInfo = fieldFinder.getAppInfo()
val encodedUrl = appInfo?.let { java.net.URLEncoder.encode(it, "UTF-8") } ?: ""
- val deepLinkUrl = "aliasvault://items?itemUrl=$encodedUrl"
+ val deepLinkUrl = "aliasvault://items/autofill-open-app?itemUrl=$encodedUrl"
// Add a click listener to open AliasVault app with deep link
val intent = Intent(Intent.ACTION_VIEW).apply {
diff --git a/apps/mobile-app/android/app/src/main/res/values/strings.xml b/apps/mobile-app/android/app/src/main/res/values/strings.xml
index 08025064f..22e5ac4eb 100644
--- a/apps/mobile-app/android/app/src/main/res/values/strings.xml
+++ b/apps/mobile-app/android/app/src/main/res/values/strings.xml
@@ -11,7 +11,7 @@
Back
An unknown error occurred
Failed to retrieve, open app
- No match found, create new?
+ No match found
Open app
Vault locked
Store Encryption Key
diff --git a/apps/mobile-app/app/(tabs)/items/_layout.tsx b/apps/mobile-app/app/(tabs)/items/_layout.tsx
index d975f5a15..695460289 100644
--- a/apps/mobile-app/app/(tabs)/items/_layout.tsx
+++ b/apps/mobile-app/app/(tabs)/items/_layout.tsx
@@ -52,6 +52,28 @@ export default function ItemsLayout(): React.ReactNode {
...defaultHeaderOptions,
}}
/>
+
+
+
- {t('items.switchBackToBrowser')}
+ {t('items.switchBackToOriginalApp')}
diff --git a/apps/mobile-app/app/(tabs)/items/autofill-link-existing.tsx b/apps/mobile-app/app/(tabs)/items/autofill-link-existing.tsx
new file mode 100644
index 000000000..ee5b8bf60
--- /dev/null
+++ b/apps/mobile-app/app/(tabs)/items/autofill-link-existing.tsx
@@ -0,0 +1,384 @@
+import MaterialIcons from '@expo/vector-icons/MaterialIcons';
+import { useLocalSearchParams, useNavigation, useRouter } from 'expo-router';
+import { useCallback, useEffect, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { FlatList, Platform, StyleSheet, TextInput, TouchableOpacity, View, type ListRenderItem } from 'react-native';
+import Toast from 'react-native-toast-message';
+
+import type { Item, ItemField } from '@/utils/dist/core/models/vault';
+import {
+ FieldKey,
+ FieldTypes,
+ ItemTypes,
+ getFieldValue,
+ getFieldValues,
+} from '@/utils/dist/core/models/vault';
+
+import { useColors } from '@/hooks/useColorScheme';
+import { useVaultMutate } from '@/hooks/useVaultMutate';
+
+import { ItemIcon } from '@/components/items/ItemIcon';
+import { ThemedSafeAreaView } from '@/components/themed/ThemedSafeAreaView';
+import { ThemedText } from '@/components/themed/ThemedText';
+import { ThemedView } from '@/components/themed/ThemedView';
+import { RobustPressable } from '@/components/ui/RobustPressable';
+import { useDb } from '@/context/DbContext';
+import { useDialog } from '@/context/DialogContext';
+
+/**
+ * Screen for picking an existing credential to attach the autofill
+ * URL/package identifier to. After saving, the user is navigated to a
+ * confirmation screen so they know the link was created and that the
+ * next autofill attempt for the same app should succeed.
+ */
+export default function AutofillLinkExistingScreen(): React.ReactNode {
+ const router = useRouter();
+ const navigation = useNavigation();
+ const colors = useColors();
+ const { t } = useTranslation();
+ const dbContext = useDb();
+ const { executeVaultMutation } = useVaultMutate();
+ const { showConfirm } = useDialog();
+ const { itemUrl } = useLocalSearchParams<{ itemUrl?: string }>();
+
+ const decodedAppInfo = useMemo(() => {
+ if (!itemUrl) {
+ return '';
+ }
+ try {
+ return decodeURIComponent(itemUrl);
+ } catch {
+ return itemUrl;
+ }
+ }, [itemUrl]);
+
+ const [items, setItems] = useState- ([]);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [isSaving, setIsSaving] = useState(false);
+
+ /**
+ * Load Login-typed items from the local vault. We don't need credit
+ * cards / notes here because the URL field only applies to logins.
+ */
+ useEffect(() => {
+ /**
+ * Fetch login items from the vault.
+ */
+ const load = async (): Promise => {
+ try {
+ const all = await dbContext.sqliteClient!.items.getAll();
+ const logins = all.filter(item => item.ItemType === ItemTypes.Login);
+ setItems(logins);
+ } catch (err) {
+ console.error('Error loading items for autofill link flow:', err);
+ }
+ };
+ if (dbContext.dbAvailable) {
+ load();
+ }
+ }, [dbContext.dbAvailable, dbContext.sqliteClient]);
+
+ /**
+ * Header title — back navigation is handled by the stack's default
+ * back arrow, so we don't need a custom right-side button here.
+ */
+ useEffect(() => {
+ navigation.setOptions({
+ title: t('items.autofillLinkExisting.title'),
+ });
+ }, [navigation, t]);
+
+ /**
+ * Filter items using a substring match across name, username, email,
+ * and any of the credential's existing URLs (multi-value aware).
+ */
+ const filteredItems = useMemo(() => {
+ const q = searchQuery.trim().toLowerCase();
+ if (!q) {
+ return items;
+ }
+ const words = q.split(/\s+/).filter(Boolean);
+ return items.filter(item => {
+ const haystacks: string[] = [
+ item.Name?.toLowerCase() ?? '',
+ getFieldValue(item, FieldKey.LoginUsername)?.toLowerCase() ?? '',
+ getFieldValue(item, FieldKey.LoginEmail)?.toLowerCase() ?? '',
+ ...getFieldValues(item, FieldKey.LoginUrl).map(u => u.toLowerCase()),
+ ];
+ return words.every(word => haystacks.some(h => h.includes(word)));
+ });
+ }, [items, searchQuery]);
+
+ /**
+ * Append the autofill URL/package to the chosen item's `login.url`
+ * multi-value field and persist via the vault mutation pipeline.
+ */
+ const linkItem = useCallback(async (item: Item): Promise => {
+ if (!decodedAppInfo) {
+ return;
+ }
+ setIsSaving(true);
+ try {
+ const existingField = item.Fields.find(f => f.FieldKey === FieldKey.LoginUrl);
+ const existingValues = existingField
+ ? Array.isArray(existingField.Value) ? existingField.Value : [existingField.Value]
+ : [];
+
+ /**
+ * After a successful link the user shouldn't be able to swipe/back
+ * their way into the "what would you like to do?" screen — they're
+ * done. Pop everything in the items stack so the success screen
+ * sits directly on top of the items home.
+ */
+ const navigateToSuccess = (): void => {
+ router.dismissTo('/(tabs)/items');
+ router.push({
+ pathname: '/(tabs)/items/autofill-url-added',
+ params: { itemName: item.Name ?? '', itemUrl: decodedAppInfo },
+ });
+ };
+
+ /*
+ * Avoid duplicates — if the URL is already linked, skip the write
+ * and just send the user to the confirmation screen.
+ */
+ if (existingValues.includes(decodedAppInfo)) {
+ navigateToSuccess();
+ return;
+ }
+
+ const newValues = [...existingValues.filter(v => v && v.length > 0), decodedAppInfo];
+
+ let updatedFields: ItemField[];
+ if (existingField) {
+ updatedFields = item.Fields.map(f =>
+ f.FieldKey === FieldKey.LoginUrl ? { ...f, Value: newValues } : f
+ );
+ } else {
+ const newField: ItemField = {
+ FieldKey: FieldKey.LoginUrl,
+ Label: FieldKey.LoginUrl,
+ FieldType: FieldTypes.URL,
+ Value: newValues,
+ IsHidden: false,
+ DisplayOrder: 100,
+ IsCustomField: false,
+ EnableHistory: false,
+ };
+ updatedFields = [...item.Fields, newField];
+ }
+
+ const itemToSave: Item = {
+ ...item,
+ Fields: updatedFields,
+ UpdatedAt: new Date().toISOString(),
+ };
+
+ await executeVaultMutation(async () => {
+ await dbContext.sqliteClient!.items.update(itemToSave);
+ });
+
+ navigateToSuccess();
+ } catch (err) {
+ console.error('Error linking URL to existing item:', err);
+ Toast.show({
+ type: 'error',
+ text1: t('common.error'),
+ text2: t('common.errors.unknownErrorTryAgain'),
+ });
+ } finally {
+ setIsSaving(false);
+ }
+ }, [decodedAppInfo, dbContext.sqliteClient, executeVaultMutation, router, t]);
+
+ /**
+ * Confirm with the user before mutating the credential.
+ */
+ const handleSelectItem = useCallback((item: Item) => {
+ if (isSaving) {
+ return;
+ }
+ showConfirm(
+ t('items.autofillLinkExisting.confirmTitle'),
+ t('items.autofillLinkExisting.confirmMessage', {
+ url: decodedAppInfo,
+ name: item.Name ?? t('items.untitled'),
+ }),
+ t('common.confirm'),
+ () => linkItem(item),
+ );
+ }, [decodedAppInfo, linkItem, showConfirm, t, isSaving]);
+
+ const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ emptyState: {
+ alignItems: 'center',
+ paddingHorizontal: 24,
+ paddingVertical: 40,
+ },
+ emptyText: {
+ color: colors.textMuted,
+ fontSize: 14,
+ lineHeight: 20,
+ textAlign: 'center',
+ },
+ headerArea: {
+ paddingBottom: 8,
+ paddingHorizontal: 16,
+ paddingTop: 12,
+ },
+ introText: {
+ color: colors.textMuted,
+ fontSize: 14,
+ lineHeight: 20,
+ marginBottom: 12,
+ },
+ itemDetail: {
+ color: colors.textMuted,
+ fontSize: 13,
+ marginTop: 2,
+ },
+ itemName: {
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ itemRow: {
+ alignItems: 'center',
+ backgroundColor: colors.accentBackground,
+ borderColor: colors.accentBorder,
+ borderRadius: 10,
+ borderWidth: 1,
+ flexDirection: 'row',
+ gap: 12,
+ marginHorizontal: 16,
+ marginVertical: 4,
+ padding: 12,
+ },
+ itemTextWrapper: {
+ flex: 1,
+ },
+ listContent: {
+ paddingBottom: 32,
+ },
+ searchClearButton: {
+ padding: 4,
+ position: 'absolute',
+ right: 8,
+ top: 4,
+ },
+ searchClearText: {
+ color: colors.textMuted,
+ fontSize: 20,
+ },
+ searchContainer: {
+ marginTop: 12,
+ position: 'relative',
+ },
+ searchIcon: {
+ left: 12,
+ position: 'absolute',
+ top: 11,
+ zIndex: 1,
+ },
+ searchInput: {
+ backgroundColor: colors.accentBackground,
+ borderColor: colors.accentBorder,
+ borderRadius: 8,
+ borderWidth: 1,
+ color: colors.text,
+ fontSize: 16,
+ height: 40,
+ paddingLeft: 40,
+ paddingRight: Platform.OS === 'android' ? 40 : 12,
+ },
+ });
+
+ /**
+ * Render an individual credential row.
+ */
+ const renderItem: ListRenderItem
- = useCallback((info) => {
+ const row = info.item;
+ const username = getFieldValue(row, FieldKey.LoginUsername);
+ const email = getFieldValue(row, FieldKey.LoginEmail);
+ const detail = username || email || '';
+
+ return (
+ handleSelectItem(row)}
+ testID={`autofill-link-item-${row.Id}`}
+ >
+
+
+
+ {row.Name || t('items.untitled')}
+
+ {detail.length > 0 && (
+
+ {detail}
+
+ )}
+
+
+
+ );
+ }, [colors.primary, handleSelectItem, styles, t]);
+
+ return (
+
+
+
+ {t('items.autofillLinkExisting.intro', { target: decodedAppInfo })}
+
+
+
+
+
+ {Platform.OS === 'android' && searchQuery.length > 0 && (
+ setSearchQuery('')}
+ testID="autofill-link-clear-search"
+ >
+ ×
+
+ )}
+
+
+
+ item.Id}
+ renderItem={renderItem}
+ contentContainerStyle={styles.listContent}
+ keyboardShouldPersistTaps="handled"
+ ListEmptyComponent={(
+
+
+ {searchQuery
+ ? t('items.noMatchingItemsSearch', { search: searchQuery })
+ : t('items.noItemsFound')}
+
+
+ )}
+ />
+
+ );
+}
diff --git a/apps/mobile-app/app/(tabs)/items/autofill-open-app.tsx b/apps/mobile-app/app/(tabs)/items/autofill-open-app.tsx
new file mode 100644
index 000000000..c352294b3
--- /dev/null
+++ b/apps/mobile-app/app/(tabs)/items/autofill-open-app.tsx
@@ -0,0 +1,225 @@
+import MaterialIcons from '@expo/vector-icons/MaterialIcons';
+import { useLocalSearchParams, useRouter } from 'expo-router';
+import { useCallback, useEffect, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { AppState, StyleSheet, View } from 'react-native';
+
+import { useColors } from '@/hooks/useColorScheme';
+
+import { ThemedSafeAreaView } from '@/components/themed/ThemedSafeAreaView';
+import { ThemedText } from '@/components/themed/ThemedText';
+import { ThemedView } from '@/components/themed/ThemedView';
+import { RobustPressable } from '@/components/ui/RobustPressable';
+
+/**
+ * Landing screen shown when the user opens AliasVault from the Android
+ * autofill popup, either via the "Open app" button or via "No match
+ * found". The screen is intentionally neutral so it works in both
+ * cases (no match, or there are matches but the user wants to do
+ * something else, like add a second account or link a new app
+ * identifier to an existing credential).
+ *
+ * Lets the user choose between:
+ * - Linking an existing credential (so the app's package/URL is added
+ * to that credential and future autofill prompts succeed
+ * automatically), or
+ * - Creating a brand-new credential pre-filled with the app's URL.
+ *
+ * The user can dismiss via the system back arrow if neither applies.
+ */
+export default function AutofillOpenAppScreen(): React.ReactNode {
+ const router = useRouter();
+ const colors = useColors();
+ const { t } = useTranslation();
+ const { itemUrl } = useLocalSearchParams<{ itemUrl?: string }>();
+
+ const decodedAppInfo = useMemo(() => {
+ if (!itemUrl) {
+ return '';
+ }
+ try {
+ return decodeURIComponent(itemUrl);
+ } catch {
+ return itemUrl;
+ }
+ }, [itemUrl]);
+
+ /**
+ * Navigate to the credential picker so the user can attach this URL
+ * to an already-existing credential.
+ */
+ const handleFindExisting = useCallback(() => {
+ router.push(
+ `/(tabs)/items/autofill-link-existing?itemUrl=${encodeURIComponent(decodedAppInfo)}`
+ );
+ }, [router, decodedAppInfo]);
+
+ /**
+ * Navigate to the existing add-edit-page deep-link target with the
+ * URL pre-populated, mirroring the previous behaviour.
+ */
+ const handleCreateNew = useCallback(() => {
+ router.replace(
+ `/(tabs)/items/add-edit-page?itemUrl=${encodeURIComponent(decodedAppInfo)}`
+ );
+ }, [router, decodedAppInfo]);
+
+ /**
+ * Auto-dismiss when the app goes to the background — matches the
+ * pattern in autofill-item-created so users can't get stuck here.
+ */
+ useEffect(() => {
+ const subscription = AppState.addEventListener('change', (nextAppState) => {
+ if (nextAppState === 'background') {
+ router.back();
+ }
+ });
+ return (): void => {
+ subscription.remove();
+ };
+ }, [router]);
+
+ const styles = StyleSheet.create({
+ actionDescription: {
+ color: colors.textMuted,
+ fontSize: 14,
+ lineHeight: 20,
+ marginTop: 4,
+ },
+ actionRow: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ gap: 16,
+ },
+ actionTextWrapper: {
+ flex: 1,
+ },
+ actionTitle: {
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ appInfoBox: {
+ backgroundColor: colors.accentBackground,
+ borderColor: colors.accentBorder,
+ borderRadius: 8,
+ borderWidth: 1,
+ marginBottom: 24,
+ paddingHorizontal: 12,
+ paddingVertical: 10,
+ width: '100%',
+ },
+ appInfoLabel: {
+ color: colors.textMuted,
+ fontSize: 12,
+ marginBottom: 2,
+ textTransform: 'uppercase',
+ },
+ appInfoValue: {
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ container: {
+ flex: 1,
+ },
+ content: {
+ alignItems: 'stretch',
+ flex: 1,
+ paddingHorizontal: 20,
+ paddingTop: 24,
+ },
+ introText: {
+ color: colors.textMuted,
+ fontSize: 15,
+ lineHeight: 22,
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+ optionCard: {
+ backgroundColor: colors.accentBackground,
+ borderColor: colors.accentBorder,
+ borderRadius: 12,
+ borderWidth: 1,
+ marginBottom: 12,
+ padding: 16,
+ },
+ optionIconWrapper: {
+ alignItems: 'center',
+ backgroundColor: colors.background,
+ borderRadius: 24,
+ height: 48,
+ justifyContent: 'center',
+ width: 48,
+ },
+ optionPrimaryIconWrapper: {
+ alignItems: 'center',
+ backgroundColor: colors.primary + '20',
+ borderRadius: 24,
+ height: 48,
+ justifyContent: 'center',
+ width: 48,
+ },
+ });
+
+ return (
+
+
+
+ {t('items.autofillOpenApp.description')}
+
+
+ {decodedAppInfo.length > 0 && (
+
+
+ {t('items.autofillOpenApp.appOrUrlLabel')}
+
+
+ {decodedAppInfo}
+
+
+ )}
+
+
+
+
+
+
+
+
+ {t('items.autofillOpenApp.findExistingTitle')}
+
+
+ {t('items.autofillOpenApp.findExistingDescription')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t('items.autofillOpenApp.createNewTitle')}
+
+
+ {t('items.autofillOpenApp.createNewDescription')}
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/mobile-app/app/(tabs)/items/autofill-url-added.tsx b/apps/mobile-app/app/(tabs)/items/autofill-url-added.tsx
new file mode 100644
index 000000000..255f06807
--- /dev/null
+++ b/apps/mobile-app/app/(tabs)/items/autofill-url-added.tsx
@@ -0,0 +1,153 @@
+import MaterialIcons from '@expo/vector-icons/MaterialIcons';
+import { useLocalSearchParams, useRouter } from 'expo-router';
+import { useEffect, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { AppState, StyleSheet, View } from 'react-native';
+
+import { useColors } from '@/hooks/useColorScheme';
+
+import { ThemedSafeAreaView } from '@/components/themed/ThemedSafeAreaView';
+import { ThemedText } from '@/components/themed/ThemedText';
+import { ThemedView } from '@/components/themed/ThemedView';
+
+/**
+ * Confirmation screen shown after the user successfully linked an
+ * autofill URL/package to an existing credential. Mirrors the
+ * autofill-item-created pattern: auto-dismisses when the app is
+ * sent to background so the user can return to the original app
+ * and trigger autofill again.
+ */
+export default function AutofillUrlAddedScreen(): React.ReactNode {
+ const router = useRouter();
+ const colors = useColors();
+ const { t } = useTranslation();
+ const { itemName, itemUrl } = useLocalSearchParams<{
+ itemName?: string;
+ itemUrl?: string;
+ }>();
+
+ const decodedItemName = useMemo(() => {
+ if (!itemName) {
+ return t('items.untitled');
+ }
+ try {
+ return decodeURIComponent(itemName);
+ } catch {
+ return itemName;
+ }
+ }, [itemName, t]);
+
+ const decodedItemUrl = useMemo(() => {
+ if (!itemUrl) {
+ return '';
+ }
+ try {
+ return decodeURIComponent(itemUrl);
+ } catch {
+ return itemUrl;
+ }
+ }, [itemUrl]);
+
+ /*
+ * Auto-dismiss when backgrounded so when the user comes back to the
+ * app they see the items home rather than this success screen. The
+ * stack was reset before navigating here, so router.back() pops
+ * straight to the items list.
+ */
+ useEffect(() => {
+ const subscription = AppState.addEventListener('change', (nextAppState) => {
+ if (nextAppState === 'background') {
+ router.back();
+ }
+ });
+ return (): void => {
+ subscription.remove();
+ };
+ }, [router]);
+
+ const styles = StyleSheet.create({
+ boldMessage: {
+ fontWeight: 'bold',
+ marginTop: 20,
+ },
+ container: {
+ flex: 1,
+ },
+ content: {
+ alignItems: 'center',
+ flex: 1,
+ justifyContent: 'center',
+ padding: 24,
+ },
+ detailBox: {
+ backgroundColor: colors.accentBackground,
+ borderColor: colors.accentBorder,
+ borderRadius: 8,
+ borderWidth: 1,
+ marginBottom: 16,
+ paddingHorizontal: 12,
+ paddingVertical: 10,
+ width: '100%',
+ },
+ detailLabel: {
+ color: colors.textMuted,
+ fontSize: 12,
+ marginBottom: 2,
+ textTransform: 'uppercase',
+ },
+ detailValue: {
+ fontSize: 14,
+ fontWeight: '600',
+ },
+ headerRightButton: {
+ padding: 10,
+ paddingRight: 0,
+ },
+ iconContainer: {
+ marginBottom: 24,
+ },
+ message: {
+ fontSize: 16,
+ lineHeight: 24,
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+ title: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ marginBottom: 16,
+ textAlign: 'center',
+ },
+ });
+
+ return (
+
+
+
+
+
+
+ {t('items.autofillUrlAdded.title')}
+
+
+ {t('items.autofillUrlAdded.message', { name: decodedItemName })}
+
+
+ {decodedItemUrl.length > 0 && (
+
+
+ {t('items.autofillOpenApp.appOrUrlLabel')}
+
+
+ {decodedItemUrl}
+
+
+ )}
+
+
+ {t('items.switchBackToOriginalApp')}
+
+
+
+ );
+}
diff --git a/apps/mobile-app/i18n/locales/en.json b/apps/mobile-app/i18n/locales/en.json
index e48bbe50d..544358914 100644
--- a/apps/mobile-app/i18n/locales/en.json
+++ b/apps/mobile-app/i18n/locales/en.json
@@ -443,7 +443,26 @@
"vaultSyncedSuccessfully": "Vault synced successfully",
"vaultUpToDate": "Vault is up-to-date",
"offlineMessage": "You are offline. Please connect to the internet to sync your vault.",
- "switchBackToBrowser": "Switch back to your browser to continue.",
+ "switchBackToOriginalApp": "Switch back to the original app to continue.",
+ "autofillOpenApp": {
+ "title": "Autofill request",
+ "description": "Choose an action for the app or website that requested autofill.",
+ "appOrUrlLabel": "App or URL",
+ "findExistingTitle": "Link to existing credential",
+ "findExistingDescription": "Select an existing credential from your vault to link to the address above.",
+ "createNewTitle": "Create new credential",
+ "createNewDescription": "Add a new credential to your vault for this app or URL."
+ },
+ "autofillLinkExisting": {
+ "title": "Link to existing credential",
+ "intro": "Select an existing credential from your vault to link to \"{{target}}\".",
+ "confirmTitle": "Link credential?",
+ "confirmMessage": "Are you sure you want to link \"{{url}}\" to this credential?"
+ },
+ "autofillUrlAdded": {
+ "title": "Credential linked",
+ "message": "Autofill should now offer this credential the next time you open the app."
+ },
"filters": {
"all": "Items",
"showFolders": "Folders",