@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
package/README.md CHANGED
@@ -9,7 +9,7 @@ Power tools and personal dashboards for Freelens. Built for Freelens 1.x and tes
9
9
  - **Accent themes:** Default, Purple, Blue, Green, Yellow, Orange, Red, and Pink for controls, text links, and the welcome logo
10
10
  - **Cluster icons:** a searchable cluster grid, matching SVG icons, editable labels and colors, live previews, and saved editor settings
11
11
  - **macOS Dock icon:** optionally follows your accent, with a separate toggle to restore the original
12
- - **Power Dashboard:** reorderable XS/S/M/L/XL website cards in a responsive grid, cached pages, loading skeletons, interactive element selection, expansion, pinned floating windows, shared website sessions, and optional background refresh
12
+ - **Power Dashboard:** reorderable XS/S/M/L/XL website cards in a responsive grid, cached pages, loading skeletons, interactive element selection, expansion, pinned floating windows, shared website sessions, per-card 10-second refresh including pinned windows, and optional background refresh
13
13
  - **Monitoring:** optional CPU and memory trends, allocation rings, pinned tooltips, and node/pod charts using metrics-server and Kubernetes APIs
14
14
  - **Resource diagnostics:** CPU/memory consumers grouped by namespace and workload, application event timelines, and evidence-based Pending pod diagnostics
15
15
  - **Local metric history:** opt-in SQLite storage, up to seven days within a 100 MiB budget, with aggregated ranges and explicit collection gaps
@@ -1,12 +1,12 @@
1
1
  //#region src/common/dashboard-picker.ts
2
2
  var DASHBOARD_PICK_ELEMENT = "freelens-power:dashboard:pick-element";
3
- function dashboardPickerAdapter(accent) {
3
+ function dashboardPickerAdapter() {
4
4
  return new Promise((resolve) => {
5
5
  const overlay = document.createElement("div");
6
6
  overlay.style.cssText = "all:initial!important;position:fixed!important;inset:0!important;z-index:2147483647!important;pointer-events:auto!important";
7
7
  const shadow = overlay.attachShadow({ mode: "open" });
8
8
  const outline = document.createElement("div");
9
- outline.style.cssText = `position:fixed;box-sizing:border-box;border:2px solid ${accent};pointer-events:none;display:none`;
9
+ outline.style.cssText = "position:fixed;box-sizing:border-box;border:2px solid #00e5ff;border-radius:6px;box-shadow:0 0 0 1px #111,inset 0 0 0 1px #111;background:rgba(0,229,255,0.08);pointer-events:none;display:none";
10
10
  const toolbar = document.createElement("div");
11
11
  toolbar.style.cssText = "position:fixed;bottom:20px;left:50%;transform:translateX(-50%);padding:12px 16px;background:#202124;color:#fff;border-radius:8px;font:13px system-ui;pointer-events:auto;display:flex;gap:16px;align-items:center";
12
12
  const hint = document.createElement("span");
@@ -122,8 +122,8 @@ function dashboardPickerAdapter(accent) {
122
122
  window.addEventListener("resize", draw, true);
123
123
  });
124
124
  }
125
- function dashboardPickerScript(accent) {
126
- return `(${dashboardPickerAdapter.toString()})(${JSON.stringify(/^#[0-9a-f]{6}$/i.test(accent) ? accent : "#00a7a0")})`;
125
+ function dashboardPickerScript() {
126
+ return `(${dashboardPickerAdapter.toString()})()`;
127
127
  }
128
128
  //#endregion
129
129
  exports.DASHBOARD_PICK_ELEMENT = DASHBOARD_PICK_ELEMENT;
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-picker.js","names":[],"sources":["../../../src/common/dashboard-picker.ts"],"sourcesContent":["export const DASHBOARD_PICK_ELEMENT = \"freelens-power:dashboard:pick-element\";\nexport type DashboardPickerResult = { status: \"selected\"; selector: string } | { status: \"canceled\" | \"error\" };\n\n// this function runs only in an isolated website world with no extension bridge\nexport function dashboardPickerAdapter(accent: string): Promise<string | null> {\n return new Promise((resolve) => {\n const overlay = document.createElement(\"div\");\n overlay.style.cssText =\n \"all:initial!important;position:fixed!important;inset:0!important;z-index:2147483647!important;pointer-events:auto!important\";\n const shadow = overlay.attachShadow({ mode: \"open\" });\n const outline = document.createElement(\"div\");\n outline.style.cssText = `position:fixed;box-sizing:border-box;border:2px solid ${accent};pointer-events:none;display:none`;\n const toolbar = document.createElement(\"div\");\n toolbar.style.cssText =\n \"position:fixed;bottom:20px;left:50%;transform:translateX(-50%);padding:12px 16px;background:#202124;color:#fff;border-radius:8px;font:13px system-ui;pointer-events:auto;display:flex;gap:16px;align-items:center\";\n const hint = document.createElement(\"span\");\n hint.textContent = \"Hover an element, then click to select · Esc to cancel\";\n const cancel = document.createElement(\"button\");\n cancel.textContent = \"Cancel\";\n cancel.style.cssText =\n \"font:inherit;color:inherit;background:transparent;border:1px solid #888;border-radius:4px;padding:4px 10px;cursor:pointer\";\n toolbar.append(hint, cancel);\n shadow.append(outline, toolbar);\n document.documentElement.append(overlay);\n let hovered: Element | undefined;\n const escape = (value: string) => CSS.escape(value);\n function selectorFor(target: Element): string | undefined {\n const matches = (selector: string) => {\n try {\n return (\n selector.length <= 512 &&\n document.querySelectorAll(selector).length === 1 &&\n document.querySelector(selector) === target\n );\n } catch {\n return false;\n }\n };\n if (target.id) {\n const id = `#${escape(target.id)}`;\n if (matches(id)) return id;\n }\n const path: string[] = [];\n let element: Element | null = target;\n for (let depth = 0; element && depth < 64; depth++, element = element.parentElement) {\n const tag = escape(element.localName);\n const siblings = element.parentElement ? Array.from(element.parentElement.children) : [element];\n path.unshift(`${tag}:nth-child(${siblings.indexOf(element) + 1})`);\n const selector = path.join(\" > \");\n if (selector.length > 512) return;\n if (matches(selector)) return selector;\n }\n return undefined;\n }\n function finish(value: string | null) {\n window.removeEventListener(\"pointermove\", move, true);\n window.removeEventListener(\"pointerdown\", block, true);\n window.removeEventListener(\"pointerup\", block, true);\n window.removeEventListener(\"click\", click, true);\n window.removeEventListener(\"auxclick\", block, true);\n window.removeEventListener(\"dblclick\", block, true);\n window.removeEventListener(\"keydown\", key, true);\n window.removeEventListener(\"pagehide\", cancelSelection, true);\n window.removeEventListener(\"scroll\", draw, true);\n window.removeEventListener(\"resize\", draw, true);\n overlay.remove();\n resolve(value);\n }\n function block(event: Event) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n function hit(event: Event): Element | undefined {\n if (event.composedPath().includes(toolbar)) return;\n let target = event.composedPath().find((node) => node instanceof Element);\n if (event.composedPath().includes(overlay) && event instanceof MouseEvent) {\n overlay.style.setProperty(\"pointer-events\", \"none\", \"important\");\n target = document.elementFromPoint(event.clientX, event.clientY) ?? undefined;\n overlay.style.setProperty(\"pointer-events\", \"auto\", \"important\");\n }\n return target instanceof Element && target.getRootNode() === document ? target : undefined;\n }\n function draw() {\n if (!hovered?.isConnected) {\n outline.style.display = \"none\";\n return;\n }\n const rect = hovered.getBoundingClientRect();\n Object.assign(outline.style, {\n display: \"block\",\n left: `${rect.left}px`,\n top: `${rect.top}px`,\n width: `${rect.width}px`,\n height: `${rect.height}px`,\n });\n }\n function move(event: PointerEvent) {\n hovered = hit(event);\n draw();\n }\n function click(event: MouseEvent) {\n const isCancel = event.composedPath().includes(cancel);\n block(event);\n if (isCancel) {\n finish(null);\n return;\n }\n const target = hit(event);\n if (!target) return;\n const selector = selectorFor(target);\n if (selector) finish(selector);\n else hint.textContent = \"This element cannot be selected. Choose a containing element or cancel.\";\n }\n function key(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n block(event);\n finish(null);\n } else block(event);\n }\n function cancelSelection() {\n finish(null);\n }\n window.addEventListener(\"pointermove\", move, true);\n window.addEventListener(\"pointerdown\", block, true);\n window.addEventListener(\"pointerup\", block, true);\n window.addEventListener(\"click\", click, true);\n window.addEventListener(\"auxclick\", block, true);\n window.addEventListener(\"dblclick\", block, true);\n window.addEventListener(\"keydown\", key, true);\n window.addEventListener(\"pagehide\", cancelSelection, true);\n window.addEventListener(\"scroll\", draw, true);\n window.addEventListener(\"resize\", draw, true);\n });\n}\n\nexport function dashboardPickerScript(accent: string): string {\n return `(${dashboardPickerAdapter.toString()})(${JSON.stringify(/^#[0-9a-f]{6}$/i.test(accent) ? accent : \"#00a7a0\")})`;\n}\n"],"mappings":";AAAA,IAAa,yBAAyB;AAItC,SAAgB,uBAAuB,QAAwC;CAC7E,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,MAAM,UACZ;EACF,MAAM,SAAS,QAAQ,aAAa,EAAE,MAAM,OAAO,CAAC;EACpD,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,MAAM,UAAU,yDAAyD,OAAO;EACxF,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,MAAM,UACZ;EACF,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,cAAc;EACnB,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,cAAc;EACrB,OAAO,MAAM,UACX;EACF,QAAQ,OAAO,MAAM,MAAM;EAC3B,OAAO,OAAO,SAAS,OAAO;EAC9B,SAAS,gBAAgB,OAAO,OAAO;EACvC,IAAI;EACJ,MAAM,UAAU,UAAkB,IAAI,OAAO,KAAK;EAClD,SAAS,YAAY,QAAqC;GACxD,MAAM,WAAW,aAAqB;IACpC,IAAI;KACF,OACE,SAAS,UAAU,OACnB,SAAS,iBAAiB,QAAQ,CAAC,CAAC,WAAW,KAC/C,SAAS,cAAc,QAAQ,MAAM;IAEzC,QAAQ;KACN,OAAO;IACT;GACF;GACA,IAAI,OAAO,IAAI;IACb,MAAM,KAAK,IAAI,OAAO,OAAO,EAAE;IAC/B,IAAI,QAAQ,EAAE,GAAG,OAAO;GAC1B;GACA,MAAM,OAAiB,CAAC;GACxB,IAAI,UAA0B;GAC9B,KAAK,IAAI,QAAQ,GAAG,WAAW,QAAQ,IAAI,SAAS,UAAU,QAAQ,eAAe;IACnF,MAAM,MAAM,OAAO,QAAQ,SAAS;IACpC,MAAM,WAAW,QAAQ,gBAAgB,MAAM,KAAK,QAAQ,cAAc,QAAQ,IAAI,CAAC,OAAO;IAC9F,KAAK,QAAQ,GAAG,IAAI,aAAa,SAAS,QAAQ,OAAO,IAAI,EAAE,EAAE;IACjE,MAAM,WAAW,KAAK,KAAK,KAAK;IAChC,IAAI,SAAS,SAAS,KAAK;IAC3B,IAAI,QAAQ,QAAQ,GAAG,OAAO;GAChC;EAEF;EACA,SAAS,OAAO,OAAsB;GACpC,OAAO,oBAAoB,eAAe,MAAM,IAAI;GACpD,OAAO,oBAAoB,eAAe,OAAO,IAAI;GACrD,OAAO,oBAAoB,aAAa,OAAO,IAAI;GACnD,OAAO,oBAAoB,SAAS,OAAO,IAAI;GAC/C,OAAO,oBAAoB,YAAY,OAAO,IAAI;GAClD,OAAO,oBAAoB,YAAY,OAAO,IAAI;GAClD,OAAO,oBAAoB,WAAW,KAAK,IAAI;GAC/C,OAAO,oBAAoB,YAAY,iBAAiB,IAAI;GAC5D,OAAO,oBAAoB,UAAU,MAAM,IAAI;GAC/C,OAAO,oBAAoB,UAAU,MAAM,IAAI;GAC/C,QAAQ,OAAO;GACf,QAAQ,KAAK;EACf;EACA,SAAS,MAAM,OAAc;GAC3B,MAAM,eAAe;GACrB,MAAM,yBAAyB;EACjC;EACA,SAAS,IAAI,OAAmC;GAC9C,IAAI,MAAM,aAAa,CAAC,CAAC,SAAS,OAAO,GAAG;GAC5C,IAAI,SAAS,MAAM,aAAa,CAAC,CAAC,MAAM,SAAS,gBAAgB,OAAO;GACxE,IAAI,MAAM,aAAa,CAAC,CAAC,SAAS,OAAO,KAAK,iBAAiB,YAAY;IACzE,QAAQ,MAAM,YAAY,kBAAkB,QAAQ,WAAW;IAC/D,SAAS,SAAS,iBAAiB,MAAM,SAAS,MAAM,OAAO,KAAK,KAAA;IACpE,QAAQ,MAAM,YAAY,kBAAkB,QAAQ,WAAW;GACjE;GACA,OAAO,kBAAkB,WAAW,OAAO,YAAY,MAAM,WAAW,SAAS,KAAA;EACnF;EACA,SAAS,OAAO;GACd,IAAI,CAAC,SAAS,aAAa;IACzB,QAAQ,MAAM,UAAU;IACxB;GACF;GACA,MAAM,OAAO,QAAQ,sBAAsB;GAC3C,OAAO,OAAO,QAAQ,OAAO;IAC3B,SAAS;IACT,MAAM,GAAG,KAAK,KAAK;IACnB,KAAK,GAAG,KAAK,IAAI;IACjB,OAAO,GAAG,KAAK,MAAM;IACrB,QAAQ,GAAG,KAAK,OAAO;GACzB,CAAC;EACH;EACA,SAAS,KAAK,OAAqB;GACjC,UAAU,IAAI,KAAK;GACnB,KAAK;EACP;EACA,SAAS,MAAM,OAAmB;GAChC,MAAM,WAAW,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM;GACrD,MAAM,KAAK;GACX,IAAI,UAAU;IACZ,OAAO,IAAI;IACX;GACF;GACA,MAAM,SAAS,IAAI,KAAK;GACxB,IAAI,CAAC,QAAQ;GACb,MAAM,WAAW,YAAY,MAAM;GACnC,IAAI,UAAU,OAAO,QAAQ;QACxB,KAAK,cAAc;EAC1B;EACA,SAAS,IAAI,OAAsB;GACjC,IAAI,MAAM,QAAQ,UAAU;IAC1B,MAAM,KAAK;IACX,OAAO,IAAI;GACb,OAAO,MAAM,KAAK;EACpB;EACA,SAAS,kBAAkB;GACzB,OAAO,IAAI;EACb;EACA,OAAO,iBAAiB,eAAe,MAAM,IAAI;EACjD,OAAO,iBAAiB,eAAe,OAAO,IAAI;EAClD,OAAO,iBAAiB,aAAa,OAAO,IAAI;EAChD,OAAO,iBAAiB,SAAS,OAAO,IAAI;EAC5C,OAAO,iBAAiB,YAAY,OAAO,IAAI;EAC/C,OAAO,iBAAiB,YAAY,OAAO,IAAI;EAC/C,OAAO,iBAAiB,WAAW,KAAK,IAAI;EAC5C,OAAO,iBAAiB,YAAY,iBAAiB,IAAI;EACzD,OAAO,iBAAiB,UAAU,MAAM,IAAI;EAC5C,OAAO,iBAAiB,UAAU,MAAM,IAAI;CAC9C,CAAC;AACH;AAEA,SAAgB,sBAAsB,QAAwB;CAC5D,OAAO,IAAI,uBAAuB,SAAS,EAAE,IAAI,KAAK,UAAU,kBAAkB,KAAK,MAAM,IAAI,SAAS,SAAS,EAAE;AACvH"}
1
+ {"version":3,"file":"dashboard-picker.js","names":[],"sources":["../../../src/common/dashboard-picker.ts"],"sourcesContent":["export const DASHBOARD_PICK_ELEMENT = \"freelens-power:dashboard:pick-element\";\nexport type DashboardPickerResult = { status: \"selected\"; selector: string } | { status: \"canceled\" | \"error\" };\n\n// this function runs only in an isolated website world with no extension bridge\nexport function dashboardPickerAdapter(): Promise<string | null> {\n return new Promise((resolve) => {\n const overlay = document.createElement(\"div\");\n overlay.style.cssText =\n \"all:initial!important;position:fixed!important;inset:0!important;z-index:2147483647!important;pointer-events:auto!important\";\n const shadow = overlay.attachShadow({ mode: \"open\" });\n const outline = document.createElement(\"div\");\n outline.style.cssText =\n \"position:fixed;box-sizing:border-box;border:2px solid #00e5ff;border-radius:6px;box-shadow:0 0 0 1px #111,inset 0 0 0 1px #111;background:rgba(0,229,255,0.08);pointer-events:none;display:none\";\n const toolbar = document.createElement(\"div\");\n toolbar.style.cssText =\n \"position:fixed;bottom:20px;left:50%;transform:translateX(-50%);padding:12px 16px;background:#202124;color:#fff;border-radius:8px;font:13px system-ui;pointer-events:auto;display:flex;gap:16px;align-items:center\";\n const hint = document.createElement(\"span\");\n hint.textContent = \"Hover an element, then click to select · Esc to cancel\";\n const cancel = document.createElement(\"button\");\n cancel.textContent = \"Cancel\";\n cancel.style.cssText =\n \"font:inherit;color:inherit;background:transparent;border:1px solid #888;border-radius:4px;padding:4px 10px;cursor:pointer\";\n toolbar.append(hint, cancel);\n shadow.append(outline, toolbar);\n document.documentElement.append(overlay);\n let hovered: Element | undefined;\n const escape = (value: string) => CSS.escape(value);\n function selectorFor(target: Element): string | undefined {\n const matches = (selector: string) => {\n try {\n return (\n selector.length <= 512 &&\n document.querySelectorAll(selector).length === 1 &&\n document.querySelector(selector) === target\n );\n } catch {\n return false;\n }\n };\n if (target.id) {\n const id = `#${escape(target.id)}`;\n if (matches(id)) return id;\n }\n const path: string[] = [];\n let element: Element | null = target;\n for (let depth = 0; element && depth < 64; depth++, element = element.parentElement) {\n const tag = escape(element.localName);\n const siblings = element.parentElement ? Array.from(element.parentElement.children) : [element];\n path.unshift(`${tag}:nth-child(${siblings.indexOf(element) + 1})`);\n const selector = path.join(\" > \");\n if (selector.length > 512) return;\n if (matches(selector)) return selector;\n }\n return undefined;\n }\n function finish(value: string | null) {\n window.removeEventListener(\"pointermove\", move, true);\n window.removeEventListener(\"pointerdown\", block, true);\n window.removeEventListener(\"pointerup\", block, true);\n window.removeEventListener(\"click\", click, true);\n window.removeEventListener(\"auxclick\", block, true);\n window.removeEventListener(\"dblclick\", block, true);\n window.removeEventListener(\"keydown\", key, true);\n window.removeEventListener(\"pagehide\", cancelSelection, true);\n window.removeEventListener(\"scroll\", draw, true);\n window.removeEventListener(\"resize\", draw, true);\n overlay.remove();\n resolve(value);\n }\n function block(event: Event) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n function hit(event: Event): Element | undefined {\n if (event.composedPath().includes(toolbar)) return;\n let target = event.composedPath().find((node) => node instanceof Element);\n if (event.composedPath().includes(overlay) && event instanceof MouseEvent) {\n overlay.style.setProperty(\"pointer-events\", \"none\", \"important\");\n target = document.elementFromPoint(event.clientX, event.clientY) ?? undefined;\n overlay.style.setProperty(\"pointer-events\", \"auto\", \"important\");\n }\n return target instanceof Element && target.getRootNode() === document ? target : undefined;\n }\n function draw() {\n if (!hovered?.isConnected) {\n outline.style.display = \"none\";\n return;\n }\n const rect = hovered.getBoundingClientRect();\n Object.assign(outline.style, {\n display: \"block\",\n left: `${rect.left}px`,\n top: `${rect.top}px`,\n width: `${rect.width}px`,\n height: `${rect.height}px`,\n });\n }\n function move(event: PointerEvent) {\n hovered = hit(event);\n draw();\n }\n function click(event: MouseEvent) {\n const isCancel = event.composedPath().includes(cancel);\n block(event);\n if (isCancel) {\n finish(null);\n return;\n }\n const target = hit(event);\n if (!target) return;\n const selector = selectorFor(target);\n if (selector) finish(selector);\n else hint.textContent = \"This element cannot be selected. Choose a containing element or cancel.\";\n }\n function key(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n block(event);\n finish(null);\n } else block(event);\n }\n function cancelSelection() {\n finish(null);\n }\n window.addEventListener(\"pointermove\", move, true);\n window.addEventListener(\"pointerdown\", block, true);\n window.addEventListener(\"pointerup\", block, true);\n window.addEventListener(\"click\", click, true);\n window.addEventListener(\"auxclick\", block, true);\n window.addEventListener(\"dblclick\", block, true);\n window.addEventListener(\"keydown\", key, true);\n window.addEventListener(\"pagehide\", cancelSelection, true);\n window.addEventListener(\"scroll\", draw, true);\n window.addEventListener(\"resize\", draw, true);\n });\n}\n\nexport function dashboardPickerScript(): string {\n return `(${dashboardPickerAdapter.toString()})()`;\n}\n"],"mappings":"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"}
@@ -2,24 +2,23 @@ const require_dashboard_selector = require("./dashboard-selector.js");
2
2
  //#region src/common/dashboard-readiness.ts
3
3
  function dashboardReadinessAdapter(selector) {
4
4
  return new Promise((resolve) => {
5
- let lastChange = Date.now();
6
- let lastLayout = "";
7
- let painting = false;
8
5
  let finished = false;
9
- const changed = () => {
10
- lastChange = Date.now();
6
+ let painting = false;
7
+ const target = () => {
8
+ const projection = globalThis.powerSelectorState;
9
+ if (projection) selector = projection.selector;
10
+ let element = document.body;
11
+ if (selector) {
12
+ if (projection?.selector !== selector || !projection.isApplied()) return null;
13
+ element = projection.target;
14
+ }
15
+ if (!element || document.readyState === "loading") return null;
16
+ const bounds = element.getBoundingClientRect();
17
+ return bounds.width > 0 && bounds.height > 0 ? element : null;
11
18
  };
12
- const observer = new MutationObserver(changed);
13
- observer.observe(document.documentElement, {
14
- subtree: true,
15
- childList: true,
16
- attributes: true,
17
- characterData: true
18
- });
19
19
  const finish = () => {
20
20
  if (finished) return;
21
21
  finished = true;
22
- observer.disconnect();
23
22
  clearInterval(interval);
24
23
  clearTimeout(deadline);
25
24
  resolve();
@@ -35,45 +34,16 @@ function dashboardReadinessAdapter(selector) {
35
34
  painted();
36
35
  });
37
36
  });
38
- const ready = () => {
39
- let target = document.body;
40
- if (selector) try {
41
- target = document.querySelector(selector);
42
- } catch {
43
- target = null;
44
- }
45
- if (!target || document.readyState !== "complete" || document.fonts?.status === "loading") return false;
46
- const bounds = target.getBoundingClientRect();
47
- if (bounds.width <= 0 || bounds.height <= 0) return false;
48
- const layout = [
49
- bounds.x,
50
- bounds.y,
51
- bounds.width,
52
- bounds.height,
53
- target.scrollWidth,
54
- target.scrollHeight
55
- ].join(",");
56
- if (layout !== lastLayout) {
57
- lastLayout = layout;
58
- changed();
59
- }
60
- const images = Array.from(target.querySelectorAll("img"));
61
- if (target instanceof HTMLImageElement) images.unshift(target);
62
- return !images.some((image) => {
63
- const rect = image.getBoundingClientRect();
64
- const style = getComputedStyle(image);
65
- return !image.complete && rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth && style.display !== "none" && style.visibility !== "hidden";
66
- }) && Date.now() - lastChange >= 350;
67
- };
68
37
  const check = () => {
69
- if (finished || painting || !ready()) return;
38
+ const element = target();
39
+ if (finished || painting || !element) return;
70
40
  painting = true;
71
- frame().then(frame).then(() => new Promise((settled) => setTimeout(settled, 200))).then(() => {
41
+ frame().then(frame).then(() => {
72
42
  painting = false;
73
- if (!finished && ready() && Date.now() - lastChange >= 550) finish();
43
+ if (!finished && target() === element) finish();
74
44
  });
75
45
  };
76
- const interval = setInterval(check, 100);
46
+ const interval = setInterval(check, 50);
77
47
  const deadline = setTimeout(finish, 8e3);
78
48
  check();
79
49
  });
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-readiness.js","names":[],"sources":["../../../src/common/dashboard-readiness.ts"],"sourcesContent":["import { normalizeDashboardSelector } from \"./dashboard-selector\";\n\n// this function runs only inside the isolated website world\nexport function dashboardReadinessAdapter(selector: string): Promise<void> {\n return new Promise((resolve) => {\n let lastChange = Date.now();\n let lastLayout = \"\";\n let painting = false;\n let finished = false;\n const changed = () => {\n lastChange = Date.now();\n };\n const observer = new MutationObserver(changed);\n observer.observe(document.documentElement, {\n subtree: true,\n childList: true,\n attributes: true,\n characterData: true,\n });\n const finish = () => {\n if (finished) return;\n finished = true;\n observer.disconnect();\n clearInterval(interval);\n clearTimeout(deadline);\n resolve();\n };\n const frame = () =>\n new Promise<void>((painted) => {\n let id = 0;\n const timer = setTimeout(() => {\n cancelAnimationFrame(id);\n painted();\n }, 100);\n id = requestAnimationFrame(() => {\n clearTimeout(timer);\n painted();\n });\n });\n const ready = () => {\n let target: Element | null = document.body;\n if (selector) {\n try {\n target = document.querySelector(selector);\n } catch {\n target = null;\n }\n }\n if (!target || document.readyState !== \"complete\" || document.fonts?.status === \"loading\") return false;\n const bounds = target.getBoundingClientRect();\n if (bounds.width <= 0 || bounds.height <= 0) return false;\n const layout = [bounds.x, bounds.y, bounds.width, bounds.height, target.scrollWidth, target.scrollHeight].join(\n \",\",\n );\n if (layout !== lastLayout) {\n lastLayout = layout;\n changed();\n }\n const images = Array.from(target.querySelectorAll(\"img\"));\n if (target instanceof HTMLImageElement) images.unshift(target);\n const pendingImages = images.some((image) => {\n const rect = image.getBoundingClientRect();\n const style = getComputedStyle(image);\n return (\n !image.complete &&\n rect.width > 0 &&\n rect.height > 0 &&\n rect.bottom > 0 &&\n rect.right > 0 &&\n rect.top < window.innerHeight &&\n rect.left < window.innerWidth &&\n style.display !== \"none\" &&\n style.visibility !== \"hidden\"\n );\n });\n return !pendingImages && Date.now() - lastChange >= 350;\n };\n const check = () => {\n if (finished || painting || !ready()) return;\n painting = true;\n void frame()\n .then(frame)\n .then(() => new Promise<void>((settled) => setTimeout(settled, 200)))\n .then(() => {\n painting = false;\n if (!finished && ready() && Date.now() - lastChange >= 550) finish();\n });\n };\n const interval = setInterval(check, 100);\n const deadline = setTimeout(finish, 8000);\n check();\n });\n}\n\nexport function dashboardReadinessScript(selector?: string): string {\n return `(${dashboardReadinessAdapter.toString()})(${JSON.stringify(normalizeDashboardSelector(selector) ?? \"\")})`;\n}\n"],"mappings":";;AAGA,SAAgB,0BAA0B,UAAiC;CACzE,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,aAAa,KAAK,IAAI;EAC1B,IAAI,aAAa;EACjB,IAAI,WAAW;EACf,IAAI,WAAW;EACf,MAAM,gBAAgB;GACpB,aAAa,KAAK,IAAI;EACxB;EACA,MAAM,WAAW,IAAI,iBAAiB,OAAO;EAC7C,SAAS,QAAQ,SAAS,iBAAiB;GACzC,SAAS;GACT,WAAW;GACX,YAAY;GACZ,eAAe;EACjB,CAAC;EACD,MAAM,eAAe;GACnB,IAAI,UAAU;GACd,WAAW;GACX,SAAS,WAAW;GACpB,cAAc,QAAQ;GACtB,aAAa,QAAQ;GACrB,QAAQ;EACV;EACA,MAAM,cACJ,IAAI,SAAe,YAAY;GAC7B,IAAI,KAAK;GACT,MAAM,QAAQ,iBAAiB;IAC7B,qBAAqB,EAAE;IACvB,QAAQ;GACV,GAAG,GAAG;GACN,KAAK,4BAA4B;IAC/B,aAAa,KAAK;IAClB,QAAQ;GACV,CAAC;EACH,CAAC;EACH,MAAM,cAAc;GAClB,IAAI,SAAyB,SAAS;GACtC,IAAI,UACF,IAAI;IACF,SAAS,SAAS,cAAc,QAAQ;GAC1C,QAAQ;IACN,SAAS;GACX;GAEF,IAAI,CAAC,UAAU,SAAS,eAAe,cAAc,SAAS,OAAO,WAAW,WAAW,OAAO;GAClG,MAAM,SAAS,OAAO,sBAAsB;GAC5C,IAAI,OAAO,SAAS,KAAK,OAAO,UAAU,GAAG,OAAO;GACpD,MAAM,SAAS;IAAC,OAAO;IAAG,OAAO;IAAG,OAAO;IAAO,OAAO;IAAQ,OAAO;IAAa,OAAO;GAAY,CAAC,CAAC,KACxG,GACF;GACA,IAAI,WAAW,YAAY;IACzB,aAAa;IACb,QAAQ;GACV;GACA,MAAM,SAAS,MAAM,KAAK,OAAO,iBAAiB,KAAK,CAAC;GACxD,IAAI,kBAAkB,kBAAkB,OAAO,QAAQ,MAAM;GAgB7D,OAAO,CAfe,OAAO,MAAM,UAAU;IAC3C,MAAM,OAAO,MAAM,sBAAsB;IACzC,MAAM,QAAQ,iBAAiB,KAAK;IACpC,OACE,CAAC,MAAM,YACP,KAAK,QAAQ,KACb,KAAK,SAAS,KACd,KAAK,SAAS,KACd,KAAK,QAAQ,KACb,KAAK,MAAM,OAAO,eAClB,KAAK,OAAO,OAAO,cACnB,MAAM,YAAY,UAClB,MAAM,eAAe;GAEzB,CACQ,KAAiB,KAAK,IAAI,IAAI,cAAc;EACtD;EACA,MAAM,cAAc;GAClB,IAAI,YAAY,YAAY,CAAC,MAAM,GAAG;GACtC,WAAW;GACX,MAAW,CAAC,CACT,KAAK,KAAK,CAAC,CACX,WAAW,IAAI,SAAe,YAAY,WAAW,SAAS,GAAG,CAAC,CAAC,CAAC,CACpE,WAAW;IACV,WAAW;IACX,IAAI,CAAC,YAAY,MAAM,KAAK,KAAK,IAAI,IAAI,cAAc,KAAK,OAAO;GACrE,CAAC;EACL;EACA,MAAM,WAAW,YAAY,OAAO,GAAG;EACvC,MAAM,WAAW,WAAW,QAAQ,GAAI;EACxC,MAAM;CACR,CAAC;AACH;AAEA,SAAgB,yBAAyB,UAA2B;CAClE,OAAO,IAAI,0BAA0B,SAAS,EAAE,IAAI,KAAK,UAAU,2BAAA,2BAA2B,QAAQ,KAAK,EAAE,EAAE;AACjH"}
1
+ {"version":3,"file":"dashboard-readiness.js","names":[],"sources":["../../../src/common/dashboard-readiness.ts"],"sourcesContent":["import { normalizeDashboardSelector } from \"./dashboard-selector\";\n\n// this function runs inside the website document\nexport function dashboardReadinessAdapter(selector: string): Promise<void> {\n return new Promise((resolve) => {\n let finished = false;\n let painting = false;\n const target = () => {\n const projection = (\n globalThis as typeof globalThis & {\n powerSelectorState?: { selector: string; target: Element | null; isApplied: () => boolean };\n }\n ).powerSelectorState;\n if (projection) selector = projection.selector;\n let element: Element | null = document.body;\n if (selector) {\n if (projection?.selector !== selector || !projection.isApplied()) return null;\n element = projection.target;\n }\n if (!element || document.readyState === \"loading\") return null;\n const bounds = element.getBoundingClientRect();\n return bounds.width > 0 && bounds.height > 0 ? element : null;\n };\n const finish = () => {\n if (finished) return;\n finished = true;\n clearInterval(interval);\n clearTimeout(deadline);\n resolve();\n };\n const frame = () =>\n new Promise<void>((painted) => {\n let id = 0;\n const timer = setTimeout(() => {\n cancelAnimationFrame(id);\n painted();\n }, 100);\n id = requestAnimationFrame(() => {\n clearTimeout(timer);\n painted();\n });\n });\n const check = () => {\n const element = target();\n if (finished || painting || !element) return;\n painting = true;\n void frame()\n .then(frame)\n .then(() => {\n painting = false;\n if (!finished && target() === element) finish();\n });\n };\n const interval = setInterval(check, 50);\n const deadline = setTimeout(finish, 8000);\n check();\n });\n}\n\nexport function dashboardReadinessScript(selector?: string): string {\n return `(${dashboardReadinessAdapter.toString()})(${JSON.stringify(normalizeDashboardSelector(selector) ?? \"\")})`;\n}\n"],"mappings":";;AAGA,SAAgB,0BAA0B,UAAiC;CACzE,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,WAAW;EACf,IAAI,WAAW;EACf,MAAM,eAAe;GACnB,MAAM,aACJ,WAGA;GACF,IAAI,YAAY,WAAW,WAAW;GACtC,IAAI,UAA0B,SAAS;GACvC,IAAI,UAAU;IACZ,IAAI,YAAY,aAAa,YAAY,CAAC,WAAW,UAAU,GAAG,OAAO;IACzE,UAAU,WAAW;GACvB;GACA,IAAI,CAAC,WAAW,SAAS,eAAe,WAAW,OAAO;GAC1D,MAAM,SAAS,QAAQ,sBAAsB;GAC7C,OAAO,OAAO,QAAQ,KAAK,OAAO,SAAS,IAAI,UAAU;EAC3D;EACA,MAAM,eAAe;GACnB,IAAI,UAAU;GACd,WAAW;GACX,cAAc,QAAQ;GACtB,aAAa,QAAQ;GACrB,QAAQ;EACV;EACA,MAAM,cACJ,IAAI,SAAe,YAAY;GAC7B,IAAI,KAAK;GACT,MAAM,QAAQ,iBAAiB;IAC7B,qBAAqB,EAAE;IACvB,QAAQ;GACV,GAAG,GAAG;GACN,KAAK,4BAA4B;IAC/B,aAAa,KAAK;IAClB,QAAQ;GACV,CAAC;EACH,CAAC;EACH,MAAM,cAAc;GAClB,MAAM,UAAU,OAAO;GACvB,IAAI,YAAY,YAAY,CAAC,SAAS;GACtC,WAAW;GACX,MAAW,CAAC,CACT,KAAK,KAAK,CAAC,CACX,WAAW;IACV,WAAW;IACX,IAAI,CAAC,YAAY,OAAO,MAAM,SAAS,OAAO;GAChD,CAAC;EACL;EACA,MAAM,WAAW,YAAY,OAAO,EAAE;EACtC,MAAM,WAAW,WAAW,QAAQ,GAAI;EACxC,MAAM;CACR,CAAC;AACH;AAEA,SAAgB,yBAAyB,UAA2B;CAClE,OAAO,IAAI,0BAA0B,SAAS,EAAE,IAAI,KAAK,UAAU,2BAAA,2BAA2B,QAAQ,KAAK,EAAE,EAAE;AACjH"}
@@ -6,20 +6,30 @@ function normalizeDashboardSelector(value) {
6
6
  function dashboardSelectorAdapter(selector) {
7
7
  const state = globalThis;
8
8
  state.powerSelectorCleanup?.();
9
- if (!selector) return;
9
+ if (!selector) {
10
+ state.powerSelectorState = {
11
+ selector,
12
+ target: null,
13
+ isApplied: () => true
14
+ };
15
+ state.powerSelectorCleanup = () => {
16
+ delete state.powerSelectorState;
17
+ delete state.powerSelectorCleanup;
18
+ };
19
+ return;
20
+ }
10
21
  const hidden = "data-power-selector-hidden";
11
22
  const path = "data-power-selector-path";
12
23
  const inset = "data-power-selector-inset";
13
24
  let insetRoot;
14
25
  let marked = [];
15
- let timer;
16
26
  const style = document.createElement("style");
17
27
  style.textContent = `[${hidden}] { display: none !important; } [${path}] { display: block !important; position: static !important; width: auto !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; margin: 0 !important; padding: 0 !important; transform: none !important; } body[${inset}] { padding: 12px !important; box-sizing: border-box !important; }`;
18
28
  const notice = document.createElement("div");
19
29
  notice.textContent = "Selected element unavailable. Showing the whole page. Clear CSS selector in card settings to reset.";
20
30
  notice.style.cssText = "position:fixed;top:0;left:0;right:0;z-index:2147483647;padding:8px;background:#292929;color:white;font:12px sans-serif";
21
31
  const observer = new MutationObserver(() => {
22
- if (!timer) timer = setTimeout(update, 200);
32
+ if (!projection.isApplied()) update();
23
33
  });
24
34
  function restore() {
25
35
  for (const element of marked) {
@@ -32,8 +42,31 @@ function dashboardSelectorAdapter(selector) {
32
42
  style.remove();
33
43
  notice.remove();
34
44
  }
45
+ const projection = {
46
+ selector,
47
+ target: null,
48
+ isApplied: () => {
49
+ const target = projection.target;
50
+ if (!target || !style.isConnected) return false;
51
+ try {
52
+ if (document.querySelector(selector) !== target) return false;
53
+ } catch {
54
+ return false;
55
+ }
56
+ if (target === document.documentElement || target === document.body) return document.body?.hasAttribute(inset) ?? false;
57
+ let child = target;
58
+ let depth = 0;
59
+ while (child.parentElement) {
60
+ const parent = child.parentElement;
61
+ if (!parent.hasAttribute(path) || ++depth >= 128) return false;
62
+ for (const sibling of parent.children) if (sibling !== child && sibling !== style && sibling.localName !== "freelens-power-loading" && !sibling.hasAttribute(hidden)) return false;
63
+ child = parent;
64
+ }
65
+ return document.body?.hasAttribute(inset) ?? false;
66
+ }
67
+ };
68
+ state.powerSelectorState = projection;
35
69
  function update() {
36
- timer = void 0;
37
70
  observer.disconnect();
38
71
  restore();
39
72
  let target = null;
@@ -48,7 +81,7 @@ function dashboardSelectorAdapter(selector) {
48
81
  const parent = child.parentElement;
49
82
  ancestors.push(parent);
50
83
  for (const sibling of parent.children) {
51
- if (sibling !== child) branches.push(sibling);
84
+ if (sibling !== child && sibling.localName !== "freelens-power-loading") branches.push(sibling);
52
85
  if (branches.length >= 2e3) break;
53
86
  }
54
87
  child = parent;
@@ -65,6 +98,7 @@ function dashboardSelectorAdapter(selector) {
65
98
  }
66
99
  document.documentElement.append(style);
67
100
  } else document.documentElement.append(notice);
101
+ projection.target = target;
68
102
  observer.observe(document.documentElement, {
69
103
  subtree: true,
70
104
  childList: true,
@@ -73,9 +107,9 @@ function dashboardSelectorAdapter(selector) {
73
107
  }
74
108
  state.powerSelectorCleanup = () => {
75
109
  observer.disconnect();
76
- clearTimeout(timer);
77
110
  restore();
78
111
  delete state.powerSelectorCleanup;
112
+ delete state.powerSelectorState;
79
113
  };
80
114
  update();
81
115
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-selector.js","names":[],"sources":["../../../src/common/dashboard-selector.ts"],"sourcesContent":["export const dashboardSelectorLimit = 512;\n\nexport function normalizeDashboardSelector(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const selector = value.trim();\n return selector && selector.length <= dashboardSelectorLimit && !/[\\u0000-\\u001f\\u007f]/.test(selector)\n ? selector\n : undefined;\n}\n\n// this function runs only inside the isolated website world\nexport function dashboardSelectorAdapter(selector: string): void {\n const state = globalThis as typeof globalThis & { powerSelectorCleanup?: () => void };\n state.powerSelectorCleanup?.();\n if (!selector) return;\n const hidden = \"data-power-selector-hidden\";\n const path = \"data-power-selector-path\";\n const inset = \"data-power-selector-inset\";\n let insetRoot: HTMLElement | undefined;\n let marked: Element[] = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n const style = document.createElement(\"style\");\n style.textContent = `[${hidden}] { display: none !important; } [${path}] { display: block !important; position: static !important; width: auto !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; margin: 0 !important; padding: 0 !important; transform: none !important; } body[${inset}] { padding: 12px !important; box-sizing: border-box !important; }`;\n const notice = document.createElement(\"div\");\n notice.textContent =\n \"Selected element unavailable. Showing the whole page. Clear CSS selector in card settings to reset.\";\n notice.style.cssText =\n \"position:fixed;top:0;left:0;right:0;z-index:2147483647;padding:8px;background:#292929;color:white;font:12px sans-serif\";\n const observer = new MutationObserver(() => {\n if (!timer) timer = setTimeout(update, 200);\n });\n function restore() {\n for (const element of marked) {\n element.removeAttribute(hidden);\n element.removeAttribute(path);\n }\n marked = [];\n insetRoot?.removeAttribute(inset);\n insetRoot = undefined;\n style.remove();\n notice.remove();\n }\n function update() {\n timer = undefined;\n observer.disconnect();\n restore();\n let target: Element | null = null;\n try {\n target = document.querySelector(selector);\n } catch {}\n const branches: Element[] = [];\n const ancestors: Element[] = [];\n if (target && target !== document.documentElement && target !== document.body) {\n let child: Element = target;\n while (child.parentElement && ancestors.length < 128 && branches.length < 2000) {\n const parent = child.parentElement;\n ancestors.push(parent);\n for (const sibling of parent.children) {\n if (sibling !== child) branches.push(sibling);\n if (branches.length >= 2000) break;\n }\n child = parent;\n }\n if (ancestors.length >= 128 || branches.length >= 2000) target = null;\n }\n if (target) {\n for (const element of branches) element.setAttribute(hidden, \"\");\n for (const element of ancestors) element.setAttribute(path, \"\");\n marked = [...branches, ...ancestors];\n if (document.body) {\n insetRoot = document.body;\n insetRoot.setAttribute(inset, \"\");\n }\n document.documentElement.append(style);\n } else {\n document.documentElement.append(notice);\n }\n observer.observe(document.documentElement, { subtree: true, childList: true, attributes: true });\n }\n state.powerSelectorCleanup = () => {\n observer.disconnect();\n clearTimeout(timer);\n restore();\n delete state.powerSelectorCleanup;\n };\n update();\n}\n\nexport function dashboardSelectorScript(selector?: string): string {\n return `(${dashboardSelectorAdapter.toString()})(${JSON.stringify(normalizeDashboardSelector(selector) ?? \"\")})`;\n}\n"],"mappings":"AAEA,SAAgB,2BAA2B,OAAoC;CAC7E,IAAI,OAAO,UAAU,UAAU,OAAO,KAAA;CACtC,MAAM,WAAW,MAAM,KAAK;CAC5B,OAAO,YAAY,SAAS,UAAA,OAAoC,CAAC,wBAAwB,KAAK,QAAQ,IAClG,WACA,KAAA;AACN;AAGA,SAAgB,yBAAyB,UAAwB;CAC/D,MAAM,QAAQ;CACd,MAAM,uBAAuB;CAC7B,IAAI,CAAC,UAAU;CACf,MAAM,SAAS;CACf,MAAM,OAAO;CACb,MAAM,QAAQ;CACd,IAAI;CACJ,IAAI,SAAoB,CAAC;CACzB,IAAI;CACJ,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,cAAc,IAAI,OAAO,mCAAmC,KAAK,oRAAoR,MAAM;CACjW,MAAM,SAAS,SAAS,cAAc,KAAK;CAC3C,OAAO,cACL;CACF,OAAO,MAAM,UACX;CACF,MAAM,WAAW,IAAI,uBAAuB;EAC1C,IAAI,CAAC,OAAO,QAAQ,WAAW,QAAQ,GAAG;CAC5C,CAAC;CACD,SAAS,UAAU;EACjB,KAAK,MAAM,WAAW,QAAQ;GAC5B,QAAQ,gBAAgB,MAAM;GAC9B,QAAQ,gBAAgB,IAAI;EAC9B;EACA,SAAS,CAAC;EACV,WAAW,gBAAgB,KAAK;EAChC,YAAY,KAAA;EACZ,MAAM,OAAO;EACb,OAAO,OAAO;CAChB;CACA,SAAS,SAAS;EAChB,QAAQ,KAAA;EACR,SAAS,WAAW;EACpB,QAAQ;EACR,IAAI,SAAyB;EAC7B,IAAI;GACF,SAAS,SAAS,cAAc,QAAQ;EAC1C,QAAQ,CAAC;EACT,MAAM,WAAsB,CAAC;EAC7B,MAAM,YAAuB,CAAC;EAC9B,IAAI,UAAU,WAAW,SAAS,mBAAmB,WAAW,SAAS,MAAM;GAC7E,IAAI,QAAiB;GACrB,OAAO,MAAM,iBAAiB,UAAU,SAAS,OAAO,SAAS,SAAS,KAAM;IAC9E,MAAM,SAAS,MAAM;IACrB,UAAU,KAAK,MAAM;IACrB,KAAK,MAAM,WAAW,OAAO,UAAU;KACrC,IAAI,YAAY,OAAO,SAAS,KAAK,OAAO;KAC5C,IAAI,SAAS,UAAU,KAAM;IAC/B;IACA,QAAQ;GACV;GACA,IAAI,UAAU,UAAU,OAAO,SAAS,UAAU,KAAM,SAAS;EACnE;EACA,IAAI,QAAQ;GACV,KAAK,MAAM,WAAW,UAAU,QAAQ,aAAa,QAAQ,EAAE;GAC/D,KAAK,MAAM,WAAW,WAAW,QAAQ,aAAa,MAAM,EAAE;GAC9D,SAAS,CAAC,GAAG,UAAU,GAAG,SAAS;GACnC,IAAI,SAAS,MAAM;IACjB,YAAY,SAAS;IACrB,UAAU,aAAa,OAAO,EAAE;GAClC;GACA,SAAS,gBAAgB,OAAO,KAAK;EACvC,OACE,SAAS,gBAAgB,OAAO,MAAM;EAExC,SAAS,QAAQ,SAAS,iBAAiB;GAAE,SAAS;GAAM,WAAW;GAAM,YAAY;EAAK,CAAC;CACjG;CACA,MAAM,6BAA6B;EACjC,SAAS,WAAW;EACpB,aAAa,KAAK;EAClB,QAAQ;EACR,OAAO,MAAM;CACf;CACA,OAAO;AACT;AAEA,SAAgB,wBAAwB,UAA2B;CACjE,OAAO,IAAI,yBAAyB,SAAS,EAAE,IAAI,KAAK,UAAU,2BAA2B,QAAQ,KAAK,EAAE,EAAE;AAChH"}
1
+ {"version":3,"file":"dashboard-selector.js","names":[],"sources":["../../../src/common/dashboard-selector.ts"],"sourcesContent":["export const dashboardSelectorLimit = 512;\n\nexport function normalizeDashboardSelector(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const selector = value.trim();\n return selector && selector.length <= dashboardSelectorLimit && !/[\\u0000-\\u001f\\u007f]/.test(selector)\n ? selector\n : undefined;\n}\n\n// this function runs inside the website document\nexport function dashboardSelectorAdapter(selector: string): void {\n const state = globalThis as typeof globalThis & {\n powerSelectorCleanup?: () => void;\n powerSelectorState?: { selector: string; target: Element | null; isApplied: () => boolean };\n };\n state.powerSelectorCleanup?.();\n if (!selector) {\n state.powerSelectorState = { selector, target: null, isApplied: () => true };\n state.powerSelectorCleanup = () => {\n delete state.powerSelectorState;\n delete state.powerSelectorCleanup;\n };\n return;\n }\n const hidden = \"data-power-selector-hidden\";\n const path = \"data-power-selector-path\";\n const inset = \"data-power-selector-inset\";\n let insetRoot: HTMLElement | undefined;\n let marked: Element[] = [];\n const style = document.createElement(\"style\");\n style.textContent = `[${hidden}] { display: none !important; } [${path}] { display: block !important; position: static !important; width: auto !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; margin: 0 !important; padding: 0 !important; transform: none !important; } body[${inset}] { padding: 12px !important; box-sizing: border-box !important; }`;\n const notice = document.createElement(\"div\");\n notice.textContent =\n \"Selected element unavailable. Showing the whole page. Clear CSS selector in card settings to reset.\";\n notice.style.cssText =\n \"position:fixed;top:0;left:0;right:0;z-index:2147483647;padding:8px;background:#292929;color:white;font:12px sans-serif\";\n const observer = new MutationObserver(() => {\n if (!projection.isApplied()) update();\n });\n function restore() {\n for (const element of marked) {\n element.removeAttribute(hidden);\n element.removeAttribute(path);\n }\n marked = [];\n insetRoot?.removeAttribute(inset);\n insetRoot = undefined;\n style.remove();\n notice.remove();\n }\n const projection = {\n selector,\n target: null as Element | null,\n isApplied: () => {\n const target = projection.target;\n if (!target || !style.isConnected) return false;\n try {\n if (document.querySelector(selector) !== target) return false;\n } catch {\n return false;\n }\n if (target === document.documentElement || target === document.body)\n return document.body?.hasAttribute(inset) ?? false;\n let child = target;\n let depth = 0;\n while (child.parentElement) {\n const parent = child.parentElement;\n if (!parent.hasAttribute(path) || ++depth >= 128) return false;\n for (const sibling of parent.children) {\n if (\n sibling !== child &&\n sibling !== style &&\n sibling.localName !== \"freelens-power-loading\" &&\n !sibling.hasAttribute(hidden)\n )\n return false;\n }\n child = parent;\n }\n return document.body?.hasAttribute(inset) ?? false;\n },\n };\n state.powerSelectorState = projection;\n function update() {\n observer.disconnect();\n restore();\n let target: Element | null = null;\n try {\n target = document.querySelector(selector);\n } catch {}\n const branches: Element[] = [];\n const ancestors: Element[] = [];\n if (target && target !== document.documentElement && target !== document.body) {\n let child: Element = target;\n while (child.parentElement && ancestors.length < 128 && branches.length < 2000) {\n const parent = child.parentElement;\n ancestors.push(parent);\n for (const sibling of parent.children) {\n if (sibling !== child && sibling.localName !== \"freelens-power-loading\") branches.push(sibling);\n if (branches.length >= 2000) break;\n }\n child = parent;\n }\n if (ancestors.length >= 128 || branches.length >= 2000) target = null;\n }\n if (target) {\n for (const element of branches) element.setAttribute(hidden, \"\");\n for (const element of ancestors) element.setAttribute(path, \"\");\n marked = [...branches, ...ancestors];\n if (document.body) {\n insetRoot = document.body;\n insetRoot.setAttribute(inset, \"\");\n }\n document.documentElement.append(style);\n } else {\n document.documentElement.append(notice);\n }\n projection.target = target;\n observer.observe(document.documentElement, { subtree: true, childList: true, attributes: true });\n }\n state.powerSelectorCleanup = () => {\n observer.disconnect();\n restore();\n delete state.powerSelectorCleanup;\n delete state.powerSelectorState;\n };\n update();\n}\n\nexport function dashboardSelectorScript(selector?: string): string {\n return `(${dashboardSelectorAdapter.toString()})(${JSON.stringify(normalizeDashboardSelector(selector) ?? \"\")})`;\n}\n"],"mappings":"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"}
@@ -27,6 +27,7 @@ function sanitizeDashboardCards(value) {
27
27
  url,
28
28
  size: card.size === "xs" || card.size === "s" || card.size === "l" || card.size === "xl" ? card.size : "m",
29
29
  autoRefresh: card.autoRefresh === true,
30
+ ...card.refreshEvery10Seconds === true ? { refreshEvery10Seconds: true } : {},
30
31
  ...require_dashboard_selector.normalizeDashboardSelector(card.selector) ? { selector: require_dashboard_selector.normalizeDashboardSelector(card.selector) } : {}
31
32
  }];
32
33
  });
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","names":[],"sources":["../../../src/common/dashboard.ts"],"sourcesContent":["import { normalizeDashboardSelector } from \"./dashboard-selector\";\n\nexport type DashboardCardSize = \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\";\n\nexport interface DashboardCard {\n id: string;\n title: string;\n url: string;\n size: DashboardCardSize;\n autoRefresh?: boolean;\n selector?: string;\n}\n\nexport function normalizeDashboardUrl(value: string): string | undefined {\n try {\n const url = new URL(value.trim());\n if ((url.protocol !== \"http:\" && url.protocol !== \"https:\") || !url.hostname) return undefined;\n return url.href;\n } catch {\n return undefined;\n }\n}\n\nexport function sanitizeDashboardCards(value: unknown): DashboardCard[] {\n if (!Array.isArray(value)) return [];\n const ids = new Set<string>();\n return value.flatMap((entry: unknown) => {\n if (!entry || typeof entry !== \"object\") return [];\n const card = entry as Partial<DashboardCard>;\n const url = typeof card.url === \"string\" ? normalizeDashboardUrl(card.url) : undefined;\n if (!url) return [];\n const preferredId = typeof card.id === \"string\" ? card.id.trim() : \"\";\n let id = preferredId || `card-${encodeURIComponent(url)}`;\n for (let suffix = 2; ids.has(id); suffix++) id = `${preferredId || `card-${encodeURIComponent(url)}`}-${suffix}`;\n ids.add(id);\n return [\n {\n id,\n title: typeof card.title === \"string\" && card.title.trim() ? card.title.trim() : new URL(url).hostname,\n url,\n size: card.size === \"xs\" || card.size === \"s\" || card.size === \"l\" || card.size === \"xl\" ? card.size : \"m\",\n autoRefresh: card.autoRefresh === true,\n ...(normalizeDashboardSelector(card.selector) ? { selector: normalizeDashboardSelector(card.selector) } : {}),\n } satisfies DashboardCard,\n ];\n });\n}\n"],"mappings":";;AAaA,SAAgB,sBAAsB,OAAmC;CACvE,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,MAAM,KAAK,CAAC;EAChC,IAAK,IAAI,aAAa,WAAW,IAAI,aAAa,YAAa,CAAC,IAAI,UAAU,OAAO,KAAA;EACrF,OAAO,IAAI;CACb,QAAQ;EACN;CACF;AACF;AAEA,SAAgB,uBAAuB,OAAiC;CACtE,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;CACnC,MAAM,sBAAM,IAAI,IAAY;CAC5B,OAAO,MAAM,SAAS,UAAmB;EACvC,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO,CAAC;EACjD,MAAM,OAAO;EACb,MAAM,MAAM,OAAO,KAAK,QAAQ,WAAW,sBAAsB,KAAK,GAAG,IAAI,KAAA;EAC7E,IAAI,CAAC,KAAK,OAAO,CAAC;EAClB,MAAM,cAAc,OAAO,KAAK,OAAO,WAAW,KAAK,GAAG,KAAK,IAAI;EACnE,IAAI,KAAK,eAAe,QAAQ,mBAAmB,GAAG;EACtD,KAAK,IAAI,SAAS,GAAG,IAAI,IAAI,EAAE,GAAG,UAAU,KAAK,GAAG,eAAe,QAAQ,mBAAmB,GAAG,IAAI,GAAG;EACxG,IAAI,IAAI,EAAE;EACV,OAAO,CACL;GACE;GACA,OAAO,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC;GAC9F;GACA,MAAM,KAAK,SAAS,QAAQ,KAAK,SAAS,OAAO,KAAK,SAAS,OAAO,KAAK,SAAS,OAAO,KAAK,OAAO;GACvG,aAAa,KAAK,gBAAgB;GAClC,GAAI,2BAAA,2BAA2B,KAAK,QAAQ,IAAI,EAAE,UAAU,2BAAA,2BAA2B,KAAK,QAAQ,EAAE,IAAI,CAAC;EAC7G,CACF;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"dashboard.js","names":[],"sources":["../../../src/common/dashboard.ts"],"sourcesContent":["import { normalizeDashboardSelector } from \"./dashboard-selector\";\n\nexport type DashboardCardSize = \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\";\n\nexport interface DashboardCard {\n id: string;\n title: string;\n url: string;\n size: DashboardCardSize;\n autoRefresh?: boolean;\n refreshEvery10Seconds?: boolean;\n selector?: string;\n}\n\nexport function normalizeDashboardUrl(value: string): string | undefined {\n try {\n const url = new URL(value.trim());\n if ((url.protocol !== \"http:\" && url.protocol !== \"https:\") || !url.hostname) return undefined;\n return url.href;\n } catch {\n return undefined;\n }\n}\n\nexport function sanitizeDashboardCards(value: unknown): DashboardCard[] {\n if (!Array.isArray(value)) return [];\n const ids = new Set<string>();\n return value.flatMap((entry: unknown) => {\n if (!entry || typeof entry !== \"object\") return [];\n const card = entry as Partial<DashboardCard>;\n const url = typeof card.url === \"string\" ? normalizeDashboardUrl(card.url) : undefined;\n if (!url) return [];\n const preferredId = typeof card.id === \"string\" ? card.id.trim() : \"\";\n let id = preferredId || `card-${encodeURIComponent(url)}`;\n for (let suffix = 2; ids.has(id); suffix++) id = `${preferredId || `card-${encodeURIComponent(url)}`}-${suffix}`;\n ids.add(id);\n return [\n {\n id,\n title: typeof card.title === \"string\" && card.title.trim() ? card.title.trim() : new URL(url).hostname,\n url,\n size: card.size === \"xs\" || card.size === \"s\" || card.size === \"l\" || card.size === \"xl\" ? card.size : \"m\",\n autoRefresh: card.autoRefresh === true,\n ...(card.refreshEvery10Seconds === true ? { refreshEvery10Seconds: true } : {}),\n ...(normalizeDashboardSelector(card.selector) ? { selector: normalizeDashboardSelector(card.selector) } : {}),\n } satisfies DashboardCard,\n ];\n });\n}\n"],"mappings":";;AAcA,SAAgB,sBAAsB,OAAmC;CACvE,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,MAAM,KAAK,CAAC;EAChC,IAAK,IAAI,aAAa,WAAW,IAAI,aAAa,YAAa,CAAC,IAAI,UAAU,OAAO,KAAA;EACrF,OAAO,IAAI;CACb,QAAQ;EACN;CACF;AACF;AAEA,SAAgB,uBAAuB,OAAiC;CACtE,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;CACnC,MAAM,sBAAM,IAAI,IAAY;CAC5B,OAAO,MAAM,SAAS,UAAmB;EACvC,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO,CAAC;EACjD,MAAM,OAAO;EACb,MAAM,MAAM,OAAO,KAAK,QAAQ,WAAW,sBAAsB,KAAK,GAAG,IAAI,KAAA;EAC7E,IAAI,CAAC,KAAK,OAAO,CAAC;EAClB,MAAM,cAAc,OAAO,KAAK,OAAO,WAAW,KAAK,GAAG,KAAK,IAAI;EACnE,IAAI,KAAK,eAAe,QAAQ,mBAAmB,GAAG;EACtD,KAAK,IAAI,SAAS,GAAG,IAAI,IAAI,EAAE,GAAG,UAAU,KAAK,GAAG,eAAe,QAAQ,mBAAmB,GAAG,IAAI,GAAG;EACxG,IAAI,IAAI,EAAE;EACV,OAAO,CACL;GACE;GACA,OAAO,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC;GAC9F;GACA,MAAM,KAAK,SAAS,QAAQ,KAAK,SAAS,OAAO,KAAK,SAAS,OAAO,KAAK,SAAS,OAAO,KAAK,OAAO;GACvG,aAAa,KAAK,gBAAgB;GAClC,GAAI,KAAK,0BAA0B,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAC;GAC7E,GAAI,2BAAA,2BAA2B,KAAK,QAAQ,IAAI,EAAE,UAAU,2BAAA,2BAA2B,KAAK,QAAQ,EAAE,IAAI,CAAC;EAC7G,CACF;CACF,CAAC;AACH"}
@@ -23,7 +23,7 @@ function parseSettingsImport(value) {
23
23
  "m",
24
24
  "l",
25
25
  "xl"
26
- ].includes(card.size) || card.autoRefresh !== void 0 && typeof card.autoRefresh !== "boolean" || card.selector !== void 0 && (typeof card.selector !== "string" || card.selector.trim() && !require_dashboard_selector.normalizeDashboardSelector(card.selector))) return fail();
26
+ ].includes(card.size) || card.autoRefresh !== void 0 && typeof card.autoRefresh !== "boolean" || card.refreshEvery10Seconds !== void 0 && typeof card.refreshEvery10Seconds !== "boolean" || card.selector !== void 0 && (typeof card.selector !== "string" || card.selector.trim() && !require_dashboard_selector.normalizeDashboardSelector(card.selector))) return fail();
27
27
  ids.add(card.id);
28
28
  }
29
29
  if (require_dashboard.sanitizeDashboardCards(cards).length !== cards.length || require_ai_shortcuts.sanitizeAiShortcuts(shortcuts).length !== shortcuts.length) return fail();
@@ -1 +1 @@
1
- {"version":3,"file":"settings-export.js","names":[],"sources":["../../../src/common/settings-export.ts"],"sourcesContent":["import { sanitizeAiShortcuts } from \"./ai-shortcuts\";\nimport { sanitizeDashboardCards } from \"./dashboard\";\nimport { normalizeDashboardSelector } from \"./dashboard-selector\";\n\nexport const SETTINGS_EXPORT = \"freelens-power:settings:export\";\nexport type SettingsExportResult = \"saved\" | \"canceled\" | \"error\";\nexport const SETTINGS_IMPORT = \"freelens-power:settings:import\";\nexport type SettingsImportResult = \"restored\" | \"canceled\" | \"error\";\nexport const SETTINGS_BACKUP_MAX_BYTES = 1024 * 1024;\n\nexport function parseSettingsImport(value: unknown) {\n const fail = () => {\n throw new Error(\"Invalid settings backup\");\n };\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return fail();\n const backup = value as {\n format?: unknown;\n version?: unknown;\n dashboard?: { cards?: unknown };\n ai?: { shortcuts?: unknown };\n };\n if (backup.format !== \"freelens-power-settings\" || backup.version !== 1) return fail();\n const cards = backup.dashboard?.cards;\n const shortcuts = backup.ai?.shortcuts;\n if (!Array.isArray(cards) || !Array.isArray(shortcuts)) return fail();\n const ids = new Set<string>();\n for (const card of cards) {\n if (\n !card ||\n typeof card !== \"object\" ||\n typeof card.id !== \"string\" ||\n !card.id.trim() ||\n ids.has(card.id) ||\n typeof card.title !== \"string\" ||\n !card.title.trim() ||\n typeof card.url !== \"string\" ||\n ![\"xs\", \"s\", \"m\", \"l\", \"xl\"].includes(card.size) ||\n (card.autoRefresh !== undefined && typeof card.autoRefresh !== \"boolean\") ||\n (card.selector !== undefined &&\n (typeof card.selector !== \"string\" || (card.selector.trim() && !normalizeDashboardSelector(card.selector))))\n )\n return fail();\n ids.add(card.id);\n }\n if (\n sanitizeDashboardCards(cards).length !== cards.length ||\n sanitizeAiShortcuts(shortcuts).length !== shortcuts.length\n )\n return fail();\n return createSettingsExport(cards, shortcuts);\n}\n\nfunction credentialKey(value: string): boolean {\n const key = value.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n return (\n /(?:token|password|passwd|secret|credential|authorization|signature|apikey|session)/.test(key) ||\n [\"auth\", \"key\", \"sig\", \"code\", \"jwt\", \"assertion\", \"ticket\", \"samlresponse\"].includes(key)\n );\n}\n\nfunction exportUrl(value: string): string {\n const url = new URL(value);\n url.username = \"\";\n url.password = \"\";\n for (const key of [...url.searchParams.keys()]) {\n if (credentialKey(key)) url.searchParams.delete(key);\n }\n // preserve SPA routes unless the fragment contains credential parameters\n const fragment = url.hash.slice(1);\n const parameters = new URLSearchParams(fragment.includes(\"?\") ? fragment.slice(fragment.indexOf(\"?\") + 1) : fragment);\n if ([...parameters.keys()].some(credentialKey)) url.hash = \"\";\n return url.href;\n}\n\nexport function createSettingsExport(cards: unknown, shortcuts: unknown) {\n const backup = {\n format: \"freelens-power-settings\",\n version: 1,\n dashboard: {\n cards: sanitizeDashboardCards(cards).map((card, index, all) => {\n const url = exportUrl(card.url);\n let id = card.id;\n if (/^card-https?%3a%2f%2f/i.test(id)) {\n id = `export-card-${index + 1}`;\n while (all.some((entry) => entry.id === id)) id += \"-\";\n }\n return { ...card, url, id };\n }),\n },\n ai: { shortcuts: sanitizeAiShortcuts(shortcuts) },\n };\n if (new TextEncoder().encode(`${JSON.stringify(backup, null, 2)}\\n`).byteLength > SETTINGS_BACKUP_MAX_BYTES)\n throw new Error(\"Settings backup is too large\");\n return backup;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"settings-export.js","names":[],"sources":["../../../src/common/settings-export.ts"],"sourcesContent":["import { sanitizeAiShortcuts } from \"./ai-shortcuts\";\nimport { sanitizeDashboardCards } from \"./dashboard\";\nimport { normalizeDashboardSelector } from \"./dashboard-selector\";\n\nexport const SETTINGS_EXPORT = \"freelens-power:settings:export\";\nexport type SettingsExportResult = \"saved\" | \"canceled\" | \"error\";\nexport const SETTINGS_IMPORT = \"freelens-power:settings:import\";\nexport type SettingsImportResult = \"restored\" | \"canceled\" | \"error\";\nexport const SETTINGS_BACKUP_MAX_BYTES = 1024 * 1024;\n\nexport function parseSettingsImport(value: unknown) {\n const fail = () => {\n throw new Error(\"Invalid settings backup\");\n };\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return fail();\n const backup = value as {\n format?: unknown;\n version?: unknown;\n dashboard?: { cards?: unknown };\n ai?: { shortcuts?: unknown };\n };\n if (backup.format !== \"freelens-power-settings\" || backup.version !== 1) return fail();\n const cards = backup.dashboard?.cards;\n const shortcuts = backup.ai?.shortcuts;\n if (!Array.isArray(cards) || !Array.isArray(shortcuts)) return fail();\n const ids = new Set<string>();\n for (const card of cards) {\n if (\n !card ||\n typeof card !== \"object\" ||\n typeof card.id !== \"string\" ||\n !card.id.trim() ||\n ids.has(card.id) ||\n typeof card.title !== \"string\" ||\n !card.title.trim() ||\n typeof card.url !== \"string\" ||\n ![\"xs\", \"s\", \"m\", \"l\", \"xl\"].includes(card.size) ||\n (card.autoRefresh !== undefined && typeof card.autoRefresh !== \"boolean\") ||\n (card.refreshEvery10Seconds !== undefined && typeof card.refreshEvery10Seconds !== \"boolean\") ||\n (card.selector !== undefined &&\n (typeof card.selector !== \"string\" || (card.selector.trim() && !normalizeDashboardSelector(card.selector))))\n )\n return fail();\n ids.add(card.id);\n }\n if (\n sanitizeDashboardCards(cards).length !== cards.length ||\n sanitizeAiShortcuts(shortcuts).length !== shortcuts.length\n )\n return fail();\n return createSettingsExport(cards, shortcuts);\n}\n\nfunction credentialKey(value: string): boolean {\n const key = value.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n return (\n /(?:token|password|passwd|secret|credential|authorization|signature|apikey|session)/.test(key) ||\n [\"auth\", \"key\", \"sig\", \"code\", \"jwt\", \"assertion\", \"ticket\", \"samlresponse\"].includes(key)\n );\n}\n\nfunction exportUrl(value: string): string {\n const url = new URL(value);\n url.username = \"\";\n url.password = \"\";\n for (const key of [...url.searchParams.keys()]) {\n if (credentialKey(key)) url.searchParams.delete(key);\n }\n // preserve SPA routes unless the fragment contains credential parameters\n const fragment = url.hash.slice(1);\n const parameters = new URLSearchParams(fragment.includes(\"?\") ? fragment.slice(fragment.indexOf(\"?\") + 1) : fragment);\n if ([...parameters.keys()].some(credentialKey)) url.hash = \"\";\n return url.href;\n}\n\nexport function createSettingsExport(cards: unknown, shortcuts: unknown) {\n const backup = {\n format: \"freelens-power-settings\",\n version: 1,\n dashboard: {\n cards: sanitizeDashboardCards(cards).map((card, index, all) => {\n const url = exportUrl(card.url);\n let id = card.id;\n if (/^card-https?%3a%2f%2f/i.test(id)) {\n id = `export-card-${index + 1}`;\n while (all.some((entry) => entry.id === id)) id += \"-\";\n }\n return { ...card, url, id };\n }),\n },\n ai: { shortcuts: sanitizeAiShortcuts(shortcuts) },\n };\n if (new TextEncoder().encode(`${JSON.stringify(backup, null, 2)}\\n`).byteLength > SETTINGS_BACKUP_MAX_BYTES)\n throw new Error(\"Settings backup is too large\");\n return backup;\n}\n"],"mappings":"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"}
@@ -39,12 +39,13 @@ var DashboardPreferencesStore = class extends require_extensions.Common.Store.Ex
39
39
  this.cards.splice(destination, 0, card);
40
40
  }
41
41
  toJSON() {
42
- return { cards: this.cards.map(({ id, title, url, size, autoRefresh, selector }) => ({
42
+ return { cards: this.cards.map(({ id, title, url, size, autoRefresh, refreshEvery10Seconds, selector }) => ({
43
43
  id,
44
44
  title,
45
45
  url,
46
46
  size,
47
47
  autoRefresh: autoRefresh === true,
48
+ ...refreshEvery10Seconds === true ? { refreshEvery10Seconds: true } : {},
48
49
  ...selector ? { selector } : {}
49
50
  })) };
50
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-preferences-store.js","names":[],"sources":["../../../../src/common/store/dashboard-preferences-store.ts"],"sourcesContent":["import { Common } from \"@freelensapp/extensions\";\nimport { action, makeObservable, observable } from \"mobx\";\nimport { type DashboardCard, sanitizeDashboardCards } from \"../dashboard\";\n\nexport interface DashboardPreferencesModel {\n cards: DashboardCard[];\n}\n\nexport class DashboardPreferencesStore extends Common.Store.ExtensionStore<DashboardPreferencesModel> {\n cards: DashboardCard[] = [];\n\n constructor() {\n super({ configName: \"dashboard-preferences\", defaults: { cards: [] } });\n makeObservable(this, {\n cards: observable,\n fromStore: action,\n upsertCard: action,\n removeCard: action,\n moveCard: action,\n });\n }\n\n fromStore(data: Partial<DashboardPreferencesModel>): void {\n this.cards = sanitizeDashboardCards(data?.cards);\n }\n\n upsertCard(card: DashboardCard): void {\n const normalized = sanitizeDashboardCards([card])[0];\n if (!normalized) return;\n const index = this.cards.findIndex((entry) => entry.id === normalized.id);\n if (index < 0) this.cards.push(normalized);\n else this.cards[index] = normalized;\n }\n\n removeCard(id: string): void {\n this.cards = this.cards.filter((card) => card.id !== id);\n }\n\n moveCard(id: string, targetIndex: number): void {\n const index = this.cards.findIndex((card) => card.id === id);\n if (index < 0 || !Number.isFinite(targetIndex)) return;\n const destination = Math.max(0, Math.min(this.cards.length - 1, Math.trunc(targetIndex)));\n if (index === destination) return;\n const [card] = this.cards.splice(index, 1);\n this.cards.splice(destination, 0, card);\n }\n\n toJSON(): DashboardPreferencesModel {\n return {\n cards: this.cards.map(({ id, title, url, size, autoRefresh, selector }) => ({\n id,\n title,\n url,\n size,\n autoRefresh: autoRefresh === true,\n ...(selector ? { selector } : {}),\n })),\n };\n }\n}\n"],"mappings":";;;;AAQA,IAAa,4BAAb,cAA+C,mBAAA,OAAO,MAAM,eAA0C;CACpG,QAAyB,CAAC;CAE1B,cAAc;EACZ,MAAM;GAAE,YAAY;GAAyB,UAAU,EAAE,OAAO,CAAC,EAAE;EAAE,CAAC;EACtE,8BAAA,eAAe,MAAM;GACnB,OAAO,8BAAA;GACP,WAAW,8BAAA;GACX,YAAY,8BAAA;GACZ,YAAY,8BAAA;GACZ,UAAU,8BAAA;EACZ,CAAC;CACH;CAEA,UAAU,MAAgD;EACxD,KAAK,QAAQ,kBAAA,uBAAuB,MAAM,KAAK;CACjD;CAEA,WAAW,MAA2B;EACpC,MAAM,aAAa,kBAAA,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;EAClD,IAAI,CAAC,YAAY;EACjB,MAAM,QAAQ,KAAK,MAAM,WAAW,UAAU,MAAM,OAAO,WAAW,EAAE;EACxE,IAAI,QAAQ,GAAG,KAAK,MAAM,KAAK,UAAU;OACpC,KAAK,MAAM,SAAS;CAC3B;CAEA,WAAW,IAAkB;EAC3B,KAAK,QAAQ,KAAK,MAAM,QAAQ,SAAS,KAAK,OAAO,EAAE;CACzD;CAEA,SAAS,IAAY,aAA2B;EAC9C,MAAM,QAAQ,KAAK,MAAM,WAAW,SAAS,KAAK,OAAO,EAAE;EAC3D,IAAI,QAAQ,KAAK,CAAC,OAAO,SAAS,WAAW,GAAG;EAChD,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,WAAW,CAAC,CAAC;EACxF,IAAI,UAAU,aAAa;EAC3B,MAAM,CAAC,QAAQ,KAAK,MAAM,OAAO,OAAO,CAAC;EACzC,KAAK,MAAM,OAAO,aAAa,GAAG,IAAI;CACxC;CAEA,SAAoC;EAClC,OAAO,EACL,OAAO,KAAK,MAAM,KAAK,EAAE,IAAI,OAAO,KAAK,MAAM,aAAa,gBAAgB;GAC1E;GACA;GACA;GACA;GACA,aAAa,gBAAgB;GAC7B,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACjC,EAAE,EACJ;CACF;AACF"}
1
+ {"version":3,"file":"dashboard-preferences-store.js","names":[],"sources":["../../../../src/common/store/dashboard-preferences-store.ts"],"sourcesContent":["import { Common } from \"@freelensapp/extensions\";\nimport { action, makeObservable, observable } from \"mobx\";\nimport { type DashboardCard, sanitizeDashboardCards } from \"../dashboard\";\n\nexport interface DashboardPreferencesModel {\n cards: DashboardCard[];\n}\n\nexport class DashboardPreferencesStore extends Common.Store.ExtensionStore<DashboardPreferencesModel> {\n cards: DashboardCard[] = [];\n\n constructor() {\n super({ configName: \"dashboard-preferences\", defaults: { cards: [] } });\n makeObservable(this, {\n cards: observable,\n fromStore: action,\n upsertCard: action,\n removeCard: action,\n moveCard: action,\n });\n }\n\n fromStore(data: Partial<DashboardPreferencesModel>): void {\n this.cards = sanitizeDashboardCards(data?.cards);\n }\n\n upsertCard(card: DashboardCard): void {\n const normalized = sanitizeDashboardCards([card])[0];\n if (!normalized) return;\n const index = this.cards.findIndex((entry) => entry.id === normalized.id);\n if (index < 0) this.cards.push(normalized);\n else this.cards[index] = normalized;\n }\n\n removeCard(id: string): void {\n this.cards = this.cards.filter((card) => card.id !== id);\n }\n\n moveCard(id: string, targetIndex: number): void {\n const index = this.cards.findIndex((card) => card.id === id);\n if (index < 0 || !Number.isFinite(targetIndex)) return;\n const destination = Math.max(0, Math.min(this.cards.length - 1, Math.trunc(targetIndex)));\n if (index === destination) return;\n const [card] = this.cards.splice(index, 1);\n this.cards.splice(destination, 0, card);\n }\n\n toJSON(): DashboardPreferencesModel {\n return {\n cards: this.cards.map(({ id, title, url, size, autoRefresh, refreshEvery10Seconds, selector }) => ({\n id,\n title,\n url,\n size,\n autoRefresh: autoRefresh === true,\n ...(refreshEvery10Seconds === true ? { refreshEvery10Seconds: true } : {}),\n ...(selector ? { selector } : {}),\n })),\n };\n }\n}\n"],"mappings":";;;;AAQA,IAAa,4BAAb,cAA+C,mBAAA,OAAO,MAAM,eAA0C;CACpG,QAAyB,CAAC;CAE1B,cAAc;EACZ,MAAM;GAAE,YAAY;GAAyB,UAAU,EAAE,OAAO,CAAC,EAAE;EAAE,CAAC;EACtE,8BAAA,eAAe,MAAM;GACnB,OAAO,8BAAA;GACP,WAAW,8BAAA;GACX,YAAY,8BAAA;GACZ,YAAY,8BAAA;GACZ,UAAU,8BAAA;EACZ,CAAC;CACH;CAEA,UAAU,MAAgD;EACxD,KAAK,QAAQ,kBAAA,uBAAuB,MAAM,KAAK;CACjD;CAEA,WAAW,MAA2B;EACpC,MAAM,aAAa,kBAAA,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;EAClD,IAAI,CAAC,YAAY;EACjB,MAAM,QAAQ,KAAK,MAAM,WAAW,UAAU,MAAM,OAAO,WAAW,EAAE;EACxE,IAAI,QAAQ,GAAG,KAAK,MAAM,KAAK,UAAU;OACpC,KAAK,MAAM,SAAS;CAC3B;CAEA,WAAW,IAAkB;EAC3B,KAAK,QAAQ,KAAK,MAAM,QAAQ,SAAS,KAAK,OAAO,EAAE;CACzD;CAEA,SAAS,IAAY,aAA2B;EAC9C,MAAM,QAAQ,KAAK,MAAM,WAAW,SAAS,KAAK,OAAO,EAAE;EAC3D,IAAI,QAAQ,KAAK,CAAC,OAAO,SAAS,WAAW,GAAG;EAChD,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,WAAW,CAAC,CAAC;EACxF,IAAI,UAAU,aAAa;EAC3B,MAAM,CAAC,QAAQ,KAAK,MAAM,OAAO,OAAO,CAAC;EACzC,KAAK,MAAM,OAAO,aAAa,GAAG,IAAI;CACxC;CAEA,SAAoC;EAClC,OAAO,EACL,OAAO,KAAK,MAAM,KAAK,EAAE,IAAI,OAAO,KAAK,MAAM,aAAa,uBAAuB,gBAAgB;GACjG;GACA;GACA;GACA;GACA,aAAa,gBAAgB;GAC7B,GAAI,0BAA0B,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAC;GACxE,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EACjC,EAAE,EACJ;CACF;AACF"}
@@ -1,4 +1,3 @@
1
- const require_appearance_preferences_store = require("./common/store/appearance-preferences-store.js");
2
1
  const require_dashboard_selector = require("./common/dashboard-selector.js");
3
2
  const require_dashboard = require("./common/dashboard.js");
4
3
  const require_dashboard_picker = require("./common/dashboard-picker.js");
@@ -98,9 +97,7 @@ function startDashboardPicker() {
98
97
  cancel();
99
98
  return;
100
99
  }
101
- const preferences = require_appearance_preferences_store.AppearancePreferencesStore.getInstanceOrCreate();
102
- const accent = preferences.enabled ? require_appearance_preferences_store.ACCENT_COLOR_OPTIONS.find((option) => option.value === preferences.accentColor)?.color : void 0;
103
- picker.webContents.executeJavaScriptInIsolatedWorld(1007, [{ code: `globalThis.powerDashboardPickerSelection = ${require_dashboard_picker.dashboardPickerScript(accent ?? "#00a7a0")}; true` }]).then((installed) => {
100
+ picker.webContents.executeJavaScriptInIsolatedWorld(1007, [{ code: `globalThis.powerDashboardPickerSelection = ${require_dashboard_picker.dashboardPickerScript()}; true` }]).then((installed) => {
104
101
  if (done) return null;
105
102
  if (installed !== true) throw new Error("Picker could not start");
106
103
  picker.contentView.setVisible(true);
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-picker.js","names":[],"sources":["../../src/main/dashboard-picker.ts"],"sourcesContent":["import { BrowserWindow, type IpcMainInvokeEvent, ipcMain, session } from \"electron\";\nimport { normalizeDashboardUrl } from \"../common/dashboard\";\nimport { DASHBOARD_PICK_ELEMENT, type DashboardPickerResult, dashboardPickerScript } from \"../common/dashboard-picker\";\nimport { normalizeDashboardSelector } from \"../common/dashboard-selector\";\nimport { ACCENT_COLOR_OPTIONS, AppearancePreferencesStore } from \"../common/store/appearance-preferences-store\";\n\nexport function trustedPickerSender(event: IpcMainInvokeEvent): boolean {\n const trusted = (value: string) => {\n try {\n const url = new URL(value);\n return (\n url.protocol === \"https:\" &&\n (url.hostname === \"renderer.freelens.app\" || url.hostname.endsWith(\".renderer.freelens.app\")) &&\n (url.pathname === \"/preferences\" || url.pathname.startsWith(\"/preferences/\"))\n );\n } catch {\n return false;\n }\n };\n return (\n event.senderFrame === event.sender.mainFrame &&\n trusted(event.senderFrame?.url ?? \"\") &&\n trusted(event.sender.getURL())\n );\n}\n\nexport function startDashboardPicker(): () => void {\n const active = new Map<BrowserWindow, () => void>();\n const pending = new WeakSet<object>();\n let running = true;\n ipcMain.handle(DASHBOARD_PICK_ELEMENT, async (event, payload: unknown): Promise<DashboardPickerResult> => {\n if (!running || !trustedPickerSender(event) || pending.has(event.sender) || !payload || typeof payload !== \"object\")\n return { status: \"error\" };\n const rawUrl = (payload as { url?: unknown }).url;\n const url = typeof rawUrl === \"string\" && rawUrl.length <= 8192 ? normalizeDashboardUrl(rawUrl) : undefined;\n const owner = BrowserWindow.fromWebContents(event.sender);\n if (!url || !owner || owner.isDestroyed()) return { status: \"error\" };\n pending.add(event.sender);\n try {\n const websiteSession = session.fromPartition(\"persist:freelens-power-websites\");\n websiteSession.setPermissionRequestHandler((_contents, _permission, callback) => callback(false));\n websiteSession.setPermissionCheckHandler(() => false);\n const picker = new BrowserWindow({\n parent: owner,\n modal: true,\n width: 1100,\n height: 800,\n show: false,\n title: \"Select website element · Click to select · Esc to cancel\",\n autoHideMenuBar: true,\n webPreferences: {\n nodeIntegration: false,\n contextIsolation: true,\n sandbox: true,\n webSecurity: true,\n partition: \"persist:freelens-power-websites\",\n },\n });\n picker.setMenu(null);\n picker.contentView.setVisible(false);\n return await new Promise<DashboardPickerResult>((resolve) => {\n let done = false;\n let loaded = false;\n const finish = (result: DashboardPickerResult) => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n active.delete(picker);\n event.sender.removeListener(\"destroyed\", cancel);\n event.sender.removeListener(\"did-start-navigation\", cancel);\n if (!picker.isDestroyed()) picker.destroy();\n resolve(result);\n };\n const cancel = () => finish({ status: \"canceled\" });\n const timer = setTimeout(cancel, 5 * 60_000);\n active.set(picker, cancel);\n event.sender.once(\"destroyed\", cancel);\n event.sender.on(\"did-start-navigation\", cancel);\n picker.once(\"closed\", cancel);\n picker.webContents.setWindowOpenHandler(() => ({ action: \"deny\" }));\n picker.webContents.on(\"will-navigate\", (navigation, target) => {\n if (loaded || !normalizeDashboardUrl(target)) {\n navigation.preventDefault();\n cancel();\n }\n });\n picker.webContents.on(\"did-start-navigation\", (_navigation, _target, _inPlace, mainFrame) => {\n if (loaded && mainFrame) cancel();\n });\n picker.webContents.on(\"will-redirect\", (navigation, target) => {\n if (!normalizeDashboardUrl(target)) {\n navigation.preventDefault();\n cancel();\n }\n });\n picker.webContents.on(\"will-attach-webview\", (attach) => attach.preventDefault());\n picker.webContents.on(\"before-input-event\", (inputEvent, input) => {\n if (input.key === \"Escape\") {\n inputEvent.preventDefault();\n cancel();\n }\n });\n picker.webContents.once(\"render-process-gone\", () => finish({ status: \"error\" }));\n picker.webContents.once(\"did-finish-load\", () => {\n if (done) return;\n loaded = true;\n if (!normalizeDashboardUrl(picker.webContents.getURL())) {\n cancel();\n return;\n }\n const preferences = AppearancePreferencesStore.getInstanceOrCreate<AppearancePreferencesStore>();\n const accent = preferences.enabled\n ? ACCENT_COLOR_OPTIONS.find((option) => option.value === preferences.accentColor)?.color\n : undefined;\n void picker.webContents\n .executeJavaScriptInIsolatedWorld(1007, [\n {\n code: `globalThis.powerDashboardPickerSelection = ${dashboardPickerScript(accent ?? \"#00a7a0\")}; true`,\n },\n ])\n .then((installed: unknown) => {\n if (done) return null;\n if (installed !== true) throw new Error(\"Picker could not start\");\n picker.contentView.setVisible(true);\n return picker.webContents.executeJavaScriptInIsolatedWorld(1007, [\n {\n code: \"globalThis.powerDashboardPickerSelection.then((selection) => { delete globalThis.powerDashboardPickerSelection; return selection; })\",\n },\n ]);\n })\n .then((result: unknown) => {\n if (done) return;\n const selector = normalizeDashboardSelector(result);\n finish(selector ? { status: \"selected\", selector } : { status: \"canceled\" });\n })\n .catch(() => finish({ status: \"error\" }));\n });\n picker.show();\n void picker.loadURL(url).catch(() => finish({ status: \"error\" }));\n });\n } catch {\n return { status: \"error\" };\n } finally {\n pending.delete(event.sender);\n }\n });\n return () => {\n running = false;\n ipcMain.removeHandler(DASHBOARD_PICK_ELEMENT);\n for (const cancel of active.values()) cancel();\n };\n}\n"],"mappings":";;;;;;AAMA,SAAgB,oBAAoB,OAAoC;CACtE,MAAM,WAAW,UAAkB;EACjC,IAAI;GACF,MAAM,MAAM,IAAI,IAAI,KAAK;GACzB,OACE,IAAI,aAAa,aAChB,IAAI,aAAa,2BAA2B,IAAI,SAAS,SAAS,wBAAwB,OAC1F,IAAI,aAAa,kBAAkB,IAAI,SAAS,WAAW,eAAe;EAE/E,QAAQ;GACN,OAAO;EACT;CACF;CACA,OACE,MAAM,gBAAgB,MAAM,OAAO,aACnC,QAAQ,MAAM,aAAa,OAAO,EAAE,KACpC,QAAQ,MAAM,OAAO,OAAO,CAAC;AAEjC;AAEA,SAAgB,uBAAmC;CACjD,MAAM,yBAAS,IAAI,IAA+B;CAClD,MAAM,0BAAU,IAAI,QAAgB;CACpC,IAAI,UAAU;CACd,SAAA,QAAQ,OAAO,yBAAA,wBAAwB,OAAO,OAAO,YAAqD;EACxG,IAAI,CAAC,WAAW,CAAC,oBAAoB,KAAK,KAAK,QAAQ,IAAI,MAAM,MAAM,KAAK,CAAC,WAAW,OAAO,YAAY,UACzG,OAAO,EAAE,QAAQ,QAAQ;EAC3B,MAAM,SAAU,QAA8B;EAC9C,MAAM,MAAM,OAAO,WAAW,YAAY,OAAO,UAAU,OAAO,kBAAA,sBAAsB,MAAM,IAAI,KAAA;EAClG,MAAM,QAAQ,SAAA,cAAc,gBAAgB,MAAM,MAAM;EACxD,IAAI,CAAC,OAAO,CAAC,SAAS,MAAM,YAAY,GAAG,OAAO,EAAE,QAAQ,QAAQ;EACpE,QAAQ,IAAI,MAAM,MAAM;EACxB,IAAI;GACF,MAAM,iBAAiB,SAAA,QAAQ,cAAc,iCAAiC;GAC9E,eAAe,6BAA6B,WAAW,aAAa,aAAa,SAAS,KAAK,CAAC;GAChG,eAAe,gCAAgC,KAAK;GACpD,MAAM,SAAS,IAAI,SAAA,cAAc;IAC/B,QAAQ;IACR,OAAO;IACP,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;IACP,iBAAiB;IACjB,gBAAgB;KACd,iBAAiB;KACjB,kBAAkB;KAClB,SAAS;KACT,aAAa;KACb,WAAW;IACb;GACF,CAAC;GACD,OAAO,QAAQ,IAAI;GACnB,OAAO,YAAY,WAAW,KAAK;GACnC,OAAO,MAAM,IAAI,SAAgC,YAAY;IAC3D,IAAI,OAAO;IACX,IAAI,SAAS;IACb,MAAM,UAAU,WAAkC;KAChD,IAAI,MAAM;KACV,OAAO;KACP,aAAa,KAAK;KAClB,OAAO,OAAO,MAAM;KACpB,MAAM,OAAO,eAAe,aAAa,MAAM;KAC/C,MAAM,OAAO,eAAe,wBAAwB,MAAM;KAC1D,IAAI,CAAC,OAAO,YAAY,GAAG,OAAO,QAAQ;KAC1C,QAAQ,MAAM;IAChB;IACA,MAAM,eAAe,OAAO,EAAE,QAAQ,WAAW,CAAC;IAClD,MAAM,QAAQ,WAAW,QAAQ,IAAI,GAAM;IAC3C,OAAO,IAAI,QAAQ,MAAM;IACzB,MAAM,OAAO,KAAK,aAAa,MAAM;IACrC,MAAM,OAAO,GAAG,wBAAwB,MAAM;IAC9C,OAAO,KAAK,UAAU,MAAM;IAC5B,OAAO,YAAY,4BAA4B,EAAE,QAAQ,OAAO,EAAE;IAClE,OAAO,YAAY,GAAG,kBAAkB,YAAY,WAAW;KAC7D,IAAI,UAAU,CAAC,kBAAA,sBAAsB,MAAM,GAAG;MAC5C,WAAW,eAAe;MAC1B,OAAO;KACT;IACF,CAAC;IACD,OAAO,YAAY,GAAG,yBAAyB,aAAa,SAAS,UAAU,cAAc;KAC3F,IAAI,UAAU,WAAW,OAAO;IAClC,CAAC;IACD,OAAO,YAAY,GAAG,kBAAkB,YAAY,WAAW;KAC7D,IAAI,CAAC,kBAAA,sBAAsB,MAAM,GAAG;MAClC,WAAW,eAAe;MAC1B,OAAO;KACT;IACF,CAAC;IACD,OAAO,YAAY,GAAG,wBAAwB,WAAW,OAAO,eAAe,CAAC;IAChF,OAAO,YAAY,GAAG,uBAAuB,YAAY,UAAU;KACjE,IAAI,MAAM,QAAQ,UAAU;MAC1B,WAAW,eAAe;MAC1B,OAAO;KACT;IACF,CAAC;IACD,OAAO,YAAY,KAAK,6BAA6B,OAAO,EAAE,QAAQ,QAAQ,CAAC,CAAC;IAChF,OAAO,YAAY,KAAK,yBAAyB;KAC/C,IAAI,MAAM;KACV,SAAS;KACT,IAAI,CAAC,kBAAA,sBAAsB,OAAO,YAAY,OAAO,CAAC,GAAG;MACvD,OAAO;MACP;KACF;KACA,MAAM,cAAc,qCAAA,2BAA2B,oBAAgD;KAC/F,MAAM,SAAS,YAAY,UACvB,qCAAA,qBAAqB,MAAM,WAAW,OAAO,UAAU,YAAY,WAAW,CAAC,EAAE,QACjF,KAAA;KACJ,OAAY,YACT,iCAAiC,MAAM,CACtC,EACE,MAAM,8CAA8C,yBAAA,sBAAsB,UAAU,SAAS,EAAE,QACjG,CACF,CAAC,CAAC,CACD,MAAM,cAAuB;MAC5B,IAAI,MAAM,OAAO;MACjB,IAAI,cAAc,MAAM,MAAM,IAAI,MAAM,wBAAwB;MAChE,OAAO,YAAY,WAAW,IAAI;MAClC,OAAO,OAAO,YAAY,iCAAiC,MAAM,CAC/D,EACE,MAAM,uIACR,CACF,CAAC;KACH,CAAC,CAAC,CACD,MAAM,WAAoB;MACzB,IAAI,MAAM;MACV,MAAM,WAAW,2BAAA,2BAA2B,MAAM;MAClD,OAAO,WAAW;OAAE,QAAQ;OAAY;MAAS,IAAI,EAAE,QAAQ,WAAW,CAAC;KAC7E,CAAC,CAAC,CACD,YAAY,OAAO,EAAE,QAAQ,QAAQ,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK;IACZ,OAAY,QAAQ,GAAG,CAAC,CAAC,YAAY,OAAO,EAAE,QAAQ,QAAQ,CAAC,CAAC;GAClE,CAAC;EACH,QAAQ;GACN,OAAO,EAAE,QAAQ,QAAQ;EAC3B,UAAU;GACR,QAAQ,OAAO,MAAM,MAAM;EAC7B;CACF,CAAC;CACD,aAAa;EACX,UAAU;EACV,SAAA,QAAQ,cAAc,yBAAA,sBAAsB;EAC5C,KAAK,MAAM,UAAU,OAAO,OAAO,GAAG,OAAO;CAC/C;AACF"}
1
+ {"version":3,"file":"dashboard-picker.js","names":[],"sources":["../../src/main/dashboard-picker.ts"],"sourcesContent":["import { BrowserWindow, type IpcMainInvokeEvent, ipcMain, session } from \"electron\";\nimport { normalizeDashboardUrl } from \"../common/dashboard\";\nimport { DASHBOARD_PICK_ELEMENT, type DashboardPickerResult, dashboardPickerScript } from \"../common/dashboard-picker\";\nimport { normalizeDashboardSelector } from \"../common/dashboard-selector\";\n\nexport function trustedPickerSender(event: IpcMainInvokeEvent): boolean {\n const trusted = (value: string) => {\n try {\n const url = new URL(value);\n return (\n url.protocol === \"https:\" &&\n (url.hostname === \"renderer.freelens.app\" || url.hostname.endsWith(\".renderer.freelens.app\")) &&\n (url.pathname === \"/preferences\" || url.pathname.startsWith(\"/preferences/\"))\n );\n } catch {\n return false;\n }\n };\n return (\n event.senderFrame === event.sender.mainFrame &&\n trusted(event.senderFrame?.url ?? \"\") &&\n trusted(event.sender.getURL())\n );\n}\n\nexport function startDashboardPicker(): () => void {\n const active = new Map<BrowserWindow, () => void>();\n const pending = new WeakSet<object>();\n let running = true;\n ipcMain.handle(DASHBOARD_PICK_ELEMENT, async (event, payload: unknown): Promise<DashboardPickerResult> => {\n if (!running || !trustedPickerSender(event) || pending.has(event.sender) || !payload || typeof payload !== \"object\")\n return { status: \"error\" };\n const rawUrl = (payload as { url?: unknown }).url;\n const url = typeof rawUrl === \"string\" && rawUrl.length <= 8192 ? normalizeDashboardUrl(rawUrl) : undefined;\n const owner = BrowserWindow.fromWebContents(event.sender);\n if (!url || !owner || owner.isDestroyed()) return { status: \"error\" };\n pending.add(event.sender);\n try {\n const websiteSession = session.fromPartition(\"persist:freelens-power-websites\");\n websiteSession.setPermissionRequestHandler((_contents, _permission, callback) => callback(false));\n websiteSession.setPermissionCheckHandler(() => false);\n const picker = new BrowserWindow({\n parent: owner,\n modal: true,\n width: 1100,\n height: 800,\n show: false,\n title: \"Select website element · Click to select · Esc to cancel\",\n autoHideMenuBar: true,\n webPreferences: {\n nodeIntegration: false,\n contextIsolation: true,\n sandbox: true,\n webSecurity: true,\n partition: \"persist:freelens-power-websites\",\n },\n });\n picker.setMenu(null);\n picker.contentView.setVisible(false);\n return await new Promise<DashboardPickerResult>((resolve) => {\n let done = false;\n let loaded = false;\n const finish = (result: DashboardPickerResult) => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n active.delete(picker);\n event.sender.removeListener(\"destroyed\", cancel);\n event.sender.removeListener(\"did-start-navigation\", cancel);\n if (!picker.isDestroyed()) picker.destroy();\n resolve(result);\n };\n const cancel = () => finish({ status: \"canceled\" });\n const timer = setTimeout(cancel, 5 * 60_000);\n active.set(picker, cancel);\n event.sender.once(\"destroyed\", cancel);\n event.sender.on(\"did-start-navigation\", cancel);\n picker.once(\"closed\", cancel);\n picker.webContents.setWindowOpenHandler(() => ({ action: \"deny\" }));\n picker.webContents.on(\"will-navigate\", (navigation, target) => {\n if (loaded || !normalizeDashboardUrl(target)) {\n navigation.preventDefault();\n cancel();\n }\n });\n picker.webContents.on(\"did-start-navigation\", (_navigation, _target, _inPlace, mainFrame) => {\n if (loaded && mainFrame) cancel();\n });\n picker.webContents.on(\"will-redirect\", (navigation, target) => {\n if (!normalizeDashboardUrl(target)) {\n navigation.preventDefault();\n cancel();\n }\n });\n picker.webContents.on(\"will-attach-webview\", (attach) => attach.preventDefault());\n picker.webContents.on(\"before-input-event\", (inputEvent, input) => {\n if (input.key === \"Escape\") {\n inputEvent.preventDefault();\n cancel();\n }\n });\n picker.webContents.once(\"render-process-gone\", () => finish({ status: \"error\" }));\n picker.webContents.once(\"did-finish-load\", () => {\n if (done) return;\n loaded = true;\n if (!normalizeDashboardUrl(picker.webContents.getURL())) {\n cancel();\n return;\n }\n void picker.webContents\n .executeJavaScriptInIsolatedWorld(1007, [\n {\n code: `globalThis.powerDashboardPickerSelection = ${dashboardPickerScript()}; true`,\n },\n ])\n .then((installed: unknown) => {\n if (done) return null;\n if (installed !== true) throw new Error(\"Picker could not start\");\n picker.contentView.setVisible(true);\n return picker.webContents.executeJavaScriptInIsolatedWorld(1007, [\n {\n code: \"globalThis.powerDashboardPickerSelection.then((selection) => { delete globalThis.powerDashboardPickerSelection; return selection; })\",\n },\n ]);\n })\n .then((result: unknown) => {\n if (done) return;\n const selector = normalizeDashboardSelector(result);\n finish(selector ? { status: \"selected\", selector } : { status: \"canceled\" });\n })\n .catch(() => finish({ status: \"error\" }));\n });\n picker.show();\n void picker.loadURL(url).catch(() => finish({ status: \"error\" }));\n });\n } catch {\n return { status: \"error\" };\n } finally {\n pending.delete(event.sender);\n }\n });\n return () => {\n running = false;\n ipcMain.removeHandler(DASHBOARD_PICK_ELEMENT);\n for (const cancel of active.values()) cancel();\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/main/dashboard-preload.ts
3
+ (() => {
4
+ const properties = [["opacity", "0"], ["pointer-events", "none"]];
5
+ const masked = /* @__PURE__ */ new Map();
6
+ const overlay = document.createElement("freelens-power-loading");
7
+ const style = (element, values) => {
8
+ for (const [property, value] of Object.entries(values)) element.style.setProperty(property, value, "important");
9
+ };
10
+ const dark = window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
11
+ style(overlay, {
12
+ position: "fixed",
13
+ inset: "0",
14
+ display: "block",
15
+ "z-index": "2147483647",
16
+ "pointer-events": "all",
17
+ opacity: "1",
18
+ background: dark ? "#202124" : "#ffffff",
19
+ padding: "20px",
20
+ "box-sizing": "border-box",
21
+ overflow: "hidden"
22
+ });
23
+ const shadow = overlay.attachShadow({ mode: "open" });
24
+ for (let index = 0; index < 4; index++) {
25
+ const bar = document.createElement("div");
26
+ style(bar, {
27
+ display: "block",
28
+ background: dark ? "#393b40" : "#e8e9eb",
29
+ "border-radius": "6px",
30
+ width: index === 0 ? "45%" : "100%",
31
+ height: index === 0 ? "20px" : index === 3 ? "calc(100vh - 148px)" : "12px",
32
+ "min-height": index === 3 ? "30px" : "0",
33
+ "margin-bottom": index === 0 ? "20px" : "12px"
34
+ });
35
+ shadow.append(bar);
36
+ }
37
+ const animations = [];
38
+ let preparing = false;
39
+ const restore = (element) => {
40
+ for (const [property, value] of properties) {
41
+ const saved = masked.get(element)?.get(property);
42
+ if (saved && element.style.getPropertyValue(property) === value && element.style.getPropertyPriority(property) === "important") if (saved.value) element.style.setProperty(property, saved.value, saved.priority);
43
+ else element.style.removeProperty(property);
44
+ }
45
+ masked.delete(element);
46
+ };
47
+ const apply = () => {
48
+ if (!preparing || !document.documentElement) return;
49
+ if (overlay.parentElement !== document.documentElement) document.documentElement.append(overlay);
50
+ for (const element of masked.keys()) if (element.parentElement !== document.documentElement) restore(element);
51
+ for (const element of Array.from(document.documentElement.children)) {
52
+ if (!(element instanceof HTMLElement) || element === overlay) continue;
53
+ let previous = masked.get(element);
54
+ if (!previous) {
55
+ previous = /* @__PURE__ */ new Map();
56
+ masked.set(element, previous);
57
+ }
58
+ for (const [property, value] of properties) {
59
+ const current = element.style.getPropertyValue(property);
60
+ const priority = element.style.getPropertyPriority(property);
61
+ if (!previous.has(property) || current !== value || priority !== "important") {
62
+ previous.set(property, {
63
+ value: current,
64
+ priority
65
+ });
66
+ element.style.setProperty(property, value, "important");
67
+ }
68
+ }
69
+ }
70
+ };
71
+ const observer = new MutationObserver(apply);
72
+ const prepare = () => {
73
+ if (!preparing && !(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false)) {
74
+ for (const element of Array.from(shadow.children)) if (element instanceof HTMLElement && typeof element.animate === "function") try {
75
+ animations.push(element.animate([{ opacity: .55 }, { opacity: 1 }], {
76
+ duration: 1200,
77
+ direction: "alternate",
78
+ iterations: Infinity
79
+ }));
80
+ } catch {}
81
+ }
82
+ preparing = true;
83
+ observer.observe(document, {
84
+ childList: true,
85
+ subtree: true,
86
+ attributes: true,
87
+ attributeFilter: ["style"]
88
+ });
89
+ apply();
90
+ };
91
+ const release = () => {
92
+ preparing = false;
93
+ for (const animation of animations) animation.cancel();
94
+ animations.length = 0;
95
+ observer.disconnect();
96
+ for (const element of masked.keys()) restore(element);
97
+ overlay.remove();
98
+ };
99
+ document.addEventListener("freelens-power:dashboard:preparing", prepare);
100
+ document.addEventListener("freelens-power:dashboard:prepared", release);
101
+ window.addEventListener("pagehide", () => {
102
+ release();
103
+ document.removeEventListener("freelens-power:dashboard:preparing", prepare);
104
+ document.removeEventListener("freelens-power:dashboard:prepared", release);
105
+ });
106
+ prepare();
107
+ })();
108
+ //#endregion
109
+
110
+ //# sourceMappingURL=dashboard-preload.js.map