puter.ui.launchApp() returns a Promise that needs to resolve whether the
app uses the Puter SDK or not. Non-SDK apps are tricky because they
don't send a READY message on startup, and we don't know in advance
whether an app will use the SDK or not.
This is a workaround to ensure that launchApp() always resolves. When an
app is closed, if it wasn't using the SDK, we send an artificial
notification that it launched, followed by an extra notification that
it has closed (because the original close notification was sent before
this point). This means any users of launchApp() can await it, and get
an AppConnection, and listen to the close event. They can't otherwise
interact with a non-SDK app because it will have closed already, but we
can improve this in the future without breaking the API.
Apps are not required to use the Puter SDK. If they don't, then we can
still launch them, close them, and listen to their close event, but are
unable to send messages to them.
Send an appClosed message with the instance ID of the app that was
closed. This will be picked up by Puter.js's AppConnection and reported
as a 'close' event.
To make this work, a `data-parent_instance_id` attribute is set on child
app windows. This is very similar to the `data-parent_uuid` attribute,
which tracks parent windows instead of parent app instances. (Dialogs
have a parent window, but are not apps, so don't have a parent app
instance.) The difference is subtle, and we may want to combine these in
the future, but currently closing an app will close any child windows,
which is not behaviour we want for child apps.
This is left open to future additions, by naming it 'locale', and having
the language just be an object field.
Side note, maybe we should have a LocaleService for this?
Calling `puter.ui.launchApp()` now treats the new app as a child of the
one that launched it.
A child app is given a `puter.parent_instance_id` URL param, containing
its parent's instance ID.
Previously, `launchApp()` would resolve as soon as the app was launched.
This commit changes that, so that it is notified after the child app
sends its READY event to Puter. This means that as soon as `launchApp()`
has completed, the child app is ready to receive messages. The downside
is that launching an app that does not include Puter.js will now not
cause a notification, so `await puter.ui.launchApp()` will not resolve
in that case.