🎨 Improve code readability by reformatting functions and cleaning up whitespace

This commit is contained in:
2026-01-28 19:50:00 +00:00
parent b3d6bb0772
commit 07c4379d81
6 changed files with 319 additions and 14 deletions

View File

@@ -0,0 +1,65 @@
/**
* Clipboard Composable
* Handles clipboard operations with feedback
*/
import { ref } from 'vue';
/**
* Clipboard operations composable
* @returns {Object} Clipboard functions and state
*/
export function useClipboard() {
const copied = ref(false);
const error = ref(null);
/**
* Copy text to clipboard
* @param {string} text - Text to copy
* @returns {Promise<boolean>} Success status
*/
const copyToClipboard = async (text) => {
error.value = null;
copied.value = false;
try {
await navigator.clipboard.writeText(text);
copied.value = true;
// Reset after 2 seconds
setTimeout(() => {
copied.value = false;
}, 2000);
return true;
} catch (err) {
error.value = err.message;
console.error('Failed to copy to clipboard:', err);
return false;
}
};
/**
* Read text from clipboard
* @returns {Promise<string|null>} Clipboard text or null
*/
const readFromClipboard = async () => {
error.value = null;
try {
const text = await navigator.clipboard.readText();
return text;
} catch (err) {
error.value = err.message;
console.error('Failed to read from clipboard:', err);
return null;
}
};
return {
copied,
error,
copyToClipboard,
readFromClipboard
};
}

View File

@@ -0,0 +1,57 @@
/**
* Keyboard Shortcuts Composable
* Manages keyboard event listeners and shortcuts
*/
import { onMounted, onUnmounted } from 'vue';
/**
* Register keyboard shortcuts
* @param {Object} shortcuts - Map of key combinations to handlers
* @returns {Object} Control functions
*
* Example shortcuts object:
* {
* 'ctrl+f': () => focusSearch(),
* 'ctrl+c': () => copySelected(),
* 'escape': () => clearSelection()
* }
*/
export function useKeyboardShortcuts(shortcuts = {}) {
const handleKeyDown = (event) => {
const key = event.key.toLowerCase();
const ctrl = event.ctrlKey || event.metaKey; // Support both Ctrl and Cmd
const shift = event.shiftKey;
const alt = event.altKey;
// Build combination string
let combination = '';
if (ctrl) combination += 'ctrl+';
if (shift) combination += 'shift+';
if (alt) combination += 'alt+';
combination += key;
// Also check without modifiers
const simpleKey = key;
// Try to find and execute handler
const handler = shortcuts[combination] || shortcuts[simpleKey];
if (handler) {
event.preventDefault();
handler(event);
}
};
onMounted(() => {
window.addEventListener('keydown', handleKeyDown);
});
onUnmounted(() => {
window.removeEventListener('keydown', handleKeyDown);
});
return {
// Can add control functions here if needed
};
}

View File

@@ -0,0 +1,81 @@
/**
* LocalStorage Composable
* Type-safe localStorage operations with Vue reactivity
*/
import { ref, watch } from 'vue';
/**
* Use localStorage with reactivity
* @param {string} key - Storage key
* @param {any} defaultValue - Default value
* @returns {Ref} Reactive ref synced with localStorage
*/
export function useLocalStorage(key, defaultValue) {
// Try to load from localStorage
const loadValue = () => {
try {
const item = localStorage.getItem(key);
if (item !== null) {
return JSON.parse(item);
}
} catch (error) {
console.error(`Error loading ${key} from localStorage:`, error);
}
return defaultValue;
};
const storedValue = ref(loadValue());
// Watch for changes and save to localStorage
watch(
storedValue,
(newValue) => {
try {
localStorage.setItem(key, JSON.stringify(newValue));
} catch (error) {
console.error(`Error saving ${key} to localStorage:`, error);
}
},
{ deep: true }
);
return storedValue;
}
/**
* Manage search history in localStorage
* @param {string} key - Storage key
* @param {number} maxItems - Maximum number of items to store
* @returns {Object} History management functions
*/
export function useSearchHistory(key = 'searchHistory', maxItems = 5) {
const history = useLocalStorage(key, []);
const addToHistory = (query) => {
if (!query || query.trim() === '') return;
// Remove duplicates and add to front
const newHistory = [
query,
...history.value.filter(item => item !== query)
].slice(0, maxItems);
history.value = newHistory;
};
const removeFromHistory = (query) => {
history.value = history.value.filter(item => item !== query);
};
const clearHistory = () => {
history.value = [];
};
return {
history,
addToHistory,
removeFromHistory,
clearHistory
};
}

View File

@@ -0,0 +1,84 @@
/**
* URL State Composable
* Synchronizes component state with URL query parameters
*/
import { watch, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
/**
* Sync state with URL query parameters
* @param {Object} stateRefs - Object of refs to sync {key: ref}
* @returns {Object} Control functions
*/
export function useUrlState(stateRefs) {
const router = useRouter();
const route = useRoute();
/**
* Update URL with current state
*/
const updateUrl = () => {
const query = {};
Object.entries(stateRefs).forEach(([key, ref]) => {
const value = ref.value;
if (value !== null && value !== undefined && value !== '') {
if (Array.isArray(value)) {
query[key] = value.join(',');
} else {
query[key] = String(value);
}
}
});
router.replace({ query });
};
/**
* Load state from URL
*/
const loadFromUrl = () => {
Object.entries(stateRefs).forEach(([key, ref]) => {
const value = route.query[key];
if (value) {
if (Array.isArray(ref.value)) {
ref.value = value.split(',');
} else if (typeof ref.value === 'number') {
ref.value = parseInt(value, 10) || 0;
} else if (typeof ref.value === 'boolean') {
ref.value = value === 'true';
} else {
ref.value = value;
}
}
});
};
/**
* Handle browser back/forward
*/
const handlePopState = () => {
loadFromUrl();
};
onMounted(() => {
// Load initial state from URL
loadFromUrl();
// Listen for browser back/forward
window.addEventListener('popstate', handlePopState);
});
// Watch for state changes and update URL
Object.values(stateRefs).forEach(ref => {
watch(ref, updateUrl, { deep: true });
});
return {
updateUrl,
loadFromUrl
};
}

View File

@@ -11,7 +11,13 @@
* @param {number} maxDepth - Maximum recursion depth
* @param {number} currentDepth - Current recursion depth
*/
function extractPathsRecursive(obj, prefix = '', paths = new Set(), maxDepth = 5, currentDepth = 0) {
function extractPathsRecursive(
obj,
prefix = '',
paths = new Set(),
maxDepth = 5,
currentDepth = 0
) {
if (currentDepth >= maxDepth || obj === null || typeof obj !== 'object') {
return;
}
@@ -20,7 +26,11 @@ function extractPathsRecursive(obj, prefix = '', paths = new Set(), maxDepth = 5
const path = prefix ? `${prefix}.${key}` : key;
paths.add(path);
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
if (
typeof obj[key] === 'object' &&
obj[key] !== null &&
!Array.isArray(obj[key])
) {
extractPathsRecursive(obj[key], path, paths, maxDepth, currentDepth + 1);
}
});
@@ -56,12 +66,18 @@ export function extractJsonPaths(data, sampleSize = 100) {
* @param {Function} callback - Callback when new paths found
* @param {number} chunkSize - Items to process per chunk
*/
export function extractJsonPathsLazy(data, startIndex, existingPaths, callback, chunkSize = 100) {
export function extractJsonPathsLazy(
data,
startIndex,
existingPaths,
callback,
chunkSize = 100
) {
if (startIndex >= data.length) {
return;
}
const processChunk = (index) => {
const processChunk = index => {
const end = Math.min(index + chunkSize, data.length);
const chunk = data.slice(index, end);
const newPaths = new Set(existingPaths);
@@ -74,10 +90,12 @@ export function extractJsonPathsLazy(data, startIndex, existingPaths, callback,
if (addedPaths.length > 0) {
addedPaths.forEach(p => existingPaths.add(p));
callback(addedPaths.map(path => ({
callback(
addedPaths.map(path => ({
path,
breadcrumb: path.replace(/\./g, ' ')
})));
}))
);
}
if (end < data.length) {