@64mb/freelens-power 1.2.0 → 1.2.2

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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/out/main/common/dashboard-picker.js +4 -4
  3. package/out/main/common/dashboard-picker.js.map +1 -1
  4. package/out/main/common/dashboard-readiness.js +17 -47
  5. package/out/main/common/dashboard-readiness.js.map +1 -1
  6. package/out/main/common/dashboard-selector.js +40 -6
  7. package/out/main/common/dashboard-selector.js.map +1 -1
  8. package/out/main/common/dashboard.js +1 -0
  9. package/out/main/common/dashboard.js.map +1 -1
  10. package/out/main/common/settings-export.js +1 -1
  11. package/out/main/common/settings-export.js.map +1 -1
  12. package/out/main/common/store/dashboard-preferences-store.js +2 -1
  13. package/out/main/common/store/dashboard-preferences-store.js.map +1 -1
  14. package/out/main/dashboard-picker.js +1 -4
  15. package/out/main/dashboard-picker.js.map +1 -1
  16. package/out/main/dashboard-preload.js +110 -0
  17. package/out/main/dashboard-preload.js.map +1 -0
  18. package/out/main/dashboard-views.js +216 -61
  19. package/out/main/dashboard-views.js.map +1 -1
  20. package/out/main/dock-icon.js +25 -10
  21. package/out/main/dock-icon.js.map +1 -1
  22. package/out/main/index.js +2 -0
  23. package/out/main/index.js.map +1 -1
  24. package/out/main/splash-accent.js +84 -0
  25. package/out/main/splash-accent.js.map +1 -0
  26. package/out/renderer/appearance.js +6 -2
  27. package/out/renderer/appearance.js.map +1 -1
  28. package/out/renderer/dashboard.js +69 -15
  29. package/out/renderer/dashboard.js.map +1 -1
  30. package/out/renderer/preferences/dashboard-preferences.js +16 -1
  31. package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
  32. package/out/renderer/preferences/settings-modal.js.map +1 -1
  33. package/out/renderer/src/common/dashboard-picker.js.map +1 -1
  34. package/out/renderer/src/common/dashboard-selector.js.map +1 -1
  35. package/out/renderer/src/common/dashboard.js +1 -0
  36. package/out/renderer/src/common/dashboard.js.map +1 -1
  37. package/out/renderer/src/common/settings-export.js.map +1 -1
  38. package/out/renderer/src/common/store/dashboard-preferences-store.js +2 -1
  39. package/out/renderer/src/common/store/dashboard-preferences-store.js.map +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-preload.js","names":[],"sources":["../../src/main/dashboard-preload.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n// this preload runs before website scripts without requiring modules in the sandbox\n(() => {\n const properties = [\n [\"opacity\", \"0\"],\n [\"pointer-events\", \"none\"],\n ] as const;\n const masked = new Map<HTMLElement, Map<string, { value: string; priority: string }>>();\n const overlay = document.createElement(\"freelens-power-loading\");\n const style = (element: HTMLElement, values: Record<string, string>) => {\n for (const [property, value] of Object.entries(values)) element.style.setProperty(property, value, \"important\");\n };\n const dark = window.matchMedia?.(\"(prefers-color-scheme: dark)\").matches ?? false;\n style(overlay, {\n position: \"fixed\",\n inset: \"0\",\n display: \"block\",\n \"z-index\": \"2147483647\",\n \"pointer-events\": \"all\",\n opacity: \"1\",\n background: dark ? \"#202124\" : \"#ffffff\",\n padding: \"20px\",\n \"box-sizing\": \"border-box\",\n overflow: \"hidden\",\n });\n const shadow = overlay.attachShadow({ mode: \"open\" });\n for (let index = 0; index < 4; index++) {\n const bar = document.createElement(\"div\");\n style(bar, {\n display: \"block\",\n background: dark ? \"#393b40\" : \"#e8e9eb\",\n \"border-radius\": \"6px\",\n width: index === 0 ? \"45%\" : \"100%\",\n height: index === 0 ? \"20px\" : index === 3 ? \"calc(100vh - 148px)\" : \"12px\",\n \"min-height\": index === 3 ? \"30px\" : \"0\",\n \"margin-bottom\": index === 0 ? \"20px\" : \"12px\",\n });\n shadow.append(bar);\n }\n const animations: Animation[] = [];\n let preparing = false;\n const restore = (element: HTMLElement) => {\n for (const [property, value] of properties) {\n const saved = masked.get(element)?.get(property);\n if (\n saved &&\n element.style.getPropertyValue(property) === value &&\n element.style.getPropertyPriority(property) === \"important\"\n ) {\n if (saved.value) element.style.setProperty(property, saved.value, saved.priority);\n else element.style.removeProperty(property);\n }\n }\n masked.delete(element);\n };\n const apply = () => {\n if (!preparing || !document.documentElement) return;\n if (overlay.parentElement !== document.documentElement) document.documentElement.append(overlay);\n for (const element of masked.keys()) {\n if (element.parentElement !== document.documentElement) restore(element);\n }\n for (const element of Array.from(document.documentElement.children)) {\n if (!(element instanceof HTMLElement) || element === overlay) continue;\n let previous = masked.get(element);\n if (!previous) {\n previous = new Map();\n masked.set(element, previous);\n }\n for (const [property, value] of properties) {\n const current = element.style.getPropertyValue(property);\n const priority = element.style.getPropertyPriority(property);\n if (!previous.has(property) || current !== value || priority !== \"important\") {\n previous.set(property, { value: current, priority });\n element.style.setProperty(property, value, \"important\");\n }\n }\n }\n };\n const observer = new MutationObserver(apply);\n const prepare = () => {\n if (!preparing && !(window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches ?? false)) {\n for (const element of Array.from(shadow.children)) {\n if (element instanceof HTMLElement && typeof element.animate === \"function\") {\n try {\n animations.push(\n element.animate([{ opacity: 0.55 }, { opacity: 1 }], {\n duration: 1200,\n direction: \"alternate\",\n iterations: Infinity,\n }),\n );\n } catch {}\n }\n }\n }\n preparing = true;\n observer.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: [\"style\"] });\n apply();\n };\n const release = () => {\n preparing = false;\n for (const animation of animations) animation.cancel();\n animations.length = 0;\n observer.disconnect();\n for (const element of masked.keys()) restore(element);\n overlay.remove();\n };\n document.addEventListener(\"freelens-power:dashboard:preparing\", prepare);\n document.addEventListener(\"freelens-power:dashboard:prepared\", release);\n window.addEventListener(\"pagehide\", () => {\n release();\n document.removeEventListener(\"freelens-power:dashboard:preparing\", prepare);\n document.removeEventListener(\"freelens-power:dashboard:prepared\", release);\n });\n prepare();\n})();\n\nexport {};\n"],"mappings":"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"}
@@ -14,6 +14,7 @@ var reloadChannel = "freelens-power:dashboard-views:reload";
14
14
  var clearChannel = "freelens-power:dashboard-views:clear";
15
15
  var dashboardPath = "/extension/freelens-power/dashboard";
16
16
  var websiteStyles = "* { scrollbar-width: none !important; } *::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }";
17
+ var expandedWebsiteStyles = "* { scrollbar-width: auto !important; } *::-webkit-scrollbar { display: revert !important; width: revert !important; height: revert !important; }";
17
18
  function rendererUrl(value, dashboardOnly = false) {
18
19
  try {
19
20
  const url = new URL(value);
@@ -48,19 +49,58 @@ function rectangle(value) {
48
49
  height: rect.height
49
50
  };
50
51
  }
51
- async function waitForReadiness(contents, network, selector, current) {
52
+ function createDashboardSkeleton() {
53
+ const view = new electron.View();
54
+ view.setBackgroundColor(electron.nativeTheme.shouldUseDarkColors ? "#202124" : "#ffffff");
55
+ const bars = Array.from({ length: 4 }, () => {
56
+ const bar = new electron.View();
57
+ bar.setBackgroundColor(electron.nativeTheme.shouldUseDarkColors ? "#393b40" : "#e8e9eb");
58
+ bar.setBorderRadius(6);
59
+ view.addChildView(bar);
60
+ return bar;
61
+ });
62
+ return {
63
+ view,
64
+ layout: (width, height) => {
65
+ view.setBounds({
66
+ x: 0,
67
+ y: 0,
68
+ width,
69
+ height
70
+ });
71
+ bars.forEach((bar, index) => {
72
+ bar.setBounds({
73
+ x: 20,
74
+ y: index === 0 ? 20 : 60 + (index - 1) * 24,
75
+ width: Math.max(1, Math.round((width - 40) * (index === 0 ? .45 : 1))),
76
+ height: index === 0 ? 20 : index === 3 ? Math.max(30, height - 128) : 12
77
+ });
78
+ });
79
+ }
80
+ };
81
+ }
82
+ async function applyWebsiteStyles(card) {
83
+ const contents = card.view.webContents;
84
+ const generation = (card.websiteStylesGeneration ?? 0) + 1;
85
+ card.websiteStylesGeneration = generation;
86
+ const previous = card.websiteStylesKey;
87
+ card.websiteStylesKey = void 0;
88
+ if (contents.isDestroyed()) return;
89
+ if (previous) await contents.removeInsertedCSS(previous).catch(() => {});
90
+ if (card.websiteStylesGeneration !== generation || contents.isDestroyed()) return;
91
+ const key = await contents.insertCSS(card.scrollbars ? expandedWebsiteStyles : websiteStyles, { cssOrigin: "user" }).catch(() => void 0);
92
+ if (!key) return;
93
+ if (card.websiteStylesGeneration === generation && !contents.isDestroyed()) card.websiteStylesKey = key;
94
+ else if (!contents.isDestroyed()) await contents.removeInsertedCSS(key).catch(() => {});
95
+ }
96
+ async function waitForReadiness(contents, _network, selector, current) {
52
97
  let deadline;
53
98
  try {
54
99
  await Promise.race([(async () => {
55
100
  if (!current()) return;
56
- await network.ready();
101
+ await contents.mainFrame.executeJavaScript(require_dashboard_selector.dashboardSelectorScript(selector())).catch(() => {});
57
102
  if (!current()) return;
58
- await contents.executeJavaScriptInIsolatedWorld(1004, [{ code: require_dashboard_selector.dashboardSelectorScript(selector()) }]).catch(() => {});
59
- do {
60
- await network.wait();
61
- if (!current()) return;
62
- await contents.executeJavaScriptInIsolatedWorld(1004, [{ code: require_dashboard_readiness.dashboardReadinessScript(selector()) }]);
63
- } while (current() && !network.quiet());
103
+ await contents.mainFrame.executeJavaScript(require_dashboard_readiness.dashboardReadinessScript(selector()));
64
104
  })().catch(() => new Promise((resolve) => setTimeout(resolve, 200))), new Promise((resolve) => {
65
105
  deadline = setTimeout(resolve, 8500);
66
106
  })]);
@@ -68,6 +108,21 @@ async function waitForReadiness(contents, network, selector, current) {
68
108
  clearTimeout(deadline);
69
109
  }
70
110
  }
111
+ async function prepareDashboardPage(contents) {
112
+ if (contents.isDestroyed()) return;
113
+ let deadline;
114
+ try {
115
+ await Promise.race([contents.mainFrame.executeJavaScript("document.dispatchEvent(new Event(\"freelens-power:dashboard:preparing\"))").catch(() => {}), new Promise((resolve) => {
116
+ deadline = setTimeout(resolve, 250);
117
+ })]);
118
+ } finally {
119
+ clearTimeout(deadline);
120
+ }
121
+ }
122
+ function releaseDashboardPage(contents) {
123
+ if (contents.isDestroyed()) return Promise.resolve();
124
+ return contents.mainFrame.executeJavaScript("document.dispatchEvent(new Event(\"freelens-power:dashboard:prepared\"))").catch(() => {});
125
+ }
71
126
  function startDashboardViews() {
72
127
  const websiteSession = electron.session.fromPartition("persist:freelens-power-websites");
73
128
  websiteSession.setPermissionRequestHandler((_contents, _permission, callback) => callback(false));
@@ -78,9 +133,11 @@ function startDashboardViews() {
78
133
  const card = owner.cards.get(id);
79
134
  if (!card) return;
80
135
  owner.cards.delete(id);
136
+ card.nativeNavigation?.fail();
81
137
  card.stopPreparation?.();
138
+ card.websiteStylesGeneration = (card.websiteStylesGeneration ?? 0) + 1;
82
139
  card.faviconRequest?.abort();
83
- clearInterval(card.refreshTimer);
140
+ clearTimeout(card.refreshTimer);
84
141
  card.floatingPreparation?.stop();
85
142
  card.floating?.destroy();
86
143
  card.floating = void 0;
@@ -129,10 +186,12 @@ function startDashboardViews() {
129
186
  });
130
187
  }
131
188
  };
132
- const reload = (card, configuredUrl, initial = false) => {
189
+ const reload = (card, configuredUrl, initial = false, navigation) => {
133
190
  const current = card.view.webContents.getURL();
134
191
  const target = !initial && card.url === configuredUrl && webUrl(current) ? current : configuredUrl;
192
+ const prepareExisting = !initial && !navigation && !card.loading && !card.failed && webUrl(current);
135
193
  card.faviconRequest?.abort();
194
+ if (!navigation) card.nativeNavigation?.fail();
136
195
  card.url = configuredUrl;
137
196
  card.failed = false;
138
197
  card.loading = true;
@@ -142,23 +201,27 @@ function startDashboardViews() {
142
201
  if (backgroundThrottling) contents.setBackgroundThrottling(false);
143
202
  const network = require_dashboard_network.observeDashboardNetwork(card.view.webContents);
144
203
  let stopped = false;
204
+ let documentReady;
145
205
  const stopPreparation = () => {
146
206
  if (stopped) return;
147
207
  stopped = true;
148
208
  network.dispose();
149
209
  contents.removeListener("destroyed", stopPreparation);
210
+ if (documentReady) contents.removeListener("dom-ready", documentReady);
150
211
  if (!contents.isDestroyed() && backgroundThrottling && !contents.getBackgroundThrottling()) contents.setBackgroundThrottling(backgroundThrottling);
151
212
  if (card.stopPreparation === stopPreparation) card.stopPreparation = void 0;
152
213
  };
153
214
  card.stopPreparation = stopPreparation;
154
215
  contents.once("destroyed", stopPreparation);
155
216
  const generation = ++card.generation;
156
- card.container.setVisible(false);
217
+ card.skeleton.view.setVisible(true);
218
+ card.container.setVisible(card.visible && !card.floating);
157
219
  publishLoading(card);
158
220
  const settled = (failed) => {
159
221
  if (card.generation !== generation || card.view.webContents.isDestroyed()) return;
160
222
  card.failed = failed;
161
223
  card.loading = false;
224
+ card.skeleton.view.setVisible(false);
162
225
  for (const [sender, owner] of owners) {
163
226
  if (![...owner.cards.values()].includes(card)) continue;
164
227
  card.container.setVisible(card.visible && !card.floating && !failed && dashboardUrl(sender.getURL()));
@@ -167,19 +230,33 @@ function startDashboardViews() {
167
230
  };
168
231
  const load = () => {
169
232
  if (card.generation !== generation || card.view.webContents.isDestroyed() || ![...owners.values()].some((owner) => [...owner.cards.values()].includes(card))) return Promise.resolve();
170
- return card.view.webContents.loadURL(target);
233
+ if (navigation) return navigation;
234
+ const navigate = () => {
235
+ if (card.generation !== generation || contents.isDestroyed()) return Promise.resolve();
236
+ card.requestedNavigation = true;
237
+ return contents.loadURL(target).finally(() => {
238
+ if (card.generation === generation) card.requestedNavigation = false;
239
+ });
240
+ };
241
+ return prepareExisting ? prepareDashboardPage(contents).then(navigate) : navigate();
171
242
  };
172
- card.pending = load().then(async () => {
243
+ const ready = new Promise((resolve) => {
244
+ documentReady = resolve;
245
+ contents.once("dom-ready", documentReady);
246
+ });
247
+ card.pending = Promise.race([load(), ready]).then(async () => {
173
248
  let preparing = true;
174
249
  const current = () => preparing && card.generation === generation && !card.view.webContents.isDestroyed() && [...owners.values()].some((owner) => [...owner.cards.values()].includes(card));
175
250
  try {
176
251
  await waitForReadiness(contents, network, () => card.selector, current);
252
+ if (current()) await releaseDashboardPage(contents);
177
253
  } finally {
178
254
  preparing = false;
179
255
  stopPreparation();
180
256
  }
181
257
  settled(false);
182
258
  }, () => {
259
+ if (card.generation === generation) releaseDashboardPage(contents);
183
260
  stopPreparation();
184
261
  settled(true);
185
262
  });
@@ -187,7 +264,7 @@ function startDashboardViews() {
187
264
  };
188
265
  const applySelector = (card) => {
189
266
  if (card.view.webContents.isDestroyed()) return Promise.resolve();
190
- return card.view.webContents.executeJavaScriptInIsolatedWorld(1004, [{ code: require_dashboard_selector.dashboardSelectorScript(card.selector) }]).catch(() => {});
267
+ return card.view.webContents.mainFrame.executeJavaScript(require_dashboard_selector.dashboardSelectorScript(card.selector)).catch(() => {});
191
268
  };
192
269
  const refreshTimers = () => {
193
270
  const configured = configuredCards();
@@ -207,24 +284,57 @@ function startDashboardViews() {
207
284
  card.floatingPreparation?.selectorChanged();
208
285
  }
209
286
  if (card.floating && !card.floating.isDestroyed()) card.floating.setTitle(setting.title);
210
- if (!setting.autoRefresh) {
211
- clearInterval(card.refreshTimer);
212
- card.refreshTimer = void 0;
213
- continue;
287
+ if (card.floatingStyles) {
288
+ const scrollbars = setting.size === "l" || setting.size === "xl";
289
+ if (card.floatingStyles.scrollbars !== scrollbars) {
290
+ card.floatingStyles.scrollbars = scrollbars;
291
+ applyWebsiteStyles(card.floatingStyles);
292
+ }
214
293
  }
215
- if (card.refreshTimer) continue;
216
- card.refreshTimer = setInterval(() => {
217
- const latest = configuredCards().find((item) => item.id === id);
218
- if (owners.get(sender) !== owner || card.floating || dashboardUrl(sender.getURL()) || owner.cards.get(id) !== card || !latest?.autoRefresh || latest.url !== card.url || card.view.webContents.isDestroyed() || card.view.webContents.isLoading()) return;
219
- reload(card, latest.url);
220
- }, 6e5);
294
+ const interval = setting.refreshEvery10Seconds ? 1e4 : setting.autoRefresh ? 6e5 : void 0;
295
+ if (card.refreshInterval === interval) continue;
296
+ clearTimeout(card.refreshTimer);
297
+ card.refreshTimer = void 0;
298
+ card.refreshInterval = interval;
299
+ const refreshGeneration = (card.refreshGeneration ?? 0) + 1;
300
+ card.refreshGeneration = refreshGeneration;
301
+ if (!interval) continue;
302
+ const schedule = () => {
303
+ if (owners.get(sender) !== owner || owner.cards.get(id) !== card || card.refreshGeneration !== refreshGeneration) return;
304
+ if (card.loading && card.pending) {
305
+ card.pending.then(schedule);
306
+ return;
307
+ }
308
+ card.refreshTimer = setTimeout(tick, interval);
309
+ };
310
+ const tick = async () => {
311
+ try {
312
+ const latest = configuredCards().find((item) => item.id === id);
313
+ if (owners.get(sender) !== owner || owner.cards.get(id) !== card || !latest || latest.url !== card.url || (latest.refreshEvery10Seconds ? 1e4 : latest.autoRefresh ? 6e5 : void 0) !== interval || card.view.webContents.isDestroyed()) return;
314
+ if (card.loading) {
315
+ await card.pending;
316
+ return;
317
+ }
318
+ if (latest.refreshEvery10Seconds) {
319
+ if (card.floating) {
320
+ if (card.floating.isDestroyed() || card.floatingPreparation?.isPreparing()) return;
321
+ await Promise.all([reload(card, latest.url), card.floatingPreparation?.refresh()]);
322
+ } else if (card.visible && dashboardUrl(sender.getURL())) await reload(card, latest.url);
323
+ } else if (!card.floating && !dashboardUrl(sender.getURL())) await reload(card, latest.url);
324
+ } finally {
325
+ schedule();
326
+ }
327
+ };
328
+ schedule();
221
329
  }
222
330
  };
223
- const stopSettingsReaction = require__global_external_mobx.reaction(() => JSON.stringify(configuredCards().map(({ id, title, url, autoRefresh, selector }) => ({
331
+ const stopSettingsReaction = require__global_external_mobx.reaction(() => JSON.stringify(configuredCards().map(({ id, title, url, size, autoRefresh, refreshEvery10Seconds, selector }) => ({
224
332
  id,
225
333
  title,
226
334
  url,
335
+ size,
227
336
  autoRefresh,
337
+ refreshEvery10Seconds,
228
338
  selector
229
339
  }))), refreshTimers);
230
340
  const trusted = (event) => event.senderFrame === event.sender.mainFrame && dashboardUrl(event.senderFrame?.url ?? "") && dashboardUrl(event.sender.getURL());
@@ -293,12 +403,10 @@ function startDashboardViews() {
293
403
  contextIsolation: true,
294
404
  sandbox: true,
295
405
  webSecurity: true,
296
- partition: "persist:freelens-power-websites"
406
+ partition: "persist:freelens-power-websites",
407
+ preload: (0, node_path.join)(__dirname, "dashboard-preload.js")
297
408
  } });
298
409
  view.setBackgroundColor("#00000000");
299
- view.webContents.on("did-finish-load", () => {
300
- if (!view.webContents.isDestroyed()) view.webContents.insertCSS(websiteStyles, { cssOrigin: "user" }).catch(() => {});
301
- });
302
410
  view.webContents.setWindowOpenHandler(({ url }) => {
303
411
  if (webUrl(url)) electron.shell.openExternal(url).catch(() => {});
304
412
  return { action: "deny" };
@@ -312,9 +420,12 @@ function startDashboardViews() {
312
420
  if (!webUrl(event.url)) event.preventDefault();
313
421
  });
314
422
  container.addChildView(view);
423
+ const skeleton = createDashboardSkeleton();
424
+ container.addChildView(skeleton.view);
315
425
  window.contentView.addChildView(container);
316
426
  card = {
317
427
  container,
428
+ skeleton,
318
429
  view,
319
430
  url: "",
320
431
  failed: false,
@@ -329,7 +440,38 @@ function startDashboardViews() {
329
440
  event.sender.send("freelens-power:dashboard-escape");
330
441
  });
331
442
  created.selector = configured.selector;
332
- view.webContents.on("did-finish-load", () => applySelector(created));
443
+ view.webContents.on("did-start-navigation", (_event, _url, inPlace, mainFrame) => {
444
+ if (!mainFrame) return;
445
+ if (created.requestedNavigation) {
446
+ created.requestedNavigation = false;
447
+ return;
448
+ }
449
+ created.nativeNavigation?.fail();
450
+ const navigation = new Promise((resolve, reject) => {
451
+ created.nativeNavigation = {
452
+ finish: () => {
453
+ created.nativeNavigation = void 0;
454
+ resolve();
455
+ },
456
+ fail: () => {
457
+ created.nativeNavigation = void 0;
458
+ reject(/* @__PURE__ */ new Error("dashboard navigation failed"));
459
+ }
460
+ };
461
+ });
462
+ if (inPlace) view.webContents.mainFrame.executeJavaScript("document.dispatchEvent(new Event(\"freelens-power:dashboard:preparing\"))").catch(() => {});
463
+ reload(created, created.url, false, navigation);
464
+ if (inPlace) created.nativeNavigation?.finish();
465
+ });
466
+ view.webContents.on("did-finish-load", () => {
467
+ applyWebsiteStyles(created);
468
+ applySelector(created);
469
+ created.nativeNavigation?.finish();
470
+ });
471
+ view.webContents.on("did-fail-load", (_event, code, _description, _url, mainFrame) => {
472
+ if (!mainFrame || code === -3 && view.webContents.isLoading()) return;
473
+ created.nativeNavigation?.fail();
474
+ });
333
475
  view.webContents.on("page-favicon-updated", (_event, favicons) => {
334
476
  const favicon = favicons.find((value) => {
335
477
  if (!webUrl(value)) return false;
@@ -411,8 +553,12 @@ function startDashboardViews() {
411
553
  })().catch(() => {}).finally(() => clearTimeout(timeout));
412
554
  });
413
555
  }
556
+ if (card.scrollbars !== (item.scrollbars === true)) {
557
+ card.scrollbars = item.scrollbars === true;
558
+ applyWebsiteStyles(card);
559
+ }
414
560
  card.visible = true;
415
- card.container.setVisible(!card.loading && !card.failed && card.url === configured.url);
561
+ card.container.setVisible(!card.failed && (card.loading || card.url === configured.url));
416
562
  const zoom = event.sender.getZoomFactor();
417
563
  card.container.setBorderRadius(Math.round(12 * zoom));
418
564
  card.view.setBorderRadius(Math.round(12 * zoom));
@@ -423,6 +569,7 @@ function startDashboardViews() {
423
569
  height: Math.round(rect.height * zoom)
424
570
  });
425
571
  card.container.setBounds(scale(clip));
572
+ card.skeleton.layout(Math.round(clip.width * zoom), Math.round(clip.height * zoom));
426
573
  card.view.setBounds(scale({
427
574
  x: bounds.x - clip.x,
428
575
  y: bounds.y - clip.y,
@@ -483,7 +630,8 @@ function startDashboardViews() {
483
630
  contextIsolation: true,
484
631
  sandbox: true,
485
632
  webSecurity: true,
486
- partition: "persist:freelens-power-websites"
633
+ partition: "persist:freelens-power-websites",
634
+ preload: (0, node_path.join)(__dirname, "dashboard-preload.js")
487
635
  }
488
636
  });
489
637
  floating.setMenu(null);
@@ -492,6 +640,11 @@ function startDashboardViews() {
492
640
  card.floating = floating;
493
641
  card.container.setVisible(false);
494
642
  const contents = floating.webContents;
643
+ const floatingStyles = {
644
+ view: { webContents: contents },
645
+ scrollbars: configured.size === "l" || configured.size === "xl"
646
+ };
647
+ card.floatingStyles = floatingStyles;
495
648
  contents.setWindowOpenHandler(({ url }) => {
496
649
  if (webUrl(url)) electron.shell.openExternal(url).catch(() => {});
497
650
  return { action: "deny" };
@@ -506,40 +659,22 @@ function startDashboardViews() {
506
659
  });
507
660
  contents.on("page-title-updated", (event) => event.preventDefault());
508
661
  contents.on("will-prevent-unload", (event) => event.preventDefault());
509
- const skeleton = new electron.View();
510
- skeleton.setBackgroundColor(electron.nativeTheme.shouldUseDarkColors ? "#202124" : "#ffffff");
511
- const bars = Array.from({ length: 4 }, () => {
512
- const bar = new electron.View();
513
- bar.setBackgroundColor(electron.nativeTheme.shouldUseDarkColors ? "#393b40" : "#e8e9eb");
514
- bar.setBorderRadius(6);
515
- skeleton.addChildView(bar);
516
- return bar;
517
- });
662
+ const loadingSkeleton = createDashboardSkeleton();
663
+ const skeleton = loadingSkeleton.view;
518
664
  const layoutSkeleton = () => {
519
665
  const { width, height } = floating.getContentBounds();
520
- skeleton.setBounds({
521
- x: 0,
522
- y: 0,
523
- width,
524
- height
525
- });
526
- bars.forEach((bar, index) => {
527
- bar.setBounds({
528
- x: 20,
529
- y: index === 0 ? 20 : 60 + (index - 1) * 24,
530
- width: Math.max(1, Math.round((width - 40) * (index === 0 ? .45 : 1))),
531
- height: index === 0 ? 20 : index === 3 ? Math.max(30, height - 128) : 12
532
- });
533
- });
666
+ loadingSkeleton.layout(width, height);
534
667
  };
535
668
  floating.contentView.addChildView(skeleton);
536
669
  floating.on("resize", layoutSkeleton);
537
670
  layoutSkeleton();
538
671
  let generation = 0;
672
+ let floatingPreparing = false;
539
673
  let stopPreparation = () => {};
540
674
  let finishLoad;
541
675
  const begin = () => {
542
676
  stopPreparation();
677
+ floatingPreparing = true;
543
678
  const token = ++generation;
544
679
  const backgroundThrottling = contents.getBackgroundThrottling();
545
680
  if (backgroundThrottling) contents.setBackgroundThrottling(false);
@@ -551,6 +686,7 @@ function startDashboardViews() {
551
686
  stopPreparation = () => {
552
687
  if (!active) return;
553
688
  active = false;
689
+ floatingPreparing = false;
554
690
  network.dispose();
555
691
  if (!contents.isDestroyed() && backgroundThrottling) contents.setBackgroundThrottling(true);
556
692
  };
@@ -559,6 +695,8 @@ function startDashboardViews() {
559
695
  finishing ??= (async () => {
560
696
  await waitForReadiness(contents, network, () => card.selector, current);
561
697
  if (!current()) return false;
698
+ await releaseDashboardPage(contents);
699
+ if (!current()) return false;
562
700
  skeleton.setVisible(false);
563
701
  stop();
564
702
  return false;
@@ -570,6 +708,7 @@ function startDashboardViews() {
570
708
  finish,
571
709
  fail: () => {
572
710
  if (current()) {
711
+ releaseDashboardPage(contents);
573
712
  skeleton.setVisible(false);
574
713
  stop();
575
714
  }
@@ -579,16 +718,26 @@ function startDashboardViews() {
579
718
  const refresh = () => {
580
719
  const preparation = begin();
581
720
  const current = contents.getURL();
582
- return contents.loadURL(webUrl(current) ? current : configured.url).then(() => finishLoad?.() ?? false, () => {
721
+ let documentReady;
722
+ const ready = new Promise((resolve) => {
723
+ documentReady = () => {
724
+ (finishLoad?.() ?? Promise.resolve(false)).then(resolve);
725
+ };
726
+ contents.once("dom-ready", documentReady);
727
+ });
728
+ const loading = prepareDashboardPage(contents).then(() => contents.loadURL(webUrl(current) ? current : configured.url)).then(() => finishLoad?.() ?? false, () => {
583
729
  preparation.fail();
584
730
  return !contents.isDestroyed();
585
731
  });
732
+ return Promise.race([loading, ready]).finally(() => {
733
+ contents.removeListener("dom-ready", documentReady);
734
+ });
586
735
  };
587
736
  card.floatingPreparation = {
588
737
  refresh,
738
+ isPreparing: () => floatingPreparing,
589
739
  selectorChanged: () => {
590
- const preparation = begin();
591
- if (!contents.isLoading()) preparation.finish();
740
+ begin().finish();
592
741
  },
593
742
  stop: () => {
594
743
  ++generation;
@@ -600,14 +749,18 @@ function startDashboardViews() {
600
749
  const preparation = begin();
601
750
  if (inPlace) preparation.finish();
602
751
  });
752
+ contents.on("dom-ready", () => {
753
+ applyWebsiteStyles(floatingStyles);
754
+ if (!contents.isDestroyed()) finishLoad?.();
755
+ });
603
756
  contents.on("did-finish-load", () => {
604
- if (contents.isDestroyed()) return;
605
- contents.insertCSS(websiteStyles, { cssOrigin: "user" }).catch(() => {});
606
- finishLoad?.();
757
+ applyWebsiteStyles(floatingStyles);
758
+ if (!contents.isDestroyed()) finishLoad?.();
607
759
  });
608
760
  contents.on("did-fail-load", (_event, code, _description, _url, mainFrame) => {
609
761
  if (!mainFrame || code === -3) return;
610
762
  stopPreparation();
763
+ releaseDashboardPage(contents);
611
764
  skeleton.setVisible(false);
612
765
  });
613
766
  contents.once("destroyed", () => {
@@ -618,6 +771,8 @@ function startDashboardViews() {
618
771
  if (card.floating !== floating) return;
619
772
  card.floatingPreparation?.stop();
620
773
  card.floatingPreparation = void 0;
774
+ floatingStyles.websiteStylesGeneration = (floatingStyles.websiteStylesGeneration ?? 0) + 1;
775
+ card.floatingStyles = void 0;
621
776
  card.floating = void 0;
622
777
  if (owner.cards.get(id) === card) publishPinned(event.sender, owner);
623
778
  });