- {/* Home + Actions, pinned to the far left of the bar — out in the
- margin to the left of the centred tool cluster, so they read as
- window chrome rather than editing tools. The universal navbar is
- landing-page only; while a doc is open the dark toolbar is the
- whole chrome. md+ only: that margin only exists on wider screens.
- Mobile keeps a home button inside the cluster below. */}
+ {/* Home, pinned to the far left of the bar — out in the margin to the
+ left of the centred tool cluster, so it reads as window chrome
+ rather than an editing tool. The universal navbar is landing-page
+ only; while a doc is open the dark toolbar is the whole chrome.
+ lg+ only: that margin only exists on wider screens. Mobile keeps a
+ home button inside the cluster below.
+
+ Actions used to sit here too. It now rides in the profile pill at
+ the far right — one control, one dropdown — matching the other
+ Universal Apps. */}
-
- {/* Mobile-only Actions. On md+ it lives in the universal navbar,
- or — when that bar is hidden — in the far-left chrome group. */}
-
-
-
@@ -223,7 +221,7 @@ export default function App() {
far right of the bar below, mirroring the Home/Actions chrome
on the far left. */}
-
+ } />
-
+ } />
so guests sign in via a popup and
* stay in the app; modified clicks still follow hubLoginHref.
+ *
+ * Pass `actions` to merge the Actions menu into this control the way the other
+ * Universal Apps do — one pill, one dropdown, app rows above the account rows —
+ * instead of an Actions button and an avatar sitting apart in the same bar.
*/
-export default function ToolbarUserProfile() {
+export default function ToolbarUserProfile({ actions }: { actions?: ReactNode }) {
const { user, loading: userLoading } = useUser()
const { profile, loading: profileLoading } = useProfile()
const { supabase, session } = useUniversal()
@@ -46,7 +50,15 @@ export default function ToolbarUserProfile() {
return (
<>
-
+ setSignInOpen(false)}
diff --git a/src/components/Toolbar/FileMenu.tsx b/src/components/Toolbar/FileMenu.tsx
index 2ad888b..2803dd7 100644
--- a/src/components/Toolbar/FileMenu.tsx
+++ b/src/components/Toolbar/FileMenu.tsx
@@ -7,7 +7,18 @@ import { LANGS, persistLang, readSavedLang, type LangCode } from '../../lib/lang
import { RedactIcon } from '../icons/RedactIcon'
interface Props {
- variant?: 'header' | 'toolbar'
+ /**
+ * `toolbar` (default) and `header` own their trigger and panel — the dark
+ * editor bar and the white navbar respectively.
+ *
+ * `rows` renders the menu BODY only, no trigger and no panel, for the SDK's
+ * `actions` slot: since @unisim/sdk 0.78.0 the app's actions and the profile
+ * are one pill with one dropdown, so this menu supplies its rows and the SDK
+ * supplies the container. The body is a flat accordion list — every submenu
+ * expands in place rather than flying out — which is exactly why it can be
+ * dropped into someone else's panel unchanged.
+ */
+ variant?: 'header' | 'toolbar' | 'rows'
}
export default function FileMenu({ variant = 'toolbar' }: Props) {
@@ -225,27 +236,17 @@ export default function FileMenu({ variant = 'toolbar' }: Props) {
)
}
- return (
-
-
-
- {open && renderMenu(
+ const fileInput = (
+
+ )
+
+ const body = (
<>
{/* Current file name — a non-interactive header at the very top of the
dropdown so the user always knows which PDF the actions apply to. */}
@@ -601,7 +602,11 @@ export default function FileMenu({ variant = 'toolbar' }: Props) {
>
)}
- {/* Language submenu */}
+ {/* Language submenu — "Document" is load-bearing, not decoration. This
+ sets the PDF's own `document.documentElement.lang`; the SDK's
+ profile menu carries the SUITE-WIDE UI language, and since 0.78.0
+ both live in this one dropdown. Two rows called "Language" in one
+ panel is a coin toss for the user. */}
)}
- >,
- )}
+ >
+ )
+
+ // Rows mode: the SDK's dropdown is the container, so there is no trigger, no
+ // panel and no `open` state in play (the outside-click and positioning
+ // effects above are both gated on `open`, which stays false here).
+ if (variant === 'rows') {
+ return (
+ <>
+ {fileInput}
+ {body}
+ >
+ )
+ }
+
+ return (
+