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

  • Three of them are tail nodes. Go Page, Replace 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, Get Page Param and the three Is Surface … reads live in Surface, widget, and Blueprint Value blueprints; Emit Page Event in Surface and widget blueprints. None of them exists in App logic, which has no current Page to ask about. The navigation, layer, window, pointer, screenshot and link nodes are offered in every blueprint except a Story Action blueprint, and Keep Window Open only in App logic and Surface blueprints.
  • Opening a page gives its params. When the page picked under Page declares params, Go Page, Replace Page, Show Layer and Set Frame Page grow one input per param, named after it, in front of Page props. A String, Text, Number or Boolean param can be typed on the card, and a List param takes an array of its row struct. An input left unwired and empty gives nothing, and the page reads the param's default. A value given this way replaces the field of the same name in Page props. While the Page input of Go Page or Replace Page is wired, the param inputs are not shown: the page comes from the wire and is opened with Page props alone.
  • The editor canvas has no application to talk to. On the author-time canvas every effectful node here fails with This node runs only in a running game. Open Dev Mode to run it.; 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 every Page above the first one.

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 every Page above the first one.

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 every Page above the first one instead.

The card also has one input per param of the picked page, described under Conventions.

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 the Pages over the stage to reveal it again. To leave the game itself, use Quit Game from Game — it names the Page to return to.

Replace Page

blueprint.page.replace · Latent

Opens a page in place of the current page, and closes the current page. Use it for a page the player does not come back to, such as a splash or a disclaimer: after Go Page, that page would stay beneath the next one, and Go back and Go Page with None would land on it again.

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. There is no None: with no page picked and nothing wired, the node fails with “Replace Page”: pick a page.

The card also has one input per param of the picked page, described under Conventions.

The new page arrives through the same transition as Go Page, and the replaced page leaves the stack once it has. Replacing the entry page makes the new page the first page on the stack. While a game is running and no page is over the stage, there is nothing to replace, and the node opens its target above the stage as Go Page does.

Go back

blueprint.page.back · Latent

Closes the top layer the player can dismiss: a dismissible layer opened with Show Layer if one is on top, otherwise the top Page, returning 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 closes exactly one, so Escape over a game two pages deep lands on the page underneath. Go Page with None returns to the first Page, closing everything above it. 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.

The card also has one input per param of the picked page, described under Conventions.

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. It is not offered inside a Blueprint Value.

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.

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. It gives the page two params: message, the question, and buttons, a list of Confirm button rows with the fields id, text, index and disabled. The Confirm page of the Skeleton template declares both: message is a Text param shown by the message text, and buttons is a List param whose Row struct is Confirm button, drawn by the button list. A page of your own for Show Confirm declares the same two params.

message is shown as given. To show the question in the player's language, wire message from Translation Key Text.

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 · exec—Taken 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.

App logic 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 · dataarray<float>Multiples 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.

Is Window Focused

blueprint.app.isWindowFocused · Latent

Whether the game's window is the one the player is working in. On Window Focus Changed runs when this changes; this node reads the current state, for a graph that runs for some other reason or a Page opened while the player was already in another window.

PinDirectionTypeNotes
inin · exec—
nextout · exec—
isFocusedout · databooleantrue in a shell with no window of its own, such as the story editor's Game UI preview.

Save Screenshot

blueprint.app.saveScreenshot · Latent

Captures what the player is looking at and writes it to a PNG file. The node takes no folder or file name and returns the path of the file it wrote.

PinDirectionTypeNotes
inin · exec—
nextout · exec—Taken when the file was written.
failedout · exec—
pathout · datastringThe file that was written. Empty on failed.
errorout · datastringWhy nothing was written.

A desktop build writes into a screenshots folder beside the player's saves. Dev Mode writes into Studio's own data, in a folder per project, so a test capture never lands in the project directory. Each file is named by the time it was taken, to the millisecond.

failed covers both a platform without screenshots (the web export, a mobile build, the story editor's Game UI preview) and a capture or write that did not succeed, with error saying which. As with Move Mouse To, the build console warns when a project holding this node is exported for a non-desktop target.

Open Screenshots Folder

blueprint.app.openScreenshotsFolder · Latent

Opens the folder Save Screenshot writes into, in the player's file manager. The folder is created first if it is not there, so a settings screen can offer this before the player has taken a screenshot.

PinDirectionTypeNotes
inin · exec—
nextout · exec—Taken when the folder was opened.
failedout · exec—
pathout · datastringThe folder that was opened. Empty on failed.
errorout · datastringWhy it was not opened.

It leaves through failed on the same platforms Save Screenshot does.

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 · exec—Taken 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.

Like the nodes in Element, it is offered in the palette only once the graph holds an Element literal or an Element Flush / Element Click head bound to a widget on the same Surface (or, in a component's blueprint, the same component). When there is exactly one such binding, placing the node wires element to it.

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 and the params it gave; a Page embedded in an nl.frame reads the Frame's params. Every param the page declares is in it, with its default where nothing was given. Never null: a Page that declares no params and is opened with nothing reads {}.

PinDirectionTypeNotes
propsout · datajson

To read one declared param, use Get Page Param. A field the page does not declare is read with Get JSON Field from Data, which takes a dotted path.

Get Page Param

blueprint.frame.getParam · Pure

The value the page was opened with for one of its params, or the param's default when nothing was given. Pick the param under Parameter on the card, from the params of the page the blueprint belongs to.

PinDirectionTypeNotes
valueout · datajsonTyped by the picked param: String and Text give string, Number float, Boolean boolean, JSON json, and List an array of its row struct, or array when the Row struct is Unspecified.

On-card fields

FieldWhat
ParameterOne of the params of the page this blueprint belongs to.
  • Where it is offered. In the page's own blueprint, in the blueprints of the widgets on the page, and in their Blueprint Values. On a page shown in a Page widget, it reads the values that widget gives.
  • A Text param gives the words it was given, or its default in the source language. To show a Text param in the player's language, choose it under Page param on the text or button itself.
  • A param the page no longer declares is a warning on the canvas and the project check Missing page param (blueprint/page-param-missing). The node then reads nothing.
  • No param picked. The card then shows a key input (string, accepts an on-card literal) and reads the Page prop of that name, null when it is not there. Graphs from earlier versions hold the node this way.

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