Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions OPEN-CORE.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ static|embed>`. Core never talks to a server about it.
| `loadRemoteScene({…, keepGameId?})` | `keepGameId: true` = the OWNER opening their own published scene (no fork); otherwise a community load is a remix |
| `onExportBuilt(fn)` → off | after an export zip is built: `fn({gameId, buildId, preset, title, template, parentGameId, appVersion})` |
| `onTemplateOpen(fn)` → off | a Games-tab template was started: `fn(slug)` (one "template open") |
| `onFunnelEvent(fn)` → off | 76 FW2: one anonymous first-visit funnel event `fn({e, c, b, d, x})` — closed vocabularies only (event name, Welcome answer, release, desktop/touch/vr, a time bucket or quest step), never an id. Core has already applied once-per-browser, Do Not Track / Global Privacy Control and the under-test guard; events from before registration are queued (20 max) and handed to the first listener. With no plugin nothing is sent anywhere. Probe with `typeof` |
| `funnelEvent(name)` → Promise\<boolean\> | 76 FW2: the plugin's own funnel event through the same rules; only `'ob_waitlist'` (a feature-update opt-in) is accepted |
| `toast(message, actions?, {keep?})` | fix-live F-2: with `keep: true` an action toast's buttons also stay on its notification-history entry until one is pressed (a role offer the user sees later) |
| `setPeerMenuItems(items)` | fix-live: `[{id, label, run(peerId, {name})}]` (≤ 8, labels ≤ 40) appended to the Users peer menu (right-click a peer) under core's own entries — the cloud's "Report…"; `[]` clears |
| `setConnectBadge(badge \| null)` | fix-live F-5: `{text, title?, onClick?}` — a small badge on the Connect pill for a standing setting (e.g. "Public · guests join"); `null` hides it. Probe with `typeof` |
Expand Down
8 changes: 7 additions & 1 deletion src/components/menu/Connect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import { peerColor } from '$lib/lockControl';
import Icon from '../ui/Icon.svelte';
import { phoneShellActive } from '$lib/ui/phoneShell.js';
import { tours } from '$lib/tours/index.js'; // 76 FW1: the copy is the first-win quest's share step
import { SIGNALS } from '$lib/onboarding/quests.js';
import { funnelEvent } from '$lib/onboarding/funnel.js'; // 76 FW2: …and the funnel's first share
// 43 K3/K4: whether the pill is shown (Edit positions / Customize), and the top-row items it measures against
import { toolbarSlots } from '$lib/toolbarSlots';
import { peers, userdata, waitingForApproval, pendingApprovals, showToast, settingsOpen, settingsSection, connectDrawerOpen, connectDrawerTab, connectDrawerPinned, showRoomsButton, connectDocked, connectBarHeight, toastStore, toastsInDrawerOnly, touchTools } from '../../stores/appStore'
Expand Down Expand Up @@ -503,7 +506,10 @@
navigator.clipboard
.writeText(window.location.origin + '#' + myidcap + inviteServerParam(srv))
.then(function () {
// alert("yeah!"); // success
// 76 FW1: the first-win quest's last step waits for this
// (the funnel first: the signal can end the quest, whose own event follows it)
funnelEvent('ob_first_share');
tours.signal(SIGNALS.copied);
})
.catch(function () {
// alert("err"); // error
Expand Down
13 changes: 9 additions & 4 deletions src/components/menu/Controls.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import { dockShownKeys, dockOccupants, bottomInset, FLOW_FAMILY, armDockMode, DOCK_TITLES } from '$lib/bottomDock';
import { togglePanel } from '$lib/panelToggles';
import { requestPlay, willEnterXR, willEnterAR, xrSessionFailed, playAsItems } from '$lib/playMode';
import { simpleUntilPlay, holdAdvanced } from '$lib/onboarding/onboardState.js'; // 76 FW1
// 60 Q1: Play here — a roster button (Customize), a row in Play's own menu, Ctrl+Shift+Enter
import { playHere, playHereItems, playingHere } from '$lib/playHere';
// 30 P2: Test play — the play button's right-click row beside the game chip's button
Expand Down Expand Up @@ -1566,15 +1567,19 @@
// positions runs (it shows every item, wrapping) and on a phone-width bar (its cells shrink instead).
const foldPlan = $derived.by(() => {
if (controlsLayout.collapsed) return { shown: [SPACER], folded: [] as string[] };
const row = visualIds().filter((id) => !(id === 'pivot' && viewportW < NARROW_BAR));
if (editing || viewportW <= FOLD_MIN_VW || !barRoom.ok) return { shown: row, folded: [] as string[] };
return foldToFit(row, {
const all = visualIds().filter((id) => !(id === 'pivot' && viewportW < NARROW_BAR));
if (editing) return { shown: all, folded: [] as string[] };
// 76 FW1: until a new user's first Play the advanced buttons wait in "»" (still one press away)
const { row, held } = holdAdvanced(all, $simpleUntilPlay);
if (viewportW <= FOLD_MIN_VW || !barRoom.ok) return { shown: row, folded: held };
const fit = foldToFit(row, {
...barMetrics,
// a px of slack: the arithmetic is in whole layout px, the browser's in fractions of one
budget: barRoom.right - barRoom.left - 1,
// the "…" is drawn only on a wide window (hud.css hides it below 720 px)
more: controlsLayout.more !== false && viewportW >= 720
});
return held.length ? { shown: fit.shown, folded: [...fit.folded, ...held] } : fit;
});
const visibleCells = $derived(foldPlan.shown.map((id) => ({ id })));
/** the cells folded into "»", in the bar's left → right order */
Expand Down Expand Up @@ -3863,7 +3868,7 @@
<!-- 43 J10: the buttons that do not fit wait in "»" (right to left by the user's order, Play never);
once anything folds it ends the bar in place of the "…", and its menu closes with Customize -->
<span class="hud-sep" aria-hidden="true"></span>
<button id="toolbar-overflow" type="button" class="hud-cell" class:on={overflowMenu !== null} data-folded={folded.join(',')} title="More buttons — {folded.length} {folded.length === 1 ? 'does' : 'do'} not fit" aria-label="More buttons ({folded.length})" aria-haspopup="menu" aria-expanded={overflowMenu !== null} use:onPress={openOverflow} use:cellMenu={BAR}
<button id="toolbar-overflow" type="button" class="hud-cell" class:on={overflowMenu !== null} data-folded={folded.join(',')} title={$simpleUntilPlay ? 'More buttons — the node editor, animation and more (the bar shows them all after your first Play)' : `More buttons — ${folded.length} ${folded.length === 1 ? 'does' : 'do'} not fit`} aria-label="More buttons ({folded.length})" aria-haspopup="menu" aria-expanded={overflowMenu !== null} use:onPress={openOverflow} use:cellMenu={BAR}
><Icon name="chevrons-right" size={20} aria-hidden="true" /></button
>
{:else if !controlsLayout.collapsed && controlsLayout.more !== false}
Expand Down
65 changes: 38 additions & 27 deletions src/components/menu/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import { openProblemReport } from '$lib/problemReport';
import '../../app.css';
import { moduleToolboxes, openToolboxes, buildToolboxItems } from '$lib/moduleToolboxes';
import { simpleUntilPlay } from '$lib/onboarding/onboardState.js'; // 76 FW1
/** 76 FW1: the "More" row was pressed this time the menu is open */
let sceneMore = $state(false);
import '../../styles/menu.css';
import { tick } from 'svelte';
import { fade } from 'svelte/transition';
Expand Down Expand Up @@ -278,32 +281,39 @@
<button id="open-asset-browser" class="side-row" onclick={() => { openAssetBrowser(); closeMenu.set(true); }}>
<span class="side-ico"><Icon name="package-open" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">Free assets (CC0)</span>
</button>
<button id="open-modules-manager" class="side-row" onclick={() => { modulesOpen.set(true); closeMenu.set(true); }}>
<span class="side-ico"><Icon name="puzzle" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">Modules</span>
</button>
<!-- A5: a module's own toolbox, one row each, indented under Modules. This is
what makes moduleSDK's JSDoc true — it already CLAIMED a sidebar Modules
section that did not exist. Deliberately not a Controls HUD button: the
corner HUD already reflows four buttons at <=600px and does not scale to N
modules. The rows come from the SAME builder the viewport menu uses, which is
asked as the 'sidebar' surface: a toolbox may opt OUT of this permanent row
(`sidebar: false`) and keep its viewport-menu one. -->
{#each buildToolboxItems($moduleToolboxes, $openToolboxes, 'sidebar') as box (box.id)}
<button
id="open-toolbox-{box.id}"
class="side-row side-sub"
class:active={box.checked}
onclick={() => { box.action(); closeMenu.set(true); }}
>
<span class="side-ico"><Icon name="wrench" size={16} aria-hidden="true" /></span>
<span class="flex-1 whitespace-nowrap">{box.label}</span>
{#if box.shortcut}<span class="side-hint">{box.shortcut}</span>{/if}
<!-- 76 FW1: until a new user's first Play, Modules and Layouts wait under "More" -->
{#if !$simpleUntilPlay || sceneMore}
<button id="open-modules-manager" class="side-row" onclick={() => { modulesOpen.set(true); closeMenu.set(true); }}>
<span class="side-ico"><Icon name="puzzle" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">Modules</span>
</button>
{/each}
<!-- 37 R14: named workspace layouts (windows, docks, sizes) -->
<button id="open-layouts" class="side-row" onclick={() => { layoutsMenuOpen.set(true); closeMenu.set(true); }}>
<span class="side-ico"><Icon name="panels-top-left" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">Layouts</span>
</button>
<!-- A5: a module's own toolbox, one row each, indented under Modules. This is
what makes moduleSDK's JSDoc true — it already CLAIMED a sidebar Modules
section that did not exist. Deliberately not a Controls HUD button: the
corner HUD already reflows four buttons at <=600px and does not scale to N
modules. The rows come from the SAME builder the viewport menu uses, which is
asked as the 'sidebar' surface: a toolbox may opt OUT of this permanent row
(`sidebar: false`) and keep its viewport-menu one. -->
{#each buildToolboxItems($moduleToolboxes, $openToolboxes, 'sidebar') as box (box.id)}
<button
id="open-toolbox-{box.id}"
class="side-row side-sub"
class:active={box.checked}
onclick={() => { box.action(); closeMenu.set(true); }}
>
<span class="side-ico"><Icon name="wrench" size={16} aria-hidden="true" /></span>
<span class="flex-1 whitespace-nowrap">{box.label}</span>
{#if box.shortcut}<span class="side-hint">{box.shortcut}</span>{/if}
</button>
{/each}
<!-- 37 R14: named workspace layouts (windows, docks, sizes) -->
<button id="open-layouts" class="side-row" onclick={() => { layoutsMenuOpen.set(true); closeMenu.set(true); }}>
<span class="side-ico"><Icon name="panels-top-left" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">Layouts</span>
</button>
{:else}
<button id="sidebar-more" class="side-row" aria-expanded="false" onclick={() => (sceneMore = true)}>
<span class="side-ico"><Icon name="ellipsis" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">More</span>
</button>
{/if}
<div class="side-div"></div>
<div class="side-label">Collaborate</div>
<button id="open-sessions-manager" class="side-row" onclick={() => { sessionsOpen.set(true); closeMenu.set(true); }}>
Expand Down Expand Up @@ -352,9 +362,10 @@
<span class="flex-1 whitespace-nowrap">What's new</span>
<span class="row-dot" class:row-dot-on={$whatsNewUnseen}></span>
</button>
<!-- 36 I5: the first-run editor tour, again (Settings ▸ Tours has the VR welcome + reset) -->
<!-- 36 I5: the editor tour (Settings ▸ Tours has the VR welcome + reset). 76 FW1: it no longer starts by
itself after the Welcome — the first-win quest does — so this row is where it lives -->
<button id="open-tour" class="side-row" onclick={() => { closeMenu.set(true); startEditorTour(); }}>
<span class="side-ico"><Icon name="compass" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">Tours</span>
<span class="side-ico"><Icon name="compass" size={16} aria-hidden="true" /></span><span class="flex-1 whitespace-nowrap">Tour the interface</span>
</button>
</nav>
{/if}
Expand Down
Loading
Loading