A modern, dependency-free UI library for Tampermonkey scripts
Ce script ne doit pas être installé directement. C'est une librairie destinée à être incluse dans d'autres scripts avec la méta-directive // @require https://update.greasyfork.org/scripts/564901/1749919/CKUI.js
CKUI 是一个专为 Tampermonkey/Greasemonkey 用户脚本设计的现代化 UI 库,提供开箱即用的 UI 组件,无需任何外部依赖。
核心特性:
在你的用户脚本头部添加 @require 语句(页面顶部提供语句模板)
注意: CKUI 会在页面加载时自动初始化,你可以直接通过 ckui 或 unsafeWindow.ckui 访问所有功能。
显示各种类型的通知消息,自动定时关闭。
// 成功通知
ckui.success('操作成功!', '成功');
// 错误通知
ckui.error('发生错误!', '错误');
// 警告通知
ckui.warning('请注意!', '警告');
// 信息通知
ckui.info('提示信息', '提示');
ckui.notify({
title: '自定义标题',
message: '这是自定义消息内容',
type: 'success', // 'success' | 'error' | 'warning' | 'info'
duration: 5000, // 显示时长(毫秒)
shadow: false // 是否使用 Shadow DOM
});
创建各种类型的对话框,支持自定义内容和按钮。
// 简单提示
await ckui.alert('这是一个提示!');
// 带标题的提示
await ckui.alert('操作成功', '成功');
// 基本确认
try {
await ckui.confirm('确定要删除吗?', '确认');
console.log('用户点击了确定');
} catch (e) {
console.log('用户点击了取消');
}
// 自定义确认框
ckui.confirm({
title: '确认操作',
content: '你确定要执行此操作吗?此操作不可撤销。',
okText: '确定',
cancelText: '取消'
}).then(() => {
ckui.success('操作完成');
}).catch(() => {
ckui.info('操作已取消');
});
// 获取用户输入
const name = await ckui.prompt('请输入你的名字', '默认值');
console.log('用户输入:', name);
const modal = ckui.modal({
title: '自定义对话框',
content: '这里可以是任意内容,包括 HTML 元素',
width: '600px',
okText: '确定',
cancelText: '取消',
allowHtml: true, // 允许 HTML 内容
shadow: false, // 是否使用 Shadow DOM
onOk: () => {
console.log('点击了确定');
return true; // 返回 true 关闭模态框
},
onCancel: () => {
console.log('点击了取消');
}
});
modal.show(); // 显示
modal.close(); // 关闭
// 使用 Emoji 图标
ckui.alert('操作成功!', '成功', null, {
icon: '✅',
iconShape: 'circle', // 'circle' | 'square'
iconWidth: '28px'
});
// 使用图片 URL
ckui.modal({
title: '用户信息',
content: '个人资料已更新',
icon: 'https://example.com/avatar.png',
iconShape: 'circle',
iconWidth: '48px'
}).show();
创建可拖动、可调整大小的浮动窗口。
const win = ckui.floatWindow({
title: '浮动窗口',
content: '可拖动的窗口内容',
x: 100, // X 坐标
y: 100, // Y 坐标
width: '400px', // 宽度
draggable: true, // 可拖动
shadow: false // 是否使用 Shadow DOM
});
win.show(); // 显示窗口
win.close(); // 关闭窗口
win.toggle(); // 切换显示/隐藏
// 创建自定义内容
const content = ckui.createElement('div', {}, [
ckui.createElement('p', {}, ['这是一个复杂的窗口']),
ckui.button({
label: '点击我',
primary: true,
onClick: () => alert('按钮被点击了!')
})
]);
const win = ckui.floatWindow({
title: '工具面板',
content: content,
x: 200,
y: 200,
width: '500px'
});
win.show();
const win = ckui.floatWindow({
title: '我的窗口',
content: '内容'
});
// 添加关闭事件监听
win.onClose(() => {
console.log('窗口被关闭了');
ckui.info('窗口已关闭');
}, true); // true 表示一次性回调
win.show();
强大的表单系统,支持多种输入类型、验证和回调。
const form = ckui.form()
// 文本输入
.input({
label: '用户名',
name: 'username',
placeholder: '请输入用户名',
validator: (value, allValues) => {
if (!value) return '用户名不能为空';
if (value.length < 3) return '用户名至少3个字符';
return true; // 验证通过
},
onChange: (value, allValues) => {
console.log('用户名改变:', value);
}
})
// 密码输入
.input({
label: '密码',
name: 'password',
inputType: 'password',
placeholder: '请输入密码'
})
// 多行文本
.textarea({
label: '备注',
name: 'note',
placeholder: '请输入备注',
validator: (value, allValues) => {
if (value && value.length > 200) {
return `备注太长了(${value.length}/200)`;
}
return true;
}
})
// 下拉选择
.select({
label: '城市',
name: 'city',
options: [
{ label: '请选择', value: '' },
{ label: '北京', value: 'beijing' },
{ label: '上海', value: 'shanghai' }
],
validator: (value) => {
if (!value) return '请选择城市';
return true;
}
})
// 标签输入
.tags({
label: '技能标签',
name: 'skills',
placeholder: '输入后按空格添加',
value: ['JavaScript', 'Python'],
maxTags: 5,
validator: (tag, allTags) => {
if (tag.length > 20) return '标签太长了';
return true;
}
})
// 下拉选择标签
.selectTags({
label: '兴趣爱好',
name: 'hobbies',
placeholder: '输入或选择',
value: ['编程'],
options: ['编程', '阅读', '音乐', '运动'],
allowCustom: true, // 允许自定义标签
maxTags: 8
})
// 复选框
.checkbox({
label: '同意用户协议',
name: 'agree',
validator: (checked) => {
if (!checked) return '必须同意用户协议';
return true;
}
})
// 单选框
.radio({
label: '性别',
name: 'gender',
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' }
]
})
// 提交按钮
.button({
label: '提交',
primary: true,
onClick: () => {
const values = form.getValues();
console.log('表单值:', values);
ckui.success('提交成功!');
}
});
// 在模态框中显示表单
ckui.modal({
title: '用户注册',
content: form.render(),
width: '500px',
footer: null // 不显示默认底部按钮
}).show();
验证器函数会在字段失去焦点时触发:
validator: (value, allValues) => {
// value: 当前字段的值
// allValues: 表单所有字段的值对象
// 返回 true 表示验证通过
if (isValid(value)) return true;
// 返回字符串表示验证失败,字符串为错误消息
return '验证失败的错误消息';
}
每当字段值改变时触发:
onChange: (value, allValues) => {
console.log('当前值:', value);
console.log('所有表单值:', allValues);
// 可以在这里实现联动逻辑
if (value === 'special') {
// 做一些特殊处理
}
}
创建响应式数据,自动更新绑定的 UI 组件。
// 创建响应式数据
const count = ckui.reactive(0);
// 创建绑定的输入框
const input = ckui.input({
placeholder: '输入文本...',
reactive: count
});
// 订阅数据变化
count.subscribe(value => {
console.log('值变化:', value);
});
// 修改值
count.value = 10; // 自动更新所有绑定的组件
const username = ckui.reactive('');
const email = ckui.reactive('');
const form = ckui.form()
.input({
label: '用户名',
name: 'username',
reactive: username
})
.input({
label: '邮箱',
name: 'email',
reactive: email
});
// 实时获取值
username.subscribe(value => {
console.log('用户名:', value);
});
email.subscribe(value => {
console.log('邮箱:', value);
});
提供灵活的布局方案。
const layout = ckui.row(
ckui.col(
ckui.card({ title: '卡片1', content: '内容1' })
),
ckui.col(
ckui.card({ title: '卡片2', content: '内容2' })
),
ckui.col(
ckui.card({ title: '卡片3', content: '内容3' })
)
);
document.body.appendChild(layout);
// 垂直间距(默认)
const layout = ckui.createElement('div', {}, [
ckui.card({ title: '卡片1', content: '内容1' }),
ckui.space(20), // 20px 间距
ckui.card({ title: '卡片2', content: '内容2' }),
ckui.space(30), // 30px 间距
ckui.card({ title: '卡片3', content: '内容3' })
]);
// 水平间距
const buttons = ckui.createElement('div', {
style: 'display: flex;'
}, [
ckui.button({ label: '按钮1' }),
ckui.space(10, 'horizontal'),
ckui.button({ label: '按钮2', primary: true }),
ckui.space(20, 'horizontal'),
ckui.button({ label: '按钮3', danger: true })
]);
const card = ckui.card({
title: '卡片标题',
content: '卡片内容',
footer: '卡片底部'
});
document.body.appendChild(card);
条件显示和折叠面板。
// 通过响应式变量控制显示/隐藏
const visible = ckui.reactive(true);
const hiddenArea = ckui.hiddenarea({
visible: visible,
content: '这里的内容可以通过 visible 控制显示/隐藏'
});
// 切换显示
visible.value = false; // 隐藏
visible.value = true; // 显示
const openState = ckui.reactive(true);
const detail = ckui.detail({
title: '点击展开/折叠',
openState: openState,
content: '这是可折叠的内容区域'
});
// 程序控制展开/折叠
openState.value = false; // 折叠
openState.value = true; // 展开
内置浅色和深色主题。
// 切换到暗色主题
ckui.setTheme('dark');
// 切换到亮色主题
ckui.setTheme('light');
// 获取当前主题
const currentTheme = ckui.getTheme(); // 'light' 或 'dark'
使用 Shadow DOM 完全隔离样式,避免与页面样式冲突。
ckui.modal({
title: 'Shadow Modal',
content: '这个 Modal 的样式完全隔离',
shadow: true // 启用 Shadow DOM
}).show();
ckui.floatWindow({
title: 'Shadow Window',
content: '样式隔离的窗口',
shadow: true
}).show();
ckui.success('成功消息', '成功', {
shadow: true
});
何时使用 Shadow DOM:
通过 ID 管理和复用组件实例。
// 创建浮动窗口并指定 ID
ckui.floatWindow({
id: 'my-window',
title: '命名窗口',
content: '这个窗口有 ID'
}).show();
// 通过 ID 获取实例
const window = ckui.getFloatWindow('my-window');
if (window) {
window.close(); // 关闭窗口
}
// 如果 ID 已存在,会更新现有实例而不是创建新的
ckui.floatWindow({
id: 'my-window',
title: '更新后的标题',
content: '更新后的内容'
}).show();
创建 DOM 元素的便捷方法。
const div = ckui.createElement('div', {
class: 'my-class',
style: 'color: red;',
id: 'my-id'
}, [
'这是文本内容',
ckui.createElement('span', {}, ['这是子元素'])
]);
// 基本按钮
ckui.button({
label: '点击我',
onClick: () => alert('被点击了!')
});
// 主要按钮
ckui.button({
label: '主要按钮',
primary: true,
onClick: () => {}
});
// 成功按钮
ckui.button({
label: '成功',
success: true
});
// 危险按钮
ckui.button({
label: '删除',
danger: true
});
(function() {
'use strict';
// 创建面板内容
const createPanel = () => {
const count = ckui.reactive(0);
return ckui.createElement('div', {
style: 'padding: 10px;'
}, [
ckui.createElement('h3', {}, ['工具面板']),
ckui.space(10),
ckui.createElement('div', {}, [
ckui.createElement('span', {}, ['计数器: ']),
ckui.createElement('strong', {}, [count.value.toString()])
]),
ckui.space(10),
ckui.createElement('div', {
style: 'display: flex; gap: 10px;'
}, [
ckui.button({
label: '增加',
primary: true,
onClick: () => {
count.value++;
ckui.success(`当前计数: ${count.value}`);
}
}),
ckui.button({
label: '减少',
danger: true,
onClick: () => {
count.value--;
ckui.warning(`当前计数: ${count.value}`);
}
}),
ckui.button({
label: '重置',
onClick: () => {
count.value = 0;
ckui.info('计数已重置');
}
})
])
]);
};
// 创建浮动窗口
const panel = ckui.floatWindow({
id: 'tool-panel',
title: '🛠️ 我的工具',
content: createPanel(),
x: 100,
y: 100,
width: '300px',
shadow: true
});
panel.show();
// 添加快捷键
document.addEventListener('keydown', (e) => {
// Ctrl + Shift + P 切换面板显示
if (e.ctrlKey && e.shiftKey && e.key === 'P') {
panel.toggle();
}
});
console.log('工具面板已加载!按 Ctrl+Shift+P 切换显示');
})();
CKUI 基于 GPL-3.0-only 协议开源。
这意味着:
unsafeWindow.ckui 访问shadow: true 参数ckui.reactive() 创建响应式变量,通过 .value 访问和修改id 参数可以避免重复创建相同窗口