From 6128ac5acc5fcdbf65a242908779f8572e6e9170 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 10:26:30 +0000 Subject: [PATCH] Merge the Actions menu into the profile pill in the editor toolbar (0.79.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a document open the dark toolbar had an Actions button pinned to the far left next to Home and an avatar pinned to the far right — the same split the other Universal Apps just removed. Actions now rides in the profile pill at the far right: one control, one dropdown, the PDF's rows above the account rows. Home keeps the far-left margin on its own. FileMenu gains a `rows` variant that returns the menu BODY only, no trigger and no panel, for the SDK's `actions` slot. That works unchanged because the body is a flat accordion list — every submenu expands in place rather than flying out — so it drops into someone else's panel as-is. The `open` state simply stays false there, which parks the outside-click and portal-positioning effects with it. `variant="header"` is untouched and still unused. The pill takes SDK 0.79.0's `pillTheme="dark"`: this bar is slate-900, and the light pill would read as a white chip punched into it rather than one of the bar's buttons. One rename falls out of the merge: the PDF's own "Language" row is now "Document language". It sets `document.documentElement.lang` while the SDK carries the suite-wide UI language, and until now the two lived in separate panels where nobody had to tell them apart. In one dropdown, two rows called "Language" is a coin toss. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015TVwu9Zr3bL9vQDfAcEroy --- package-lock.json | 8 +- package.json | 2 +- src/App.tsx | 26 +++--- src/components/Header/ToolbarUserProfile.tsx | 18 ++++- src/components/Toolbar/FileMenu.tsx | 85 ++++++++++++++------ 5 files changed, 91 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index cfce25b..368a20b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.5.0", "license": "MIT", "dependencies": { - "@unisim/sdk": "^0.77.1", + "@unisim/sdk": "^0.79.0", "konva": "^9.3.20", "pdf-lib": "^1.17.1", "pdfjs-dist": "^4.10.38", @@ -4052,9 +4052,9 @@ } }, "node_modules/@unisim/sdk": { - "version": "0.77.1", - "resolved": "https://registry.npmjs.org/@unisim/sdk/-/sdk-0.77.1.tgz", - "integrity": "sha512-gQuuNsAWY/g7LsfWIzCqNFFuVLC9FBjKvS+l+2aW4bHgJanQZchamvX2FyqIXbNOTOO/M6OWx/b0jKUP1s/T1Q==", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@unisim/sdk/-/sdk-0.79.0.tgz", + "integrity": "sha512-r4sYKo/lEOFL2R1fjwjIv0xfmOcO4t7rHkn2a20oYTfysZB73qS1saDKxGeeH22tK6T9n4g4EYBoN5MoiiTEFw==", "license": "MIT", "peerDependencies": { "@supabase/supabase-js": "^2.45.0", diff --git a/package.json b/package.json index f09e4e2..e503b75 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "cap:open:android": "npx cap open android" }, "dependencies": { - "@unisim/sdk": "^0.77.1", + "@unisim/sdk": "^0.79.0", "konva": "^9.3.20", "pdf-lib": "^1.17.1", "pdfjs-dist": "^4.10.38", diff --git a/src/App.tsx b/src/App.tsx index d3763b6..6b715ab 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -165,12 +165,16 @@ export default function App() { {doc && } {doc && (
- {/* 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 ( +
+ + {fileInput} + {open && renderMenu(body)}
) }