mirror of
https://github.com/Egonex-AI/Understand-Anything.git
synced 2026-06-22 10:58:03 +08:00
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -15,6 +15,7 @@ const githubUrl = 'https://github.com/Lum1104/Understand-Anything';
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="#install" class="hero-cta">Get Started</a>
|
||||
<a href="/Understand-Anything/demo/" class="hero-demo">Live Demo →</a>
|
||||
<a href={githubUrl} target="_blank" rel="noopener noreferrer" class="hero-secondary">
|
||||
View on GitHub →
|
||||
</a>
|
||||
@@ -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;
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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<string, string | undefined> = {
|
||||
"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<string | null>(resolveInitialToken);
|
||||
|
||||
@@ -59,6 +72,11 @@ function App() {
|
||||
setAccessToken(token);
|
||||
}, []);
|
||||
|
||||
// In demo mode, skip token gate entirely
|
||||
if (DEMO_MODE) {
|
||||
return <Dashboard accessToken="__demo__" />;
|
||||
}
|
||||
|
||||
// Show the token gate when no token is available
|
||||
if (accessToken === null) {
|
||||
return <TokenGate onTokenValid={handleTokenValid} />;
|
||||
@@ -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();
|
||||
|
||||
@@ -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()],
|
||||
});
|
||||
Reference in New Issue
Block a user