@eclipse-docks/extension-pwa 0.7.93 → 0.7.95

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/dist/index.js CHANGED
@@ -10,7 +10,7 @@ extensionRegistry.registerExtension({
10
10
  id: pkg.name,
11
11
  name: t.EXT_PWA_NAME,
12
12
  description: t.EXT_PWA_DESC,
13
- loader: () => import("./pwa-extension-Ceo_RWeH.js"),
13
+ loader: () => import("./pwa-extension-BaUCo7Mu.js"),
14
14
  icon: "download"
15
15
  });
16
16
  //#endregion
@@ -10,6 +10,7 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
10
10
  this.updateAvailable = false;
11
11
  this.updateProgress = 0;
12
12
  this.updateProgressVisible = false;
13
+ this.updateCurrentFile = "";
13
14
  this.pendingReload = false;
14
15
  this.registration = null;
15
16
  this.periodicInterval = null;
@@ -27,6 +28,7 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
27
28
  const ratio = Math.max(0, Math.min(1, data.completed / data.total));
28
29
  this.updateProgress = ratio;
29
30
  this.updateProgressVisible = !this.updateAvailable && !data.done && ratio < 1;
31
+ this.updateCurrentFile = this.toDisplayFileName(data.currentFile);
30
32
  };
31
33
  this.onUpdateFound = () => {
32
34
  const registration = this.registration;
@@ -37,6 +39,7 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
37
39
  if (navigator.serviceWorker.controller) {
38
40
  this.updateProgress = 0;
39
41
  this.updateProgressVisible = true;
42
+ this.updateCurrentFile = "";
40
43
  }
41
44
  const signal = this.attachAbort?.signal;
42
45
  if (!signal) return;
@@ -44,6 +47,7 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
44
47
  if (installing.state === "redundant") {
45
48
  this.updateProgress = 0;
46
49
  this.updateProgressVisible = false;
50
+ this.updateCurrentFile = "";
47
51
  return;
48
52
  }
49
53
  if (installing.state !== "installed") return;
@@ -52,6 +56,15 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
52
56
  }, { signal });
53
57
  };
54
58
  }
59
+ toDisplayFileName(pathname) {
60
+ if (!pathname) return "";
61
+ const normalized = pathname.split("?")[0].split("#")[0];
62
+ const raw = normalized.split("/").filter(Boolean).at(-1) ?? normalized;
63
+ if (!raw) return "";
64
+ const decoded = decodeURIComponent(raw);
65
+ if (decoded.length <= 32) return decoded;
66
+ return `${decoded.slice(0, 29)}...`;
67
+ }
55
68
  connectedCallback() {
56
69
  super.connectedCallback();
57
70
  if (!("serviceWorker" in navigator)) return;
@@ -100,6 +113,7 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
100
113
  this.updateAvailable = false;
101
114
  this.updateProgress = 0;
102
115
  this.updateProgressVisible = false;
116
+ this.updateCurrentFile = "";
103
117
  }
104
118
  /**
105
119
  * Show only when a new worker is waiting and an older version is already controlling
@@ -112,6 +126,7 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
112
126
  if (this.updateAvailable) {
113
127
  this.updateProgress = 1;
114
128
  this.updateProgressVisible = false;
129
+ this.updateCurrentFile = "";
115
130
  }
116
131
  }
117
132
  attach(registration) {
@@ -141,16 +156,26 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
141
156
  window.location.reload();
142
157
  }
143
158
  render() {
144
- if (this.updateProgressVisible && !this.updateAvailable) return html`
159
+ if (this.updateProgressVisible && !this.updateAvailable) {
160
+ const percent = Math.round(this.updateProgress * 100);
161
+ return html`
145
162
  <div
146
- style="display: inline-flex; align-items: center; gap: 0.5rem; min-width: 170px;"
163
+ style="display: inline-flex; align-items: center; gap: 0.5rem; min-width: 210px;"
147
164
  title="Downloading the latest update..."
148
165
  aria-label="Downloading the latest update"
149
166
  >
150
- <wa-progress-bar value=${Math.round(this.updateProgress * 100)}></wa-progress-bar>
151
- <span style="font-size: 0.8rem; opacity: 0.8;">${Math.round(this.updateProgress * 100)}%</span>
167
+ <wa-progress-bar value=${percent}></wa-progress-bar>
168
+ <div style="display: inline-flex; flex-direction: column; gap: 0.1rem; max-width: 140px;">
169
+ <span style="font-size: 0.75rem; opacity: 0.8;">${percent}%</span>
170
+ ${this.updateCurrentFile ? html`<span
171
+ style="font-size: 0.7rem; opacity: 0.7; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
172
+ title=${this.updateCurrentFile}
173
+ >${this.updateCurrentFile}</span
174
+ >` : html``}
175
+ </div>
152
176
  </div>
153
177
  `;
178
+ }
154
179
  if (!this.updateAvailable) return html``;
155
180
  return html`
156
181
  <wa-animation
@@ -180,6 +205,7 @@ var DocksSwUpdateIndicator = class DocksSwUpdateIndicator extends DocksElement {
180
205
  _decorate([state()], DocksSwUpdateIndicator.prototype, "updateAvailable", void 0);
181
206
  _decorate([state()], DocksSwUpdateIndicator.prototype, "updateProgress", void 0);
182
207
  _decorate([state()], DocksSwUpdateIndicator.prototype, "updateProgressVisible", void 0);
208
+ _decorate([state()], DocksSwUpdateIndicator.prototype, "updateCurrentFile", void 0);
183
209
  DocksSwUpdateIndicator = _decorate([customElement("docks-sw-update-indicator")], DocksSwUpdateIndicator);
184
210
  //#endregion
185
211
  //#region src/pwa-install-button.ts
@@ -259,4 +285,4 @@ contributionRegistry.registerContribution(TOOLBAR_MAIN_CENTER, {
259
285
  });
260
286
  //#endregion
261
287
 
262
- //# sourceMappingURL=pwa-extension-Ceo_RWeH.js.map
288
+ //# sourceMappingURL=pwa-extension-BaUCo7Mu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pwa-extension-BaUCo7Mu.js","names":[],"sources":["../src/sw-update-indicator.ts","../src/pwa-install-button.ts","../src/pwa-extension.ts"],"sourcesContent":["import { html } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\n\nimport { DocksElement } from '@eclipse-docks/core';\n\ntype SwUpdateProgressMessage = {\n type: 'SW_UPDATE_PROGRESS';\n completed: number;\n total: number;\n done?: boolean;\n currentFile?: string;\n};\n\n/**\n * Shows a toolbar icon when a new service worker is waiting (vite-plugin-pwa injectManifest + SKIP_WAITING message).\n * Hidden when there is no update or when service workers are unavailable.\n *\n * Safe when the extension loads late: attaches via `getRegistration()`, reads `registration.waiting`,\n * and subscribes to `updatefound` for subsequent updates.\n */\n@customElement('docks-sw-update-indicator')\nexport class DocksSwUpdateIndicator extends DocksElement {\n @state()\n private updateAvailable = false;\n @state()\n private updateProgress = 0;\n @state()\n private updateProgressVisible = false;\n @state()\n private updateCurrentFile = '';\n\n private pendingReload = false;\n private registration: ServiceWorkerRegistration | null = null;\n private periodicInterval: number | null = null;\n private pollInterval: number | null = null;\n private pollAttempts = 0;\n private attachAbort: AbortController | null = null;\n\n private readonly onControllerChange = (): void => {\n if (!this.pendingReload) {\n return;\n }\n window.location.reload();\n };\n\n private readonly onServiceWorkerMessage = (event: MessageEvent): void => {\n const data = event.data as SwUpdateProgressMessage | undefined;\n if (!data || data.type !== 'SW_UPDATE_PROGRESS') {\n return;\n }\n if (data.total <= 0) {\n return;\n }\n const ratio = Math.max(0, Math.min(1, data.completed / data.total));\n this.updateProgress = ratio;\n this.updateProgressVisible = !this.updateAvailable && !data.done && ratio < 1;\n this.updateCurrentFile = this.toDisplayFileName(data.currentFile);\n };\n\n private toDisplayFileName(pathname?: string): string {\n if (!pathname) {\n return '';\n }\n const normalized = pathname.split('?')[0].split('#')[0];\n const raw = normalized.split('/').filter(Boolean).at(-1) ?? normalized;\n if (!raw) {\n return '';\n }\n const decoded = decodeURIComponent(raw);\n if (decoded.length <= 32) {\n return decoded;\n }\n return `${decoded.slice(0, 29)}...`;\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n if (!('serviceWorker' in navigator)) {\n return;\n }\n navigator.serviceWorker.addEventListener('controllerchange', this.onControllerChange);\n navigator.serviceWorker.addEventListener('message', this.onServiceWorkerMessage);\n void this.findOrAttachRegistration();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n navigator.serviceWorker.removeEventListener('controllerchange', this.onControllerChange);\n navigator.serviceWorker.removeEventListener('message', this.onServiceWorkerMessage);\n this.teardownAttachment();\n }\n\n private async findOrAttachRegistration(): Promise<void> {\n const existing = await navigator.serviceWorker.getRegistration();\n if (existing) {\n this.attach(existing);\n return;\n }\n\n this.pollInterval = window.setInterval(async () => {\n this.pollAttempts += 1;\n const reg = await navigator.serviceWorker.getRegistration();\n if (reg) {\n this.clearPoll();\n this.attach(reg);\n return;\n }\n if (this.pollAttempts >= 30) {\n this.clearPoll();\n }\n }, 1000);\n }\n\n private clearPoll(): void {\n if (this.pollInterval !== null) {\n window.clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n }\n\n private teardownAttachment(): void {\n this.clearPoll();\n if (this.periodicInterval !== null) {\n window.clearInterval(this.periodicInterval);\n this.periodicInterval = null;\n }\n this.attachAbort?.abort();\n this.attachAbort = null;\n this.registration = null;\n this.updateAvailable = false;\n this.updateProgress = 0;\n this.updateProgressVisible = false;\n this.updateCurrentFile = '';\n }\n\n /**\n * Show only when a new worker is waiting and an older version is already controlling\n * the page (real update). Avoid treating the first install as an “update”.\n */\n private syncUpdateState(registration: ServiceWorkerRegistration): void {\n const waiting = Boolean(registration.waiting);\n const hasActiveController = Boolean(navigator.serviceWorker.controller);\n this.updateAvailable = waiting && hasActiveController;\n if (this.updateAvailable) {\n this.updateProgress = 1;\n this.updateProgressVisible = false;\n this.updateCurrentFile = '';\n }\n }\n\n private readonly onUpdateFound = (): void => {\n const registration = this.registration;\n if (!registration) {\n return;\n }\n // Fast installs (common in standalone PWA): worker may already be `waiting` when\n // this runs, with `installing` already null — still an update if a controller exists.\n this.syncUpdateState(registration);\n\n const installing = registration.installing;\n if (!installing) {\n return;\n }\n if (navigator.serviceWorker.controller) {\n this.updateProgress = 0;\n this.updateProgressVisible = true;\n this.updateCurrentFile = '';\n }\n const signal = this.attachAbort?.signal;\n if (!signal) {\n return;\n }\n installing.addEventListener(\n 'statechange',\n () => {\n if (installing.state === 'redundant') {\n this.updateProgress = 0;\n this.updateProgressVisible = false;\n this.updateCurrentFile = '';\n return;\n }\n if (installing.state !== 'installed') {\n return;\n }\n if (!navigator.serviceWorker.controller) {\n return;\n }\n this.syncUpdateState(registration);\n },\n { signal },\n );\n };\n\n private attach(registration: ServiceWorkerRegistration): void {\n if (this.registration === registration) {\n return;\n }\n this.attachAbort?.abort();\n this.attachAbort = new AbortController();\n const signal = this.attachAbort.signal;\n\n this.registration = registration;\n this.syncUpdateState(registration);\n\n registration.addEventListener('updatefound', this.onUpdateFound, { signal });\n\n void registration.update().catch(() => {});\n this.periodicInterval = window.setInterval(() => {\n void registration.update().catch(() => {});\n }, 60 * 60 * 1000);\n\n queueMicrotask(() => {\n if (this.registration !== registration) {\n return;\n }\n this.syncUpdateState(registration);\n });\n }\n\n private onActivateClick(): void {\n const w = this.registration?.waiting;\n if (w) {\n this.pendingReload = true;\n w.postMessage({ type: 'SKIP_WAITING' });\n return;\n }\n window.location.reload();\n }\n\n protected render() {\n if (this.updateProgressVisible && !this.updateAvailable) {\n const percent = Math.round(this.updateProgress * 100);\n return html`\n <div\n style=\"display: inline-flex; align-items: center; gap: 0.5rem; min-width: 210px;\"\n title=\"Downloading the latest update...\"\n aria-label=\"Downloading the latest update\"\n >\n <wa-progress-bar value=${percent}></wa-progress-bar>\n <div style=\"display: inline-flex; flex-direction: column; gap: 0.1rem; max-width: 140px;\">\n <span style=\"font-size: 0.75rem; opacity: 0.8;\">${percent}%</span>\n ${this.updateCurrentFile\n ? html`<span\n style=\"font-size: 0.7rem; opacity: 0.7; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\"\n title=${this.updateCurrentFile}\n >${this.updateCurrentFile}</span\n >`\n : html``}\n </div>\n </div>\n `;\n }\n\n if (!this.updateAvailable) {\n return html``;\n }\n\n return html`\n <wa-animation\n style=\"display: inline-flex; align-items: center;\"\n name=\"zoomIn\"\n duration=\"1400\"\n easing=\"ease-out\"\n iterations=\"Infinity\"\n ?play=${true}\n >\n <wa-button\n appearance=\"plain\"\n title=\"A new version is available. Click to reload.\"\n aria-label=\"A new version is available. Reload to update.\"\n @click=${this.onActivateClick}\n >\n <wa-icon\n name=\"arrows-rotate\"\n label=\"\"\n style=\"color: var(--wa-color-success-fill-loud)\"\n ></wa-icon>\n </wa-button>\n </wa-animation>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'docks-sw-update-indicator': DocksSwUpdateIndicator;\n }\n}\n","import { html } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\n\nimport { DocksElement } from '@eclipse-docks/core';\n\nimport {\n clearInstallPromptCapture,\n peekInstallPrompt,\n PWA_INSTALL_PROMPT_AVAILABLE,\n type PwaInstallPromptEvent,\n} from './install-prompt-capture';\n\nfunction isRunningAsInstalledPwa(): boolean {\n return (\n window.matchMedia('(display-mode: standalone)').matches ||\n window.matchMedia('(display-mode: window-controls-overlay)').matches ||\n (window.navigator as Navigator & { standalone?: boolean }).standalone === true\n );\n}\n\n@customElement('docks-pwa-install')\nexport class DocksPwaInstall extends DocksElement {\n @state()\n private showInstall = false;\n\n private deferredPrompt: PwaInstallPromptEvent | null = null;\n\n private readonly onPromptAvailable = (): void => {\n this.applyCapturedPrompt();\n };\n\n private readonly onAppInstalled = (): void => {\n clearInstallPromptCapture();\n this.deferredPrompt = null;\n this.showInstall = false;\n };\n\n private applyCapturedPrompt(): void {\n const p = peekInstallPrompt();\n if (!p) {\n return;\n }\n this.deferredPrompt = p;\n this.showInstall = true;\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n if (isRunningAsInstalledPwa()) {\n return;\n }\n this.applyCapturedPrompt();\n window.addEventListener(PWA_INSTALL_PROMPT_AVAILABLE, this.onPromptAvailable);\n window.addEventListener('appinstalled', this.onAppInstalled);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener(PWA_INSTALL_PROMPT_AVAILABLE, this.onPromptAvailable);\n window.removeEventListener('appinstalled', this.onAppInstalled);\n }\n\n private async onInstallClick(): Promise<void> {\n const promptEvent = this.deferredPrompt;\n if (!promptEvent) {\n return;\n }\n await promptEvent.prompt();\n await promptEvent.userChoice.catch(() => {});\n clearInstallPromptCapture();\n this.deferredPrompt = null;\n this.showInstall = false;\n }\n\n protected render() {\n if (!this.showInstall) {\n return html``;\n }\n\n return html`\n <wa-button\n appearance=\"plain\"\n title=\"Install this app on your device\"\n aria-label=\"Install app\"\n @click=${() => void this.onInstallClick()}\n >\n <wa-icon name=\"download\" label=\"\"></wa-icon>\n </wa-button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'docks-pwa-install': DocksPwaInstall;\n }\n}\n","import { contributionRegistry, type HTMLContribution, TOOLBAR_MAIN_CENTER } from '@eclipse-docks/core';\nimport './sw-update-indicator';\nimport './pwa-install-button';\n\nconst TOOLBAR_SW_UPDATE = 'toolbar.swUpdate';\nconst TOOLBAR_PWA_INSTALL = 'toolbar.pwaInstall';\n\ncontributionRegistry.registerContribution(TOOLBAR_MAIN_CENTER, {\n name: TOOLBAR_SW_UPDATE,\n label: 'App update',\n component: `<docks-sw-update-indicator></docks-sw-update-indicator>`,\n} as HTMLContribution);\n\ncontributionRegistry.registerContribution(TOOLBAR_MAIN_CENTER, {\n name: TOOLBAR_PWA_INSTALL,\n label: 'Install app',\n component: `<docks-pwa-install></docks-pwa-install>`,\n} as HTMLContribution);\n"],"mappings":"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"}
@@ -10,6 +10,7 @@ export declare class DocksSwUpdateIndicator extends DocksElement {
10
10
  private updateAvailable;
11
11
  private updateProgress;
12
12
  private updateProgressVisible;
13
+ private updateCurrentFile;
13
14
  private pendingReload;
14
15
  private registration;
15
16
  private periodicInterval;
@@ -18,6 +19,7 @@ export declare class DocksSwUpdateIndicator extends DocksElement {
18
19
  private attachAbort;
19
20
  private readonly onControllerChange;
20
21
  private readonly onServiceWorkerMessage;
22
+ private toDisplayFileName;
21
23
  connectedCallback(): void;
22
24
  disconnectedCallback(): void;
23
25
  private findOrAttachRegistration;
@@ -1 +1 @@
1
- {"version":3,"file":"sw-update-indicator.d.ts","sourceRoot":"","sources":["../src/sw-update-indicator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AASnD;;;;;;GAMG;AACH,qBACa,sBAAuB,SAAQ,YAAY;IAEtD,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,cAAc,CAAK;IAE3B,OAAO,CAAC,qBAAqB,CAAS;IAEtC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,WAAW,CAAgC;IAEnD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAKjC;IAEF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAWrC;IAEF,iBAAiB,IAAI,IAAI;IAUzB,oBAAoB,IAAI,IAAI;YAOd,wBAAwB;IAqBtC,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,kBAAkB;IAc1B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAuC5B;IAEF,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,eAAe;IAUvB,SAAS,CAAC,MAAM;CA0CjB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,2BAA2B,EAAE,sBAAsB,CAAC;KACrD;CACF"}
1
+ {"version":3,"file":"sw-update-indicator.d.ts","sourceRoot":"","sources":["../src/sw-update-indicator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAUnD;;;;;;GAMG;AACH,qBACa,sBAAuB,SAAQ,YAAY;IAEtD,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,cAAc,CAAK;IAE3B,OAAO,CAAC,qBAAqB,CAAS;IAEtC,OAAO,CAAC,iBAAiB,CAAM;IAE/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,WAAW,CAAgC;IAEnD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAKjC;IAEF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAYrC;IAEF,OAAO,CAAC,iBAAiB;IAgBzB,iBAAiB,IAAI,IAAI;IAUzB,oBAAoB,IAAI,IAAI;YAOd,wBAAwB;IAqBtC,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,kBAAkB;IAe1B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAyC5B;IAEF,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,eAAe;IAUvB,SAAS,CAAC,MAAM;CAoDjB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,2BAA2B,EAAE,sBAAsB,CAAC;KACrD;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eclipse-docks/extension-pwa",
3
- "version": "0.7.93",
3
+ "version": "0.7.95",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
package/src/sw.ts CHANGED
@@ -15,6 +15,7 @@ type SwUpdateProgressMessage = {
15
15
  completed: number;
16
16
  total: number;
17
17
  done: boolean;
18
+ currentFile?: string;
18
19
  };
19
20
 
20
21
  const PRECACHE_MANIFEST = self.__WB_MANIFEST;
@@ -26,13 +27,14 @@ const updateProgressFetched = new Set<string>();
26
27
  async function broadcastUpdateProgress(
27
28
  completed: number,
28
29
  total: number,
30
+ currentFile?: string,
29
31
  done = false,
30
32
  ): Promise<void> {
31
33
  if (total <= 0) {
32
34
  return;
33
35
  }
34
36
  const clients = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
35
- const message: SwUpdateProgressMessage = { type: 'SW_UPDATE_PROGRESS', completed, total, done };
37
+ const message: SwUpdateProgressMessage = { type: 'SW_UPDATE_PROGRESS', completed, total, done, currentFile };
36
38
  clients.forEach((client) => client.postMessage(message));
37
39
  }
38
40
 
@@ -71,7 +73,8 @@ addPlugins([
71
73
  !updateProgressFetched.has(request.url)
72
74
  ) {
73
75
  updateProgressFetched.add(request.url);
74
- void broadcastUpdateProgress(updateProgressFetched.size, PRECACHE_TOTAL);
76
+ const currentFile = new URL(request.url).pathname;
77
+ void broadcastUpdateProgress(updateProgressFetched.size, PRECACHE_TOTAL, currentFile);
75
78
  }
76
79
  return withCoopCoep(response);
77
80
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"pwa-extension-Ceo_RWeH.js","names":[],"sources":["../src/sw-update-indicator.ts","../src/pwa-install-button.ts","../src/pwa-extension.ts"],"sourcesContent":["import { html } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\n\nimport { DocksElement } from '@eclipse-docks/core';\n\ntype SwUpdateProgressMessage = {\n type: 'SW_UPDATE_PROGRESS';\n completed: number;\n total: number;\n done?: boolean;\n};\n\n/**\n * Shows a toolbar icon when a new service worker is waiting (vite-plugin-pwa injectManifest + SKIP_WAITING message).\n * Hidden when there is no update or when service workers are unavailable.\n *\n * Safe when the extension loads late: attaches via `getRegistration()`, reads `registration.waiting`,\n * and subscribes to `updatefound` for subsequent updates.\n */\n@customElement('docks-sw-update-indicator')\nexport class DocksSwUpdateIndicator extends DocksElement {\n @state()\n private updateAvailable = false;\n @state()\n private updateProgress = 0;\n @state()\n private updateProgressVisible = false;\n\n private pendingReload = false;\n private registration: ServiceWorkerRegistration | null = null;\n private periodicInterval: number | null = null;\n private pollInterval: number | null = null;\n private pollAttempts = 0;\n private attachAbort: AbortController | null = null;\n\n private readonly onControllerChange = (): void => {\n if (!this.pendingReload) {\n return;\n }\n window.location.reload();\n };\n\n private readonly onServiceWorkerMessage = (event: MessageEvent): void => {\n const data = event.data as SwUpdateProgressMessage | undefined;\n if (!data || data.type !== 'SW_UPDATE_PROGRESS') {\n return;\n }\n if (data.total <= 0) {\n return;\n }\n const ratio = Math.max(0, Math.min(1, data.completed / data.total));\n this.updateProgress = ratio;\n this.updateProgressVisible = !this.updateAvailable && !data.done && ratio < 1;\n };\n\n connectedCallback(): void {\n super.connectedCallback();\n if (!('serviceWorker' in navigator)) {\n return;\n }\n navigator.serviceWorker.addEventListener('controllerchange', this.onControllerChange);\n navigator.serviceWorker.addEventListener('message', this.onServiceWorkerMessage);\n void this.findOrAttachRegistration();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n navigator.serviceWorker.removeEventListener('controllerchange', this.onControllerChange);\n navigator.serviceWorker.removeEventListener('message', this.onServiceWorkerMessage);\n this.teardownAttachment();\n }\n\n private async findOrAttachRegistration(): Promise<void> {\n const existing = await navigator.serviceWorker.getRegistration();\n if (existing) {\n this.attach(existing);\n return;\n }\n\n this.pollInterval = window.setInterval(async () => {\n this.pollAttempts += 1;\n const reg = await navigator.serviceWorker.getRegistration();\n if (reg) {\n this.clearPoll();\n this.attach(reg);\n return;\n }\n if (this.pollAttempts >= 30) {\n this.clearPoll();\n }\n }, 1000);\n }\n\n private clearPoll(): void {\n if (this.pollInterval !== null) {\n window.clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n }\n\n private teardownAttachment(): void {\n this.clearPoll();\n if (this.periodicInterval !== null) {\n window.clearInterval(this.periodicInterval);\n this.periodicInterval = null;\n }\n this.attachAbort?.abort();\n this.attachAbort = null;\n this.registration = null;\n this.updateAvailable = false;\n this.updateProgress = 0;\n this.updateProgressVisible = false;\n }\n\n /**\n * Show only when a new worker is waiting and an older version is already controlling\n * the page (real update). Avoid treating the first install as an “update”.\n */\n private syncUpdateState(registration: ServiceWorkerRegistration): void {\n const waiting = Boolean(registration.waiting);\n const hasActiveController = Boolean(navigator.serviceWorker.controller);\n this.updateAvailable = waiting && hasActiveController;\n if (this.updateAvailable) {\n this.updateProgress = 1;\n this.updateProgressVisible = false;\n }\n }\n\n private readonly onUpdateFound = (): void => {\n const registration = this.registration;\n if (!registration) {\n return;\n }\n // Fast installs (common in standalone PWA): worker may already be `waiting` when\n // this runs, with `installing` already null — still an update if a controller exists.\n this.syncUpdateState(registration);\n\n const installing = registration.installing;\n if (!installing) {\n return;\n }\n if (navigator.serviceWorker.controller) {\n this.updateProgress = 0;\n this.updateProgressVisible = true;\n }\n const signal = this.attachAbort?.signal;\n if (!signal) {\n return;\n }\n installing.addEventListener(\n 'statechange',\n () => {\n if (installing.state === 'redundant') {\n this.updateProgress = 0;\n this.updateProgressVisible = false;\n return;\n }\n if (installing.state !== 'installed') {\n return;\n }\n if (!navigator.serviceWorker.controller) {\n return;\n }\n this.syncUpdateState(registration);\n },\n { signal },\n );\n };\n\n private attach(registration: ServiceWorkerRegistration): void {\n if (this.registration === registration) {\n return;\n }\n this.attachAbort?.abort();\n this.attachAbort = new AbortController();\n const signal = this.attachAbort.signal;\n\n this.registration = registration;\n this.syncUpdateState(registration);\n\n registration.addEventListener('updatefound', this.onUpdateFound, { signal });\n\n void registration.update().catch(() => {});\n this.periodicInterval = window.setInterval(() => {\n void registration.update().catch(() => {});\n }, 60 * 60 * 1000);\n\n queueMicrotask(() => {\n if (this.registration !== registration) {\n return;\n }\n this.syncUpdateState(registration);\n });\n }\n\n private onActivateClick(): void {\n const w = this.registration?.waiting;\n if (w) {\n this.pendingReload = true;\n w.postMessage({ type: 'SKIP_WAITING' });\n return;\n }\n window.location.reload();\n }\n\n protected render() {\n if (this.updateProgressVisible && !this.updateAvailable) {\n return html`\n <div\n style=\"display: inline-flex; align-items: center; gap: 0.5rem; min-width: 170px;\"\n title=\"Downloading the latest update...\"\n aria-label=\"Downloading the latest update\"\n >\n <wa-progress-bar value=${Math.round(this.updateProgress * 100)}></wa-progress-bar>\n <span style=\"font-size: 0.8rem; opacity: 0.8;\">${Math.round(this.updateProgress * 100)}%</span>\n </div>\n `;\n }\n\n if (!this.updateAvailable) {\n return html``;\n }\n\n return html`\n <wa-animation\n style=\"display: inline-flex; align-items: center;\"\n name=\"zoomIn\"\n duration=\"1400\"\n easing=\"ease-out\"\n iterations=\"Infinity\"\n ?play=${true}\n >\n <wa-button\n appearance=\"plain\"\n title=\"A new version is available. Click to reload.\"\n aria-label=\"A new version is available. Reload to update.\"\n @click=${this.onActivateClick}\n >\n <wa-icon\n name=\"arrows-rotate\"\n label=\"\"\n style=\"color: var(--wa-color-success-fill-loud)\"\n ></wa-icon>\n </wa-button>\n </wa-animation>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'docks-sw-update-indicator': DocksSwUpdateIndicator;\n }\n}\n","import { html } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\n\nimport { DocksElement } from '@eclipse-docks/core';\n\nimport {\n clearInstallPromptCapture,\n peekInstallPrompt,\n PWA_INSTALL_PROMPT_AVAILABLE,\n type PwaInstallPromptEvent,\n} from './install-prompt-capture';\n\nfunction isRunningAsInstalledPwa(): boolean {\n return (\n window.matchMedia('(display-mode: standalone)').matches ||\n window.matchMedia('(display-mode: window-controls-overlay)').matches ||\n (window.navigator as Navigator & { standalone?: boolean }).standalone === true\n );\n}\n\n@customElement('docks-pwa-install')\nexport class DocksPwaInstall extends DocksElement {\n @state()\n private showInstall = false;\n\n private deferredPrompt: PwaInstallPromptEvent | null = null;\n\n private readonly onPromptAvailable = (): void => {\n this.applyCapturedPrompt();\n };\n\n private readonly onAppInstalled = (): void => {\n clearInstallPromptCapture();\n this.deferredPrompt = null;\n this.showInstall = false;\n };\n\n private applyCapturedPrompt(): void {\n const p = peekInstallPrompt();\n if (!p) {\n return;\n }\n this.deferredPrompt = p;\n this.showInstall = true;\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n if (isRunningAsInstalledPwa()) {\n return;\n }\n this.applyCapturedPrompt();\n window.addEventListener(PWA_INSTALL_PROMPT_AVAILABLE, this.onPromptAvailable);\n window.addEventListener('appinstalled', this.onAppInstalled);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener(PWA_INSTALL_PROMPT_AVAILABLE, this.onPromptAvailable);\n window.removeEventListener('appinstalled', this.onAppInstalled);\n }\n\n private async onInstallClick(): Promise<void> {\n const promptEvent = this.deferredPrompt;\n if (!promptEvent) {\n return;\n }\n await promptEvent.prompt();\n await promptEvent.userChoice.catch(() => {});\n clearInstallPromptCapture();\n this.deferredPrompt = null;\n this.showInstall = false;\n }\n\n protected render() {\n if (!this.showInstall) {\n return html``;\n }\n\n return html`\n <wa-button\n appearance=\"plain\"\n title=\"Install this app on your device\"\n aria-label=\"Install app\"\n @click=${() => void this.onInstallClick()}\n >\n <wa-icon name=\"download\" label=\"\"></wa-icon>\n </wa-button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'docks-pwa-install': DocksPwaInstall;\n }\n}\n","import { contributionRegistry, type HTMLContribution, TOOLBAR_MAIN_CENTER } from '@eclipse-docks/core';\nimport './sw-update-indicator';\nimport './pwa-install-button';\n\nconst TOOLBAR_SW_UPDATE = 'toolbar.swUpdate';\nconst TOOLBAR_PWA_INSTALL = 'toolbar.pwaInstall';\n\ncontributionRegistry.registerContribution(TOOLBAR_MAIN_CENTER, {\n name: TOOLBAR_SW_UPDATE,\n label: 'App update',\n component: `<docks-sw-update-indicator></docks-sw-update-indicator>`,\n} as HTMLContribution);\n\ncontributionRegistry.registerContribution(TOOLBAR_MAIN_CENTER, {\n name: TOOLBAR_PWA_INSTALL,\n label: 'Install app',\n component: `<docks-pwa-install></docks-pwa-install>`,\n} as HTMLContribution);\n"],"mappings":"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"}