From d6a80224b680eb5cab5eec59133b121b6e2cb773 Mon Sep 17 00:00:00 2001
From: Lum1104
Date: Fri, 3 Apr 2026 11:49:00 +0800
Subject: [PATCH] feat: add live demo dashboard on GitHub Pages
Add a demo mode to the dashboard that fetches graph data from Supabase
Storage instead of the local Vite dev server, bypasses the token gate,
and deploys alongside the homepage at /Understand-Anything/demo/.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.github/workflows/deploy-homepage.yml | 17 ++++++++-
homepage/src/components/Hero.astro | 19 ++++++++++
.../packages/dashboard/package.json | 1 +
.../packages/dashboard/src/App.tsx | 36 ++++++++++++++-----
.../packages/dashboard/vite.config.demo.ts | 22 ++++++++++++
5 files changed, 85 insertions(+), 10 deletions(-)
create mode 100644 understand-anything-plugin/packages/dashboard/vite.config.demo.ts
diff --git a/.github/workflows/deploy-homepage.yml b/.github/workflows/deploy-homepage.yml
index 2a12343..d6df0d9 100644
--- a/.github/workflows/deploy-homepage.yml
+++ b/.github/workflows/deploy-homepage.yml
@@ -5,6 +5,8 @@ on:
branches: [main]
paths:
- 'homepage/**'
+ - 'understand-anything-plugin/packages/dashboard/**'
+ - 'understand-anything-plugin/packages/core/**'
- '.github/workflows/deploy-homepage.yml'
workflow_dispatch:
@@ -34,10 +36,23 @@ jobs:
- name: Install dependencies
run: pnpm install
- - name: Build
+ - name: Build homepage
working-directory: homepage
run: pnpm build
+ - name: Build core
+ run: pnpm --filter @understand-anything/core build
+
+ - name: Build demo dashboard
+ run: pnpm --filter @understand-anything/dashboard build:demo
+ env:
+ VITE_GRAPH_URL: ${{ vars.DEMO_GRAPH_URL }}
+ VITE_DOMAIN_GRAPH_URL: ${{ vars.DEMO_DOMAIN_GRAPH_URL }}
+ VITE_META_URL: ${{ vars.DEMO_META_URL }}
+
+ - name: Merge demo into homepage output
+ run: cp -r understand-anything-plugin/packages/dashboard/dist homepage/dist/demo
+
- uses: actions/upload-pages-artifact@v3
with:
path: homepage/dist
diff --git a/homepage/src/components/Hero.astro b/homepage/src/components/Hero.astro
index d13a170..7844618 100644
--- a/homepage/src/components/Hero.astro
+++ b/homepage/src/components/Hero.astro
@@ -15,6 +15,7 @@ const githubUrl = 'https://github.com/Lum1104/Understand-Anything';
Get Started
+
Live Demo →
View on GitHub →
@@ -107,6 +108,24 @@ const githubUrl = 'https://github.com/Lum1104/Understand-Anything';
transform: translateY(-2px);
}
+ .hero-demo {
+ border: 1.5px solid var(--accent);
+ color: var(--accent);
+ padding: 0.85rem 2.5rem;
+ border-radius: 8px;
+ font-weight: 600;
+ font-size: 1.1rem;
+ text-decoration: none;
+ transition: background 0.3s ease, box-shadow 0.3s ease, transform 0.2s ease;
+ }
+
+ .hero-demo:hover {
+ background: rgba(212, 165, 116, 0.1);
+ box-shadow: 0 0 20px var(--accent-glow);
+ transform: translateY(-2px);
+ text-decoration: none;
+ }
+
.hero-secondary {
color: var(--text-muted);
font-size: 1rem;
diff --git a/understand-anything-plugin/packages/dashboard/package.json b/understand-anything-plugin/packages/dashboard/package.json
index 960000c..b9bdbe4 100644
--- a/understand-anything-plugin/packages/dashboard/package.json
+++ b/understand-anything-plugin/packages/dashboard/package.json
@@ -6,6 +6,7 @@
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
+ "build:demo": "tsc -b && vite build --config vite.config.demo.ts",
"preview": "vite preview"
},
"dependencies": {
diff --git a/understand-anything-plugin/packages/dashboard/src/App.tsx b/understand-anything-plugin/packages/dashboard/src/App.tsx
index d3d472d..c6e262e 100644
--- a/understand-anything-plugin/packages/dashboard/src/App.tsx
+++ b/understand-anything-plugin/packages/dashboard/src/App.tsx
@@ -24,13 +24,31 @@ import { ThemeProvider } from "./themes/index.ts";
import { ThemePicker } from "./components/ThemePicker.tsx";
import type { ThemeConfig } from "./themes/index.ts";
+const DEMO_MODE = import.meta.env.VITE_DEMO_MODE === "true";
const SESSION_TOKEN_KEY = "understand-anything-token";
+/** Resolve data file URL — in demo mode, use env var URLs; otherwise use local paths with token. */
+function dataUrl(fileName: string, token: string | null): string {
+ if (DEMO_MODE) {
+ const envMap: Record
= {
+ "knowledge-graph.json": import.meta.env.VITE_GRAPH_URL,
+ "domain-graph.json": import.meta.env.VITE_DOMAIN_GRAPH_URL,
+ "meta.json": import.meta.env.VITE_META_URL,
+ "diff-overlay.json": import.meta.env.VITE_DIFF_OVERLAY_URL,
+ };
+ const url = envMap[fileName];
+ if (url) return url;
+ }
+ const path = `/${fileName}`;
+ return token ? `${path}?token=${encodeURIComponent(token)}` : path;
+}
+
/**
* Resolve the access token from the URL query string or sessionStorage.
* If found in the URL, persist to sessionStorage and strip the param from the address bar.
*/
function resolveInitialToken(): string | null {
+ if (DEMO_MODE) return "__demo__";
const params = new URLSearchParams(window.location.search);
const urlToken = params.get("token");
if (urlToken) {
@@ -46,11 +64,6 @@ function resolveInitialToken(): string | null {
return sessionStorage.getItem(SESSION_TOKEN_KEY);
}
-/** Build a URL with the token query param appended. */
-function tokenUrl(path: string, token: string | null): string {
- return token ? `${path}?token=${encodeURIComponent(token)}` : path;
-}
-
function App() {
const [accessToken, setAccessToken] = useState(resolveInitialToken);
@@ -59,6 +72,11 @@ function App() {
setAccessToken(token);
}, []);
+ // In demo mode, skip token gate entirely
+ if (DEMO_MODE) {
+ return ;
+ }
+
// Show the token gate when no token is available
if (accessToken === null) {
return ;
@@ -90,7 +108,7 @@ function Dashboard({ accessToken }: { accessToken: string }) {
const setDomainGraph = useDashboardStore((s) => s.setDomainGraph);
useEffect(() => {
- fetch(tokenUrl("/meta.json", accessToken))
+ fetch(dataUrl("meta.json", accessToken))
.then((r) => (r.ok ? r.json() : null))
.then((meta) => {
if (meta?.theme) setMetaTheme(meta.theme);
@@ -215,7 +233,7 @@ function Dashboard({ accessToken }: { accessToken: string }) {
useKeyboardShortcuts(shortcuts);
useEffect(() => {
- fetch(tokenUrl("/knowledge-graph.json", accessToken))
+ fetch(dataUrl("knowledge-graph.json", accessToken))
.then((res) => res.json())
.then((data: unknown) => {
const result = validateGraph(data);
@@ -244,7 +262,7 @@ function Dashboard({ accessToken }: { accessToken: string }) {
}, [setGraph]);
useEffect(() => {
- fetch(tokenUrl("/diff-overlay.json", accessToken))
+ fetch(dataUrl("diff-overlay.json", accessToken))
.then((res) => {
if (!res.ok) return null;
return res.json();
@@ -270,7 +288,7 @@ function Dashboard({ accessToken }: { accessToken: string }) {
}, [setDiffOverlay]);
useEffect(() => {
- fetch(tokenUrl("/domain-graph.json", accessToken))
+ fetch(dataUrl("domain-graph.json", accessToken))
.then((res) => {
if (!res.ok) return null;
return res.json();
diff --git a/understand-anything-plugin/packages/dashboard/vite.config.demo.ts b/understand-anything-plugin/packages/dashboard/vite.config.demo.ts
new file mode 100644
index 0000000..aa616bc
--- /dev/null
+++ b/understand-anything-plugin/packages/dashboard/vite.config.demo.ts
@@ -0,0 +1,22 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
+import path from "path";
+
+export default defineConfig({
+ base: "/Understand-Anything/demo/",
+
+ resolve: {
+ alias: {
+ "@understand-anything/core/schema": path.resolve(__dirname, "../core/dist/schema.js"),
+ "@understand-anything/core/search": path.resolve(__dirname, "../core/dist/search.js"),
+ "@understand-anything/core/types": path.resolve(__dirname, "../core/dist/types.js"),
+ },
+ },
+
+ define: {
+ "import.meta.env.VITE_DEMO_MODE": JSON.stringify("true"),
+ },
+
+ plugins: [react(), tailwindcss()],
+});