From 41ccdce3e5a1ae308d188075e19536a40bcb68cb Mon Sep 17 00:00:00 2001
From: joshunrau
Date: Tue, 9 Jun 2026 13:39:57 -0400
Subject: [PATCH] feat(playground): add preview mode option to share button
Add a "Preview mode" toggle to the share popover. When enabled, the
share URL includes a fullscreen=1 query param, and opening that link
renders the instrument fullscreen as a read-only preview with no editor.
Co-Authored-By: Claude Opus 4.8
---
.../Header/ShareButton/ShareButton.tsx | 48 +++++++++++++++++--
apps/playground/src/pages/IndexPage.tsx | 29 ++++++++++-
apps/playground/src/utils/encode.ts | 13 ++++-
3 files changed, 83 insertions(+), 7 deletions(-)
diff --git a/apps/playground/src/components/Header/ShareButton/ShareButton.tsx b/apps/playground/src/components/Header/ShareButton/ShareButton.tsx
index 74b071114..1e21f3731 100644
--- a/apps/playground/src/components/Header/ShareButton/ShareButton.tsx
+++ b/apps/playground/src/components/Header/ShareButton/ShareButton.tsx
@@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import { formatByteSize } from '@douglasneuroinformatics/libjs';
-import { Heading, Input, Popover, Tooltip } from '@douglasneuroinformatics/libui/components';
+import { Heading, Input, Label, Popover, Tooltip } from '@douglasneuroinformatics/libui/components';
import { useTranslation } from '@douglasneuroinformatics/libui/hooks';
import { CopyButton } from '@opendatacapture/react-core';
-import { Share2Icon } from 'lucide-react';
+import { CircleHelpIcon, Share2Icon } from 'lucide-react';
import { useFilesRef } from '@/hooks/useFilesRef';
import { useAppStore } from '@/store';
@@ -13,6 +13,7 @@ import { encodeShareURL } from '@/utils/encode';
export const ShareButton = () => {
const label = useAppStore((store) => store.selectedInstrument.label);
const editorFilesRef = useFilesRef();
+ const [isFullscreen, setIsFullscreen] = useState(false);
const [shareURL, setShareURL] = useState(encodeShareURL({ files: editorFilesRef.current, label }));
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
@@ -21,9 +22,9 @@ export const ShareButton = () => {
// The user cannot modify the editor without closing the popover
useEffect(() => {
if (isPopoverOpen) {
- setShareURL(encodeShareURL({ files: editorFilesRef.current, label }));
+ setShareURL(encodeShareURL({ files: editorFilesRef.current, fullscreen: isFullscreen, label }));
}
- }, [isPopoverOpen, label]);
+ }, [isFullscreen, isPopoverOpen, label]);
return (
{
{formatByteSize(shareURL.size)}.
+
+
+
+
+
+
+
+
+
+ {t({
+ en: 'Anyone with the link opens the instrument fullscreen as a preview they can try, with no editor and no ability to make changes.',
+ fr: "Toute personne disposant du lien ouvre l'instrument en plein écran comme un aperçu qu'elle peut essayer, sans éditeur ni possibilité de le modifier."
+ })}
+
+
+
+
+
+
diff --git a/apps/playground/src/pages/IndexPage.tsx b/apps/playground/src/pages/IndexPage.tsx
index 2e61857d2..c8428ea64 100644
--- a/apps/playground/src/pages/IndexPage.tsx
+++ b/apps/playground/src/pages/IndexPage.tsx
@@ -1,13 +1,14 @@
import { useEffect } from 'react';
-import { Separator } from '@douglasneuroinformatics/libui/components';
+import { LanguageToggle, Separator, ThemeToggle } from '@douglasneuroinformatics/libui/components';
import esbuildWasmUrl from 'esbuild-wasm/esbuild.wasm?url';
import { Header } from '@/components/Header';
import { MainContent } from '@/components/MainContent';
+import { Viewer } from '@/components/Viewer';
import type { InstrumentRepository } from '@/models/instrument-repository.model';
import { useAppStore } from '@/store';
-import { decodeShareURL } from '@/utils/encode';
+import { decodeShareURL, isFullscreenShareURL } from '@/utils/encode';
const { initialize } = await import('esbuild-wasm');
await initialize({
@@ -77,6 +78,30 @@ const IndexPage = () => {
};
}, [location.href]);
+ const isFullscreen = isFullscreenShareURL(new URL(location.href));
+
+ if (isFullscreen) {
+ return (
+