This commit is contained in:
2026-04-25 16:36:34 +08:00
commit db90e7579b
1876 changed files with 189777 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
{
"label": "Intro plugin",
"label.zh-CN": "初始引导插件",
"name": "com.msgbyte.intro",
"url": "/plugins/com.msgbyte.intro/index.js",
"version": "0.0.0",
"author": "msgbyte",
"description": "Turn on the ability to introduce the app for the first time for the app",
"description.zh-CN": "为应用首次打开介绍应用的能力",
"requireRestart": true
}

View File

@@ -0,0 +1,9 @@
{
"name": "@plugins/com.msgbyte.intro",
"main": "src/index.ts",
"version": "0.0.0",
"private": true,
"dependencies": {
"shepherd.js": "^11.1.1"
}
}

View File

@@ -0,0 +1,4 @@
/**
* 异步加载
*/
import('./tour');

View File

@@ -0,0 +1,87 @@
import Shepherd from 'shepherd.js';
import { Translate } from './translate';
function buildWatchDom(selector: string) {
return () => {
return new Promise<void>((resolve) => {
const findDom = () => {
if (document.querySelector(selector)) {
resolve();
} else {
setTimeout(() => {
findDom();
}, 200);
}
};
findDom();
});
};
}
function buildStepOption(options: {
id: string;
text: string;
selector: string;
position?: Shepherd.Step.StepOptions['attachTo']['on'];
canClickTarget?: boolean;
}): Shepherd.Step.StepOptions {
return {
id: options.id,
text: options.text,
attachTo: {
element: options.selector,
on: options.position,
},
canClickTarget: false,
beforeShowPromise: buildWatchDom(options.selector),
};
}
export const steps: Shepherd.Step.StepOptions[] = [
{
id: 'start',
text: Translate.step1,
beforeShowPromise: buildWatchDom('[data-tc-role=navbar]'), // 仅当主界面出现后才显示欢迎
},
buildStepOption({
id: 'navbar',
text: Translate.step2,
selector: '[data-tc-role=navbar]',
position: 'right',
}),
buildStepOption({
id: 'personal',
text: Translate.step3,
selector: '[data-tc-role=navbar-personal]',
position: 'right',
}),
buildStepOption({
id: 'groups',
text: Translate.step4,
selector: '[data-tc-role=navbar-groups]',
position: 'right',
}),
buildStepOption({
id: 'settings',
text: Translate.step5,
selector: '[data-tc-role=navbar-settings]',
position: 'right',
}),
buildStepOption({
id: 'sidebar',
text: Translate.step6,
selector: '[data-tc-role^=sidebar-]',
position: 'right',
}),
buildStepOption({
id: 'content',
text: Translate.step7,
selector: '[data-tc-role^=content-]',
position: 'right',
}),
{
id: 'end',
text: Translate.step8,
},
];

View File

@@ -0,0 +1,176 @@
.shepherd-button {
background: #3288e6;
border: 0;
border-radius: 3px;
color: hsla(0, 0%, 100%, 0.75);
cursor: pointer;
margin-right: 0.5rem;
padding: 0.5rem 1.5rem;
transition: all 0.5s ease;
}
.shepherd-button:not(:disabled):hover {
background: #196fcc;
color: hsla(0, 0%, 100%, 0.75);
}
.shepherd-button.shepherd-button-secondary {
background: #f1f2f3;
color: rgba(0, 0, 0, 0.75);
}
.shepherd-button.shepherd-button-secondary:not(:disabled):hover {
background: #d6d9db;
color: rgba(0, 0, 0, 0.75);
}
.shepherd-button:disabled {
cursor: not-allowed;
}
.shepherd-footer {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
display: flex;
justify-content: flex-end;
padding: 0 0.75rem 0.75rem;
}
.shepherd-footer .shepherd-button:last-child {
margin-right: 0;
}
.shepherd-cancel-icon {
background: transparent;
border: none;
color: hsla(0, 0%, 50.2%, 0.75);
font-size: 2em;
cursor: pointer;
font-weight: 400;
margin: 0;
padding: 0;
transition: color 0.5s ease;
}
.shepherd-cancel-icon:hover {
color: rgba(0, 0, 0, 0.75);
}
.shepherd-has-title .shepherd-content .shepherd-cancel-icon {
color: hsla(0, 0%, 50.2%, 0.75);
}
.shepherd-has-title .shepherd-content .shepherd-cancel-icon:hover {
color: rgba(0, 0, 0, 0.75);
}
.shepherd-title {
color: rgba(0, 0, 0, 0.75);
display: flex;
font-size: 1rem;
font-weight: 400;
flex: 1 0 auto;
margin: 0;
padding: 0;
}
.shepherd-header {
align-items: center;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
display: flex;
justify-content: flex-end;
line-height: 2em;
padding: 0.75rem 0.75rem 0;
}
.shepherd-has-title .shepherd-content .shepherd-header {
background: #e6e6e6;
padding: 1em;
}
.shepherd-text {
color: rgba(0, 0, 0, 0.75);
font-size: 1rem;
line-height: 1.3em;
padding: 0.75em;
}
.shepherd-text p {
margin-top: 0;
}
.shepherd-text p:last-child {
margin-bottom: 0;
}
.shepherd-content {
border-radius: 5px;
outline: none;
padding: 0;
}
.shepherd-element {
background: #fff;
border-radius: 5px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
max-width: 400px;
opacity: 0;
outline: none;
transition: opacity 0.3s, visibility 0.3s;
visibility: hidden;
width: 100%;
z-index: 9999;
}
.shepherd-enabled.shepherd-element {
opacity: 1;
visibility: visible;
}
.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered) {
opacity: 0;
pointer-events: none;
visibility: hidden;
}
.shepherd-element,
.shepherd-element *,
.shepherd-element :after,
.shepherd-element :before {
box-sizing: border-box;
}
.shepherd-arrow,
.shepherd-arrow:before {
position: absolute;
width: 16px;
height: 16px;
z-index: -1;
}
.shepherd-arrow:before {
content: '';
transform: rotate(45deg);
background: #fff;
}
.shepherd-element[data-popper-placement^='top'] > .shepherd-arrow {
bottom: -8px;
}
.shepherd-element[data-popper-placement^='bottom'] > .shepherd-arrow {
top: -8px;
}
.shepherd-element[data-popper-placement^='left'] > .shepherd-arrow {
right: -8px;
}
.shepherd-element[data-popper-placement^='right'] > .shepherd-arrow {
left: -8px;
}
.shepherd-element.shepherd-centered > .shepherd-arrow {
opacity: 0;
}
.shepherd-element.shepherd-has-title[data-popper-placement^='bottom']
> .shepherd-arrow:before {
background-color: #e6e6e6;
}
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target,
.shepherd-target-click-disabled.shepherd-enabled.shepherd-target * {
pointer-events: none;
}
.shepherd-modal-overlay-container {
height: 0;
left: 0;
opacity: 0;
overflow: hidden;
pointer-events: none;
position: fixed;
top: 0;
transition: all 0.3s ease-out, height 0ms 0.3s, opacity 0.3s 0ms;
width: 100vw;
z-index: 9997;
}
.shepherd-modal-overlay-container.shepherd-modal-is-visible {
height: 100vh;
opacity: 0.5;
transition: all 0.3s ease-out, height 0s 0s, opacity 0.3s 0s;
}
.shepherd-modal-overlay-container.shepherd-modal-is-visible path {
pointer-events: all;
}

View File

@@ -0,0 +1,42 @@
import Shepherd from 'shepherd.js';
import { Translate } from './translate';
import { steps } from './steps';
import './style.less';
const KEY = 'plugin:com.msgbyte.intro/hasRun';
if (!window.localStorage.getItem(KEY)) {
const tour = new Shepherd.Tour({
useModalOverlay: true,
defaultStepOptions: {
classes: 'shadow-md',
scrollTo: true,
arrow: true,
modalOverlayOpeningRadius: 4,
modalOverlayOpeningPadding: 4,
buttons: [
{
text: Translate.skip,
secondary: true,
action() {
this.complete();
},
},
{
text: Translate.next,
action() {
this.next();
},
},
],
},
});
tour.on('complete', () => {
window.localStorage.setItem(KEY, 'true');
});
tour.addSteps(steps);
tour.start();
}

View File

@@ -0,0 +1,54 @@
import { localTrans } from '@capital/common';
export const Translate = {
next: localTrans({
'zh-CN': '下一步',
'en-US': 'Next',
}),
skip: localTrans({
'zh-CN': '跳过引导',
'en-US': 'Skip Tour',
}),
step1: localTrans({
'zh-CN': '欢迎使用 Tailchat, 一个插件化的开源IM应用',
'en-US': 'Welcome to Tailchat, a pluginify open source IM application!',
}),
step2: localTrans({
'zh-CN': '这是导航栏, 在这里可以切换tailchat的各个主要页面。',
'en-US':
'This is the navigation bar, where you can switch between the main pages of tailchat.',
}),
step3: localTrans({
'zh-CN': '这是个人信息入口,在这里可以访问您的好友、插件、以及私信等功能。',
'en-US':
'This is the personal information entry, where you can access functions such as your friends, plug-ins, and private messages.',
}),
step4: localTrans({
'zh-CN':
'这是群组列表, 会显示所有已加入的群组,您可以通过点击切换切换群组,也可以点击 + 号按钮来创建群组',
'en-US':
'This is a list of groups, which will display all the groups you have joined. You can click to switch between groups, or you can click the + button to create a group',
}),
step5: localTrans({
'zh-CN':
'这是设置按钮,可以通过此按钮来进行个人信息的变更、系统设置的变更、软件信息等内容',
'en-US':
'This is the setting button, through which you can change personal information, system settings, software information, etc.',
}),
step6: localTrans({
'zh-CN': '这是侧边栏,用于切换内容。在未来会经常使用它来切换不同的面板。',
'en-US':
'This is the sidebar, used to toggle content. Will use it frequently in the future to switch between different panels.',
}),
step7: localTrans({
'zh-CN': '这是内容区用于显示主要内容也是Tailchat展示内容的地方。',
'en-US':
'This is the content area, which is used to display the main content, and it is also where Tailchat displays the content.',
}),
step8: localTrans({
'zh-CN':
'如果有更多疑问, 欢迎访问官方文档了解更多: <br /><a href="https://tailchat.msgbyte.com/" target="_blank" style="text-decoration: underline;">点击此处打开官方文档</a>',
'en-US':
'If you have more questions, please visit the official document to learn more: <br /><a href="https://tailchat.msgbyte.com/" target="_blank" style="text-decoration: underline;">Click here to open the official documentation</a>',
}),
};

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react",
"paths": {
"@capital/*": ["../../src/plugin/*"],
}
}
}