-
Notifications
You must be signed in to change notification settings - Fork 0
Phone Integration
🛟 Need help or found a bug? Get support at support.doodesch.de/sideload.
What being on the phone gives your app, and the short list of things it does not.
Ship icon.png in your bundle: a square PNG, 256x256 is plenty, transparent outside the rounded square, drawn
the way the vanilla app icons are. Without one your app gets a flat coloured square derived from its id.
The caption under it is iconLabel from Apps.Register, defaulting to the title.
An app names the orientations it supports, in preference order. The first is what it opens in; naming two is what lets the player turn the phone.
.Orientation("landscape") // landscape only, the phone never turns
.Orientation("landscape", "portrait") // both, opens landscape
.Orientation("portrait", "landscape") // both, opens portrait
// no call at all -> landscape onlySaying nothing locks you to landscape, which is the only safe reading of silence: an app that never styled portrait must not be turned into it.
Do not build a turn button. The player turns the phone with the game's own rotate keys - Q and E out of the box, gamepad triggers too, rebindable in the game's controls screen - and Sideload adds a "Rotate Phone" line to the game's key-hint strip while a turnable app is open. Your app's screen stays yours.
The choice is remembered per app. Do not store it yourself.
s1.setOrientation(v) exists for the rare app that must turn itself for a reason of its own - a video, a map.
It is refused with a log line when the app never declared that orientation. s1.orientation reads the current
one.
A turn re-measures the viewport and lays the page out again; the document and the script survive, so scroll positions and field contents are kept. Style both from one sheet:
@media (orientation: portrait) {
.app { flex-direction: column; }
.sidebar { width: 100%; flex: 1; }
.detail { display: none; }
.app.on-detail .sidebar { display: none; }
.app.on-detail .detail { display: flex; }
}A two-pane split rarely survives 400px, so portrait usually wants push navigation - the script marks which pane
is showing, the stylesheet decides what that means. Pair it with orientationchange and back, both in
JavaScript, DOM and Events.
app.Badge(unreadCount); // zero clears itThe same red badge the vanilla apps use. Counts above 99 read as "99+". Set it whenever your own count changes, not on a timer: the value survives the phone being rebuilt, so setting it once is enough and setting it again is cheap.
app.Notify("Jessi Waters", "on my way");Raises one of the game's own phone notifications - the slide-in the vanilla apps use - carrying your app's icon. Nothing happens if the app is not on a phone yet.
This interrupts whatever the player is doing, so spend it on what they would want to be interrupted for. A count
that can wait belongs in Badge.
if (!app.IsOnScreen) app.Notify(sender, text);True while the player can actually see your app: it is the one the phone has open, and the phone is up. Ask before interrupting - an event the player is already watching happen does not deserve a notification, and the same event with the phone in their pocket does.
<img src="..."> paints a file from your bundle:
<img class="mark" src="glyph.png" alt="">.mark { width: 20px; height: 20px; color: #FFFFFF; }Both dimensions are required. The layout runs without Unity and cannot open a PNG to learn an intrinsic size
the way a browser does, so an image with no width and height is a box of nothing. The aspect ratio is preserved
inside whatever box you give it, and color tints the image - so one white glyph works on a dark bar and on a
light one.
For a picture your mod produces at runtime - fetched, generated, per player - hand it over as PNG bytes and reference it by name:
app.Image("avatar/" + steamId, pngBytes); // null or empty bytes remove it<img class="avatar" src="s1://avatar/76561198000000001">Sprites are cached per app. Supplying the same name again replaces the picture.
Hard walls, not gaps you can work around from the page. Check here before promising a feature.
| You want | Reality |
|---|---|
| read or set a scroll offset |
No. scrollToEnd() is the only scroll member. You cannot restore a per-view scroll position, and you cannot ask "is the reader at the bottom". The host restores offsets across a rebuild by itself. |
| know the app was opened or closed |
No from the page. orientationchange is the only signal about its own state. Your mod can ask with app.IsOnScreen. |
| a key other than Enter |
No. keydown fires for Enter in a field and nothing else. No shortcuts, no arrow keys, no modifiers. |
| focus a control from script | No. Focus follows the player's click. |
| the clipboard, a file, a time zone |
No. There is no window and no navigator. |
Anything a page cannot do, your mod usually can - it is ordinary C# with the whole game in reach. The
question is not "how do I do this in JavaScript" but "does this belong behind an s1.call".