NarraLeaf

App

Surface navigation, the application window, and what a Page knows about itself.

App nodes are where a blueprint reaches the application it is running inside: open a Surface, stack one over it, size the window, follow a link, quit. The Surface model itself — the stack, the layers, the game stage — is described in Surfaces.

Conventions

  • Every node here is event and macro only, except Is Layer Mounted, which is pure and also runs in a function graph and inside a Blueprint Value. Nothing else in the category does.
  • Two of them are tail nodes. Go Page and Quit Application have no next pin at all. What follows is a different Surface, or no application — there is nothing left of this graph to run.
  • The type ids do not all say app. This group was called Page before it grew window, layer and application nodes. Ids are what a document stores, so they were never renamed: blueprint.page.*, blueprint.frame.*, blueprint.layer.* and blueprint.app.* are all this one category.
  • Owners. Get Page Props and the three Is Surface … reads live in Surface, widget, and Blueprint Value blueprints; Get Page Param and Emit Page Event in Surface and widget blueprints. None of them exists in a global blueprint, which has no current Page to ask about. The navigation, layer, window, pointer and link nodes have no owner restriction.
  • The editor canvas has no application to talk to. On the author-time canvas every effectful node here fails with Host API unavailable (use Dev Mode); the pure reads degrade quietly instead, so Get Page Props reads {} and the three Is Surface … reads report false. In the story editor's Game UI preview, navigation and quit reach stubs and do nothing, and the two fullscreen nodes report that they are not available there. Verify all of it in Dev Mode.

Go Page

blueprint.page.go · Latent

Opens the Surface picked on the card, through the same navigation path the runtime uses for every Page change — same enter and exit animations, same Surface lifecycle, same widget blueprints. Picking None instead closes the current top Page layer.

Go Page replaces what the player is looking at. To put a page over the current one and get a handle back, use Show Layer. To close one, prefer Go back: navigation is a stack, so Go Page at a page the player came from pushes a third page over the two already there rather than returning to it. Go Page with None closes the current top Page layer.

PinDirectionTypeNotes
inin · exec
surfaceIdin · datastringOptional. Wire it to choose the target at run time; the card's Page field is what is used when it is unwired.
propsin · datajsonHanded to the target Page as its Page props, where Get Page Props reads it. Optional — unwired it is {}.

On-card fields

FieldWhat
PageThe Surface to open, chosen from the project's app Surfaces. None closes the current top Page layer instead.

Once a game is running, the Page stack is hidden beneath the stage. Go Page then opens its target as a UI layer above the stage, and None closes that layer to reveal the stage again. To leave the game itself, use Quit Game from Game — it names the Page to return to.

Go back

blueprint.page.back · Latent

Pops the top Page layer and returns to the one beneath it.

Every page a game opens over a running story — save, load, config, backlog — needs a way out, and Go Page is not it: navigation is a stack, so "go to the page I came from" pushes a third page over the two already there and the game is still buried.

Closing the last remaining page is a no-op rather than an error. A page reached from the title screen is the bottom of the stack, and a Back button that threw there would be unusable on the same screen depending on how the player arrived.

PinDirectionTypeNotes
inin · exec
nextout · exec

Clear Page

blueprint.page.clear · Latent

Removes every page a running game is wearing, leaving the stage bare — Escape, on any page, without the graph having to know how deep the player is.

The three closes are not interchangeable. Go back pops exactly one, so Escape over a game two pages deep lands on the page underneath. Go Page with None empties the stack, which on the title screen would dismiss the title itself. Clear Page clears only the overlay pages a game session put up, and outside a game it does nothing at all — which is what makes Clear Page → Go back a single Escape handler that reads as "leave the game if I am in one, otherwise step back".

PinDirectionTypeNotes
inin · exec
nextout · exec

Show Layer

blueprint.layer.show · Latent

Puts a Page over what is on screen instead of replacing it, and hands back a handle naming that one showing of it.

Nothing about a layer's depth is addressable. Stacking order is mount order, and the handle is the only thing that names a layer — meaningless to any graph that was not given it. That is deliberate: a screen cannot be built to depend on sitting third from the top, so the first project that wants one more layer above the pause menu does not turn the composite into a numbering scheme.

PinDirectionTypeNotes
inin · exec
nextout · exec
propsin · datajsonThe layer's Page props, read by Get Page Props inside it. Optional.
modalin · databooleanBlocks interaction with what is underneath. Accepts an on-card literal. Unwired reads as false.
dismissiblein · databooleanWhether the player may close it without the graph's help. Accepts an on-card literal. Unwired reads as true — a layer the player cannot escape is a decision, and an untouched pin should not make it.
groupin · datastringOptional. Layers claiming the same group queue behind one another rather than stacking. Accepts an on-card literal.
layerout · datastringThe handle. Feed it to Hide Layer, Wait For Layer, or Is Layer Mounted.

On-card fields

FieldWhat
PageThe Surface to show, from the same list Go Page picks from. A layer is a page; two different lists would suggest otherwise.

A Page the project does not hold fails on this node with the host's own sentence, so the failure lands on the row you can fix.

Hide Layer

blueprint.layer.hide · Latent

Closes the layer a handle names.

PinDirectionTypeNotes
inin · exec
nextout · exec
layerin · datastringThe handle from Show Layer.

A handle naming nothing — never set, already closed — is a no-op rather than an error, the same bargain Go back makes at the bottom of the stack: the layer being gone already is the outcome the node was asked for.

Wait For Layer

blueprint.layer.wait · Latent

Suspends until the layer closes, then continues with whatever it closed with.

PinDirectionTypeNotes
inin · exec
nextout · exec
layerin · datastringThe handle from Show Layer.
resultout · datajsonWhat Close This Layer was given. null for a layer dismissed by the player, or closed with nothing.

Close This Layer

blueprint.layer.closeSelf · Latent

Closes the layer the running blueprint is inside, optionally with an answer for whoever is waiting on it. This is the other half of Wait For Layer: the page that was shown reports its own outcome rather than the opener reaching in to read it.

PinDirectionTypeNotes
inin · exec
nextout · exec
resultin · datajsonOptional. Unwired the layer closes with null.

Is Layer Mounted

blueprint.layer.isMounted · Pure

Whether the layer a handle names is still on screen. The one pure node in this category, so it is the only one that can back a Blueprint Value — a pause button that disables itself while its own menu is up.

PinDirectionTypeNotes
layerin · datastringThe handle from Show Layer.
mountedout · databooleanfalse for an empty or unknown handle.

Show Confirm

blueprint.layer.confirm · Latent

Asks a question on a Page of your own and leaves through the button the player pressed.

There is no confirm-specific machinery underneath. The node shows the chosen Page as a modal, dismissible layer in the confirm group — so a second question queues behind the first instead of over it — and hands it the question through ordinary Page props: message, and buttons as { id, text, index, disabled } for a List to bind to. A page built by hand out of Show Layer reads exactly the same props.

PinDirectionTypeNotes
inin · exec
messagein · datastringThe question. Accepts an on-card literal.
tagin · datastringOptional. Passed through to the page, for one page answering several questions. Accepts an on-card literal.
datain · datajsonOptional. Passed through to the page.
dismissedout · execTaken when the player closed the question without answering it.
indexout · dataintegerThe button's position, -1 on dismissed.
labelout · datastringThe button's text, "" on dismissed.

On-card fields

FieldWhat
PageThe Surface that draws the question.

Add Button appends a string input button_N_label and its matching exec output button_N_pressed, inserted above the fixed dismissed. The pairs are matched by id rather than by position, so deleting a button from the middle renumbers nothing and misroutes nothing. The page reports its answer by closing itself with Close This Layer — a bare index, or an object with an index field. Anything else, including a page that closes with nothing, leaves through dismissed.

Keep Window Open

blueprint.app.keepWindowOpen

Holds the current window close request open, so the game decides rather than the window closing under it. A game that wants to ask "really quit?" runs this first, shows its own question, and calls Quit Application if the player says yes.

It stops nothing else. It is not a way to swallow a key, dismiss a page, or keep an element's event from reaching anything — and outside a close request it has nothing at all to act on, which is an execution error rather than a quiet no-op, because a graph holding one there is asking for a guarantee it is not being given.

Global and Surface graphs only.

PinDirectionTypeNotes
inin · exec
nextout · exec

Quit Application

blueprint.page.quit · Latent

Asks the runtime to shut the application down cleanly — leaving room for state saving and crash reporting — rather than killing the process. In Dev Mode it ends the Dev Mode session and returns you to Studio; it does not close Studio itself.

PinDirectionTypeNotes
inin · exec

This is a runtime-initiated quit, so it deliberately does not fire On Window Close Requested. That event exists for the player closing the window, and a confirmation handler hanging off it must not be triggered by the node that already decided to quit.

Set Fullscreen

blueprint.app.setFullscreen · Latent

Puts the application window into or out of fullscreen.

PinDirectionTypeNotes
inin · exec
nextout · exec

On-card fields

FieldWhat
ModeEnter Fullscreen, Exit Fullscreen, or Toggle Fullscreen. An unset dropdown behaves as toggle — the useful default for a fullscreen button.

Toggle Fullscreen reads the current window state first and inverts it, so one button covers both directions without you tracking the state yourself.

Get Fullscreen

blueprint.app.getFullscreen · Latent

Reads whether the application window is currently fullscreen.

PinDirectionTypeNotes
inin · exec
nextout · exec
isFullscreenout · databoolean

It is latent rather than pure because the answer comes from the window, not from the graph — which also means it cannot back a Blueprint Value. Drive a fullscreen indicator from an event graph instead.

Get Window Size

blueprint.app.getWindowSize · Latent

The stage's current size in pixels, whatever the window is at.

PinDirectionTypeNotes
inin · exec
nextout · exec
widthout · datafloat
heightout · datafloat

Set Window Size

blueprint.app.setWindowSize · Latent

Resizes the stage to a size in pixels.

The sizes offered in Project → App → Window are what a configuration screen is built from, not a limit on what a graph may ask for: a game whose size comes from somewhere else — a value it remembered, a display it measured, a number a player typed — says it here. What it cannot do is leave the screen. The size is fitted to the display's work area and to the window's own minimum, because a window bigger than the desktop is one nobody can use.

Fullscreen and maximised are left first, for the reason Set Window Scale leaves them: both are answers to the same question, and a window sized underneath either would spring back the moment the player left it.

PinDirectionTypeNotes
inin · exec
nextout · exec
widthin · datafloat
heightin · datafloat

Get Window Scale

blueprint.app.getWindowScale · Latent

The size the window is at now, as a multiple of the game's design size.

PinDirectionTypeNotes
inin · exec
nextout · exec
scaleout · datafloat1 is the design size, 0.5 half of it.

Set Window Scale

blueprint.app.setWindowScale · Latent

Resizes the window to a multiple of the design size.

A scale the project does not offer is answered with the nearest one it does, rather than refused: the ladder belongs to the author, and a graph that computed 0.8 should move the window rather than fail. Fullscreen and maximised are left first.

PinDirectionTypeNotes
inin · exec
nextout · exec
scalein · datafloatA value that is not a positive number falls back to 1.

Get Window Scale Options

blueprint.app.getWindowScaleOptions · Latent

The sizes a configuration screen may offer, as the author wrote them in Project → App → Window. Bind a List to this and the offered sizes come from the project rather than from numbers typed into the graph.

PinDirectionTypeNotes
inin · exec
nextout · exec
scalesout · dataarrayMultiples of the design size.

Empty is an answer, not a failure. A shell with no window of its own to size — the web export, Dev Mode, the story preview — returns nothing here, and a row built from it draws nothing there. That is the point: a size control that cannot move a window is worse than no control at all.

blueprint.app.openExternal · Latent

Hands an address to the browser the player already uses: a store page, a patch note, a support form.

There is nothing to declare anywhere else. The author wrote the graph, so an address in it is the author's decision. What the shell checks — in the process that would open the page, never in the renderer — is the scheme, and only http:, https: and mailto: are reachable. That is an allowlist rather than a ban on the known-bad ones: shell.openExternal hands the address to whatever the operating system registered for it, and any software the player installs can register a new scheme. A scheme outside the set — steam: is the one that comes up — is reached through a plugin, where the pattern is named in the manifest and approved by name at install.

PinDirectionTypeNotes
inin · exec
urlin · datastringWired wins over the card. An empty address is an execution error.
nextout · execTaken when the page was handed over.
failedout · exec
errorout · datastringWhich refusal it was.

On-card fields

FieldWhat
URLThe address, typed. Used when the url pin is unwired.

failed covers both a scheme this node does not open and a browser that would not open the page. They share a pin because the author's answer to both is the same — the player did not get the page, so show them something else.

This is not a network permission. No request is made and no bytes come back into the game, so it is not gated on the project's network settings, and turning the network off does not disable it. See Security.

Move Mouse To

blueprint.app.movePointerTo · Latent

Puts the player's real cursor on a point of the Surface. It is the same act Windows performs when "snap to default button" is on, and it is what a controller-friendly menu needs and a mouse-only one cannot fake.

Coordinates are Surface design coordinates — the same ones Get Measured Rect, Get Bounds, and every mouse event's x / y are in. The author never sees the window and should not have to.

PinDirectionTypeNotes
inin · exec
pointin · dataVector2DBuild one with Make Vector2D from Data.
nextout · exec
failedout · exec
errorout · datastring

On-card fields

FieldWhat
Duration (s)0 puts the cursor there at once; any positive value makes it travel.
Easinglinear, easeIn, easeOut, or easeInOut.

Desktop builds and Dev Mode only. A web export cannot position the system pointer, so the node reports failed there, and the build console warns when a project holding one is exported for a non-desktop target — an author should learn this from the build rather than from a player.

Move Mouse To Element

blueprint.app.movePointerToElement · Latent

The same act aimed at a widget's centre, measured rather than computed from the document.

It is a node of its own rather than Get Element Measured Rect wired into Move Mouse To, because the two halves have to agree about which Surface the widget turned out to be painted on. A component instance renders its contents wherever it was placed, so that is not always the Surface the element was authored under.

PinDirectionTypeNotes
inin · exec
elementin · dataelement
nextout · exec
failedout · exec
errorout · datastring

On-card fields

FieldWhat
Duration (s)As above.
EasingAs above.

Get Page Props

blueprint.page.getProps · Pure

The whole props object of the Page this blueprint runs in. A Page opened by Go Page reads that node's props input; a Page embedded in an nl.frame reads the Frame's params. Never null — a Page opened with nothing reads {}.

PinDirectionTypeNotes
propsout · datajson

Pull a single field out with Get JSON Field from Data, which takes a dotted path.

Get Page Param

blueprint.frame.getParam · Pure

Reads one field of the current Page props by name.

This node is not in the palette. You cannot add one; it is documented because older documents still contain it and it still runs. It was replaced by Get Page Props followed by Get JSON Field, which reads nested paths instead of one flat key.

PinDirectionTypeNotes
keyin · datastringAccepts an on-card literal.
valueout · datajsonA field that is not there reads as null.

Emit Page Event

blueprint.frame.emit · Latent

Sends an event from an embedded Page up to the nl.frame element that embeds it. The Frame's own blueprint receives it through the Page Event head in Events, which hands on the same name and payload.

PinDirectionTypeNotes
inin · exec
nextout · exec
eventin · datastringEvent name. Accepts an on-card literal.
datain · datajsonPayload delivered to the handler.

A top-level Page has no parent Frame. The node still completes and execution continues out of next — the event just has nobody to reach, and nothing reports it. An empty event name is the opposite: that is an execution error, not a silent no-op.

Is Surface Entering

blueprint.page.isSurfaceEntering · Pure

True from the moment this Surface's runtime scope mounts until its enter animation has finished.

PinDirectionTypeNotes
isEnteringout · databoolean

The flag is cleared before After Surface Enter fires, so reading it inside that event gives false. That is deliberate: by the time the event runs, the Surface has finished entering.

Is Surface Exiting

blueprint.page.isSurfaceExiting · Pure

True from the moment Before Surface Exit fires and the exit animation starts, until the Surface unmounts. Use it to refuse input on a screen that is already leaving.

PinDirectionTypeNotes
isExitingout · databoolean

Is Surface Transitioning

blueprint.page.isSurfaceTransitioning · Pure

True while either of the other two is — one read for "this screen is not settled yet".

PinDirectionTypeNotes
isTransitioningout · databoolean

On this page