feat(editor): session entry points in the File and Account menus
The editor menubar's Account menu hosted only the shared `user-menu`
slot, so it had no way in or out of a session: the bottom-left dropdown
renders My projects / Sign in from its own markup, which this menubar
never sees. Signed-in users had no route to their saved work from the
editor, and anonymous ones had no way to sign in without leaving it.
Declares two account-scoped command ids and adds an `optional` item flag
that HIDES a row whose command is unregistered instead of the default
"render disabled". Absence here is not "temporarily unavailable" but
"does not apply": OSS has no accounts, and in pro exactly one of the two
is meaningful at a time — a greyed-out My projects would read as broken.
My projects sits third in File, at the head of the project group rather
than tacked onto the two "new …" rows: it opens saved work, which is
what Open/Save beneath it are for.
Labels reuse header.auth.{myProjects,signIn}, already translated in all
nine locales — no new strings.
This commit is contained in:
parent
8fd81fa080
commit
28eca48168
|
|
@ -35,7 +35,20 @@ import {
|
|||
import './EditorMenuBar.css';
|
||||
|
||||
type Item =
|
||||
| { kind: 'command'; id: EditorCommandId; label: string; shortcut?: string; pro?: boolean }
|
||||
| {
|
||||
kind: 'command';
|
||||
id: EditorCommandId;
|
||||
label: string;
|
||||
shortcut?: string;
|
||||
pro?: boolean;
|
||||
/** Hide the row entirely when no handler is registered, instead of
|
||||
* the default "render disabled". For account-scoped items the
|
||||
* absence of a handler is not "temporarily unavailable" but "does
|
||||
* not apply here": OSS has no accounts at all, and in pro exactly
|
||||
* one of Sign in / My projects is meaningful at a time. A greyed-out
|
||||
* "My projects" would read as a broken feature in both. */
|
||||
optional?: boolean;
|
||||
}
|
||||
| { kind: 'link'; href: string; label: string }
|
||||
| { kind: 'separator' };
|
||||
|
||||
|
|
@ -90,6 +103,15 @@ export const EditorMenuBar: React.FC = () => {
|
|||
{ kind: 'command', id: 'project.new', label: t('editor.menu.newProject', 'New workspace') },
|
||||
{ kind: 'command', id: 'file.new', label: t('editor.menu.newFile', 'New file') },
|
||||
{ kind: 'separator' },
|
||||
// Third item, and deliberately at the head of the project group rather
|
||||
// than tacked onto the "new …" pair above: it opens the user's saved
|
||||
// work, which is what Open/Save below are about.
|
||||
{
|
||||
kind: 'command',
|
||||
id: 'account.myProjects',
|
||||
label: t('header.auth.myProjects', 'My projects'),
|
||||
optional: true,
|
||||
},
|
||||
{ kind: 'command', id: 'project.open', label: t('editor.menu.open', 'Open project…') },
|
||||
{
|
||||
kind: 'command',
|
||||
|
|
@ -116,6 +138,25 @@ export const EditorMenuBar: React.FC = () => {
|
|||
{ kind: 'command', id: 'sim.record', label: t('editor.toolbar.recordLabel', 'Record simulation'), pro: true },
|
||||
];
|
||||
|
||||
// Sign in / My projects for the Account menu. The bottom-left account
|
||||
// dropdown gets these from its own (pro) markup; this menubar only ever
|
||||
// hosted the shared `user-menu` slot, which is why the editor's Account
|
||||
// menu had no way in or out of a session.
|
||||
const accountItems: Item[] = [
|
||||
{
|
||||
kind: 'command',
|
||||
id: 'account.myProjects',
|
||||
label: t('header.auth.myProjects', 'My projects'),
|
||||
optional: true,
|
||||
},
|
||||
{
|
||||
kind: 'command',
|
||||
id: 'account.login',
|
||||
label: t('header.auth.signIn', 'Sign in'),
|
||||
optional: true,
|
||||
},
|
||||
];
|
||||
|
||||
const helpItems: Item[] = [
|
||||
{ kind: 'link', href: `${SITE}/docs`, label: t('header.nav.documentation', 'Documentation') },
|
||||
{ kind: 'link', href: '/examples', label: t('header.nav.examples', 'Examples') },
|
||||
|
|
@ -224,6 +265,16 @@ export const EditorMenuBar: React.FC = () => {
|
|||
)}
|
||||
{which === 'account' && (
|
||||
<>
|
||||
{/* Session entry points, above the pro extras. Exactly one of
|
||||
the two is registered at a time (pro registers by session
|
||||
state) and neither exists in OSS, so this block renders
|
||||
nothing in an OSS build. */}
|
||||
{accountItems
|
||||
.filter((item) => item.kind !== 'command' || hasEditorCommand(item.id))
|
||||
.map((item) => (item.kind === 'command' ? renderCommand(item) : null))}
|
||||
{accountItems.some(
|
||||
(item) => item.kind === 'command' && hasEditorCommand(item.id),
|
||||
) && <div className="emb-separator" />}
|
||||
{/* Pro account items (PRO badge, Subscribe / Manage
|
||||
subscription, licenses, history, replays, Privacy) mount
|
||||
here via the SAME user-menu slot the bottom-left account
|
||||
|
|
@ -291,15 +342,20 @@ export const EditorMenuBar: React.FC = () => {
|
|||
</button>
|
||||
</>
|
||||
)}
|
||||
{items.map((item, i) =>
|
||||
item.kind === 'separator' ? (
|
||||
<div key={`sep-${i}`} className="emb-separator" />
|
||||
) : item.kind === 'link' ? (
|
||||
renderLink(item)
|
||||
) : (
|
||||
renderCommand(item)
|
||||
),
|
||||
)}
|
||||
{items
|
||||
.filter(
|
||||
(item) =>
|
||||
item.kind !== 'command' || !item.optional || hasEditorCommand(item.id),
|
||||
)
|
||||
.map((item, i) =>
|
||||
item.kind === 'separator' ? (
|
||||
<div key={`sep-${i}`} className="emb-separator" />
|
||||
) : item.kind === 'link' ? (
|
||||
renderLink(item)
|
||||
) : (
|
||||
renderCommand(item)
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ export type EditorCommandId =
|
|||
| 'project.exportBom'
|
||||
| 'project.exportScreenshot'
|
||||
| 'file.new'
|
||||
// Account-scoped, registered ONLY by the pro overlay and only in the
|
||||
// matching session state (myProjects when signed in, login when not).
|
||||
// OSS has no auth at all, so both stay unregistered here and the menu
|
||||
// hides them outright — see `optional` in EditorMenuBar.
|
||||
| 'account.myProjects'
|
||||
| 'account.login'
|
||||
| 'project.share'
|
||||
| 'project.githubSync'
|
||||
| 'firmware.upload'
|
||||
|
|
|
|||
Loading…
Reference in New Issue