Files
MetonaEditor/src/constants.js
T
thzxx 45ed8d5351 release: v0.1.6 — plugin v2, i18n instance isolation, shortcuts, toolbar, toast
feat(plugins): plugin system v2
- depends: declarative plugin dependencies with topological sort (Kahn algorithm)
- async plugins: install() returning Promise auto-await
- editor.unuse(name): uninstall individual plugins
- pluginUtils.validateConfig(schema, config): configuration validation
- plugin priority field controls install order within same dependency level

feat(i18n): instance-level locale isolation, pluralization, dynamic loading
- createInstanceI18n(): per-editor independent locale contexts
- editor.setLocale()/getLocale()/t() instance methods
- Plural rules: t('items', { count: 5 }) auto-selects one/other/few/many
- i18nUtils.loadRemote(url, locale): fetch translation packs from remote
- editor.on('localeChange') event

feat(core): toolbar & shortcut customization, context menu, toast
- editor.registerShortcut(combo, handler): custom keyboard shortcuts
- editor.configureToolbar(tools): dynamic toolbar rebuild
- editor.removeToolbarButton(action): remove single button
- editor.registerContextMenu(items): right-click context menu
- editor.toast(msg, {type, duration, animation}): toast notifications

style: toast notifications, context menu, dropdown CSS
- .me-toast with success/error/warning/info types, fade/slide animations
- .me-context-menu with items, separators, shortcut hints

test: 22 new tests covering plugin deps, unuse, shortcuts, toast, i18n plural (521 total)

chore: bump version 0.1.5 → 0.1.6 across all files, site, types
2026-07-24 10:17:57 +08:00

199 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* MetonaEditor Constants - 常量定义
* @module constants
* @version 0.1.6
* @description 默认配置、主题、动画、工具栏等常量
*/
import { ICONS } from './icons.js';
import { LOCALES } from './locales.js';
/**
* 默认工具栏按钮序列
* '|' 表示分组分隔符
*/
export const DEFAULT_TOOLBAR = [
'bold', 'italic', 'strikethrough', 'code', '|',
'h1', 'h2', 'h3', '|',
'quote', 'ul', 'ol', '|',
'link', 'image', 'table', 'hr', '|',
'undo', 'redo', '|',
'edit', 'split', 'preview', 'fullscreen',
];
/**
* 默认配置
*/
export const DEFAULTS = Object.freeze({
// 内容
value: '',
placeholder: '',
// 视图模式:edit / split / preview
mode: 'split',
// 高度:数字为 px,字符串原样使用
height: 400,
// 工具栏:数组或 false(隐藏)
toolbar: DEFAULT_TOOLBAR,
// 字数统计状态栏
wordCount: true,
// 自动聚焦
autofocus: false,
// 拼写检查
spellcheck: false,
// 历史栈上限
historyLimit: 100,
// 历史栈防抖延迟(ms
historyDebounce: 400,
// 同步滚动(分屏模式)
syncScroll: true,
// 制表符插入的空格数(0 表示插入 \t)
tabSize: 2,
// 只读模式
readOnly: false,
// 主题:light / dark / auto / warm / 自定义
theme: 'auto',
// 国际化
locale: 'zh-CN',
// 自定义 Markdown 渲染函数 (md, env) => html,覆盖内置解析器
render: null,
// 自定义代码高亮函数 (code, lang) => html
highlight: null,
// 自定义渲染前的 HTML 净化函数(接收渲染后的 HTML,返回安全 HTML)
sanitize: null,
// 外观
className: '',
style: {},
// 插件
plugins: [],
// 生命周期回调
onChange: null,
onInput: null,
onFocus: null,
onBlur: null,
onSave: null,
onModeChange: null,
onFullscreen: null,
onCreate: null,
onDestroy: null,
});
// Icons - 从 icons.js 导入并重新导出(保持单一来源)
export { ICONS };
/**
* 类型颜色配置(保留用于状态提示与按钮强调)
*/
export const TYPE_COLORS = {
success: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' },
error: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' },
warning: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' },
info: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' },
loading: { fg: '#6366f1', bg: 'rgba(99, 102, 241, 0.1)' },
default: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' },
};
/**
* 主题配置
*/
export const THEMES = {
light: {
bg: 'rgba(255, 255, 255, 0.96)',
text: '#1f2937',
border: 'rgba(0, 0, 0, 0.08)',
shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.18), 0 4px 14px -4px rgba(0, 0, 0, 0.08)',
hoverShadow: '0 14px 48px -10px rgba(0, 0, 0, 0.22), 0 6px 18px -4px rgba(0, 0, 0, 0.10)',
// 编辑器专属
toolbarBg: 'rgba(248, 249, 250, 0.92)',
textareaBg: '#ffffff',
previewBg: '#ffffff',
codeBg: 'rgba(243, 244, 246, 1)',
codeText: '#1f2937',
accent: '#3b82f6',
muted: '#6b7280',
},
dark: {
bg: 'rgba(28, 32, 40, 0.94)',
text: '#e6e8eb',
border: 'rgba(255, 255, 255, 0.1)',
shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.6), 0 4px 14px -4px rgba(0, 0, 0, 0.4)',
hoverShadow: '0 14px 48px -8px rgba(0, 0, 0, 0.6), 0 6px 18px -4px rgba(0, 0, 0, 0.4)',
toolbarBg: 'rgba(22, 26, 33, 0.92)',
textareaBg: '#1c2028',
previewBg: '#1c2028',
codeBg: 'rgba(15, 18, 24, 1)',
codeText: '#e6e8eb',
accent: '#60a5fa',
muted: '#9ca3af',
},
auto: 'auto',
warm: {
bg: 'rgba(255, 251, 235, 0.96)',
text: '#78350f',
border: 'rgba(245, 158, 11, 0.2)',
shadow: '0 10px 36px -10px rgba(245, 158, 11, 0.18), 0 4px 14px -4px rgba(245, 158, 11, 0.08)',
hoverShadow: '0 14px 48px -10px rgba(245, 158, 11, 0.22), 0 6px 18px -4px rgba(245, 158, 11, 0.10)',
toolbarBg: 'rgba(254, 243, 199, 0.92)',
textareaBg: '#fffbeb',
previewBg: '#fffbeb',
codeBg: 'rgba(254, 215, 170, 0.6)',
codeText: '#78350f',
accent: '#d97706',
muted: '#a16207',
},
};
/**
* 位置配置(保留,预览面板提示等场景可用)
*/
export const POSITIONS = [
'top-left', 'top-center', 'top-right',
'bottom-left', 'bottom-center', 'bottom-right',
];
/**
* 动画配置(保留,DOM 过渡与未来扩展使用)
*/
export const ANIMATIONS = {
slide: { enter: { transform: 'translateX(80px)', opacity: 0 }, leave: { transform: 'translateX(120%)', opacity: 0 }, duration: 400, easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)' },
fade: { enter: { opacity: 0, filter: 'blur(3px)' }, leave: { opacity: 0, filter: 'blur(3px)' }, duration: 500, easing: 'ease' },
scale: { enter: { transform: 'scale(0.55)', opacity: 0 }, leave: { transform: 'scale(0.55)', opacity: 0 }, duration: 450, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' },
bounce: { enter: { transform: 'translateY(-80px)', opacity: 0 }, leave: { transform: 'translateY(20px)', opacity: 0 }, duration: 650, easing: 'ease' },
flip: { enter: { transform: 'perspective(500px) rotateX(-90deg)', opacity: 0 }, leave: { transform: 'perspective(500px) rotateX(90deg)', opacity: 0 }, duration: 500, easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)' },
rotate: { enter: { transform: 'rotate(-25deg) scale(0.6)', opacity: 0 }, leave: { transform: 'rotate(25deg) scale(0.6)', opacity: 0 }, duration: 500, easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)' },
zoom: { enter: { transform: 'scale(0.1)', opacity: 0 }, leave: { transform: 'scale(0.1)', opacity: 0 }, duration: 500, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' },
};
/**
* 主题类型
*/
export const THEME_TYPES = ['light', 'dark', 'auto'];
/**
* 动画类型
*/
export const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', 'zoom'];
/**
* 编辑器视图模式
*/
export const EDIT_MODES = ['edit', 'split', 'preview'];
/**
* 内置工具栏动作清单
*/
export const TOOLBAR_ACTIONS = [
'bold', 'italic', 'strikethrough', 'underline', 'code',
'h1', 'h2', 'h3', 'quote', 'ul', 'ol', 'indent', 'outdent', 'hr',
'link', 'image', 'table',
'undo', 'redo',
'edit', 'split', 'preview', 'fullscreen',
];
// Locales - 从 locales.js 导入并重新导出
export { LOCALES };
/**
* 进度条方向(保留兼容)
*/
export const PROGRESS_DIRECTIONS = ['horizontal', 'vertical'];