pear-electron 0.0.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.
package/decal.html ADDED
@@ -0,0 +1,733 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <script>
5
+ document.querySelector('html').classList.add(process.platform)
6
+ const runtime = Pear[Pear.constructor.UI]
7
+ window.DECAL = runtime[runtime.constructor.DECAL]
8
+ </script>
9
+ <link rel="stylesheet" href="./fonts.css">
10
+ <style>
11
+ body {
12
+ user-select: none;
13
+ background: #151517;
14
+ margin:0;
15
+ padding: 0;
16
+ border: 0.5px solid rgba(0, 0, 0, 0);
17
+ box-sizing: border-box;
18
+ border-radius: 8px;
19
+ font-family:'overpass-mono';
20
+ overflow: hidden;
21
+ -webkit-font-smoothing: antialiased;
22
+ }
23
+ a:visited, a:active, a {
24
+ color: #95E6CB;
25
+ outline: none;
26
+ }
27
+ #bar {
28
+ background: #151517;
29
+ height: 50px;
30
+ padding-top: 12px;
31
+ padding-right: 8px;
32
+ padding-left: 8px;
33
+ margin-left: auto;
34
+ margin-right: auto;
35
+ border-radius: none;
36
+ border-top-left-radius: 8px;
37
+ border-top-right-radius: 8px;
38
+ color: #FFF;
39
+ white-space: nowrap;
40
+ box-sizing: border-box;
41
+ position: relative;
42
+ z-index: 2;
43
+ width: calc(100vw + 2px);
44
+ left: -2px;
45
+ top: -2px;
46
+ }
47
+ #bar > * {
48
+ -webkit-app-region: no-drag;
49
+ }
50
+
51
+ #win-ctrl {
52
+ display: none;
53
+ }
54
+
55
+ #window-controls {
56
+ -webkit-app-region: drag;
57
+ padding: 0;
58
+ border-top-left-radius: 8px;
59
+ border-top-right-radius: 8px;
60
+ white-space: nowrap;
61
+ position: fixed;
62
+ z-index: 100;
63
+ width: 100%;
64
+ left: 0;
65
+ top: 0;
66
+ }
67
+
68
+ pear-ctrl[data-platform=darwin] {
69
+ margin-top: 22px;
70
+ margin-left: 12px;
71
+ }
72
+
73
+ .dialog pear-ctrl[data-platform=darwin] {
74
+ margin-top: -32px;
75
+ }
76
+
77
+ .dialog.darwin #bar {
78
+ background: rgba(0, 0, 0, 0);
79
+ display: block;
80
+ }
81
+
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <div id="window-controls"><pear-ctrl></pear-ctrl></div>
86
+ <style>
87
+ /* windows styles, every selector *MUST* be preceded by .win32 */
88
+ .win32 #win-ctrl {
89
+ float: right;
90
+ margin-left: .6em;
91
+ margin-top: 0.22em;
92
+ border-spacing: 0.3em 0;
93
+ }
94
+ .win32 #win-ctrl > .win-ctrl {
95
+ opacity: 0.8;
96
+ height: 24px;
97
+ width: 24px;
98
+ display: table-cell;
99
+ vertical-align: middle;
100
+ text-align: center;
101
+ }
102
+ .win32 #win-ctrl > .win-ctrl:hover {
103
+ opacity: 1;
104
+ }
105
+
106
+ .win32 #bar {
107
+ width: calc(100vw + 8px);
108
+ left: -4px;
109
+ }
110
+
111
+ #win-ctrl.max #win-max {
112
+ display: none;
113
+ }
114
+ #win-ctrl.max #win-restore {
115
+ display: table-cell;
116
+ }
117
+ #win-ctrl #win-restore {
118
+ display: none;
119
+ }
120
+ </style>
121
+
122
+ <template id="decal-dialog-tmpl">
123
+ <style>
124
+ #dialog {
125
+ width: 500px;
126
+ height: 340px;
127
+ position: absolute;
128
+ top: 40px;
129
+ left: 0px;
130
+ background-color: #25262C;
131
+ padding: 35px;
132
+ font-family: "Inter", sans-serif;
133
+ z-index: 99;
134
+ }
135
+ #header {
136
+ color: #FFFFFF;
137
+ font-size: 18px;
138
+ margin-left: 50px;
139
+ }
140
+ #p1 {
141
+ color: #FFFFFF;
142
+ margin-top: 30px;
143
+ margin-bottom: 5px;
144
+ font-size: 14px;
145
+ font-weight: 400;
146
+ }
147
+ #key {
148
+ color:#B0D944;
149
+ font-size: 14px;
150
+ font-weight: 600;
151
+ margin-top: 0px;
152
+ margin-bottom: 25px;
153
+ }
154
+ #p2 {
155
+ color: #ACB1BB;
156
+ font-size: 14px;
157
+ font-weight: 400;
158
+ }
159
+ #p2 > span {
160
+ color: #FFFFFF;
161
+ font-weight: 600;
162
+ }
163
+ input {
164
+ display: block;
165
+ padding: 16px 16px 16px 16px;
166
+ border: 2px solid #4A4E56;
167
+ color: #5E636D;
168
+ background-color: #202126;
169
+ width: 90%;
170
+ font-size: 14px;
171
+ border-radius: 8px;
172
+ margin-top: 25px;
173
+ outline: none;
174
+ }
175
+ #cancel, #confirm {
176
+ border: 2px solid #4A4E56;
177
+ color: #FFFFFF;
178
+ font-weight: 600;
179
+ background-color: #202126;
180
+ border-radius: 8px;
181
+ font-size: 16px;
182
+ width: 48%;
183
+ height: 55px;
184
+ }
185
+ #confirm.green-confirm, #cancel {
186
+ cursor: pointer;
187
+ }
188
+ #cancel {
189
+ margin-right: 2%;
190
+ }
191
+ #logo {
192
+ float: left;
193
+ }
194
+ #confirm {
195
+ background-color: #4A4E56;
196
+ color: #8C919D;
197
+ }
198
+ .green-confirm {
199
+ color: #B0D944 !important;
200
+ background-color: #222E02 !important;
201
+ border: 2px solid #B0D944 !important;
202
+ }
203
+ #error {
204
+ color: #FF6161;
205
+ font-size: 12px;
206
+ margin-bottom: 30px;
207
+ visibility: hidden;
208
+ }
209
+ .show {
210
+ visibility: visible !important;
211
+ }
212
+ </style>
213
+ <div id="dialog">
214
+ <svg id="logo" width="36" height="37" viewBox="0 0 36 37" fill="none" xmlns="http://www.w3.org/2000/svg">
215
+ <path d="M17.7632 2.75H19.3421V5.7125H17.7632V2.75Z" fill="#B0D944"/>
216
+ <path d="M16.9737 6.65V7.25H15.3947V8H21.7105V7.25H20.1316V6.0875H18.5526V6.65H16.9737Z" fill="#B0D944"/>
217
+ <path d="M23.2895 8.375H18.5526V8.9375H13.8158V10.2875H23.2895V8.375Z" fill="#B0D944"/>
218
+ <path d="M24.8684 10.6625H18.5526V11.225H12.2368V12.575H24.8684V10.6625Z" fill="#B0D944"/>
219
+ <path d="M24.8684 12.95H18.5526V13.5125H12.2368V14.8625H24.8684V12.95Z" fill="#B0D944"/>
220
+ <path d="M26.4474 15.2375H18.5526V15.8H10.6579V17.15H26.4474V15.2375Z" fill="#B0D944"/>
221
+ <path d="M26.4474 17.525H18.5526V18.0875H10.6579V19.4375H26.4474V17.525Z" fill="#B0D944"/>
222
+ <path d="M28.0263 19.8125H18.5526V20.375H9.07895V21.725H28.0263V19.8125Z" fill="#B0D944"/>
223
+ <path d="M29.6053 22.1H18.5526V22.6625H7.5V24.0125H29.6053V22.1Z" fill="#B0D944"/>
224
+ <path d="M29.6053 24.3875H18.5526V24.95H7.5V26.3H29.6053V24.3875Z" fill="#B0D944"/>
225
+ <path d="M29.6053 26.675H18.5526V27.2375H7.5V28.5875H29.6053V26.675Z" fill="#B0D944"/>
226
+ <path d="M26.4474 28.9625H18.5526V29.525H10.6579V30.875H26.4474V28.9625Z" fill="#B0D944"/>
227
+ <path d="M23.2895 31.25H18.5526V31.8125H13.8158V32.75H23.2895V31.25Z" fill="#B0D944"/>
228
+ <path d="M19.5 24.7143L27.75 20L36 24.7143V25.5627C36 30.6414 32.6333 35.1048 27.75 36.5C22.8667 35.1048 19.5 30.6414 19.5 25.5627V24.7143Z" fill="#E44B44"/>
229
+ <path d="M27.75 25.1562V29.5562M27.75 32.3062V32.3172" stroke="#391C20" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
230
+ </svg>
231
+ <h1 id="header"></h1>
232
+ <p id="p1"></p>
233
+ <p id="key"></p>
234
+ <p id="p2"></p>
235
+ <input type="text"></input>
236
+ <p id="error">Please, enter TRUST (uppercase) </p>
237
+ <button id="cancel">Cancel</button>
238
+ <button id="confirm">Confirm</button>
239
+ </div>
240
+ </template>
241
+ <script type="module">
242
+ class DecalDialog extends HTMLElement {
243
+ constructor () {
244
+ super()
245
+ const template = document.querySelector('#decal-dialog-tmpl').content
246
+ this.root = this.attachShadow({mode: 'open'})
247
+ this.root.appendChild(template.cloneNode(true))
248
+ this.confirm = this.root.querySelector('#confirm')
249
+ this.cancel = this.root.querySelector('#cancel')
250
+ this.input = this.root.querySelector('input')
251
+ this.error = this.root.querySelector('#error')
252
+ this.header = this.root.querySelector('#header')
253
+ this.p1 = this.root.querySelector('#p1')
254
+ this.p2 = this.root.querySelector('#p2')
255
+ this.key = null
256
+ this.ipc = null
257
+
258
+ this.cancel.addEventListener('click', async () => {
259
+ await Pear.Window.self.close()
260
+ })
261
+ }
262
+
263
+ #update () {
264
+ this.header.innerText = this.dataset.header
265
+ this.p1.innerText = this.dataset.p1
266
+ this.p2.innerHTML = this.dataset.p2
267
+ this.input.placeholder = this.dataset.placeholder
268
+ }
269
+
270
+ get options () {
271
+ return this.dataset
272
+ }
273
+
274
+ set options (opts) {
275
+ Object.assign(this.dataset, opts)
276
+ this.#update()
277
+ }
278
+
279
+ setLink (link) {
280
+ this.root.querySelector('#key').innerText = link
281
+ }
282
+ }
283
+
284
+ class TrustDialog extends DecalDialog {
285
+ constructor () {
286
+ super()
287
+ this.input.addEventListener('keyup', this.#onKeyup.bind(this))
288
+ this.confirm.addEventListener('click', this.#onClick.bind(this))
289
+ }
290
+
291
+ async #permit () {
292
+ await this.ipc.permit({ key: this.key })
293
+ await this.ipc.restart({ id: this.gid })
294
+ }
295
+
296
+ #onKeyup (event) {
297
+ const value = event.target.value
298
+ if (value === 'TRUST') {
299
+ this.confirm.classList.add('green-confirm')
300
+ } else {
301
+ this.confirm.classList.remove('green-confirm')
302
+ }
303
+ if (value === 'trust') {
304
+ this.error.classList.add('show')
305
+ } else {
306
+ this.error.classList.remove('show')
307
+ }
308
+ }
309
+
310
+ async #onClick () {
311
+ if (this.input.value === 'TRUST') await this.#permit()
312
+ }
313
+ }
314
+
315
+ class PasswordDialog extends DecalDialog {
316
+ constructor () {
317
+ super()
318
+ this.input.type = 'password'
319
+ this.input.addEventListener('keyup', this.#onKeyup.bind(this))
320
+ this.confirm.addEventListener('click', this.#onClick.bind(this))
321
+ }
322
+
323
+ async #permit () {
324
+ const password = this.input.value
325
+ const hypercoreid = DECAL['hypercore-id-encoding']
326
+ const t = await this.ipc.permit({ key: this.key, password })
327
+ await this.ipc.restart({ id: this.gid })
328
+ }
329
+
330
+ #onKeyup (event) {
331
+ const value = event.target.value
332
+ if (value.length > 0) {
333
+ this.confirm.classList.add('green-confirm')
334
+ } else {
335
+ this.confirm.classList.remove('green-confirm')
336
+ }
337
+ }
338
+
339
+ async #onClick () {
340
+ if (this.input.value.length > 0) await this.#permit()
341
+ }
342
+ }
343
+
344
+ customElements.define('trust-dialog', TrustDialog)
345
+ customElements.define('password-dialog', PasswordDialog)
346
+
347
+ </script>
348
+ <div id="bar">
349
+ <div id="win-ctrl">
350
+ <div id="win-min" class="win-ctrl">
351
+ <svg width="18" height="2" viewBox="0 0 18 2" fill="none" xmlns="http://www.w3.org/2000/svg">
352
+ <path d="M0 0.5625C0 0.28125 0.246094 0 0.5625 0H17.4375C17.7188 0 18 0.28125 18 0.5625C18 0.878906 17.7188 1.125 17.4375 1.125H0.5625C0.246094 1.125 0 0.878906 0 0.5625Z" fill="white"/>
353
+ </svg>
354
+ </div>
355
+ <div id="win-max" class="win-ctrl">
356
+ <svg width="18" height="17" viewBox="0 0 18 17" fill="none" xmlns="http://www.w3.org/2000/svg">
357
+ <path d="M0 2.625C0 1.39453 0.984375 0.375 2.25 0.375H15.75C16.9805 0.375 18 1.39453 18 2.625V13.875C18 15.1406 16.9805 16.125 15.75 16.125H2.25C0.984375 16.125 0 15.1406 0 13.875V2.625ZM1.125 2.625V6H16.875V2.625C16.875 2.02734 16.3477 1.5 15.75 1.5H2.25C1.61719 1.5 1.125 2.02734 1.125 2.625ZM1.125 7.125V13.875C1.125 14.5078 1.61719 15 2.25 15H15.75C16.3477 15 16.875 14.5078 16.875 13.875V7.125H1.125Z" fill="white"/>
358
+ </svg>
359
+ </div>
360
+ <div id="win-restore" class="win-ctrl">
361
+ <svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
362
+ <path d="M15.1875 1.375H7.3125C6.36328 1.375 5.625 2.14844 5.625 3.0625V3.625H4.5V3.0625C4.5 1.51562 5.73047 0.25 7.3125 0.25H15.1875C16.7344 0.25 18 1.51562 18 3.0625V10.9375C18 12.5195 16.7344 13.75 15.1875 13.75H14.625V12.625H15.1875C16.1016 12.625 16.875 11.8867 16.875 10.9375V3.0625C16.875 2.14844 16.1016 1.375 15.1875 1.375ZM11.25 4.75C12.4805 4.75 13.5 5.76953 13.5 7V16C13.5 17.2656 12.4805 18.25 11.25 18.25H2.25C0.984375 18.25 0 17.2656 0 16V7C0 5.76953 0.984375 4.75 2.25 4.75H11.25ZM11.25 5.875H2.25C1.61719 5.875 1.125 6.40234 1.125 7V9.25H12.375V7C12.375 6.40234 11.8477 5.875 11.25 5.875ZM2.25 17.125H11.25C11.8477 17.125 12.375 16.6328 12.375 16V10.375H1.125V16C1.125 16.6328 1.61719 17.125 2.25 17.125Z" fill="white"/>
363
+ </svg>
364
+ </div>
365
+ <div id="win-close" class="win-ctrl">
366
+ <svg id="win-close" width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
367
+ <path d="M14.8906 0.550781C15.1016 0.339844 15.4883 0.339844 15.6992 0.550781C15.9102 0.761719 15.9102 1.14844 15.6992 1.35938L8.77344 8.25L15.6992 15.1758C15.9102 15.3867 15.9102 15.7734 15.6992 15.9844C15.4883 16.1953 15.1016 16.1953 14.8906 15.9844L8 9.05859L1.07422 15.9844C0.863281 16.1953 0.476562 16.1953 0.265625 15.9844C0.0546875 15.7734 0.0546875 15.3867 0.265625 15.1758L7.19141 8.25L0.265625 1.35938C0.0546875 1.14844 0.0546875 0.761719 0.265625 0.550781C0.476562 0.339844 0.863281 0.339844 1.07422 0.550781L8 7.47656L14.8906 0.550781Z" fill="white"/>
368
+ </svg>
369
+ </div>
370
+ </div>
371
+ <script type="module">
372
+ document.querySelector('#bar').addEventListener('dblclick', async (evt) => {
373
+ if (evt.target !== evt.currentTarget) return
374
+ if (await Pear.Window.self.isMaximized()) await Pear.Window.self.restore()
375
+ else await Pear.Window.self.maximize()
376
+ })
377
+ if (process.platform === 'win32') {
378
+ document.querySelector('#win-min').addEventListener('click', async () => { await Pear.Window.self.minimize() })
379
+ document.querySelector('#win-max').addEventListener('click', async () => {
380
+ await Pear.Window.self.maximize()
381
+ document.querySelector('#win-ctrl').classList.add('max')
382
+ })
383
+ document.querySelector('#win-restore').addEventListener('click', async () => {
384
+ await Pear.Window.self.restore()
385
+ document.querySelector('#win-ctrl').classList.remove('max')
386
+ })
387
+ }
388
+ </script>
389
+ </div>
390
+ <decal-report>
391
+ <span slot="headline"></span>
392
+ <span slot="tagline"></span>
393
+ <span slot="cta"></span>
394
+ <template id="decal-report-tmpl">
395
+ <style>
396
+ #status {
397
+ display: none;
398
+ color: white;
399
+ padding: 2em;
400
+ }
401
+ #headline {
402
+ font-weight: 700;
403
+ font-family: 'overpass';
404
+ font-size: 2em;
405
+ margin-top: 20vh;
406
+ margin-bottom: 0.6em;
407
+ }
408
+ #tagline {
409
+ font-family: 'Open Sans';
410
+ font-size: .875em;
411
+ font-weight: 400;
412
+ margin-bottom: 2em;
413
+ }
414
+ #cta {
415
+ font-family: 'Open Sans';
416
+ cursor: pointer;
417
+ border-radius: 8px;
418
+ height: 44px;
419
+ box-sizing: border-box;
420
+ display: inline-block;
421
+ font-weight: 700;
422
+ font-size: 14px;
423
+ padding: 28px;
424
+ margin-top: 24px;
425
+ color: rgba(29, 110, 83, 1);
426
+ background: #95E6CB;
427
+ backdrop-filter: blur(48px);
428
+ width: auto;
429
+ line-height: 0;
430
+ border: none;
431
+ outline: none;
432
+ letter-spacing: 0.6px;
433
+ }
434
+ </style>
435
+ <div id="status">
436
+ <div id="headline">
437
+ <slot name="headline">HEADLINE</slot>
438
+ </div>
439
+
440
+ <div id="tagline">
441
+ <slot name="tagline">TAGLINE</slot>
442
+ </div>
443
+
444
+ <button id="cta"><slot name="cta">CTA</slot></button>
445
+ </div>
446
+ </template>
447
+ <script type="module">
448
+ customElements.define('decal-report', class extends HTMLElement {
449
+ ipc = DECAL.ipc
450
+ status = null
451
+ static get observedAttributes() { return ['show', 'action'] }
452
+ attributeChangedCallback(name, prior, value) {
453
+ if (name === 'show') {
454
+ if (value) this.shadowRoot.querySelector('#status').style.display = 'block'
455
+ else this.shadowRoot.querySelector('#status').style.display = ''
456
+ }
457
+ if (name === 'action') {
458
+ const cta = this.shadowRoot.querySelector('#cta')
459
+ const code = (value === 'reload') ? 64 : 0
460
+ const handler = (value === 'quit' || value === 'reload') ?
461
+ () => Pear.exit(code) :
462
+ () => {}
463
+ cta.addEventListener('click', handler)
464
+ }
465
+ }
466
+ show (type) { this.setAttribute("show", type) }
467
+ hide () { this.setAttribute("show", "") }
468
+ async subscribe (ipc, loader) {
469
+ this.ipc = ipc
470
+ for await (const report of ipc.reports()) {
471
+ this.status = report
472
+ this.gid = Pear.Window.self.id
473
+ loader.hide()
474
+ const { type, headline, tagline, cta, key, encrypted } = this.status
475
+ if (type === 'update' && this.status.version?.force) {
476
+ await loader.updating(this.status.version)
477
+ continue
478
+ }
479
+
480
+ if (type === 'permissionRequired') {
481
+ const permitDialog = encrypted ? document.createElement('password-dialog') : document.createElement('trust-dialog')
482
+ const ctrl = document.createElement('pear-ctrl')
483
+ ctrl.dataset.maximizable = false
484
+ document.documentElement.classList.add('dialog')
485
+ document.body.appendChild(ctrl)
486
+
487
+ const trustOpts = {
488
+ header: 'Unknown app',
489
+ p1: 'This application has not been run before:',
490
+ p2: 'Type <span>TRUST</span> to continue if you know it is safe to open and to prevent showing this dialog again. <span> Only open trusted sources.</span>',
491
+ placeholder: 'Type TRUST to confirm'
492
+ }
493
+
494
+ const passwordOpts = {
495
+ header: 'Encrypted app',
496
+ p1: 'This application is encrypted:',
497
+ p2: 'Type the password',
498
+ placeholder: 'Password'
499
+ }
500
+
501
+ permitDialog.options = encrypted ? passwordOpts : trustOpts
502
+ document.body.appendChild(permitDialog)
503
+
504
+ const hypercoreid = DECAL['hypercore-id-encoding']
505
+ const z32 = hypercoreid.encode(key)
506
+ permitDialog.setLink(`pear://${z32}`)
507
+ permitDialog.key = key
508
+ permitDialog.ipc = ipc
509
+ await this.ipc.setSize({ id: this.gid, width: 560, height: 450 })
510
+ await this.ipc.detachMainView({ id: this.gid })
511
+ continue
512
+ }
513
+
514
+ this.querySelector('[slot="headline"]').innerHTML = headline.content
515
+ this.querySelector('[slot="tagline"]').innerHTML = tagline.content
516
+ this.querySelector('[slot="cta"]').innerHTML = cta.content
517
+ this.setAttribute("action", cta.action)
518
+ await this.ipc.detachMainView({ id: this.gid })
519
+ const winctrl = document.getElementById('windows-controls')
520
+ if (!winctrl) {
521
+ const div = document.createElement('div')
522
+ div.id = 'windows-controls'
523
+ div.appendChild(document.createElement('pear-ctrl'))
524
+ document.body.prepend(div)
525
+ }
526
+ this.show(type)
527
+ await this.ipc.detachMainView({ id: this.gid })
528
+ }
529
+ }
530
+
531
+ constructor () {
532
+ super()
533
+ this.ipc = DECAL.ipc
534
+ const template = document.querySelector('#decal-report-tmpl').content
535
+ this.attachShadow({mode: 'open'}).appendChild(template.cloneNode(true))
536
+ }
537
+ })
538
+ </script>
539
+ </decal-report>
540
+ <decal-loader>
541
+ <span slot="update-length"></span>
542
+ <span slot="when"></span>
543
+ <template id="decal-loader-tmpl">
544
+ <style>
545
+ #loader {
546
+ display: none;
547
+ }
548
+
549
+ #loader {
550
+ position: absolute;
551
+ left: 0;
552
+ right: 0;
553
+ width: 100%;
554
+ text-align: center;
555
+ top: 50%;
556
+ transform: translateY(-50%);
557
+ }
558
+
559
+ #logo {
560
+ animation: beat 1.1s infinite normal;
561
+ transform-origin: center;
562
+ display: block;
563
+ margin: auto;
564
+ }
565
+
566
+ @keyframes beat {
567
+ 15% {
568
+ transform: scale(1.12);
569
+ }
570
+
571
+ 30% {
572
+ transform: scale(1.0);
573
+ }
574
+
575
+ 43% {
576
+ transform: scale(1.05);
577
+ }
578
+
579
+ 67% {
580
+ transform: scale(1.0);
581
+ }
582
+ }
583
+
584
+ #update {
585
+ display: none;
586
+ margin: 0 auto;
587
+ margin-bottom: -4em;
588
+ color: #fff;
589
+ padding: 8px;
590
+ position: fixed;
591
+ bottom: 0;
592
+ left: 0;
593
+ font-size: 0.9em;
594
+ padding-bottom: 6em;
595
+ padding-left: 3em;
596
+ width: 50%;
597
+ min-width: 600px;
598
+ transform: translateY(140%);
599
+ }
600
+ #update h2 {
601
+ margin-bottom: .5rem;
602
+ }
603
+ #update th {
604
+ font-weight: bold;
605
+ text-align: left;
606
+ }
607
+ #update td:first-child {
608
+ text-align: right;
609
+ }
610
+ #update td:nth-child(2) {
611
+ text-align: left;
612
+ }
613
+ </style>
614
+ <div id="loader">
615
+ <svg id="logo" width="102" height="146" viewBox="0 0 102 146" fill="none" xmlns="http://www.w3.org/2000/svg">
616
+ <g clip-path="url(#clip0_8912_10861)">
617
+ <path d="M47.4056 0.838379H54.5943V15.0361H47.4056V0.838379Z" fill="#B0D944"/>
618
+ <path d="M43.8113 19.5305V22.406H36.6226V26.0004H65.3774V22.406H58.1887V16.8347H51V19.5305H43.8113Z" fill="#B0D944"/>
619
+ <path d="M72.5662 27.7974H51V30.4931H29.4339V36.963H72.5662V27.7974Z" fill="#B0D944"/>
620
+ <path d="M79.7548 38.7593H51V41.455H22.2451V47.9249H79.7548V38.7593Z" fill="#B0D944"/>
621
+ <path d="M79.7548 49.7219H51V52.4177H22.2451V58.8875H79.7548V49.7219Z" fill="#B0D944"/>
622
+ <path d="M86.9436 60.6846H51V63.3803H15.0565V69.8502H86.9436V60.6846Z" fill="#B0D944"/>
623
+ <path d="M86.9436 71.6481H51V74.3438H15.0565V80.8137H86.9436V71.6481Z" fill="#B0D944"/>
624
+ <path d="M94.1323 82.61H51V85.3058H7.86774V91.7756H94.1323V82.61Z" fill="#B0D944"/>
625
+ <path d="M101.321 93.5726H51V96.2684H0.679016V102.738H101.321V93.5726Z" fill="#B0D944"/>
626
+ <path d="M101.321 104.536H51V107.232H0.679016V113.702H101.321V104.536Z" fill="#B0D944"/>
627
+ <path d="M101.321 115.499H51V118.195H0.679016V124.664H101.321V115.499Z" fill="#B0D944"/>
628
+ <path d="M86.9436 126.461H51V129.156H15.0565V135.626H86.9436V126.461Z" fill="#B0D944"/>
629
+ <path d="M72.5662 137.424H51V140.12H29.4339V144.613H72.5662V137.424Z" fill="#B0D944"/>
630
+ </g>
631
+ <defs>
632
+ <clipPath id="clip0_8912_10861">
633
+ <rect width="100.642" height="145.571" fill="white" transform="translate(0.679016 0.214233)"/>
634
+ </clipPath>
635
+ </defs>
636
+ </svg>
637
+ <table id="update">
638
+ <tr>
639
+ <th colspan="2" style="color: #95E6CB;">Auto Updating to <slot name="update-length"></slot>...</th>
640
+ </tr>
641
+ <tr id=hint>
642
+ <td colspan="2" style="text-align:left!important;text-indent:.2em">Application(s) will restart <slot name="when">after update</slot></th>
643
+ </tr>
644
+ </table>
645
+ </div>
646
+ </template>
647
+ <script type="module">
648
+ customElements.define('decal-loader', class extends HTMLElement {
649
+ #updating = false
650
+ async updating ({ force, key, length, fork, current }) {
651
+ if (!force) return
652
+ this.#updating = true
653
+ this.querySelector('[slot="update-length"]').innerHTML = length
654
+ this.shadowRoot.querySelector('#update').style.display = 'block'
655
+ this.show()
656
+ await this.ipc.detachMainView({ id: Pear.Window.self.id })
657
+ }
658
+ show () {
659
+ this.shadowRoot.querySelector('#loader').style.display = 'block'
660
+ document.getElementById('bar').style.display = 'block'
661
+ }
662
+ hide () {
663
+ this.shadowRoot.querySelector('#loader').style.display = 'none'
664
+ document.getElementById('bar').style.display = 'none'
665
+ }
666
+ async restart () {
667
+ let countdown = 3000
668
+ while (countdown > 0) {
669
+ this.querySelector('[slot="when"]').innerText = `in ${countdown/1000} seconds`
670
+ await new Promise((resolve) => setTimeout(resolve, 1000))
671
+ countdown -= 1000
672
+ }
673
+ this.querySelector('[slot="when"]').innerText = 'as of now!'
674
+ await this.ipc.restart()
675
+ }
676
+ async start (ipc, config) {
677
+ this.show()
678
+ this.ipc = ipc
679
+ await ipc.afterViewLoaded({ id: Pear.Window.self.id })
680
+ if (this.#updating) return true
681
+ this.hide()
682
+ const ctrl = document.querySelector('#window-controls pear-ctrl')
683
+ ctrl.remove()
684
+ document.getElementById('window-controls').remove()
685
+ await ctrl.closing
686
+ await ipc.attachMainView({ id: Pear.Window.self.id })
687
+ }
688
+ constructor () {
689
+ super()
690
+ const template = document.querySelector('#decal-loader-tmpl').content
691
+ this.attachShadow({mode: 'open'}).appendChild(template.cloneNode(true))
692
+ }
693
+ })
694
+ </script>
695
+ </decal-loader>
696
+ <script type="module">
697
+ const { message, messages } = Pear
698
+ const ipc = DECAL.ipc
699
+ const loader = document.querySelector('decal-loader')
700
+ const report = document.querySelector('decal-report')
701
+ report.subscribe(ipc, loader)
702
+
703
+ process.on('exit', (code) => {
704
+ const actuallyARefresh = code === undefined
705
+ if (actuallyARefresh) return
706
+ Pear.exit(code)
707
+ })
708
+
709
+ async function start () {
710
+
711
+ const win32CloseListener = process.platform === 'win32' ? () => Pear.exit(0) : null
712
+ if (process.platform === 'win32') document.querySelector('#win-close').addEventListener('click', win32CloseListener)
713
+ let config = Pear.config
714
+
715
+ if (process.platform === 'win32') {
716
+ const close = document.querySelector('#win-close')
717
+ close.removeEventListener('click', win32CloseListener)
718
+ close.addEventListener('click', async () => {
719
+ await Pear.Window.self.close()
720
+ })
721
+ }
722
+
723
+
724
+ const updating = await loader.start(ipc, config)
725
+
726
+ if (updating) return
727
+
728
+ }
729
+
730
+ start().catch(console.error)
731
+ </script>
732
+ </body>
733
+ </html>