diff --git a/README.md b/README.md
index f61ef7e1..72648126 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@
[](https://arxiv.org/abs/2607.20775)
[](https://github.com/microsoft/flint-chart/actions/workflows/ci.yml)
[](LICENSE)
+[](https://discord.gg/hnm4Y8YX8)
+[](#wechat)
**Please visit:** [**Flint Project Site**](https://microsoft.github.io/flint-chart/) | [**Visual Themes**](https://microsoft.github.io/flint-chart/#/themes) | [**MCP Server Guide**](https://microsoft.github.io/flint-chart/#/mcp) | [**中文主页**](https://microsoft.github.io/flint-chart/#/zh)
@@ -297,6 +299,19 @@ Flint is built by [Microsoft Research](https://www.microsoft.com/en-us/research/
in collaboration with the [IDEAS Lab](https://ideas-lab.net/), Renmin University
of China. We welcome you to join us — see [Contributing](#contributing) to get involved.
+## Community
+
+Chat with the team and other users:
+
+- **Discord** — [join the server](https://discord.gg/hnm4Y8YX8)
+- **WeChat** — scan the QR code to join the user group
+
+
+
+
+
+
+
A research paper describing Flint is coming soon.
## Trademarks
@@ -311,3 +326,4 @@ or logos is subject to those third parties' policies.
## License
[MIT](LICENSE) © Microsoft Corporation
+
diff --git a/docs/figs/QR-code.png b/docs/figs/QR-code.png
new file mode 100644
index 00000000..e0047a3e
Binary files /dev/null and b/docs/figs/QR-code.png differ
diff --git a/package-lock.json b/package-lock.json
index 6b9a3687..e77aae31 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6139,7 +6139,6 @@
"os": [
"android"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6161,7 +6160,6 @@
"os": [
"darwin"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6183,7 +6181,6 @@
"os": [
"darwin"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6205,7 +6202,6 @@
"os": [
"freebsd"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6227,7 +6223,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6249,7 +6244,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6271,7 +6265,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6293,7 +6286,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6315,7 +6307,6 @@
"os": [
"linux"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6337,7 +6328,6 @@
"os": [
"win32"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
@@ -6359,7 +6349,6 @@
"os": [
"win32"
],
- "peer": true,
"engines": {
"node": ">= 12.0.0"
},
diff --git a/site/src/components/SiteShell.tsx b/site/src/components/SiteShell.tsx
index fa93fb76..39a0e960 100644
--- a/site/src/components/SiteShell.tsx
+++ b/site/src/components/SiteShell.tsx
@@ -1,12 +1,13 @@
-import type { CSSProperties, KeyboardEvent, ReactNode } from 'react';
+import type { CSSProperties, KeyboardEvent, MouseEvent, ReactNode } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { useLocation } from 'react-router-dom';
+import { useLocation, useNavigate } from 'react-router-dom';
import { LocaleLink } from '../i18n/LocaleLink';
import { useLocale } from '../i18n/LocaleContext';
import { stripLocale } from '../i18n/paths';
import type { Locale } from '../i18n/locales';
-import { CONTENT_MAX_WIDTH, GITHUB_REPO, siteTheme } from '../shared/theme';
+import { CONTENT_MAX_WIDTH, DISCORD_INVITE, GITHUB_REPO, LANDING_SCROLL_TO_KEY, WECHAT_SECTION_ID, siteTheme } from '../shared/theme';
+import { scrollToHeading } from '../shared/scroll-to-heading';
const GITHUB_REPO_API = 'https://api.github.com/repos/microsoft/flint-chart';
const GITHUB_STARS_CACHE_KEY = 'flint-chart.github-stars';
@@ -84,6 +85,10 @@ export function SiteNavBar(_props: { flush?: boolean } = {}) {
@@ -327,6 +332,69 @@ function LanguageSwitch() {
);
}
+function WeChatLink() {
+ const { t } = useTranslation();
+ const { pathname } = useLocation();
+ const navigate = useNavigate();
+ const { lp } = useLocale();
+ const [hovered, setHovered] = useState(false);
+ const logical = stripLocale(pathname);
+
+ const onClick = (event: MouseEvent) => {
+ event.preventDefault();
+ if (logical === '/') {
+ scrollToHeading(WECHAT_SECTION_ID);
+ return;
+ }
+ try {
+ sessionStorage.setItem(LANDING_SCROLL_TO_KEY, WECHAT_SECTION_ID);
+ } catch {
+ // Privacy modes may block storage.
+ }
+ navigate(lp('/'));
+ };
+
+ return (
+ setHovered(true)}
+ onMouseLeave={() => setHovered(false)}
+ style={{
+ ...socialIconLinkStyle,
+ color: hovered ? siteTheme.accent : siteTheme.text,
+ }}
+ >
+
+
+ );
+}
+
+function DiscordLink() {
+ const { t } = useTranslation();
+ const [hovered, setHovered] = useState(false);
+
+ return (
+ setHovered(true)}
+ onMouseLeave={() => setHovered(false)}
+ style={{
+ ...socialIconLinkStyle,
+ color: hovered ? siteTheme.accent : siteTheme.text,
+ }}
+ >
+
+
+ );
+}
+
function GitHubLink() {
const { t, i18n } = useTranslation();
const [hovered, setHovered] = useState(false);
@@ -468,6 +536,22 @@ export function GitHubIcon({ size = 15 }: { size?: number } = {}) {
);
}
+export function DiscordIcon({ size = 16 }: { size?: number } = {}) {
+ return (
+
+
+
+ );
+}
+
+export function WeChatIcon({ size = 16 }: { size?: number } = {}) {
+ return (
+
+
+
+ );
+}
+
export function LabIcon({ size = 17 }: { size?: number } = {}) {
return (
@@ -491,6 +575,18 @@ const navLinkStyle: CSSProperties = {
letterSpacing: '0.01em',
};
+const socialIconLinkStyle: CSSProperties = {
+ ...navLinkStyle,
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: 28,
+ height: 28,
+ borderRadius: 6,
+ textDecoration: 'none',
+ transition: 'color 120ms ease, background 120ms ease',
+};
+
/**
* Required Microsoft site disclosures.
*
diff --git a/site/src/i18n/messages/en.json b/site/src/i18n/messages/en.json
index a73b5342..0b1c9b08 100644
--- a/site/src/i18n/messages/en.json
+++ b/site/src/i18n/messages/en.json
@@ -14,6 +14,10 @@
"githubAriaStars": "GitHub repository, {{count}} stars",
"githubTitle": "View on GitHub",
"githubTitleStars": "View on GitHub ({{count}} stars)",
+ "wechatAria": "WeChat community",
+ "wechatTitle": "Join the WeChat group",
+ "discordAria": "Discord community",
+ "discordTitle": "Join Discord",
"langEn": "EN",
"langZh": "中文",
"langSwitchAria": "Switch language"
@@ -405,6 +409,9 @@
"closingBody": "Open source and ready to use. Start from GitHub or browse examples in the gallery.",
"viewGithub": "View on GitHub",
"closingCollab": "Flint is built by Microsoft Research in collaboration with the IDEAS Lab , Renmin University of China.",
+ "wechatTitle": "Join the Flint WeChat group",
+ "wechatBody": "Scan the QR code to join the Flint user WeChat group and chat with other users.",
+ "wechatAlt": "QR code for the Flint WeChat user group",
"features": {
"semantic": {
"title": "Specify with semantic types",
diff --git a/site/src/i18n/messages/zh-CN.json b/site/src/i18n/messages/zh-CN.json
index e652bd7d..cf965fd2 100644
--- a/site/src/i18n/messages/zh-CN.json
+++ b/site/src/i18n/messages/zh-CN.json
@@ -14,6 +14,10 @@
"githubAriaStars": "GitHub 仓库,{{count}} 星",
"githubTitle": "在 GitHub 上查看",
"githubTitleStars": "在 GitHub 上查看({{count}} 星)",
+ "wechatAria": "微信社群",
+ "wechatTitle": "加入微信群",
+ "discordAria": "Discord 社群",
+ "discordTitle": "加入 Discord",
"langEn": "EN",
"langZh": "中文",
"langSwitchAria": "切换语言"
@@ -405,6 +409,9 @@
"closingBody": "Flint 已开源,可直接使用。前往 GitHub 获取源码,或先浏览图表示例。",
"viewGithub": "在 GitHub 上查看",
"closingCollab": "Flint 由 Microsoft Research 与中国人民大学 IDEAS Lab 合作构建。",
+ "wechatTitle": "加入 Flint 用户微信群",
+ "wechatBody": "扫码加入 Flint 用户微信群,与其他用户交流。",
+ "wechatAlt": "Flint 用户微信群二维码",
"features": {
"semantic": {
"title": "用语义类型表达数据含义",
diff --git a/site/src/routes/Landing.tsx b/site/src/routes/Landing.tsx
index 5a79cf11..7a720f24 100644
--- a/site/src/routes/Landing.tsx
+++ b/site/src/routes/Landing.tsx
@@ -19,8 +19,10 @@ import {
getSupportedBackends,
type PreviewBackend,
} from '../shared/supported-backends';
-import { GITHUB_REPO, siteTheme } from '../shared/theme';
+import { GITHUB_REPO, LANDING_SCROLL_TO_KEY, WECHAT_SECTION_ID, siteTheme } from '../shared/theme';
+import { scrollToHeading } from '../shared/scroll-to-heading';
import flintLogo from '../assets/flint-logo.svg';
+import wechatQr from '../../../docs/figs/QR-code.png';
/**
* Front page: flat "paper" look inspired by Microsoft data-formulator. A
@@ -31,6 +33,21 @@ export function Landing() {
const { t } = useTranslation();
const features = useMemo(() => getFeatures(t), [t]);
+ useEffect(() => {
+ let pending: string | null = null;
+ try {
+ pending = sessionStorage.getItem(LANDING_SCROLL_TO_KEY);
+ if (pending) sessionStorage.removeItem(LANDING_SCROLL_TO_KEY);
+ } catch {
+ // Privacy modes may block storage.
+ }
+ if (pending !== WECHAT_SECTION_ID) return;
+ const timer = window.setTimeout(() => {
+ scrollToHeading(WECHAT_SECTION_ID);
+ }, 50);
+ return () => window.clearTimeout(timer);
+ }, []);
+
return (
@@ -290,7 +307,7 @@ export function Landing() {
{/* ---- Closing CTA -------------------------------------------- */}
-
+
{t('landing.closingTitle')}
@@ -331,6 +348,25 @@ export function Landing() {
/>
+
+ {/* ---- WeChat community -------------------------------------- */}
+
+
+ {t('landing.wechatTitle')}
+
+
+ {t('landing.wechatBody')}
+
+
+
diff --git a/site/src/shared/theme.ts b/site/src/shared/theme.ts
index 78c31575..61087188 100644
--- a/site/src/shared/theme.ts
+++ b/site/src/shared/theme.ts
@@ -26,3 +26,12 @@ export const siteTheme = {
export const CONTENT_MAX_WIDTH = 1180;
export const GITHUB_REPO = 'https://github.com/microsoft/flint-chart';
+
+/** Community Discord invite. */
+export const DISCORD_INVITE = 'https://discord.gg/hnm4Y8YX8';
+
+/** Landing-page section id for the WeChat QR block. */
+export const WECHAT_SECTION_ID = 'wechat';
+
+/** sessionStorage key used to scroll to a landing section after navigating home. */
+export const LANDING_SCROLL_TO_KEY = 'flint-landing-scroll-to';