config cleanup
This commit is contained in:
@@ -436,7 +436,7 @@ const SuggestionDecoration = Extension.create({
|
|||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
charIndex = (charIndex + 1) % spinnerChars.length;
|
charIndex = (charIndex + 1) % spinnerChars.length;
|
||||||
spinner.textContent = spinnerChars[charIndex];
|
spinner.textContent = spinnerChars[charIndex];
|
||||||
}, 50);
|
}, TEXT_EDITOR_CONFIG.SPINNER_INTERVAL_MS);
|
||||||
|
|
||||||
// Store interval on element for cleanup
|
// Store interval on element for cleanup
|
||||||
(spinner as any)._spinnerInterval = interval;
|
(spinner as any)._spinnerInterval = interval;
|
||||||
@@ -834,7 +834,6 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
createSignal<number>(-1);
|
createSignal<number>(-1);
|
||||||
const [showHistoryModal, setShowHistoryModal] = createSignal(false);
|
const [showHistoryModal, setShowHistoryModal] = createSignal(false);
|
||||||
const [isLoadingHistory, setIsLoadingHistory] = createSignal(false);
|
const [isLoadingHistory, setIsLoadingHistory] = createSignal(false);
|
||||||
const MAX_HISTORY_SIZE = 100; // Match database pruning limit
|
|
||||||
let historyDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
let historyDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
let isInitialLoad = true; // Flag to prevent capturing history on initial load
|
let isInitialLoad = true; // Flag to prevent capturing history on initial load
|
||||||
let hasAttemptedHistoryLoad = false; // Flag to prevent repeated load attempts
|
let hasAttemptedHistoryLoad = false; // Flag to prevent repeated load attempts
|
||||||
@@ -934,8 +933,8 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
const requestBody = {
|
const requestBody = {
|
||||||
input_prefix: context.prefix,
|
input_prefix: context.prefix,
|
||||||
input_suffix: context.suffix,
|
input_suffix: context.suffix,
|
||||||
n_predict: 100,
|
n_predict: TEXT_EDITOR_CONFIG.INFILL_MAX_TOKENS,
|
||||||
temperature: 0.3,
|
temperature: TEXT_EDITOR_CONFIG.INFILL_TEMPERATURE,
|
||||||
stop: ["\n\n", "</s>", "<|endoftext|>"],
|
stop: ["\n\n", "</s>", "<|endoftext|>"],
|
||||||
stream: false
|
stream: false
|
||||||
};
|
};
|
||||||
@@ -1101,7 +1100,7 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
|
|
||||||
mermaidValidationTimer = setTimeout(() => {
|
mermaidValidationTimer = setTimeout(() => {
|
||||||
validateAndPreviewMermaid(content);
|
validateAndPreviewMermaid(content);
|
||||||
}, 500);
|
}, TEXT_EDITOR_CONFIG.MERMAID_VALIDATION_DEBOUNCE_MS);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Capture history snapshot
|
// Capture history snapshot
|
||||||
@@ -1139,8 +1138,10 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
|
|
||||||
// Limit history size
|
// Limit history size
|
||||||
const limitedHistory =
|
const limitedHistory =
|
||||||
updatedHistory.length > MAX_HISTORY_SIZE
|
updatedHistory.length > TEXT_EDITOR_CONFIG.MAX_HISTORY_SIZE
|
||||||
? updatedHistory.slice(updatedHistory.length - MAX_HISTORY_SIZE)
|
? updatedHistory.slice(
|
||||||
|
updatedHistory.length - TEXT_EDITOR_CONFIG.MAX_HISTORY_SIZE
|
||||||
|
)
|
||||||
: updatedHistory;
|
: updatedHistory;
|
||||||
|
|
||||||
setHistory(limitedHistory);
|
setHistory(limitedHistory);
|
||||||
@@ -1234,7 +1235,7 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
// Scroll to first change after a brief delay to allow content to render
|
// Scroll to first change after a brief delay to allow content to render
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToFirstChange(instance, oldContent, node.content);
|
scrollToFirstChange(instance, oldContent, node.content);
|
||||||
}, 100);
|
}, TEXT_EDITOR_CONFIG.SCROLL_TO_CHANGE_DELAY_MS);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find and scroll to the first difference between old and new content
|
// Find and scroll to the first difference between old and new content
|
||||||
@@ -1359,11 +1360,11 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
// Fade out and remove
|
// Fade out and remove
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
highlight.style.opacity = "0";
|
highlight.style.opacity = "0";
|
||||||
}, 100);
|
}, TEXT_EDITOR_CONFIG.HIGHLIGHT_FADE_DELAY_MS);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
highlight.remove();
|
highlight.remove();
|
||||||
}, 700);
|
}, TEXT_EDITOR_CONFIG.HIGHLIGHT_REMOVE_DELAY_MS);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load history from database
|
// Load history from database
|
||||||
@@ -1570,7 +1571,7 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
// This ensures infill works regardless of how content was loaded
|
// This ensures infill works regardless of how content was loaded
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isInitialLoad = false;
|
isInitialLoad = false;
|
||||||
}, 1000);
|
}, TEXT_EDITOR_CONFIG.INITIAL_LOAD_DELAY_MS);
|
||||||
|
|
||||||
// Listen for mermaid edit events
|
// Listen for mermaid edit events
|
||||||
editor.view.dom.addEventListener("edit-mermaid", ((e: CustomEvent) => {
|
editor.view.dom.addEventListener("edit-mermaid", ((e: CustomEvent) => {
|
||||||
@@ -1719,9 +1720,9 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
renumberAllReferences(editor);
|
renumberAllReferences(editor);
|
||||||
updateReferencesSection(editor);
|
updateReferencesSection(editor);
|
||||||
}, 100);
|
}, TEXT_EDITOR_CONFIG.REFERENCE_UPDATE_DELAY_MS);
|
||||||
|
|
||||||
// Debounced history capture (capture after 2 seconds of inactivity)
|
// Debounced history capture
|
||||||
// Skip during initial load
|
// Skip during initial load
|
||||||
if (!isInitialLoad) {
|
if (!isInitialLoad) {
|
||||||
if (historyDebounceTimer) {
|
if (historyDebounceTimer) {
|
||||||
@@ -1729,7 +1730,7 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
}
|
}
|
||||||
historyDebounceTimer = setTimeout(() => {
|
historyDebounceTimer = setTimeout(() => {
|
||||||
captureHistory(editor);
|
captureHistory(editor);
|
||||||
}, 2000);
|
}, TEXT_EDITOR_CONFIG.HISTORY_DEBOUNCE_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (infillConfig() && !isInitialLoad && infillEnabled()) {
|
if (infillConfig() && !isInitialLoad && infillEnabled()) {
|
||||||
@@ -1745,7 +1746,7 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
}
|
}
|
||||||
infillDebounceTimer = setTimeout(() => {
|
infillDebounceTimer = setTimeout(() => {
|
||||||
requestInfill();
|
requestInfill();
|
||||||
}, 500);
|
}, TEXT_EDITOR_CONFIG.INFILL_DEBOUNCE_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1812,7 +1813,7 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
migrateLegacyReferences(instance);
|
migrateLegacyReferences(instance);
|
||||||
migrateLegacyMermaidBlocks(instance);
|
migrateLegacyMermaidBlocks(instance);
|
||||||
}, 50);
|
}, TEXT_EDITOR_CONFIG.LEGACY_MIGRATION_DELAY_MS);
|
||||||
|
|
||||||
// Capture initial state in history only if no history was loaded
|
// Capture initial state in history only if no history was loaded
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -1829,12 +1830,12 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
isInitialLoad = false;
|
isInitialLoad = false;
|
||||||
}, 200);
|
}, TEXT_EDITOR_CONFIG.INITIAL_HISTORY_CAPTURE_DELAY_MS);
|
||||||
} else {
|
} else {
|
||||||
// Content already matches - this is the initial load case
|
// Content already matches - this is the initial load case
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isInitialLoad = false;
|
isInitialLoad = false;
|
||||||
}, 500);
|
}, TEXT_EDITOR_CONFIG.INITIAL_LOAD_FALLBACK_DELAY_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
167
src/config.ts
167
src/config.ts
@@ -8,21 +8,13 @@
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const AUTH_CONFIG = {
|
export const AUTH_CONFIG = {
|
||||||
/** JWT token expiration for regular sessions (with remember me) */
|
|
||||||
JWT_EXPIRY: "14d" as const,
|
JWT_EXPIRY: "14d" as const,
|
||||||
/** JWT token expiration for sessions without remember me */
|
|
||||||
JWT_EXPIRY_SHORT: "12h" as const,
|
JWT_EXPIRY_SHORT: "12h" as const,
|
||||||
/** Session cookie max age in seconds (14 days) */
|
|
||||||
SESSION_COOKIE_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
|
SESSION_COOKIE_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
|
||||||
/** Remember me cookie max age in seconds */
|
|
||||||
REMEMBER_ME_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
|
REMEMBER_ME_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
|
||||||
/** CSRF token cookie max age in seconds (14 days) */
|
|
||||||
CSRF_TOKEN_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
|
CSRF_TOKEN_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
|
||||||
/** Email login link JWT expiration (15 minutes - provides reasonable time to check email without being too permissive) */
|
|
||||||
EMAIL_LOGIN_LINK_EXPIRY: "15m" as const,
|
EMAIL_LOGIN_LINK_EXPIRY: "15m" as const,
|
||||||
/** Email verification link JWT expiration (15 minutes) */
|
|
||||||
EMAIL_VERIFICATION_LINK_EXPIRY: "15m" as const,
|
EMAIL_VERIFICATION_LINK_EXPIRY: "15m" as const,
|
||||||
/** Lineage JWT expiration for mobile game */
|
|
||||||
LINEAGE_JWT_EXPIRY: "14d" as const
|
LINEAGE_JWT_EXPIRY: "14d" as const
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -31,15 +23,10 @@ export const AUTH_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const RATE_LIMITS = {
|
export const RATE_LIMITS = {
|
||||||
/** Login: 5 attempts per 15 minutes per IP */
|
|
||||||
LOGIN_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 },
|
LOGIN_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 },
|
||||||
/** Login: 5 attempts per hour per email */
|
|
||||||
LOGIN_EMAIL: { maxAttempts: 5, windowMs: 60 * 60 * 1000 },
|
LOGIN_EMAIL: { maxAttempts: 5, windowMs: 60 * 60 * 1000 },
|
||||||
/** Password reset: 3 attempts per hour per IP */
|
|
||||||
PASSWORD_RESET_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 },
|
PASSWORD_RESET_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 },
|
||||||
/** Registration: 3 attempts per hour per IP */
|
|
||||||
REGISTRATION_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 },
|
REGISTRATION_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 },
|
||||||
/** Email verification: 5 attempts per 15 minutes per IP */
|
|
||||||
EMAIL_VERIFICATION_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 }
|
EMAIL_VERIFICATION_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 }
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -51,14 +38,11 @@ export const RATE_LIMIT_CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const ACCOUNT_LOCKOUT = {
|
export const ACCOUNT_LOCKOUT = {
|
||||||
/** Maximum failed login attempts before account lockout */
|
|
||||||
MAX_FAILED_ATTEMPTS: 5,
|
MAX_FAILED_ATTEMPTS: 5,
|
||||||
/** Account lockout duration in milliseconds (5 minutes) */
|
|
||||||
LOCKOUT_DURATION_MS: 5 * 60 * 1000
|
LOCKOUT_DURATION_MS: 5 * 60 * 1000
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const PASSWORD_RESET_CONFIG = {
|
export const PASSWORD_RESET_CONFIG = {
|
||||||
/** Password reset token expiry (1 hour) */
|
|
||||||
TOKEN_EXPIRY_MS: 60 * 60 * 1000
|
TOKEN_EXPIRY_MS: 60 * 60 * 1000
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -67,21 +51,14 @@ export const PASSWORD_RESET_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const COOLDOWN_TIMERS = {
|
export const COOLDOWN_TIMERS = {
|
||||||
/** Email login link cooldown (2 minutes) */
|
// those without * 1000 are in seconds
|
||||||
EMAIL_LOGIN_LINK_MS: 2 * 60 * 1000,
|
EMAIL_LOGIN_LINK_MS: 2 * 60 * 1000,
|
||||||
/** Email login link cookie max age in seconds */
|
|
||||||
EMAIL_LOGIN_LINK_COOKIE_MAX_AGE: 2 * 60,
|
EMAIL_LOGIN_LINK_COOKIE_MAX_AGE: 2 * 60,
|
||||||
/** Password reset request cooldown (5 minutes) */
|
|
||||||
PASSWORD_RESET_REQUEST_MS: 5 * 60 * 1000,
|
PASSWORD_RESET_REQUEST_MS: 5 * 60 * 1000,
|
||||||
/** Password reset request cookie max age in seconds */
|
|
||||||
PASSWORD_RESET_REQUEST_COOKIE_MAX_AGE: 5 * 60,
|
PASSWORD_RESET_REQUEST_COOKIE_MAX_AGE: 5 * 60,
|
||||||
/** Contact form request cooldown (1 minute) */
|
|
||||||
CONTACT_REQUEST_MS: 1 * 60 * 1000,
|
CONTACT_REQUEST_MS: 1 * 60 * 1000,
|
||||||
/** Contact form request cookie max age in seconds */
|
|
||||||
CONTACT_REQUEST_COOKIE_MAX_AGE: 1 * 60,
|
CONTACT_REQUEST_COOKIE_MAX_AGE: 1 * 60,
|
||||||
/** Email verification cooldown (15 minutes) */
|
|
||||||
EMAIL_VERIFICATION_MS: 15 * 60 * 1000,
|
EMAIL_VERIFICATION_MS: 15 * 60 * 1000,
|
||||||
/** Email verification cookie max age in seconds */
|
|
||||||
EMAIL_VERIFICATION_COOKIE_MAX_AGE: 15 * 60
|
EMAIL_VERIFICATION_COOKIE_MAX_AGE: 15 * 60
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -90,15 +67,10 @@ export const COOLDOWN_TIMERS = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const CACHE_CONFIG = {
|
export const CACHE_CONFIG = {
|
||||||
/** Blog cache TTL (24 hours) */
|
|
||||||
BLOG_CACHE_TTL_MS: 24 * 60 * 60 * 1000,
|
BLOG_CACHE_TTL_MS: 24 * 60 * 60 * 1000,
|
||||||
/** Git activity cache TTL (10 minutes) */
|
|
||||||
GIT_ACTIVITY_CACHE_TTL_MS: 10 * 60 * 1000,
|
GIT_ACTIVITY_CACHE_TTL_MS: 10 * 60 * 1000,
|
||||||
/** Blog posts list cache TTL (5 minutes) */
|
|
||||||
BLOG_POSTS_LIST_CACHE_TTL_MS: 5 * 60 * 1000,
|
BLOG_POSTS_LIST_CACHE_TTL_MS: 5 * 60 * 1000,
|
||||||
/** Maximum stale data age (7 days) */
|
|
||||||
MAX_STALE_DATA_MS: 7 * 24 * 60 * 60 * 1000,
|
MAX_STALE_DATA_MS: 7 * 24 * 60 * 60 * 1000,
|
||||||
/** Git activity max stale age (24 hours) */
|
|
||||||
GIT_ACTIVITY_MAX_STALE_MS: 24 * 60 * 60 * 1000
|
GIT_ACTIVITY_MAX_STALE_MS: 24 * 60 * 60 * 1000
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -107,15 +79,10 @@ export const CACHE_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const NETWORK_CONFIG = {
|
export const NETWORK_CONFIG = {
|
||||||
/** Default API timeout for email service (15 seconds) */
|
EMAIL_API_TIMEOUT_MS: 15 * 1000,
|
||||||
EMAIL_API_TIMEOUT_MS: 15000,
|
GITHUB_API_TIMEOUT_MS: 15 * 1000,
|
||||||
/** Default API timeout for GitHub OAuth (15 seconds) */
|
GOOGLE_API_TIMEOUT_MS: 15 * 1000,
|
||||||
GITHUB_API_TIMEOUT_MS: 15000,
|
|
||||||
/** Default API timeout for Google OAuth (15 seconds) */
|
|
||||||
GOOGLE_API_TIMEOUT_MS: 15000,
|
|
||||||
/** Maximum retry attempts for failed requests */
|
|
||||||
MAX_RETRIES: 2,
|
MAX_RETRIES: 2,
|
||||||
/** Retry delay between attempts (1 second) */
|
|
||||||
RETRY_DELAY_MS: 1000
|
RETRY_DELAY_MS: 1000
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -124,23 +91,14 @@ export const NETWORK_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const TYPEWRITER_CONFIG = {
|
export const TYPEWRITER_CONFIG = {
|
||||||
/** Default typing speed (characters per second) */
|
|
||||||
DEFAULT_SPEED: 30,
|
DEFAULT_SPEED: 30,
|
||||||
/** Fast typing speed */
|
|
||||||
FAST_SPEED: 80,
|
FAST_SPEED: 80,
|
||||||
/** Slow typing speed */
|
|
||||||
SLOW_SPEED: 10,
|
SLOW_SPEED: 10,
|
||||||
/** Very slow typing speed */
|
|
||||||
VERY_SLOW_SPEED: 100,
|
VERY_SLOW_SPEED: 100,
|
||||||
/** Extra slow typing speed */
|
|
||||||
EXTRA_SLOW_SPEED: 120,
|
EXTRA_SLOW_SPEED: 120,
|
||||||
/** Default keep alive duration (ms) */
|
|
||||||
DEFAULT_KEEP_ALIVE_MS: 2000,
|
DEFAULT_KEEP_ALIVE_MS: 2000,
|
||||||
/** Long keep alive duration (ms) */
|
|
||||||
LONG_KEEP_ALIVE_MS: 10000,
|
LONG_KEEP_ALIVE_MS: 10000,
|
||||||
/** Default initial delay (ms) */
|
|
||||||
DEFAULT_DELAY_MS: 500,
|
DEFAULT_DELAY_MS: 500,
|
||||||
/** Cursor fade delay after completion (1 second) */
|
|
||||||
CURSOR_FADE_DELAY_MS: 1000
|
CURSOR_FADE_DELAY_MS: 1000
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -149,21 +107,14 @@ export const TYPEWRITER_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const COUNTDOWN_CONFIG = {
|
export const COUNTDOWN_CONFIG = {
|
||||||
/** Email login link countdown duration (2 minutes) */
|
// these are in seconds
|
||||||
EMAIL_LOGIN_LINK_DURATION_S: 120,
|
EMAIL_LOGIN_LINK_DURATION_S: 120,
|
||||||
/** Password reset countdown duration (5 minutes) */
|
|
||||||
PASSWORD_RESET_DURATION_S: 300,
|
PASSWORD_RESET_DURATION_S: 300,
|
||||||
/** Contact form countdown duration (1 minute) */
|
|
||||||
CONTACT_FORM_DURATION_S: 60,
|
CONTACT_FORM_DURATION_S: 60,
|
||||||
/** Password reset success redirect countdown (5 seconds) */
|
|
||||||
PASSWORD_RESET_SUCCESS_DURATION_S: 5,
|
PASSWORD_RESET_SUCCESS_DURATION_S: 5,
|
||||||
/** Default timer size (pixels) */
|
|
||||||
DEFAULT_TIMER_SIZE_PX: 48,
|
DEFAULT_TIMER_SIZE_PX: 48,
|
||||||
/** Large timer size (pixels) */
|
|
||||||
LARGE_TIMER_SIZE_PX: 200,
|
LARGE_TIMER_SIZE_PX: 200,
|
||||||
/** Default stroke width */
|
|
||||||
DEFAULT_STROKE_WIDTH: 6,
|
DEFAULT_STROKE_WIDTH: 6,
|
||||||
/** Large stroke width */
|
|
||||||
LARGE_STROKE_WIDTH: 12
|
LARGE_STROKE_WIDTH: 12
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -172,11 +123,8 @@ export const COUNTDOWN_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const BREAKPOINTS = {
|
export const BREAKPOINTS = {
|
||||||
/** Mobile breakpoint (pixels) */
|
|
||||||
MOBILE_MAX_WIDTH: 768,
|
MOBILE_MAX_WIDTH: 768,
|
||||||
/** Tablet breakpoint (pixels) */
|
|
||||||
TABLET_MAX_WIDTH: 1024,
|
TABLET_MAX_WIDTH: 1024,
|
||||||
/** Desktop minimum width (pixels) */
|
|
||||||
DESKTOP_MIN_WIDTH: 1025
|
DESKTOP_MIN_WIDTH: 1025
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -185,25 +133,15 @@ export const BREAKPOINTS = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const ANIMATION_CONFIG = {
|
export const ANIMATION_CONFIG = {
|
||||||
/** Standard transition duration (ms) */
|
|
||||||
TRANSITION_DURATION_MS: 300,
|
TRANSITION_DURATION_MS: 300,
|
||||||
/** Fast transition duration (ms) */
|
|
||||||
FAST_TRANSITION_MS: 200,
|
FAST_TRANSITION_MS: 200,
|
||||||
/** Slow transition duration (ms) */
|
|
||||||
SLOW_TRANSITION_MS: 500,
|
SLOW_TRANSITION_MS: 500,
|
||||||
/** Extra slow transition duration (ms) */
|
|
||||||
EXTRA_SLOW_TRANSITION_MS: 600,
|
EXTRA_SLOW_TRANSITION_MS: 600,
|
||||||
/** Sidebar toggle duration (ms) */
|
|
||||||
SIDEBAR_DURATION_MS: 500,
|
SIDEBAR_DURATION_MS: 500,
|
||||||
/** Menu typing effect delay (ms) */
|
|
||||||
MENU_TYPING_DELAY_MS: 140,
|
MENU_TYPING_DELAY_MS: 140,
|
||||||
/** Menu initial delay (ms) */
|
|
||||||
MENU_INITIAL_DELAY_MS: 500,
|
MENU_INITIAL_DELAY_MS: 500,
|
||||||
/** Success message auto-hide duration (ms) */
|
|
||||||
SUCCESS_MESSAGE_DURATION_MS: 3000,
|
SUCCESS_MESSAGE_DURATION_MS: 3000,
|
||||||
/** Error message auto-hide duration (ms) */
|
|
||||||
ERROR_MESSAGE_DURATION_MS: 5000,
|
ERROR_MESSAGE_DURATION_MS: 5000,
|
||||||
/** Redirect delay after successful action (ms) */
|
|
||||||
REDIRECT_DELAY_MS: 500
|
REDIRECT_DELAY_MS: 500
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -212,7 +150,6 @@ export const ANIMATION_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const PDF_CONFIG = {
|
export const PDF_CONFIG = {
|
||||||
/** PDF rendering scale */
|
|
||||||
RENDER_SCALE: 1.5
|
RENDER_SCALE: 1.5
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -221,11 +158,8 @@ export const PDF_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const ERROR_PAGE_CONFIG = {
|
export const ERROR_PAGE_CONFIG = {
|
||||||
/** Glitch effect interval (ms) */
|
|
||||||
GLITCH_INTERVAL_MS: 300,
|
GLITCH_INTERVAL_MS: 300,
|
||||||
/** Glitch effect duration (ms) */
|
|
||||||
GLITCH_DURATION_MS: 100,
|
GLITCH_DURATION_MS: 100,
|
||||||
/** Number of particles for background animation */
|
|
||||||
PARTICLE_COUNT: 45
|
PARTICLE_COUNT: 45
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -243,7 +177,22 @@ export const MOBILE_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const TEXT_EDITOR_CONFIG = {
|
export const TEXT_EDITOR_CONFIG = {
|
||||||
CONTEXT_SIZE: 256
|
CONTEXT_SIZE: 256,
|
||||||
|
MAX_HISTORY_SIZE: 100,
|
||||||
|
HISTORY_DEBOUNCE_MS: 2000,
|
||||||
|
INFILL_DEBOUNCE_MS: 500,
|
||||||
|
INFILL_MAX_TOKENS: 100,
|
||||||
|
INFILL_TEMPERATURE: 0.3,
|
||||||
|
MERMAID_VALIDATION_DEBOUNCE_MS: 500,
|
||||||
|
LEGACY_MIGRATION_DELAY_MS: 50,
|
||||||
|
INITIAL_HISTORY_CAPTURE_DELAY_MS: 200,
|
||||||
|
INITIAL_LOAD_FALLBACK_DELAY_MS: 500,
|
||||||
|
INITIAL_LOAD_DELAY_MS: 1000,
|
||||||
|
SPINNER_INTERVAL_MS: 50,
|
||||||
|
HIGHLIGHT_FADE_DELAY_MS: 100,
|
||||||
|
HIGHLIGHT_REMOVE_DELAY_MS: 700,
|
||||||
|
REFERENCE_UPDATE_DELAY_MS: 100,
|
||||||
|
SCROLL_TO_CHANGE_DELAY_MS: 100
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -251,17 +200,12 @@ export const TEXT_EDITOR_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const VALIDATION_CONFIG = {
|
export const VALIDATION_CONFIG = {
|
||||||
/** Minimum password length (must match securePasswordSchema in schemas/user.ts) */
|
|
||||||
MIN_PASSWORD_LENGTH: 8,
|
MIN_PASSWORD_LENGTH: 8,
|
||||||
/** Require at least one uppercase letter in password */
|
|
||||||
PASSWORD_REQUIRE_UPPERCASE: true,
|
PASSWORD_REQUIRE_UPPERCASE: true,
|
||||||
/** Require at least one number in password */
|
|
||||||
PASSWORD_REQUIRE_NUMBER: true,
|
PASSWORD_REQUIRE_NUMBER: true,
|
||||||
/** Require at least one special character in password (false = optional but recommended) */
|
// changed to just recommended, may change again in future
|
||||||
PASSWORD_REQUIRE_SPECIAL: false,
|
PASSWORD_REQUIRE_SPECIAL: false,
|
||||||
/** Maximum message length for contact form */
|
|
||||||
MAX_CONTACT_MESSAGE_LENGTH: 500,
|
MAX_CONTACT_MESSAGE_LENGTH: 500,
|
||||||
/** Minimum password confirmation match length before showing error */
|
|
||||||
MIN_PASSWORD_CONF_LENGTH_FOR_ERROR: 6
|
MIN_PASSWORD_CONF_LENGTH_FOR_ERROR: 6
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -270,9 +214,7 @@ export const VALIDATION_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const LINEAGE_CONFIG = {
|
export const LINEAGE_CONFIG = {
|
||||||
/** Database deletion grace period (24 hours) */
|
|
||||||
DELETION_GRACE_PERIOD_MS: 24 * 60 * 60 * 1000,
|
DELETION_GRACE_PERIOD_MS: 24 * 60 * 60 * 1000,
|
||||||
/** PvP opponents returned per query */
|
|
||||||
PVP_OPPONENTS_COUNT: 3
|
PVP_OPPONENTS_COUNT: 3
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -281,71 +223,6 @@ export const LINEAGE_CONFIG = {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const AUDIT_CONFIG = {
|
export const AUDIT_CONFIG = {
|
||||||
/** Default query limit for audit logs */
|
|
||||||
DEFAULT_QUERY_LIMIT: 100,
|
DEFAULT_QUERY_LIMIT: 100,
|
||||||
/** Maximum audit log retention (90 days) */
|
|
||||||
MAX_RETENTION_DAYS: 90
|
MAX_RETENTION_DAYS: 90
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// HELPER FUNCTIONS
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert milliseconds to seconds
|
|
||||||
*/
|
|
||||||
export function msToSeconds(ms: number): number {
|
|
||||||
return Math.floor(ms / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert seconds to milliseconds
|
|
||||||
*/
|
|
||||||
export function secondsToMs(seconds: number): number {
|
|
||||||
return seconds * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert minutes to milliseconds
|
|
||||||
*/
|
|
||||||
export function minutesToMs(minutes: number): number {
|
|
||||||
return minutes * 60 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert hours to milliseconds
|
|
||||||
*/
|
|
||||||
export function hoursToMs(hours: number): number {
|
|
||||||
return hours * 60 * 60 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert days to milliseconds
|
|
||||||
*/
|
|
||||||
export function daysToMs(days: number): number {
|
|
||||||
return days * 24 * 60 * 60 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if screen width is mobile
|
|
||||||
*/
|
|
||||||
export function isMobileWidth(width: number): boolean {
|
|
||||||
return width < BREAKPOINTS.MOBILE_MAX_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if screen width is tablet
|
|
||||||
*/
|
|
||||||
export function isTabletWidth(width: number): boolean {
|
|
||||||
return (
|
|
||||||
width >= BREAKPOINTS.MOBILE_MAX_WIDTH &&
|
|
||||||
width <= BREAKPOINTS.TABLET_MAX_WIDTH
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if screen width is desktop
|
|
||||||
*/
|
|
||||||
export function isDesktopWidth(width: number): boolean {
|
|
||||||
return width >= BREAKPOINTS.DESKTOP_MIN_WIDTH;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user