mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-02-12 16:52:46 +00:00
favs, last used, sortable columns, and update splitter transforms to match selection
This commit is contained in:
@@ -1445,6 +1445,66 @@ h1, h2, h3, h4, h5 {
|
|||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
border-left: 4px solid;
|
border-left: 4px solid;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-order-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
margin-left: auto;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-title:hover .category-order-controls {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-move-btn {
|
||||||
|
background: var(--button-bg);
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-move-btn:hover {
|
||||||
|
background: var(--button-hover-bg);
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
color: var(--accent-color);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-move-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-used-section {
|
||||||
|
border: 2px dashed var(--accent-color);
|
||||||
|
background: rgba(var(--accent-color-rgb), 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-used-section .category-title {
|
||||||
|
color: var(--accent-color);
|
||||||
|
border-left-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-used-section .transform-buttons .transform-button-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-title.transform-category-encoding {
|
.category-title.transform-category-encoding {
|
||||||
@@ -1509,6 +1569,10 @@ h1, h2, h3, h4, h5 {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#category-randomizer .transform-buttons .transform-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
#category-randomizer .transform-buttons .transform-button .transform-preview {
|
#category-randomizer .transform-buttons .transform-button .transform-preview {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
@@ -2083,20 +2147,47 @@ h1, h2, h3, h4, h5 {
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Auto-copy icon styling */
|
/* Favorite icon styling */
|
||||||
.auto-copy-icon {
|
.favorite-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
bottom: 8px;
|
bottom: 8px;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 10;
|
||||||
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.transform-button:hover .auto-copy-icon {
|
.favorite-icon:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
color: var(--accent-color);
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-icon.favorited {
|
||||||
|
opacity: 1;
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-icon.favorited:hover {
|
||||||
|
transform: scale(1.3);
|
||||||
|
color: #ffed4e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorites-section {
|
||||||
|
border: 2px dashed #ffd700;
|
||||||
|
background: rgba(255, 215, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorites-section .category-title {
|
||||||
|
color: #ffd700;
|
||||||
|
border-left-color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorites-section .transform-buttons .transform-button-group {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Output section */
|
/* Output section */
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ class SplitterTool extends Tool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getVueData() {
|
getVueData() {
|
||||||
|
// Load favorites
|
||||||
|
const favorites = this.loadFavorites();
|
||||||
|
|
||||||
|
// Load category order (same as TransformTool)
|
||||||
|
const categoryOrder = this.getCategoryOrder();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Message Splitter Tab
|
// Message Splitter Tab
|
||||||
splitterInput: '',
|
splitterInput: '',
|
||||||
@@ -26,12 +32,121 @@ class SplitterTool extends Tool {
|
|||||||
splitterTransforms: [''], // array of transform names to apply in sequence (start with one empty slot)
|
splitterTransforms: [''], // array of transform names to apply in sequence (start with one empty slot)
|
||||||
splitterStartWrap: '',
|
splitterStartWrap: '',
|
||||||
splitterEndWrap: '',
|
splitterEndWrap: '',
|
||||||
splitMessages: []
|
splitMessages: [],
|
||||||
|
favorites: favorites,
|
||||||
|
categoryOrder: categoryOrder
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCategoryOrder() {
|
||||||
|
// Get all categories from transforms
|
||||||
|
if (!window.transforms) return [];
|
||||||
|
|
||||||
|
const categorySet = new Set();
|
||||||
|
Object.values(window.transforms).forEach(transform => {
|
||||||
|
if (transform.category) {
|
||||||
|
categorySet.add(transform.category);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const allCategories = Array.from(categorySet);
|
||||||
|
const savedOrder = this.loadCategoryOrder();
|
||||||
|
|
||||||
|
return this.mergeCategoryOrder(allCategories, savedOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCategoryOrder() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('transformCategoryOrder');
|
||||||
|
if (saved) {
|
||||||
|
return JSON.parse(saved);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to load category order:', e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeCategoryOrder(allCategories, savedOrder) {
|
||||||
|
// Always ensure randomizer is last
|
||||||
|
const categoriesWithoutRandomizer = allCategories.filter(c => c !== 'randomizer');
|
||||||
|
|
||||||
|
if (!savedOrder || savedOrder.length === 0) {
|
||||||
|
// Default: alphabetical, randomizer last
|
||||||
|
const sorted = categoriesWithoutRandomizer.sort((a, b) => a.localeCompare(b));
|
||||||
|
return [...sorted, 'randomizer'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use saved order, but filter out categories that no longer exist and remove duplicates
|
||||||
|
const validSavedOrder = savedOrder
|
||||||
|
.filter(cat => allCategories.includes(cat))
|
||||||
|
.filter((cat, index, arr) => arr.indexOf(cat) === index); // Remove duplicates
|
||||||
|
|
||||||
|
// Find new categories not in saved order
|
||||||
|
const newCategories = categoriesWithoutRandomizer.filter(cat => !validSavedOrder.includes(cat));
|
||||||
|
|
||||||
|
// Build final order: saved order (filtered, deduplicated) + new categories (alphabetically) + randomizer
|
||||||
|
const finalOrder = [...validSavedOrder];
|
||||||
|
if (newCategories.length > 0) {
|
||||||
|
finalOrder.push(...newCategories.sort((a, b) => a.localeCompare(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure randomizer is always last and remove any duplicates
|
||||||
|
const finalWithoutRandomizer = finalOrder.filter(c => c !== 'randomizer');
|
||||||
|
const uniqueFinal = finalWithoutRandomizer.filter((cat, index, arr) => arr.indexOf(cat) === index);
|
||||||
|
return [...uniqueFinal, 'randomizer'];
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFavorites() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('transformFavorites');
|
||||||
|
if (saved) {
|
||||||
|
const data = JSON.parse(saved);
|
||||||
|
// Filter to only include transforms that still exist
|
||||||
|
if (window.transforms) {
|
||||||
|
return data.filter(transformName => {
|
||||||
|
return Object.values(window.transforms).some(t => t.name === transformName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to load favorites:', e);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
getVueMethods() {
|
getVueMethods() {
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
* Get favorite transforms
|
||||||
|
*/
|
||||||
|
getFavoriteTransforms: function() {
|
||||||
|
if (!this.favorites || this.favorites.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.favorites
|
||||||
|
.map(transformName => {
|
||||||
|
return this.transforms.find(t => t.name === transformName);
|
||||||
|
})
|
||||||
|
.filter(t => t !== undefined);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get transforms by category (excluding favorites)
|
||||||
|
*/
|
||||||
|
getTransformsByCategory: function(category) {
|
||||||
|
const categoryTransforms = this.transforms.filter(t => t.category === category);
|
||||||
|
// Exclude favorites from category lists (they're shown separately)
|
||||||
|
if (!this.favorites || this.favorites.length === 0) {
|
||||||
|
return categoryTransforms;
|
||||||
|
}
|
||||||
|
return categoryTransforms.filter(t => !this.favorites.includes(t.name));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get display name for category (capitalized)
|
||||||
|
*/
|
||||||
|
getCategoryDisplayName: function(category) {
|
||||||
|
return category.charAt(0).toUpperCase() + category.slice(1);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Set encapsulation start and end strings
|
* Set encapsulation start and end strings
|
||||||
* @param {string} start - The start string
|
* @param {string} start - The start string
|
||||||
|
|||||||
@@ -30,22 +30,145 @@ class TransformTool extends Tool {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort categories, but always put randomizer last
|
// Legend categories: always alphabetical (for quick link buttons)
|
||||||
const sortedCategories = Array.from(categorySet).sort((a, b) => {
|
const allCategories = Array.from(categorySet);
|
||||||
if (a === 'randomizer') return 1;
|
const categoriesWithoutRandomizer = allCategories.filter(c => c !== 'randomizer');
|
||||||
if (b === 'randomizer') return -1;
|
const legendCategories = [...categoriesWithoutRandomizer.sort((a, b) => a.localeCompare(b)), 'randomizer'];
|
||||||
return a.localeCompare(b);
|
|
||||||
});
|
// Section categories: can be reordered (load saved order or use alphabetical)
|
||||||
|
const savedOrder = this.loadCategoryOrder();
|
||||||
|
const sectionCategories = savedOrder && savedOrder.length > 0
|
||||||
|
? this.mergeCategoryOrder(allCategories, savedOrder)
|
||||||
|
: [...legendCategories]; // Create a copy so legendCategories remains immutable
|
||||||
|
|
||||||
|
// Load last used transforms
|
||||||
|
const lastUsed = this.loadLastUsed();
|
||||||
|
|
||||||
|
// Load favorites
|
||||||
|
const favorites = this.loadFavorites();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transformInput: '',
|
transformInput: '',
|
||||||
transformOutput: '',
|
transformOutput: '',
|
||||||
activeTransform: null,
|
activeTransform: null,
|
||||||
transforms: transforms,
|
transforms: transforms,
|
||||||
categories: sortedCategories
|
legendCategories: legendCategories, // Always alphabetical for legend
|
||||||
|
categories: sectionCategories, // Custom order for sections
|
||||||
|
lastUsedTransforms: lastUsed,
|
||||||
|
showLastUsed: lastUsed.length > 0,
|
||||||
|
favorites: favorites,
|
||||||
|
showFavorites: favorites.length > 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadCategoryOrder() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('transformCategoryOrder');
|
||||||
|
if (saved) {
|
||||||
|
return JSON.parse(saved);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to load category order:', e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeCategoryOrder(allCategories, savedOrder) {
|
||||||
|
// Always ensure randomizer is last
|
||||||
|
const categoriesWithoutRandomizer = allCategories.filter(c => c !== 'randomizer');
|
||||||
|
|
||||||
|
if (!savedOrder || savedOrder.length === 0) {
|
||||||
|
// Default: alphabetical, randomizer last
|
||||||
|
const sorted = categoriesWithoutRandomizer.sort((a, b) => a.localeCompare(b));
|
||||||
|
return [...sorted, 'randomizer'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use saved order, but filter out categories that no longer exist and remove duplicates
|
||||||
|
const validSavedOrder = savedOrder
|
||||||
|
.filter(cat => allCategories.includes(cat))
|
||||||
|
.filter((cat, index, arr) => arr.indexOf(cat) === index); // Remove duplicates
|
||||||
|
|
||||||
|
// Find new categories not in saved order
|
||||||
|
const newCategories = categoriesWithoutRandomizer.filter(cat => !validSavedOrder.includes(cat));
|
||||||
|
|
||||||
|
// Build final order: saved order (filtered, deduplicated) + new categories (alphabetically) + randomizer
|
||||||
|
const finalOrder = [...validSavedOrder];
|
||||||
|
if (newCategories.length > 0) {
|
||||||
|
finalOrder.push(...newCategories.sort((a, b) => a.localeCompare(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure randomizer is always last and remove any duplicates
|
||||||
|
const finalWithoutRandomizer = finalOrder.filter(c => c !== 'randomizer');
|
||||||
|
const uniqueFinal = finalWithoutRandomizer.filter((cat, index, arr) => arr.indexOf(cat) === index);
|
||||||
|
return [...uniqueFinal, 'randomizer'];
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLastUsed() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('transformLastUsed');
|
||||||
|
if (saved) {
|
||||||
|
const data = JSON.parse(saved);
|
||||||
|
// Filter to only include transforms that still exist
|
||||||
|
if (window.transforms) {
|
||||||
|
return data.filter(item => {
|
||||||
|
return Object.values(window.transforms).some(t => t.name === item.name);
|
||||||
|
}).slice(0, 5); // Keep only top 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to load last used transforms:', e);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
saveLastUsed(transformName) {
|
||||||
|
try {
|
||||||
|
let lastUsed = this.loadLastUsed();
|
||||||
|
|
||||||
|
// Remove if already exists
|
||||||
|
lastUsed = lastUsed.filter(item => item.name !== transformName);
|
||||||
|
|
||||||
|
// Add to front with timestamp
|
||||||
|
lastUsed.unshift({
|
||||||
|
name: transformName,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keep only last 10
|
||||||
|
lastUsed = lastUsed.slice(0, 10);
|
||||||
|
|
||||||
|
localStorage.setItem('transformLastUsed', JSON.stringify(lastUsed));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to save last used transform:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFavorites() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('transformFavorites');
|
||||||
|
if (saved) {
|
||||||
|
const data = JSON.parse(saved);
|
||||||
|
// Filter to only include transforms that still exist
|
||||||
|
if (window.transforms) {
|
||||||
|
return data.filter(transformName => {
|
||||||
|
return Object.values(window.transforms).some(t => t.name === transformName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to load favorites:', e);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFavorites(favorites) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem('transformFavorites', JSON.stringify(favorites));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to save favorites:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getVueMethods() {
|
getVueMethods() {
|
||||||
return {
|
return {
|
||||||
getDisplayCategory: function(transformName) {
|
getDisplayCategory: function(transformName) {
|
||||||
@@ -70,6 +193,9 @@ class TransformTool extends Tool {
|
|||||||
if (this.transformInput) {
|
if (this.transformInput) {
|
||||||
this.activeTransform = transform;
|
this.activeTransform = transform;
|
||||||
|
|
||||||
|
// Track last used
|
||||||
|
this.saveLastUsedTransform(transform.name);
|
||||||
|
|
||||||
if (transform.name === 'Random Mix') {
|
if (transform.name === 'Random Mix') {
|
||||||
this.transformOutput = window.transforms.randomizer.func(this.transformInput);
|
this.transformOutput = window.transforms.randomizer.func(this.transformInput);
|
||||||
const transformInfo = window.transforms.randomizer.getLastTransformInfo();
|
const transformInfo = window.transforms.randomizer.getLastTransformInfo();
|
||||||
@@ -103,6 +229,121 @@ class TransformTool extends Tool {
|
|||||||
this.ignoreKeyboardEvents = false;
|
this.ignoreKeyboardEvents = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
saveLastUsedTransform: function(transformName) {
|
||||||
|
try {
|
||||||
|
let lastUsed = this.lastUsedTransforms || [];
|
||||||
|
|
||||||
|
// Remove if already exists
|
||||||
|
lastUsed = lastUsed.filter(item => item.name !== transformName);
|
||||||
|
|
||||||
|
// Add to front with timestamp
|
||||||
|
lastUsed.unshift({
|
||||||
|
name: transformName,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keep only last 5
|
||||||
|
lastUsed = lastUsed.slice(0, 5);
|
||||||
|
|
||||||
|
this.lastUsedTransforms = lastUsed;
|
||||||
|
this.showLastUsed = lastUsed.length > 0;
|
||||||
|
|
||||||
|
localStorage.setItem('transformLastUsed', JSON.stringify(lastUsed));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to save last used transform:', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getLastUsedTransforms: function() {
|
||||||
|
if (!this.lastUsedTransforms || this.lastUsedTransforms.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.lastUsedTransforms
|
||||||
|
.map(item => {
|
||||||
|
return this.transforms.find(t => t.name === item.name);
|
||||||
|
})
|
||||||
|
.filter(t => t !== undefined);
|
||||||
|
},
|
||||||
|
toggleFavorite: function(transformName, event) {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = this.favorites.indexOf(transformName);
|
||||||
|
if (index > -1) {
|
||||||
|
// Remove from favorites
|
||||||
|
this.favorites.splice(index, 1);
|
||||||
|
this.showNotification('Removed from favorites', 'success', 'fas fa-star');
|
||||||
|
} else {
|
||||||
|
// Add to favorites
|
||||||
|
this.favorites.push(transformName);
|
||||||
|
this.showNotification('Added to favorites', 'success', 'fas fa-star');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showFavorites = this.favorites.length > 0;
|
||||||
|
this.saveFavorites(this.favorites);
|
||||||
|
},
|
||||||
|
isFavorite: function(transformName) {
|
||||||
|
return this.favorites && this.favorites.includes(transformName);
|
||||||
|
},
|
||||||
|
getFavoriteTransforms: function() {
|
||||||
|
if (!this.favorites || this.favorites.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.favorites
|
||||||
|
.map(transformName => {
|
||||||
|
return this.transforms.find(t => t.name === transformName);
|
||||||
|
})
|
||||||
|
.filter(t => t !== undefined);
|
||||||
|
},
|
||||||
|
saveFavorites: function(favorites) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem('transformFavorites', JSON.stringify(favorites));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to save favorites:', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveCategoryUp: function(categoryIndex) {
|
||||||
|
if (categoryIndex <= 0) return;
|
||||||
|
|
||||||
|
// Never allow moving randomizer itself
|
||||||
|
if (this.categories[categoryIndex] === 'randomizer') return;
|
||||||
|
|
||||||
|
// Use Vue's array mutation methods for proper reactivity
|
||||||
|
const categoryToMove = this.categories[categoryIndex];
|
||||||
|
this.categories.splice(categoryIndex, 1);
|
||||||
|
this.categories.splice(categoryIndex - 1, 0, categoryToMove);
|
||||||
|
|
||||||
|
this.saveCategoryOrder(this.categories);
|
||||||
|
this.showNotification('Category order saved', 'success', 'fas fa-check');
|
||||||
|
},
|
||||||
|
moveCategoryDown: function(categoryIndex) {
|
||||||
|
// Don't allow moving if already at or past the last valid position
|
||||||
|
// Last position is reserved for randomizer, so we can't move to it
|
||||||
|
if (categoryIndex >= this.categories.length - 2) return;
|
||||||
|
|
||||||
|
// Never allow moving randomizer itself
|
||||||
|
if (this.categories[categoryIndex] === 'randomizer') return;
|
||||||
|
|
||||||
|
// Use Vue's array mutation methods for proper reactivity
|
||||||
|
const categoryToMove = this.categories[categoryIndex];
|
||||||
|
this.categories.splice(categoryIndex, 1);
|
||||||
|
this.categories.splice(categoryIndex + 1, 0, categoryToMove);
|
||||||
|
|
||||||
|
this.saveCategoryOrder(this.categories);
|
||||||
|
this.showNotification('Category order saved', 'success', 'fas fa-check');
|
||||||
|
},
|
||||||
|
saveCategoryOrder: function(categories) {
|
||||||
|
try {
|
||||||
|
// Remove duplicates before saving
|
||||||
|
const uniqueCategories = categories.filter((cat, index, arr) => arr.indexOf(cat) === index);
|
||||||
|
localStorage.setItem('transformCategoryOrder', JSON.stringify(uniqueCategories));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to save category order:', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
autoTransform: function() {
|
autoTransform: function() {
|
||||||
if (this.transformInput && this.activeTransform && this.activeTab === 'transforms') {
|
if (this.transformInput && this.activeTransform && this.activeTab === 'transforms') {
|
||||||
const segments = window.EmojiUtils.splitEmojis(this.transformInput);
|
const segments = window.EmojiUtils.splitEmojis(this.transformInput);
|
||||||
@@ -171,6 +412,17 @@ class TransformTool extends Tool {
|
|||||||
return {
|
return {
|
||||||
mounted() {
|
mounted() {
|
||||||
this.initializeCategoryNavigation();
|
this.initializeCategoryNavigation();
|
||||||
|
|
||||||
|
// Save initial category order to localStorage if it doesn't exist
|
||||||
|
// This ensures consistent state for category reordering operations
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('transformCategoryOrder');
|
||||||
|
if (!saved && this.categories && this.categories.length > 0) {
|
||||||
|
this.saveCategoryOrder(this.categories);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to check/save initial category order:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,9 +71,25 @@
|
|||||||
<div v-for="(transform, index) in splitterTransforms" :key="'transform-'+index" class="transform-chain-item">
|
<div v-for="(transform, index) in splitterTransforms" :key="'transform-'+index" class="transform-chain-item">
|
||||||
<select v-model="splitterTransforms[index]" @change="handleTransformChange(index)">
|
<select v-model="splitterTransforms[index]" @change="handleTransformChange(index)">
|
||||||
<option value="">None</option>
|
<option value="">None</option>
|
||||||
<option v-for="t in transforms" :key="t.name" :value="t.name">
|
<optgroup v-if="getFavoriteTransforms().length > 0" label="⭐ Favorites">
|
||||||
{{ t.name }}
|
<option v-for="t in getFavoriteTransforms()" :key="t.name" :value="t.name">
|
||||||
</option>
|
{{ t.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup
|
||||||
|
v-for="category in categoryOrder"
|
||||||
|
:key="category"
|
||||||
|
:label="getCategoryDisplayName(category)"
|
||||||
|
v-if="getTransformsByCategory(category).length > 0"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="t in getTransformsByCategory(category)"
|
||||||
|
:key="t.name"
|
||||||
|
:value="t.name"
|
||||||
|
>
|
||||||
|
{{ t.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
<i v-if="index < splitterTransforms.length - 1" class="fas fa-arrow-right transform-arrow"></i>
|
<i v-if="index < splitterTransforms.length - 1" class="fas fa-arrow-right transform-arrow"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="legend-title">Categories:</div>
|
<div class="legend-title">Categories:</div>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
v-for="category in categories"
|
v-for="category in legendCategories"
|
||||||
:key="category"
|
:key="category"
|
||||||
:class="'legend-item transform-category-' + category"
|
:class="'legend-item transform-category-' + category"
|
||||||
:data-target="'category-' + category"
|
:data-target="'category-' + category"
|
||||||
@@ -24,7 +24,69 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="transform-categories">
|
<div class="transform-categories">
|
||||||
<template v-for="category in categories" :key="category">
|
<!-- Favorites Section -->
|
||||||
|
<div
|
||||||
|
v-if="showFavorites && getFavoriteTransforms().length > 0"
|
||||||
|
id="category-favorites"
|
||||||
|
class="transform-category-section favorites-section"
|
||||||
|
>
|
||||||
|
<h4 class="category-title">
|
||||||
|
<i class="fas fa-star"></i> Favorites
|
||||||
|
</h4>
|
||||||
|
<div class="transform-buttons">
|
||||||
|
<div v-for="transform in getFavoriteTransforms()" :key="transform.name" class="transform-button-group">
|
||||||
|
<button
|
||||||
|
@click="applyTransform(transform, $event)"
|
||||||
|
:class="'transform-button transform-category-' + getDisplayCategory(transform.name)"
|
||||||
|
:class="{ active: activeTransform === transform }"
|
||||||
|
:title="'Click to transform and copy: ' + transform.name"
|
||||||
|
>
|
||||||
|
{{ transform.name }}
|
||||||
|
<small class="transform-preview" v-if="transformInput">
|
||||||
|
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||||||
|
</small>
|
||||||
|
<i
|
||||||
|
@click.stop="toggleFavorite(transform.name, $event)"
|
||||||
|
:class="'fas fa-star favorite-icon' + (isFavorite(transform.name) ? ' favorited' : '')"
|
||||||
|
:title="isFavorite(transform.name) ? 'Remove from favorites' : 'Add to favorites'"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Last Used Section -->
|
||||||
|
<div
|
||||||
|
v-if="showLastUsed && getLastUsedTransforms().length > 0"
|
||||||
|
id="category-last-used"
|
||||||
|
class="transform-category-section last-used-section"
|
||||||
|
>
|
||||||
|
<h4 class="category-title">
|
||||||
|
<i class="fas fa-clock"></i> Last Used
|
||||||
|
</h4>
|
||||||
|
<div class="transform-buttons">
|
||||||
|
<div v-for="transform in getLastUsedTransforms()" :key="transform.name" class="transform-button-group">
|
||||||
|
<button
|
||||||
|
@click="applyTransform(transform, $event)"
|
||||||
|
:class="'transform-button transform-category-' + getDisplayCategory(transform.name)"
|
||||||
|
:class="{ active: activeTransform === transform }"
|
||||||
|
:title="'Click to transform and copy: ' + transform.name"
|
||||||
|
>
|
||||||
|
{{ transform.name }}
|
||||||
|
<small class="transform-preview" v-if="transformInput">
|
||||||
|
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||||||
|
</small>
|
||||||
|
<i
|
||||||
|
@click.stop="toggleFavorite(transform.name, $event)"
|
||||||
|
:class="'fas fa-star favorite-icon' + (isFavorite(transform.name) ? ' favorited' : '')"
|
||||||
|
:title="isFavorite(transform.name) ? 'Remove from favorites' : 'Add to favorites'"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-for="(category, categoryIndex) in categories" :key="category">
|
||||||
<!-- Special handling for randomizer category -->
|
<!-- Special handling for randomizer category -->
|
||||||
<div
|
<div
|
||||||
v-if="isSpecialCategory(category)"
|
v-if="isSpecialCategory(category)"
|
||||||
@@ -49,7 +111,11 @@
|
|||||||
<small class="transform-preview">
|
<small class="transform-preview">
|
||||||
🌀 Each word = different transform!
|
🌀 Each word = different transform!
|
||||||
</small>
|
</small>
|
||||||
<i class="fas fa-copy auto-copy-icon"></i>
|
<i
|
||||||
|
@click.stop="toggleFavorite(transform.name, $event)"
|
||||||
|
:class="'fas fa-star favorite-icon' + (isFavorite(transform.name) ? ' favorited' : '')"
|
||||||
|
:title="isFavorite(transform.name) ? 'Remove from favorites' : 'Add to favorites'"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +137,27 @@
|
|||||||
:id="'category-' + category"
|
:id="'category-' + category"
|
||||||
:class="'transform-category-section'"
|
:class="'transform-category-section'"
|
||||||
>
|
>
|
||||||
<h4 :class="'category-title transform-category-' + category">{{ category }}</h4>
|
<h4 :class="'category-title transform-category-' + category">
|
||||||
|
{{ category }}
|
||||||
|
<span class="category-order-controls">
|
||||||
|
<button
|
||||||
|
v-if="categoryIndex > 0"
|
||||||
|
@click.stop="moveCategoryUp(categoryIndex)"
|
||||||
|
class="category-move-btn"
|
||||||
|
title="Move category up"
|
||||||
|
>
|
||||||
|
<i class="fas fa-arrow-up"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="categoryIndex < categories.length - 1"
|
||||||
|
@click.stop="moveCategoryDown(categoryIndex)"
|
||||||
|
class="category-move-btn"
|
||||||
|
title="Move category down"
|
||||||
|
>
|
||||||
|
<i class="fas fa-arrow-down"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
<div class="transform-buttons">
|
<div class="transform-buttons">
|
||||||
<div v-for="transform in getTransformsByCategory(category)" :key="transform.name" class="transform-button-group">
|
<div v-for="transform in getTransformsByCategory(category)" :key="transform.name" class="transform-button-group">
|
||||||
<button
|
<button
|
||||||
@@ -84,7 +170,11 @@
|
|||||||
<small class="transform-preview" v-if="transformInput">
|
<small class="transform-preview" v-if="transformInput">
|
||||||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||||||
</small>
|
</small>
|
||||||
<i class="fas fa-copy auto-copy-icon"></i>
|
<i
|
||||||
|
@click.stop="toggleFavorite(transform.name, $event)"
|
||||||
|
:class="'fas fa-star favorite-icon' + (isFavorite(transform.name) ? ' favorited' : '')"
|
||||||
|
:title="isFavorite(transform.name) ? 'Remove from favorites' : 'Add to favorites'"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user