Merge pull request #2405 from SillyTavern/tag-import-setting

Tag import setting
This commit is contained in:
Cohee 2024-06-22 13:04:14 +03:00 committed by GitHub
commit 0f92c90b71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 168 additions and 93 deletions

View File

@ -3923,13 +3923,22 @@
<h4 data-i18n="Character Handling">
Character Handling
</h4>
<div title="If set in the advanced character definitions, this field will be displayed in the characters list." data-i18n="[title]If set in the advanced character definitions, this field will be displayed in the characters list.">
<div class="flex-container alignitemscenter" title="If set in the advanced character definitions, this field will be displayed in the characters list." data-i18n="[title]If set in the advanced character definitions, this field will be displayed in the characters list.">
<label for="aux_field"><small data-i18n="Char List Subheader">Char List Subheader</small></label>
<select id="aux_field">
<select id="aux_field" class="widthNatural flex1 margin0">
<option data-i18n="Character Version" value="character_version">Character Version</option>
<option data-i18n="Created by" value="creator">Created by</option>
</select>
</div>
<div data-newbie-hidden class="flex-container alignitemscenter" title="Defines on importing cards which action should be chosen for importing its listed tags. 'Ask' will always display the dialog." data-i18n="[title]Defines on importing cards which action should be chosen for importing its listed tags. 'Ask' will always display the dialog.">
<label for="tag_import_setting"><small data-i18n="Import Card Tags">Import Card Tags</small></label>
<select id="tag_import_setting" class="widthNatural flex1 margin0">
<option data-i18n="Ask" value="1">Ask</option>
<option data-i18n="None" value="2">None</option>
<option data-i18n="All" value="3">All</option>
<option data-i18n="Existing" value="4">Existing</option>
</select>
</div>
<label data-newbie-hidden class="checkbox_label" for="fuzzy_search_checkbox" title="Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring." data-i18n="[title]Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring">
<input id="fuzzy_search_checkbox" type="checkbox" />
<small data-i18n="Advanced Character Search">Advanced Character Search</small>
@ -3950,10 +3959,6 @@
<input id="show_card_avatar_urls" type="checkbox" />
<small data-i18n="Show avatar filenames">Show avatar filenames</small>
</label>
<label data-newbie-hidden class="checkbox_label" for="import_card_tags" title="Prompt to import embedded card tags on character import. Otherwise embedded tags are ignored." data-i18n="[title]Prompt to import embedded card tags on character import. Otherwise embedded tags are ignored">
<input id="import_card_tags" type="checkbox" />
<small data-i18n="Import Card Tags">Import Card Tags</small>
</label>
<label data-newbie-hidden class="checkbox_label" for="spoiler_free_mode" title="Hide character definitions from the editor panel behind a spoiler button." data-i18n="[title]Hide character definitions from the editor panel behind a spoiler button">
<input id="spoiler_free_mode" type="checkbox" />
<small data-i18n="Spoiler Free Mode">Spoiler Free Mode</small>

View File

@ -175,11 +175,12 @@ import {
createTagMapFromList,
renameTagKey,
importTags,
tag_filter_types,
tag_filter_type,
compareTagsForSort,
initTags,
applyTagsOnCharacterSelect,
applyTagsOnGroupSelect,
tag_import_setting,
} from './scripts/tags.js';
import {
SECRET_KEYS,
@ -1346,8 +1347,8 @@ export async function printCharacters(fullRefresh = false) {
verifyCharactersSearchSortRule();
// We are actually always reprinting filters, as it "doesn't hurt", and this way they are always up to date
printTagFilters(tag_filter_types.character);
printTagFilters(tag_filter_types.group_member);
printTagFilters(tag_filter_type.character);
printTagFilters(tag_filter_type.group_member);
// We are also always reprinting the lists on character/group edit window, as these ones doesn't get updated otherwise
applyTagsOnCharacterSelect();
@ -8474,7 +8475,7 @@ async function importCharacter(file, preserveFileName = false) {
await getCharacters();
select_rm_info('char_import', data.file_name, oldSelectedChar);
if (power_user.import_card_tags) {
if (power_user.tag_import_setting !== tag_import_setting.NONE) {
let currentContext = getContext();
let avatarFileName = `${data.file_name}.png`;
let importedCharacter = currentContext.characters.find(character => character.avatar === avatarFileName);
@ -10609,7 +10610,7 @@ jQuery(async function () {
}
} break;
case 'import_tags': {
await importTags(characters[this_chid]);
await importTags(characters[this_chid], { forceShow: true });
} break;
/*case 'delete_button':
popup_type = "del_ch";

View File

@ -28,6 +28,8 @@ export const POPUP_RESULT = {
* @property {boolean?} [allowVerticalScrolling] - Whether to allow vertical scrolling in the popup
* @property {POPUP_RESULT|number?} [defaultResult] - The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`.
* @property {CustomPopupButton[]|string[]?} [customButtons] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward.
* @property {(popup: Popup) => boolean?} [onClosing] - Handler called before the popup closes, return `false` to cancel the close
* @property {(popup: Popup) => void?} [onClose] - Handler called after the popup closes, but before the DOM is cleaned up
*/
/**
@ -78,6 +80,9 @@ export class Popup {
/** @type {POPUP_RESULT|number?} */ defaultResult;
/** @type {CustomPopupButton[]|string[]?} */ customButtons;
/** @type {(popup: Popup) => boolean?} */ onClosing;
/** @type {(popup: Popup) => void?} */ onClose;
/** @type {POPUP_RESULT|number} */ result;
/** @type {any} */ value;
@ -94,13 +99,17 @@ export class Popup {
* @param {string} [inputValue=''] - The initial value of the input field
* @param {PopupOptions} [options={}] - Additional options for the popup
*/
constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null } = {}) {
constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, onClosing = null, onClose = null } = {}) {
Popup.util.popups.push(this);
// Make this popup uniquely identifiable
this.id = uuidv4();
this.type = type;
// Utilize event handlers being passed in
this.onClosing = onClosing;
this.onClose = onClose;
/**@type {HTMLTemplateElement}*/
const template = document.querySelector('#popup_template');
// @ts-ignore
@ -317,6 +326,12 @@ export class Popup {
this.value = value;
this.result = result;
if (this.onClosing) {
const shouldClose = this.onClosing(this);
if (!shouldClose) return;
}
Popup.util.lastResult = { value, result };
this.hide();
}
@ -337,6 +352,11 @@ export class Popup {
// Call the close on the dialog
this.dlg.close();
// Run a possible custom handler right before DOM removal
if (this.onClose) {
this.onClose(this);
}
// Remove it from the dom
this.dlg.remove();

View File

@ -35,7 +35,7 @@ import {
selectInstructPreset,
} from './instruct-mode.js';
import { getTagsList, tag_map, tags } from './tags.js';
import { getTagsList, tag_import_setting, tag_map, tags } from './tags.js';
import { tokenizers } from './tokenizers.js';
import { BIAS_CACHE } from './logit-bias.js';
import { renderTemplateAsync } from './templates.js';
@ -198,6 +198,7 @@ let power_user = {
trim_spaces: true,
relaxed_api_urls: false,
world_import_dialog: true,
tag_import_setting: tag_import_setting.ASK,
disable_group_trimming: false,
single_line: false,
@ -1562,6 +1563,12 @@ function loadPowerUserSettings(settings, data) {
power_user.tokenizer = tokenizers.GPT2;
}
// Clean up old/legacy settings
if (power_user.import_card_tags !== undefined) {
power_user.tag_import_setting = power_user.import_card_tags ? tag_import_setting.ASK : tag_import_setting.NONE;
delete power_user.import_card_tags;
}
$('#single_line').prop('checked', power_user.single_line);
$('#relaxed_api_urls').prop('checked', power_user.relaxed_api_urls);
$('#world_import_dialog').prop('checked', power_user.world_import_dialog);
@ -1590,7 +1597,6 @@ function loadPowerUserSettings(settings, data) {
$('#zoomed_avatar_magnification').prop('checked', power_user.zoomed_avatar_magnification);
$(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true);
$(`#send_on_enter option[value=${power_user.send_on_enter}]`).attr('selected', true);
$('#import_card_tags').prop('checked', power_user.import_card_tags);
$('#confirm_message_delete').prop('checked', power_user.confirm_message_delete !== undefined ? !!power_user.confirm_message_delete : true);
$('#spoiler_free_mode').prop('checked', power_user.spoiler_free_mode);
$('#collapse-newlines-checkbox').prop('checked', power_user.collapse_newlines);
@ -1632,6 +1638,7 @@ function loadPowerUserSettings(settings, data) {
$('#chat_width_slider').val(power_user.chat_width);
$('#token_padding').val(power_user.token_padding);
$('#aux_field').val(power_user.aux_field);
$('#tag_import_setting').val(power_user.tag_import_setting);
$('#stscript_autocomplete_autoHide').prop('checked', power_user.stscript.autocomplete.autoHide ?? false).trigger('input');
$('#stscript_matching').val(power_user.stscript.matching ?? 'fuzzy');
@ -3516,11 +3523,6 @@ $(document).ready(() => {
saveSettingsDebounced();
});
$('#import_card_tags').on('input', function () {
power_user.import_card_tags = !!$(this).prop('checked');
saveSettingsDebounced();
});
$('#confirm_message_delete').on('input', function () {
power_user.confirm_message_delete = !!$(this).prop('checked');
saveSettingsDebounced();
@ -3759,6 +3761,12 @@ $(document).ready(() => {
saveSettingsDebounced();
});
$('#tag_import_setting').on('change', function () {
const value = $(this).find(':selected').val();
power_user.tag_import_setting = Number(value);
saveSettingsDebounced();
});
$('#stscript_autocomplete_autoHide').on('input', function () {
power_user.stscript.autocomplete.autoHide = !!$(this).prop('checked');
saveSettingsDebounced();

View File

@ -2,7 +2,6 @@ import {
characters,
saveSettingsDebounced,
this_chid,
callPopup,
menu_type,
entitiesFilter,
printCharactersDebounced,
@ -21,11 +20,12 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
import { isMobile } from './RossAscends-mods.js';
import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from './popup.js';
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
import { debounce_timeout } from './constants.js';
import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js';
import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
import { SlashCommandExecutor } from './slash-commands/SlashCommandExecutor.js';
import { renderTemplateAsync } from './templates.js';
export {
TAG_FOLDER_TYPES,
@ -62,11 +62,20 @@ function getFilterHelper(listSelector) {
return $(listSelector).is(GROUP_FILTER_SELECTOR) ? groupCandidatesFilter : entitiesFilter;
}
export const tag_filter_types = {
/** @enum {number} */
export const tag_filter_type = {
character: 0,
group_member: 1,
};
/** @enum {number} */
export const tag_import_setting = {
ASK: 1,
NONE: 2,
ALL: 3,
ONLY_EXISTING: 4,
};
/**
* @type {{ FAV: Tag, GROUP: Tag, FOLDER: Tag, VIEW: Tag, HINT: Tag, UNFILTER: Tag }}
* A collection of global actional tags for the filter panel
@ -242,16 +251,23 @@ function isBogusFolder(tag) {
}
/**
* Indicates whether a user is currently in a bogus folder.
* Retrieves all currently open bogus folders
*
* @return {Tag[]} An array of open bogus folders
*/
function getOpenBogusFolders() {
return entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.selected
.map(tagId => tags.find(x => x.id === tagId))
.filter(isBogusFolder) ?? [];
}
/**
* Indicates whether a user is currently in a bogus folder
*
* @returns {boolean} If currently viewing a folder
*/
function isBogusFolderOpen() {
const anyIsFolder = entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.selected
.map(tagId => tags.find(x => x.id === tagId))
.some(isBogusFolder);
return !!anyIsFolder;
return getOpenBogusFolders().length > 0;
}
/**
@ -662,15 +678,6 @@ function getExistingTags(newTags) {
return existingTags;
}
const tagImportSettings = {
ALWAYS_IMPORT_ALL: 1,
ONLY_IMPORT_EXISTING: 2,
IMPORT_NONE: 3,
ASK: 4,
};
let globalTagImportSetting = tagImportSettings.ASK; // Default setting
const IMPORT_EXLCUDED_TAGS = ['ROOT', 'TAVERN'];
const ANTI_TROLL_MAX_TAGS = 15;
@ -678,11 +685,13 @@ const ANTI_TROLL_MAX_TAGS = 15;
* Imports tags for a given character
*
* @param {Character} character - The character
* @param {object} [options] - Options
* @param {boolean} [options.forceShow=false] - Whether to force showing the import dialog
* @returns {Promise<boolean>} Boolean indicating whether any tag was imported
*/
async function importTags(character) {
async function importTags(character, { forceShow = false } = {}) {
// Gather the tags to import based on the selected setting
const tagNamesToImport = await handleTagImport(character);
const tagNamesToImport = await handleTagImport(character, { forceShow });
if (!tagNamesToImport?.length) {
toastr.info('No tags imported', 'Importing Tags');
return;
@ -700,9 +709,11 @@ async function importTags(character) {
* Handles the import of tags for a given character and returns the resulting list of tags to add
*
* @param {Character} character - The character
* @param {object} [options] - Options
* @param {boolean} [options.forceShow=false] - Whether to force showing the import dialog
* @returns {Promise<string[]>} Array of strings representing the tags to import
*/
async function handleTagImport(character) {
async function handleTagImport(character, { forceShow = false } = {}) {
/** @type {string[]} */
const importTags = character.tags.map(t => t.trim()).filter(t => t)
.filter(t => !IMPORT_EXLCUDED_TAGS.includes(t))
@ -710,17 +721,22 @@ async function handleTagImport(character) {
const existingTags = getExistingTags(importTags);
const newTags = importTags.filter(t => !existingTags.some(existingTag => existingTag.name.toLowerCase() === t.toLowerCase()))
.map(newTag);
const folderTags = getOpenBogusFolders();
switch (globalTagImportSetting) {
case tagImportSettings.ALWAYS_IMPORT_ALL:
return existingTags.concat(newTags).map(t => t.name);
case tagImportSettings.ONLY_IMPORT_EXISTING:
return existingTags.map(t => t.name);
case tagImportSettings.ASK:
return await showTagImportPopup(character, existingTags, newTags);
case tagImportSettings.IMPORT_NONE:
default:
// Choose the setting for this dialog. If from settings, verify the setting really exists, otherwise take "ASK".
const setting = forceShow ? tag_import_setting.ASK
: Object.values(tag_import_setting).find(setting => setting === power_user.tag_import_setting) ?? tag_import_setting.ASK;
switch (setting) {
case tag_import_setting.ALL:
return [...existingTags, ...newTags, ...folderTags].map(t => t.name);
case tag_import_setting.ONLY_EXISTING:
return [...existingTags, ...folderTags].map(t => t.name);
case tag_import_setting.ASK:
return await showTagImportPopup(character, existingTags, newTags, folderTags);
case tag_import_setting.NONE:
return [];
default: throw new Error(`Invalid tag import setting: ${setting}`);
}
}
@ -730,63 +746,55 @@ async function handleTagImport(character) {
* @param {Character} character - The character
* @param {Tag[]} existingTags - List of existing tags
* @param {Tag[]} newTags - List of new tags
* @param {Tag[]} folderTags - List of tags in the current folder
* @returns {Promise<string[]>} Array of strings representing the tags to import
*/
async function showTagImportPopup(character, existingTags, newTags) {
async function showTagImportPopup(character, existingTags, newTags, folderTags) {
/** @type {{[key: string]: import('./popup.js').CustomPopupButton}} */
const importButtons = {
EXISTING: { result: 2, text: 'Import Existing' },
NONE: { result: 2, text: 'Import None', },
ALL: { result: 3, text: 'Import All' },
NONE: { result: 4, text: 'Import None' },
EXISTING: { result: 4, text: 'Import Existing' },
};
const buttonSettingsMap = {
[POPUP_RESULT.AFFIRMATIVE]: tag_import_setting.ASK,
[importButtons.NONE.result]: tag_import_setting.NONE,
[importButtons.ALL.result]: tag_import_setting.ALL,
[importButtons.EXISTING.result]: tag_import_setting.ONLY_EXISTING,
};
const customButtonsCaptions = Object.values(importButtons).map(button => `&quot;${button.text}&quot;`);
const customButtonsString = customButtonsCaptions.slice(0, -1).join(', ') + ' or ' + customButtonsCaptions.slice(-1);
const popupContent = $(`
<h3>Import Tags For ${character.name}</h3>
<div class="import_avatar_placeholder"></div>
<div class="import_tags_content justifyLeft">
<small>
Click remove on any tag to remove it from this import.<br />
Select one of the import options to finish importing the tags.
</small>
<h4 class="m-t-1">Existing Tags</h4>
<div id="import_existing_tags_list" class="tags"></div>
<h4 class="m-t-1">New Tags</h4>
<div id="import_new_tags_list" class="tags"></div>
<small>
<label class="checkbox flex-container alignitemscenter flexNoGap m-t-3" for="import_remember_option">
<input type="checkbox" id="import_remember_option" name="import_remember_option" />
<span data-i18n="Remember my choice">
Remember my choice
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Remember the chosen import option\nIf ${customButtonsString} is selected, this dialog will not show up anymore.\nTo change this, go to the settings and modify &quot;Tag Import Option&quot;.\n\nIf the &quot;Import&quot; option is chosen, the global setting will stay on &quot;Ask&quot;."
title="Remember the chosen import option\nIf ${customButtonsString} is selected, this dialog will not show up anymore.\nTo change this, go to the settings and modify &quot;Tag Import Option&quot;.\n\nIf the &quot;Import&quot; option is chosen, the global setting will stay on &quot;Ask&quot;.">
</div>
</span>
</label>
</small>
</div>`);
const popupContent = $(await renderTemplateAsync('charTagImport', { charName: character.name }));
// Print tags after popup is shown, so that events can be added
printTagList(popupContent.find('#import_existing_tags_list'), { tags: existingTags, tagOptions: { removable: true, removeAction: tag => removeFromArray(existingTags, tag) } });
printTagList(popupContent.find('#import_new_tags_list'), { tags: newTags, tagOptions: { removable: true, removeAction: tag => removeFromArray(newTags, tag) } });
printTagList(popupContent.find('#import_folder_tags_list'), { tags: folderTags, tagOptions: { removable: true, removeAction: tag => removeFromArray(folderTags, tag) } });
const result = await callGenericPopup(popupContent, POPUP_TYPE.TEXT, null, { wider: true, okButton: 'Import', cancelButton: true, customButtons: Object.values(importButtons) });
if (folderTags.length === 0) popupContent.find('#folder_tags_block').hide();
function onCloseRemember(/** @type {Popup} */ popup) {
const rememberCheckbox = document.getElementById('import_remember_option');
if (rememberCheckbox instanceof HTMLInputElement && rememberCheckbox.checked) {
const setting = buttonSettingsMap[popup.result];
if (!setting) return;
power_user.tag_import_setting = setting;
$('#tag_import_setting').val(power_user.tag_import_setting);
saveSettingsDebounced();
console.log('Remembered tag import setting:', Object.entries(tag_import_setting).find(x => x[1] === setting)[0], setting);
}
}
const result = await callGenericPopup(popupContent, POPUP_TYPE.TEXT, null, { wider: true, okButton: 'Import', cancelButton: true, customButtons: Object.values(importButtons), onClose: onCloseRemember });
if (!result) {
return [];
}
switch (result) {
case 1:
case true:
case importButtons.ALL.result: // Default 'Import' option where it imports all selected
return existingTags.concat(newTags).map(t => t.name);
case POPUP_RESULT.AFFIRMATIVE: // Default 'Import' option where it imports all selected
case importButtons.ALL.result:
return [...existingTags, ...newTags, ...folderTags].map(t => t.name);
case importButtons.EXISTING.result:
return existingTags.map(t => t.name);
return [...existingTags, ...folderTags].map(t => t.name);
case importButtons.NONE.result:
default:
return [];
@ -1115,8 +1123,8 @@ function runTagFilters(listElement) {
filterHelper.setFilterData(FILTER_TYPES.TAG, { excluded: excludedTagIds, selected: tagIds });
}
function printTagFilters(type = tag_filter_types.character) {
const FILTER_SELECTOR = type === tag_filter_types.character ? CHARACTER_FILTER_SELECTOR : GROUP_FILTER_SELECTOR;
function printTagFilters(type = tag_filter_type.character) {
const FILTER_SELECTOR = type === tag_filter_type.character ? CHARACTER_FILTER_SELECTOR : GROUP_FILTER_SELECTOR;
$(FILTER_SELECTOR).empty();
// Print all action tags. (Rework 'Folder' button to some kind of onboarding if no folders are enabled yet)
@ -1135,9 +1143,7 @@ function printTagFilters(type = tag_filter_types.character) {
const bogusDrilldown = $(FILTER_SELECTOR).siblings('.rm_tag_bogus_drilldown');
bogusDrilldown.empty();
if (power_user.bogus_folders && bogusDrilldown.length > 0) {
const filterData = structuredClone(entitiesFilter.getFilterData(FILTER_TYPES.TAG));
const navigatedTags = filterData.selected.map(x => tags.find(t => t.id == x)).filter(x => isBogusFolder(x));
const navigatedTags = getOpenBogusFolders();
printTagList(bogusDrilldown, { tags: navigatedTags, tagOptions: { removable: true } });
}

View File

@ -0,0 +1,35 @@
<h3>Import Tags For {{charName}}</h3>
<div class="import_avatar_placeholder"></div>
<div class="import_tags_content justifyLeft">
<small data-i18n="Click remove on any tag to remove it from this import.&lt;br /&gt;Select one of the import options to finish importing the tags.">
Click remove on any tag to remove it from this import.<br />
Select one of the import options to finish importing the tags.
</small>
<h4 class="m-t-1" data-i18n="Existing Tags">Existing Tags</h4>
<div id="import_existing_tags_list" class="tags" style="min-height: 20px;"></div>
<h4 class="m-t-1" data-i18n="New Tags">New Tags</h4>
<div id="import_new_tags_list" class="tags" style="min-height: 20px;"></div>
<div id="folder_tags_block" class="m-t-1">
<h4 data-i18n="Folder Tags">Folder Tags</h4>
<small data-i18n="The following tags will be auto-imported based on the currently selected folders">
The following tags will be auto-imported based on the currently selected folders
</small>
<div id="import_folder_tags_list" class="tags" style="margin-top: 5px;"></div>
</div>
<small>
<label class="checkbox flex-container alignitemscenter flexNoGap m-t-3" for="import_remember_option">
<input type="checkbox" id="import_remember_option" name="import_remember_option" />
<span data-i18n="Remember my choice">
Remember my choice
<div class="fa-solid fa-circle-info opacity50p"
data-i18n="[title]Remember the chosen import option&#010;If anything besides 'Cancel' is selected, this dialog will not show up anymore.&#010;To change this, go to the settings and modify &quot;Tag Import Option&quot;.&#010;&#010;If the &quot;Import&quot; option is chosen, the global setting will stay on &quot;Ask&quot;."
title="Remember the chosen import option&#010;If anything besides 'Cancel' is selected, this dialog will not show up anymore.&#010;To change this, go to the settings and modify &quot;Tag Import Option&quot;.&#010;&#010;If the &quot;Import&quot; option is chosen, the global setting will stay on &quot;Ask&quot;.">
</div>
</span>
</label>
</small>
</div>