mirror of https://github.com/msgbyte/tailchat
docs: add new website homepage
parent
ba16ff6cb1
commit
1a4ac522c7
@ -0,0 +1,160 @@
|
||||
.feature-sections {
|
||||
padding: 2rem 1rem;
|
||||
|
||||
@media (min-width: 997px) {
|
||||
padding: 5rem 1rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin: auto;
|
||||
max-width: 80rem;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: var(--ifm-navbar-height);
|
||||
background-color: var(--ifm-background-color);
|
||||
z-index: 20;
|
||||
margin-top: -1rem;
|
||||
gap: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
@media (min-width: 997px) {
|
||||
height: 5rem;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: center;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
line-height: 1.5;
|
||||
|
||||
@media (min-width: 997px) {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
margin: auto;
|
||||
align-self: flex-start;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
|
||||
@media (min-width: 997px) {
|
||||
width: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.right-body {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
font-size: .875rem;
|
||||
line-height: 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--ifm-color-emphasis-200);
|
||||
overflow: auto;
|
||||
|
||||
|
||||
@media (min-width: 997px) {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.pill {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
padding: 0.5rem 1.5rem;
|
||||
text-align: center;
|
||||
font-size: .875rem;
|
||||
line-height: 1.25rem;
|
||||
border-radius: .375rem;
|
||||
|
||||
&.active {
|
||||
background-color: var(--ifm-color-primary);
|
||||
color: var(--ifm-color-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
background-color: var(--ifm-color-gray-900);
|
||||
border-radius: 1.5rem;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
[data-theme="light"] & {
|
||||
background-color: var(--ifm-color-gray-100);
|
||||
}
|
||||
|
||||
@media (min-width: 997px) {
|
||||
flex-direction: row;
|
||||
|
||||
&.reverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
|
||||
@media (min-width: 997px) {
|
||||
flex-direction: row;
|
||||
padding: 1.5rem 1.5rem 1.5rem 4rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
background-color: var(--ifm-color-gray-800);
|
||||
border-radius: 1.5rem;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
[data-theme="light"] & {
|
||||
background-color: var(--ifm-color-gray-200);
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
import { useColorMode } from '@docusaurus/theme-common';
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Head from '@docusaurus/Head';
|
||||
import Link from '@docusaurus/Link';
|
||||
import { nightlyUrl } from '../utils/consts';
|
||||
import './FeatureSection.less';
|
||||
|
||||
export const FeatureSection: React.FC = React.memo(() => {
|
||||
const { colorMode } = useColorMode();
|
||||
const [visibleSection, setVisibleSection] = useState('messenger');
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
const section = entry.target.id;
|
||||
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('intersected');
|
||||
setVisibleSection(section);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootMargin: '-50% 0% -50% 0%' }
|
||||
);
|
||||
|
||||
const elements = document.querySelectorAll(
|
||||
'.feature-sections .body > .item'
|
||||
);
|
||||
elements.forEach((el) => {
|
||||
observer.observe(el);
|
||||
});
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
function Pill({ section }) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'pill',
|
||||
'flex-1 cursor-pointer rounded-md py-2 px-6 text-center font-jakarta text-sm font-semibold',
|
||||
{ active: visibleSection === section }
|
||||
)}
|
||||
onClick={() => {
|
||||
document
|
||||
.getElementById(section)
|
||||
?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}}
|
||||
>
|
||||
{`${section[0].toUpperCase()}${section.substring(1)}`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="feature-sections">
|
||||
<Head>
|
||||
<link rel="prefetch" href="/img/hero-light.png" as="image" />
|
||||
<link rel="prefetch" href="/img/hero-dark.png" as="image" />
|
||||
<link rel="preload" href="/img/intro/hello.png" as="image" />
|
||||
<link rel="preload" href="/img/intro/plugins.png" as="image" />
|
||||
<link rel="preload" href="/img/intro/roles.png" as="image" />
|
||||
<link rel="preload" href="/img/intro/github-bot.png" as="image" />
|
||||
</Head>
|
||||
<div className="main">
|
||||
<div className="title">
|
||||
<h2 className="left">Feature Overview</h2>
|
||||
|
||||
<div className="right">
|
||||
<div className="right-body">
|
||||
<Pill section="messenger" />
|
||||
<Pill section="plugin" />
|
||||
<Pill section="roles" />
|
||||
<Pill section="bot" />
|
||||
<Pill section="platform" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="body">
|
||||
<div className="item" id="messenger">
|
||||
<div className="left">
|
||||
<h3>Messenger</h3>
|
||||
<p>
|
||||
Basic message support, multi message type like
|
||||
text/link/mention/image/file etc. and support append reaction
|
||||
for anything you want with messages.
|
||||
</p>
|
||||
<p>
|
||||
You can join multiple groups, and discuss different topics,
|
||||
perhaps information and notifications, through multiple panels
|
||||
in the group. Not just a simple chat.
|
||||
</p>
|
||||
<p>
|
||||
In the inbox, you can receive anything you need to know, such as
|
||||
mention or plugin notification. Or you can push anything on
|
||||
yourself.
|
||||
</p>
|
||||
</div>
|
||||
<div className="right">
|
||||
<img data-zoomable src="/img/intro/hello.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="item reverse" id="plugin">
|
||||
<div className="left">
|
||||
<h3>Plugin Center</h3>
|
||||
<p>
|
||||
Tailchat has a complete plugin system. With plugins, you can
|
||||
integrate your apps and projects into your chat app in any form
|
||||
you want. Unlike VSCode, Tailchat has fewer restrictions on the
|
||||
form of expression. I think Tailchat is not only a chat app, but
|
||||
also a platform for integrating different applications. You can
|
||||
start a video conference, listen to music, use online tools and
|
||||
more in Tailchat.
|
||||
</p>
|
||||
<p>
|
||||
At the same time, through plugins, you can further improve the
|
||||
chat experience, such as topic panel, end-to-end encryption,
|
||||
rich text, message notification, online drawing, receiving push
|
||||
from third-party applications, etc.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="right">
|
||||
<img data-zoomable src="/img/intro/plugins.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="item" id="roles">
|
||||
<div className="left">
|
||||
<h3>Group Roles</h3>
|
||||
<p>
|
||||
Tailchat has a builtin RBAC permission management system. Based
|
||||
on the combination of role assignment and permission points,
|
||||
various permission combinations can be matched. At the same
|
||||
time, permission points can be easily integrated by plugins,
|
||||
which are a very flexible design.
|
||||
</p>
|
||||
<Link
|
||||
className="button button--link"
|
||||
to="/docs/contribution/dev/role"
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
</div>
|
||||
<div className="right">
|
||||
<img data-zoomable src="/img/intro/roles.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="item reverse" id="bot">
|
||||
<div className="left">
|
||||
<h3>Bot</h3>
|
||||
<p>
|
||||
Tailchat has a very simple way to integrate third-party
|
||||
applications with bot like most applications. A simple url
|
||||
request or add openapi app or even create a backend plugin. You
|
||||
can use anyway to connect anything, its free!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="right">
|
||||
<img data-zoomable src="/img/intro/github-bot.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="item" id="platform">
|
||||
<div className="left">
|
||||
<h3>Multi-platform Support</h3>
|
||||
<p>
|
||||
Tailchat design on HTML, and fit any platform or os, but its
|
||||
still some native support cannot provide in web. So Tailchat
|
||||
also has client to provide os support like mobile notification ,
|
||||
desktop screenshot and etc.
|
||||
</p>
|
||||
<div className="btns">
|
||||
<Link className="button button--primary" to={nightlyUrl}>
|
||||
Web
|
||||
</Link>
|
||||
<Link className="button button--secondary disabled">
|
||||
Mobile (in Alpha Test)
|
||||
</Link>
|
||||
<Link className="button button--secondary disabled">
|
||||
Desktop (in Alpha Test)
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="right">
|
||||
<img data-zoomable src={`/img/hero-${colorMode}.png`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
});
|
||||
FeatureSection.displayName = 'FeatureSection';
|
@ -0,0 +1,13 @@
|
||||
.join-community {
|
||||
text-align: center;
|
||||
padding: 5rem 0;
|
||||
|
||||
h3 {
|
||||
font-weight: 700;
|
||||
font-size: 1.875rem;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { inviteLink } from '../utils/consts';
|
||||
import Link from '@docusaurus/Link';
|
||||
import './JoinCommunity.less';
|
||||
|
||||
export const JoinCommunity: React.FC = React.memo(() => {
|
||||
return (
|
||||
<div className="join-community">
|
||||
<h3>Join the community</h3>
|
||||
<p>
|
||||
Engage with our ever-growing community to get the latest updates,
|
||||
product support, and more.
|
||||
</p>
|
||||
<Link className="button button--primary button--lg" href={inviteLink}>
|
||||
Join Our Group
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
JoinCommunity.displayName = 'JoinCommunity';
|
@ -0,0 +1,3 @@
|
||||
export const nightlyUrl = 'https://nightly.paw.msgbyte.com/';
|
||||
|
||||
export const inviteLink = 'https://nightly.paw.msgbyte.com/invite/8Jfm1dWb';
|
@ -0,0 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import mediumZoom from 'medium-zoom';
|
||||
|
||||
export function useMediumZoom() {
|
||||
useEffect(() => {
|
||||
mediumZoom('[data-zoomable]');
|
||||
}, []);
|
||||
}
|
Loading…
Reference in New Issue