E4FA Updates · stack-auth/stack-auth@5760b90 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5760b90

Browse files
committed
Updates
1 parent 2e01464 commit 5760b90

File tree

10 files changed

+211
-209
lines changed

10 files changed

+211
-209
lines changed

apps/dashboard/.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=# enter your Stack publishable client k
55
STACK_SECRET_SERVER_KEY=# enter your Stack secret client key here. For local development, do the same as above
66
NEXT_PUBLIC_STACK_EXTRA_REQUEST_HEADERS=# a list of extra request headers to add to all Stack Auth API requests, as a JSON record
77
NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY=# enter your Stripe publishable key here
8+
NEXT_PUBLIC_STACK_DOCS_BASE_URL=https://docs.stack-auth.com
89

910
# Webhooks
1011
NEXT_PUBLIC_STACK_SVIX_SERVER_URL=# For prod, leave it empty. For local development, use `http://localhost:8113`

apps/dashboard/.env.development

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
NEXT_PUBLIC_STACK_API_URL=http://localhost:8102
2+
NEXT_PUBLIC_STACK_DOCS_BASE_URL=http://localhost:8104
23

34
NEXT_PUBLIC_STACK_PROJECT_ID=internal
45
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=this-publishable-client-key-is-for-local-development-only

apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { ArrowLeft, BookOpen, ExternalLink, Loader2, Menu } from 'lucide-react';
44
import { usePathname } from 'next/navigation';
55
import { useEffect, useState } from 'react';
6+
import { getPublicEnvVar } from '../../lib/env';
67

78
type UnifiedDocsWidgetProps = {
89
isActive: boolean,
@@ -24,13 +25,32 @@ const PLATFORM_OPTIONS = [
2425
{ value: 'python', label: 'Python', color: 'rgb(168, 85, 247)' },
2526
];
2627

28+
// Get the docs base URL from environment variable with fallback
29+
const getDocsBaseUrl = (): string => {
30+
// Use centralized environment variable system
31+
const docsBaseUrl = getPublicEnvVar('NEXT_PUBLIC_STACK_DOCS_BASE_URL');
32+
if (docsBaseUrl) {
33+
return docsBaseUrl;
34+
}
35+
36+
// Fallback logic for when env var is not set
37+
if (process.env.NODE_ENV === 'development' || window.location.hostname === 'localhost') {
38+
return 'http://localhost:8104';
39+
}
40+
41+
// Production fallback
42+
return 'https://docs.stack-auth.com';
43+
};
44+
2745
// Function to toggle sidebar in embedded docs via postMessage
2846
const toggleEmbeddedSidebar = (iframe: HTMLIFrameElement, visible: boolean) => {
2947
try {
30-
iframe.contentWindow?.postMessage({
31-
type: 'TOGGLE_SIDEBAR',
32-
visible: visible
33-
}, '*');
48+
const src = iframe.getAttribute("src") ?? "";
49+
const targetOrigin = new URL(src, window.location.origin).origin;
50+
iframe.contentWindow?.postMessage(
51+
{ type: "TOGGLE_SIDEBAR", visible },
52+
targetOrigin
53+
)
3454
} catch (error) {
3555
console.warn('Failed to communicate with embedded docs:', error);
3656
}
@@ -96,19 +116,19 @@ const getDocContentForPath = (path: string, docType: DocType, platform: string =
96116
};
97117

98118
const docMapping = dashboardToDocsMap[page];
99-
const url = `http://localhost:8104/docs-embed/${docMapping.path}`;
119+
const url = `${getDocsBaseUrl()}/docs-embed/${docMapping.path}`;
100120
const title = docMapping.title;
101121
return { title, url, type: 'dashboard' };
102122
}
103123
case 'docs': {
104124
// Default to getting started for main docs
105-
const url = `http://localhost:8104/docs-embed/${platform}/getting-started/setup`;
125+
const url = `${getDocsBaseUrl()}/docs-embed/${platform}/getting-started/setup`;
106126
const title = 'Stack Auth Documentation';
107127
return { title, url, type: 'docs' };
108128
}
109129
case 'api': {
110130
// Default to overview for API docs
111-
const url = `http://localhost:8104/api-embed/overview`;
131+
const url = `${getDocsBaseUrl()}/api-embed/overview`;
112132
const title = 'API Reference';
113133
return { title, url, type: 'api' };
114134
}
@@ -131,15 +151,16 @@ export function UnifiedDocsWidget({ isActive }: UnifiedDocsWidgetProps) {
131151
const [canGoBack< 4B92 span class=pl-kos>, setCanGoBack] = useState(false);
132152
const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);
133153
const [isSidebarVisible, setIsSidebarVisible] = useState(false);
154+
const [platformChangeSource, setPlatformChangeSource] = useState<'manual' | 'iframe'>('manual');
134155

135156
// Load documentation when the component becomes active, doc type changes, platform changes, or pathname changes
136157
useEffect(() => {
137158
if (isActive) {
138159
const newPageDoc = getDashboardPage(pathname);
139160

140-
// If this is the first time opening, doc type changed, or platform changed
161+
// If this is the first time opening, doc type changed, or platform changed manually (not from iframe)
141162
if (!docContent || docContent.type !== selectedDocType ||
142-
(selectedDocType !== 'api' && !docContent.url.includes(`/${selectedPlatform}/`))) {
163+
(selectedDocType !== 'api' && !docContent.url.includes(`/${selectedPlatform}/`) && platformChangeSource === 'manual')) {
143164
setLoading(true);
144165
setError(null);
145166
setIframeLoaded(false);
@@ -167,9 +188,9 @@ export function UnifiedDocsWidget({ isActive }: UnifiedDocsWidgetProps) {
167188
setShowSwitchPrompt(true);
168189
}
169190
}
170-
}, [isActive, pathname, selectedDocType, selectedPlatform, docContent, currentPageDoc]);
191+
}, [isActive, pathname, selectedDocType, selectedPlatform, docContent, currentPageDoc, platformChangeSource]);
171192

172-
// Monitor iframe for back button capability
193+
// Monitor iframe for back button capability and platform detection
173194
useEffect(() => {
174195
// Simple heuristic: assume we can go back after the iframe has been loaded for a while
175196
// and user has had time to navigate
@@ -182,6 +203,48 @@ export function UnifiedDocsWidget({ isActive }: UnifiedDocsWidgetProps) {
182203
}
183204
}, [iframeLoaded]);
184205

206+
// Listen for platform changes from embedded docs via postMessage
207+
useEffect(() => {
208+
const handleMessage = (event: MessageEvent) => {
209+
// Verify origin for security - allow localhost in dev and configured docs URL
210+
const isLocalhost = event.origin.includes('localhost') || event.origin.includes('127.0.0.1');
211+
const expectedDocsOrigin = new URL(getDocsBaseUrl()).origin;
212+
const isValidDocsOrigin = event.origin === expectedDocsOrigin;
213+
214+
if (!isLocalhost && !isValidDocsOrigin) return;
215+
216+
if (event.data?.type === 'PLATFORM_CHANGE') {
217+
const detectedPlatform = event.data.platform;
218+
const validPlatforms = PLATFORM_OPTIONS.map(p => p.value);
219+
220+
if (validPlatforms.includes(detectedPlatform) && detectedPlatform !== selectedPlatform) {
221+
console.log('Received platform change from iframe:', detectedPlatform);
222+
223+
// Mark this as an iframe-driven platform change
224+
setPlatformChangeSource('iframe');
225+
226+
// Update the platform selector but also update the docContent URL to reflect the current iframe URL
227+
setSelectedPlatform(detectedPlatform);
228+
229+
// Update docContent to reflect the new URL without reloading the iframe
230+
if (docContent && event.data.pathname) {
231+
const newUrl = `${getDocsBaseUrl()}${event.data.pathname}`;
232+
setDocContent({
233+
...docContent,
234+
url: newUrl
235+
});
236+
}
237+
238+
// Reset the platform change source after a short delay
239+
setTimeout(() => setPlatformChangeSource('manual'), 100);
240+
}
241+
}
242+
};
243+
244+
window.addEventListener('message', handleMessage);
245+
return () => window.removeEventListener('message', handleMessage);
246+
}, [selectedPlatform, docContent]);
247+
185248
// Handle iframe load events
186249
const handleIframeLoad = (event: React.SyntheticEvent<HTMLIFrameElement>) => {
187250
setIframeLoaded(true);
@@ -232,6 +295,7 @@ export function UnifiedDocsWidget({ isActive }: UnifiedDocsWidgetProps) {
232295
// Handle platform selection
233296
const handlePlatformChange = (platform: string) => {
234297
if (platform !== selectedPlatform) {
298+
setPlatformChangeSource('manual');
235299
setSelectedPlatform(platform);
236300
// Sidebar will automatically update to show new platform content
237301
}

apps/dashboard/src/lib/env.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const _inlineEnvVars = {
77
NEXT_PUBLIC_STACK_DASHBOARD_URL: process.env.NEXT_PUBLIC_STACK_DASHBOARD_URL,
88
NEXT_PUBLIC_BROWSER_STACK_DASHBOARD_URL: process.env.NEXT_PUBLIC_BROWSER_STACK_DASHBOARD_URL,
99
NEXT_PUBLIC_SERVER_STACK_DASHBOARD_URL: process.env.NEXT_PUBLIC_SERVER_STACK_DASHBOARD_URL,
10+
NEXT_PUBLIC_STACK_DOCS_BASE_URL: process.env.NEXT_PUBLIC_STACK_DOCS_BASE_URL,
1011
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
1112
NEXT_PUBLIC_STACK_SVIX_SERVER_URL: process.env.NEXT_PUBLIC_STACK_SVIX_SERVER_URL,
1213
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
@@ -29,6 +30,7 @@ const _postBuildEnvVars = {
2930
NEXT_PUBLIC_STACK_DASHBOARD_URL: "STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_STACK_DASHBOARD_URL",
3031
NEXT_PUBLIC_BROWSER_STACK_DASHBOARD_URL: "STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_BROWSER_STACK_DASHBOARD_URL",
3132
NEXT_PUBLIC_SERVER_STACK_DASHBOARD_URL: "STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_SERVER_STACK_DASHBOARD_URL",
33+
NEXT_PUBLIC_STACK_DOCS_BASE_URL: "STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_STACK_DOCS_BASE_URL",
3234
NEXT_PUBLIC_STACK_PROJECT_ID: "STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_STACK_PROJECT_ID",
3335
NEXT_PUBLIC_POSTHOG_KEY: "STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_POSTHOG_KEY",
3436
NEXT_PUBLIC_STACK_SVIX_SERVER_URL: "STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_STACK_SVIX_SERVER_URL",

docs/src/app/api-embed/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ export default function ApiEmbedLayout({ children }: { children: React.ReactNode
66
<div className="min-h-screen bg-fd-background">
77
<EmbeddedLinkInterceptor />
88
{/* Main content area - no header, no padding, prevent horizontal overflow */}
9-
<main className="h-screen overflow-hidden">
10-
<div className="h-full overflow-y-auto overflow-x-hidden scrollbar-hide">
9+
<main className="overflow-hidden">
10+
<div className="flex-1 overflow-y-auto overflow-x-hidden scrollbar-hide">
1111
{children}
1212
</div>
1313
</main>

docs/src/app/docs-embed/[[...slug]]/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { redirect } from 'next/navigation';
66
export default async function DocsEmbedPage({
77
params,
88
}: {
9-
params: Promise<{ slug?: string[] }>,
9+
params: { slug?: string[] },
1010
}) {
11-
const { slug } = await params;
11+
const { slug } = params;
1212

1313
// If no slug provided, redirect to overview
1414
if (!slug || slug.length === 0) {
@@ -27,7 +27,6 @@ export default async function DocsEmbedPage({
2727
return (
2828
<EmbeddedDocsWithSidebar
2929
pageTree={source.pageTree}
30-
currentSlug={slug}
3130
>
3231
<div className="p-6 prose prose-neutral dark:prose-invert max-w-none overflow-x-hidden">
3332
<div className="w-full">

docs/src/app/docs-embed/layout.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { EmbeddedLinkInterceptor } from '@/components/embedded-link-interceptor';
2+
import { PlatformChangeNotifier } from '@/components/platform-change-notifier';
23

34
// Embedded layout for main docs - no navbar, optimized for iframe
45
export default function DocsEmbedLayout({ children }: { children: React.ReactNode }) {
56
return (
67
<div className="min-h-screen bg-fd-background">
78
<EmbeddedLinkInterceptor />
9+
<PlatformChangeNotifier />
810
{/* Main content area - no header, no padding, prevent horizontal overflow */}
9-
<main className="h-screen overflow-hidden">
10-
<div className="h-full overflow-y-auto overflow-x-hidden scrollbar-hide">
11+
<main className="overflow-hidden">
12+
<div className="flex-1 overflow-y-auto overflow-x-hidden scrollbar-hide">
1113
{children}
1214
</div>
1315
</main>

docs/src/app/global.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,10 @@ button:not(.chat-gradient-active)::before {
240240

241241
/* Only break URLs and very long unbreakable strings */
242242
.prose a[href*="://"] {
243-
word-break: break-all;
243+
overflow-wrap: anywhere;
244244
}
245245

246246
/* Break very long strings that have no spaces (like tokens, hashes, etc.) */
247247
.prose code:not(pre code) {
248-
word-break: break-all;
248+
overflow-wrap: anywhere;
249249
}

0 commit comments

Comments
 (0)
0