arcane-os 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -82,6 +82,7 @@ appropriate.
82
82
  | [`modal.html`](#modalhtml) | Generic modal with population, open/close, actions, and sequential task execution. | `populate()`<br>`open()`<br>`close()`<br>`runTasks()`<br>`destroy()` | `modal-ready`<br>`modal-opened`<br>`modal-closed`<br>`modal-action` | Modal state normalized; injected task results mixed |
83
83
  | [`output-panel.html`](#output-panelhtml) | Presents status, output, body, coverage, actions, pending, error, and cleared states. | `configure()`<br>`setOutput()`<br>`setBody()`<br>`setCoverage()`<br>`setActions()`<br>`setPending()`<br>`setStatus()`<br>`setError()`<br>`clear()`<br>`destroy()` | `output-panel-ready`<br>`output-panel-state`<br>`output-panel-change`<br>`output-panel-action`<br>`output-panel-error`<br>`output-panel-cleared` | DOM-normalized |
84
84
  | [`preferences-form.html`](#preferences-formhtml) | Builds a schema-driven preferences form with submit, reset, busy, and status behavior. | `configure()`<br>`getValues()`<br>`setValues()`<br>`setBusy()`<br>`setStatus()`<br>`destroy()` | `preferences-form-ready`<br>`preferences-change`<br>`preferences-submit`<br>`preferences-reset` | Normalized form values |
85
+ | [`pwa-install.html`](#pwa-installhtml) | Presents a dismissible browser installation action with floating or inline placement. | `configure()`<br>`install()`<br>`dismiss()`<br>`destroy()`<br>`state`<br>`ready` | `pwa-install-ready`<br>`pwa-install-change`<br>`pwa-install-dismissed` | Browser install availability and outcome supplied by the shared PWA owner |
85
86
  | [`record-timeline.html`](#record-timelinehtml) | Displays complete chronological records/evidence and emits open actions. | `setItems()`<br>`populate()`<br>`destroy()` | `record-timeline-ready`<br>`record-timeline-open` | Complete item fields and inventories preserved |
86
87
  | [`relationship-board.html`](#relationship-boardhtml) | Displays complete normalized relationship nodes/edges in graph and list forms. | `setGraph()`<br>`populate()`<br>`destroy()` | `relationship-board-ready`<br>`relationship-node-open`<br>`relationship-edge-open` | Complete graph inventories and fields preserved |
87
88
  | [`screen-capture.html`](#screen-capturehtml) | Presents image, video, or GIF display-capture workflow. | `capture` (`ScreenCapture` instance)<br>`destroy()` | `screen-capture-ready`<br>`screen-capture-result` | State/result normalized; media permission/codec failures mixed |
@@ -1018,6 +1019,69 @@ Events: `preferences-form-ready`, `preferences-change`, `preferences-submit`, `p
1018
1019
  </html-import>
1019
1020
  ```
1020
1021
 
1022
+ ## pwa-install.html
1023
+
1024
+ ### Overview
1025
+
1026
+ A compact installation suggestion using the shared [PWA installation owner](pwa.md).
1027
+ The default floating panel appears near the top right only when the browser offers
1028
+ installation. It has an Install action and an explicit close button, takes no
1029
+ focus automatically, and has no dismissal timer. It uses the Arcane theme and
1030
+ primitives, wraps complete labels and errors, and scrolls its own content when
1031
+ the available height is limited. The parent page loads `ThemeBootstrap.js` to
1032
+ apply the user's appearance preferences.
1033
+
1034
+ ### Public surface
1035
+
1036
+ `configure({appName, installLabel, closeLabel, promptingLabel, description,
1037
+ presentation})` updates display configuration and returns its current record.
1038
+ Labels remain complete strings. `appName` initially uses `data-app-name` or
1039
+ `this app`; the default button label is `Install`. `presentation` is `floating`
1040
+ by default or `inline`, initially read from `data-presentation`. Inline placement
1041
+ uses the parent's layout. Set `description` to an empty string when a compact
1042
+ placement needs no supporting text; supplied descriptions remain visible.
1043
+
1044
+ `install()` calls the shared owner's native prompt synchronously and returns its
1045
+ promise of the browser outcome or `null` when no prompt is available. Call it
1046
+ directly from a user action. The component's Install button already does this.
1047
+ An explicit request's complete failure message remains visible until dismissed;
1048
+ the method rejects with the same error. The browser controls actual installation.
1049
+ Acceptance hides the suggestion without claiming installation has completed.
1050
+
1051
+ `dismiss()` closes the component. Floating dismissal also uses the owner's
1052
+ session dismissal; an explicit inline component ignores that shared dismissal
1053
+ and closes only its own instance. Closing retains any unused native prompt at
1054
+ the shared owner. `destroy()` removes the component's listeners and subscription,
1055
+ disposes its event source, hides its host, and marks `ready` false; it does not
1056
+ dispose the shared owner or change browser installation state. Both methods
1057
+ return true while active and false after destruction; `destroy()` is idempotent.
1058
+
1059
+ The readonly `state` property returns the shared owner's current install-state
1060
+ record; `ready` becomes true after methods and the state subscription are attached.
1061
+ `pwa-install-ready` carries `{ready, state}`; `pwa-install-change` carries
1062
+ `{state, visible}` for each observed owner update; `pwa-install-dismissed` carries
1063
+ `{presentation, state}`. These follow the canonical event projection contract.
1064
+
1065
+ ### Availability and normalization
1066
+
1067
+ The component requires HTMLImport and a DOM renderer. Native installation
1068
+ availability comes from the browser's `beforeinstallprompt` event through
1069
+ `getPwaInstall()`. Without an available event, the suggestion stays hidden;
1070
+ absence does not identify why installation is unavailable. Installed-app events,
1071
+ accepted prompts, and an already running installed display mode hide it.
1072
+
1073
+ ### Example
1074
+
1075
+ ```html
1076
+ <html-import
1077
+ id="install-app"
1078
+ href="/arcane/components/pwa-install.html"
1079
+ data-app-name="Example Library"
1080
+ data-presentation="inline"
1081
+ hidden>
1082
+ </html-import>
1083
+ ```
1084
+
1021
1085
  ## record-timeline.html
1022
1086
 
1023
1087
  ### Overview
@@ -39,7 +39,7 @@ for the installed-inventory-derived physical-runtime contract in SDK `0.5.18`.
39
39
  | `arcane-os/speech-playback` | Portable speech preparation, playback state, and injected media adapters. |
40
40
  | `arcane-os/speech-text` | Speech-input formatting cleanup for complete text and streamed chunks. |
41
41
  | `arcane-os/browser-device` | Synchronous mobile or desktop identity hints for application-owned settings. |
42
- | `arcane-os/pwa` | Nonblocking PWA registration, native worker updates and observable lifecycle state. |
42
+ | `arcane-os/pwa` | Nonblocking PWA registration, worker updates, native installation state and a dismissible installation component. |
43
43
  | `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
44
44
  | `arcane-os/ai/browser-speech` | Caller-selected browser-local Whisper STT and Kokoro TTS provider mechanisms, ordinary upstream assets, materialized/native routing, Workers, and cancellation. |
45
45
  | `arcane-os/mail` | Portable Mail runtime, durable outbox, complete transport responses, and provider-neutral acceptance contracts. |
@@ -129,6 +129,9 @@ browser map are cataloged separately in [Runtime modules](runtime-modules.md).
129
129
  | `getBrowserDeviceClass()` | function | `arcane-os/browser-device` | Browser device settings | Node and Browser; synchronous identity hint with no model, storage, or GPU operation |
130
130
  | `PWA_STATE_EVENT` | constant | `arcane-os/pwa` | Progressive web applications | Browser lifecycle event name; importable without registration |
131
131
  | `registerPwa()` | function | `arcane-os/pwa` | Progressive web applications | Browser service workers; synchronous unsupported state when unavailable |
132
+ | `PWA_INSTALL_STATE_EVENT` | constant | `arcane-os/pwa` | Progressive web applications | Browser installation event name; importable without starting observation |
133
+ | `getPwaInstall()` | function | `arcane-os/pwa` | Progressive web applications | Browser native installation events; waiting state while no prompt is available |
134
+ | `mountPwaInstallPrompt()` | function | `arcane-os/pwa` | Progressive web applications | Browser document and managed HTML import; resolves to null without a document |
132
135
  | `createDbopfsSpeechArtifactStore()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with ready DBOPFS, Web Locks, Fetch, File/Blob, and object URLs |
133
136
  | `removeBrowserSpeechModelCache()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser CacheStorage; explicit removal of one selected upstream model |
134
137
  | `createNativeBuildPlan()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
@@ -6124,6 +6127,99 @@ current-state replay, disposal, offline resource ownership and update behavior.
6124
6127
  The generated bootstrap registers automatically; a manual caller owns a separate
6125
6128
  entry point and must observe its `ready` rejection.
6126
6129
 
6130
+ ## PWA_INSTALL_STATE_EVENT
6131
+
6132
+ ### Overview
6133
+
6134
+ The exact event name `arcane.pwa.install.state` identifies native installation
6135
+ availability and choice state through the existing Arcane event owner. It is
6136
+ separate from service-worker registration state. Importing it starts no
6137
+ observation or installation.
6138
+
6139
+ ### Example
6140
+
6141
+ ```javascript
6142
+ import {PWA_INSTALL_STATE_EVENT} from 'arcane-os/pwa';
6143
+ console.log(PWA_INSTALL_STATE_EVENT);
6144
+ ```
6145
+
6146
+ See the [installation state contract](pwa.md#getpwainstall) for the complete
6147
+ payload and current-state subscription behavior.
6148
+
6149
+ ## getPwaInstall()
6150
+
6151
+ ### Overview
6152
+
6153
+ `getPwaInstall()` returns one synchronous page owner exposing `state`,
6154
+ `subscribe`, `prompt`, `dismiss` and `dispose`. It captures the browser's
6155
+ `beforeinstallprompt` event and observes `appinstalled` and app display-mode
6156
+ changes. A missing event leaves installation availability unknown and the owner
6157
+ waiting; it does not prove browser incompatibility.
6158
+
6159
+ State contains `status`, `available`, `dismissed`, `outcome` and `error`.
6160
+ Subscriptions replay current state by default. `prompt()` must be called
6161
+ directly within the install click to preserve native user activation. It
6162
+ consumes the event once and returns the browser choice, resolves to `null` when
6163
+ unavailable, or rejects with the actual error. `dismiss()` remembers the
6164
+ session's floating-suggestion dismissal without consuming a retained event.
6165
+
6166
+ ### Example
6167
+
6168
+ ```javascript
6169
+ import {getPwaInstall} from 'arcane-os/pwa';
6170
+
6171
+ const install = getPwaInstall();
6172
+ const button = document.querySelector('#install');
6173
+ install.subscribe(function updateInstallButton(state) {
6174
+ button.hidden = !state.available;
6175
+ });
6176
+ button.addEventListener('click', function requestInstallation() {
6177
+ install.prompt().catch(function reportInstallFailure(error) {
6178
+ console.error(error);
6179
+ });
6180
+ });
6181
+ ```
6182
+
6183
+ The [PWA guide](pwa.md#getpwainstall) defines every status, event lifetime,
6184
+ session dismissal, errors and disposal. Disposing this shared owner removes
6185
+ its page-level observation; components normally unsubscribe only their own
6186
+ listener. Installation state does not establish offline readiness or completed
6187
+ Android WebAPK creation.
6188
+
6189
+ ## mountPwaInstallPrompt()
6190
+
6191
+ ### Overview
6192
+
6193
+ `mountPwaInstallPrompt({appName = ''} = {})` starts the shared install observer
6194
+ before asynchronously mounting one initially hidden `pwa-install.html`
6195
+ component. Repeated calls return the same mounting promise. It resolves to the
6196
+ ready `html-import` host, or `null` without a document or when the owner is
6197
+ disposed before mounting. Component-load failure rejects; removal or disposal
6198
+ during loading rejects with `AbortError`. A rejected mount permits another
6199
+ explicit attempt. The first call
6200
+ supplies the initial app name.
6201
+
6202
+ The generated PWA bootstrap calls this automatically with the manifest name.
6203
+ Component mounting and worker registration run independently, and application
6204
+ rendering must not await them. The floating component offers Install and a
6205
+ manual close control; an application can also place the same component inline.
6206
+
6207
+ ### Example
6208
+
6209
+ ```javascript
6210
+ import {mountPwaInstallPrompt} from 'arcane-os/pwa';
6211
+
6212
+ mountPwaInstallPrompt({appName: 'Example Library'}).catch(
6213
+ function reportInstallComponentFailure(error) {
6214
+ console.error(error);
6215
+ }
6216
+ );
6217
+ ```
6218
+
6219
+ See the [mounting contract](pwa.md#mountpwainstallprompt) and
6220
+ [component reference](runtime-components.md#pwa-installhtml). The browser owns
6221
+ native installation eligibility and URL-bar promotion.
6222
+
6127
6223
  ## createBrowserWasmLlmProvider()
6128
6224
 
6129
6225
  ### Overview
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -67,7 +67,7 @@
67
67
  "test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
68
68
  "test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
69
69
  "test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/contracts.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
70
- "test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/update-check.test.mjs",
70
+ "test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/update-check.test.mjs",
71
71
  "test:integration": "node ./bin/arcane-test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-plan.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
72
72
  "test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
73
73
  "check": "node tools/check-source.mjs && npm test",
@@ -0,0 +1,216 @@
1
+ <link rel="stylesheet" href="./arcane/css/theme.css?v=1">
2
+ <link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
3
+ <style>
4
+ :host{
5
+ display:block;
6
+ inset-block-start:4.5rem;
7
+ inset-inline-end:1rem;
8
+ max-width:calc(100vw - 2rem);
9
+ min-width:0;
10
+ position:fixed;
11
+ width:22rem;
12
+ z-index:110;
13
+ }
14
+ :host([hidden]){display:none}
15
+ :host([data-presentation="inline"]){
16
+ max-width:100%;
17
+ position:static;
18
+ width:fit-content;
19
+ }
20
+ .panel{
21
+ background:var(--arcane-surface-raised);
22
+ border:1px solid var(--arcane-border);
23
+ border-radius:var(--arcane-radius-medium);
24
+ box-shadow:var(--arcane-shadow-medium);
25
+ box-sizing:border-box;
26
+ color:var(--text-color);
27
+ display:grid;
28
+ gap:var(--arcane-space-2);
29
+ max-height:calc(100vh - 5.5rem);
30
+ max-height:calc(100dvh - 5.5rem);
31
+ overflow:auto;
32
+ padding:var(--arcane-space-3);
33
+ }
34
+ .heading{align-items:start;display:flex;gap:var(--arcane-space-2)}
35
+ .copy{flex:1;min-width:0}
36
+ h2,p{margin:0;overflow-wrap:anywhere;white-space:pre-wrap}
37
+ h2{font-size:1rem;line-height:1.4}
38
+ .description{color:var(--arcane-muted);font-size:.9rem;line-height:1.45}
39
+ .close{flex:none}
40
+ .install{justify-self:start;max-width:100%;overflow-wrap:anywhere;white-space:normal}
41
+ .status{font-size:.9rem;line-height:1.45}
42
+ .status[data-error="true"]{color:var(--arcane-danger)}
43
+ :host([data-presentation="inline"]) .panel{box-shadow:none}
44
+ @media(prefers-reduced-motion:reduce){.install{transition:none}}
45
+ @media(forced-colors:active){.panel,.install,.close{border:1px solid CanvasText}}
46
+ </style>
47
+
48
+ <section class="panel" aria-labelledby="installTitle">
49
+ <div class="heading">
50
+ <div class="copy">
51
+ <h2 id="installTitle">Install this app</h2>
52
+ <p id="description" class="description">Open this app directly from your device.</p>
53
+ </div>
54
+ <button id="close" class="close arcane-close-button" type="button" aria-label="Close installation suggestion">&#x2715;</button>
55
+ </div>
56
+ <button id="install" class="install arcane-button" type="button">Install</button>
57
+ <p id="status" class="status" role="status" aria-live="polite" hidden></p>
58
+ </section>
59
+
60
+ <script type="module">
61
+ const host=this;
62
+ host.hidden=true;
63
+ const [
64
+ {default:Is},
65
+ {getPwaInstall},
66
+ {createArcaneEventSource,projectArcaneDOMEvent},
67
+ {arcaneLogging}
68
+ ]=await Promise.all([
69
+ import('strong-type'),
70
+ import('arcane-os/pwa'),
71
+ import('arcane-os/event-manager'),
72
+ import('arcane-os/logging')
73
+ ]);
74
+ const is=new Is(false);
75
+ const root=host.shadowRoot;
76
+ const title=root.querySelector('#installTitle');
77
+ const description=root.querySelector('#description');
78
+ const installButton=root.querySelector('#install');
79
+ const closeButton=root.querySelector('#close');
80
+ const status=root.querySelector('#status');
81
+ const lifecycleController=new AbortController();
82
+ const events=createArcaneEventSource(host,{
83
+ source:'arcane.component.pwa-install',
84
+ eventTypes:['pwa-install-ready','pwa-install-change','pwa-install-dismissed']
85
+ });
86
+ const owner=getPwaInstall();
87
+ let options={
88
+ appName:host.dataset.appName||'this app',
89
+ installLabel:'Install',
90
+ closeLabel:'Close installation suggestion',
91
+ promptingLabel:'Choose an option in the browser installation prompt.',
92
+ description:'Open this app directly from your device.',
93
+ presentation:host.dataset.presentation||'floating'
94
+ };
95
+ let currentState=owner.state;
96
+ let locallyDismissed=false;
97
+ let pending=false;
98
+ let requestError=null;
99
+ let destroyed=false;
100
+
101
+ host.configure=configure;
102
+ host.install=install;
103
+ host.dismiss=dismiss;
104
+ host.destroy=destroy;
105
+ Object.defineProperty(host,'state',{configurable:true,get:getState});
106
+
107
+ function emit(type,detail){
108
+ if(destroyed)return false;
109
+ const publication=events.dispatch(type,detail);
110
+ return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true});
111
+ }
112
+
113
+ function getState(){
114
+ return {...currentState};
115
+ }
116
+
117
+ function configure(input={}){
118
+ if(destroyed)return false;
119
+ if(input===null||!is.object(input)||is.array(input)){
120
+ throw new TypeError('PWA install configuration must be an object.');
121
+ }
122
+ const next={...options,...input};
123
+ for(const key of ['appName','installLabel','closeLabel','promptingLabel','description']){
124
+ if(!is.string(next[key]))throw new TypeError(`PWA install ${key} must be a string.`);
125
+ }
126
+ if(next.presentation!=='floating'&&next.presentation!=='inline'){
127
+ throw new TypeError('PWA install presentation must be floating or inline.');
128
+ }
129
+ options=next;
130
+ host.dataset.presentation=options.presentation;
131
+ title.textContent=`Install ${options.appName}`;
132
+ description.textContent=options.description;
133
+ installButton.textContent=options.installLabel;
134
+ closeButton.setAttribute('aria-label',options.closeLabel);
135
+ render();
136
+ return {...options};
137
+ }
138
+
139
+ function render(){
140
+ if(destroyed)return;
141
+ const terminal=['accepted','installed','running','disposed'].includes(currentState.status);
142
+ if(terminal)requestError=null;
143
+ const dismissed=locallyDismissed
144
+ ||(options.presentation==='floating'&&currentState.dismissed);
145
+ host.hidden=terminal||dismissed||(!currentState.available&&!pending&&!requestError);
146
+ installButton.disabled=pending||!currentState.available;
147
+ const message=requestError
148
+ ?is.string(requestError.message)?requestError.message:String(requestError)
149
+ :pending?options.promptingLabel:'';
150
+ status.textContent=message;
151
+ status.hidden=!message;
152
+ status.dataset.error=String(Boolean(requestError));
153
+ }
154
+
155
+ function observeInstallState(state){
156
+ if(destroyed)return;
157
+ currentState=state;
158
+ render();
159
+ emit('pwa-install-change',{state:getState(),visible:!host.hidden});
160
+ }
161
+
162
+ function install(){
163
+ if(destroyed||pending||!currentState.available)return Promise.resolve(null);
164
+ locallyDismissed=false;
165
+ requestError=null;
166
+ pending=true;
167
+ render();
168
+ let operation;
169
+ try{
170
+ // Keep the browser prompt inside the initiating click's user activation.
171
+ operation=owner.prompt();
172
+ }catch(error){
173
+ operation=Promise.reject(error);
174
+ }
175
+ return Promise.resolve(operation).catch(function reportPromptError(error){
176
+ if(!destroyed)requestError=error;
177
+ throw error;
178
+ }).finally(function settlePrompt(){
179
+ if(destroyed)return;
180
+ pending=false;
181
+ render();
182
+ });
183
+ }
184
+
185
+ function dismiss(){
186
+ if(destroyed)return false;
187
+ locallyDismissed=true;
188
+ requestError=null;
189
+ if(options.presentation==='floating')owner.dismiss();
190
+ render();
191
+ emit('pwa-install-dismissed',{presentation:options.presentation,state:getState()});
192
+ return true;
193
+ }
194
+
195
+ function destroy(){
196
+ if(destroyed)return false;
197
+ destroyed=true;
198
+ lifecycleController.abort('pwa-install-destroyed');
199
+ events.dispose();
200
+ host.hidden=true;
201
+ host.ready=false;
202
+ return true;
203
+ }
204
+
205
+ installButton.addEventListener('click',function requestInstallation(){
206
+ install().catch(function reportInstallationFailure(error){
207
+ arcaneLogging.error('Unable to open the browser installation prompt:',error);
208
+ });
209
+ },{signal:lifecycleController.signal});
210
+ closeButton.addEventListener('click',dismiss,{signal:lifecycleController.signal});
211
+
212
+ configure();
213
+ owner.subscribe(observeInstallState,{signal:lifecycleController.signal});
214
+ host.ready=true;
215
+ emit('pwa-install-ready',{ready:true,state:getState()});
216
+ </script>