@64mb/freelens-power 1.6.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/out/main/common/power-logs.js.map +1 -1
- package/out/main/common/resource-search-kind-priority.js +23 -0
- package/out/main/common/resource-search-kind-priority.js.map +1 -0
- package/out/main/common/store/logs-preferences-store.js +5 -0
- package/out/main/common/store/logs-preferences-store.js.map +1 -1
- package/out/main/dashboard-views.js +26 -0
- package/out/main/dashboard-views.js.map +1 -1
- package/out/main/power-logs-database.js +5 -0
- package/out/main/power-logs-database.js.map +1 -1
- package/out/main/power-logs-ipc.js +8 -0
- package/out/main/power-logs-ipc.js.map +1 -1
- package/out/main/power-logs-query.js +1 -0
- package/out/main/power-logs-query.js.map +1 -1
- package/out/main/resource-search-database.js +7 -2
- package/out/main/resource-search-database.js.map +1 -1
- package/out/main/resource-search-worker.js +6 -2
- package/out/main/resource-search-worker.js.map +1 -1
- package/out/renderer/_virtual/_global-external_react.js +2 -1
- package/out/renderer/ai-diagnostic-handoff.js +1 -0
- package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
- package/out/renderer/ai-extras.js +7 -2
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai.js +1 -1
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +2 -0
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/dashboard.js +35 -8
- package/out/renderer/dashboard.js.map +1 -1
- package/out/renderer/index.js +2 -1
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/power-log-json-cell.js +82 -0
- package/out/renderer/power-log-json-cell.js.map +1 -0
- package/out/renderer/power-log-json-fields.js +144 -0
- package/out/renderer/power-log-json-fields.js.map +1 -0
- package/out/renderer/power-log-json.js +223 -20
- package/out/renderer/power-log-json.js.map +1 -1
- package/out/renderer/power-log-message-tokens.js +60 -0
- package/out/renderer/power-log-message-tokens.js.map +1 -0
- package/out/renderer/power-log-message.js +26 -0
- package/out/renderer/power-log-message.js.map +1 -0
- package/out/renderer/power-log-sql-editor.js +91 -0
- package/out/renderer/power-log-sql-editor.js.map +1 -0
- package/out/renderer/power-logs-client.js +4 -0
- package/out/renderer/power-logs-client.js.map +1 -1
- package/out/renderer/power-logs-sql-drafts.js +76 -0
- package/out/renderer/power-logs-sql-drafts.js.map +1 -0
- package/out/renderer/power-logs-sql-row.js +5 -13
- package/out/renderer/power-logs-sql-row.js.map +1 -1
- package/out/renderer/power-logs.css.js +39 -6
- package/out/renderer/power-logs.css.js.map +1 -1
- package/out/renderer/power-logs.js +372 -135
- package/out/renderer/power-logs.js.map +1 -1
- package/out/renderer/preferences/logs-preferences.js +159 -38
- package/out/renderer/preferences/logs-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +2 -9
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/section-navigation.js +44 -0
- package/out/renderer/preferences/section-navigation.js.map +1 -0
- package/out/renderer/resource-search-model.js +2 -1
- package/out/renderer/resource-search-model.js.map +1 -1
- package/out/renderer/resource-search-sync.js +1 -1
- package/out/renderer/resource-search-sync.js.map +1 -1
- package/out/renderer/resource-search.js +84 -81
- package/out/renderer/resource-search.js.map +1 -1
- package/out/renderer/src/common/power-log-field-filters.js +92 -0
- package/out/renderer/src/common/power-log-field-filters.js.map +1 -0
- package/out/renderer/src/common/power-log-json-filter-sql.js +106 -0
- package/out/renderer/src/common/power-log-json-filter-sql.js.map +1 -0
- package/out/renderer/src/common/power-log-json-preset.js +34 -0
- package/out/renderer/src/common/power-log-json-preset.js.map +1 -0
- package/out/renderer/src/common/power-log-level.js +171 -0
- package/out/renderer/src/common/power-log-level.js.map +1 -0
- package/out/renderer/src/common/power-log-parser.js +7 -29
- package/out/renderer/src/common/power-log-parser.js.map +1 -1
- package/out/renderer/src/common/power-log-sql-level.js +25 -0
- package/out/renderer/src/common/power-log-sql-level.js.map +1 -0
- package/out/renderer/src/common/power-logs-ai.js +4 -0
- package/out/renderer/src/common/power-logs-ai.js.map +1 -1
- package/out/renderer/src/common/power-logs.js.map +1 -1
- package/out/renderer/src/common/resource-search-kind-priority.js +28 -0
- package/out/renderer/src/common/resource-search-kind-priority.js.map +1 -0
- package/out/renderer/src/common/store/logs-preferences-store.js +5 -0
- package/out/renderer/src/common/store/logs-preferences-store.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Power tools and personal dashboards for Freelens. Built for Freelens 1.x and tes
|
|
|
12
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 warning timelines, Pending and Not ready triage, Service/HPA checks, operational resource and namespace summaries, scoped AI handoff, and incident-summary copying
|
|
15
|
-
- **Power Logs:** opt-in colored workload logs with structured fields, source and level filters, bounded live collection, temporary SQLite captures, cancellable read-only SQL, compact density, configurable limits, NDJSON export and reviewable AI context with SQL drafts; native Pod logs remain available
|
|
15
|
+
- **Power Logs:** opt-in colored workload logs with structured fields, source and level filters, bounded live collection, temporary SQLite captures, cancellable read-only SQL, compact density, configurable limits, NDJSON export and reviewable AI context with SQL drafts; native Pod logs remain available. JSON details support syntax coloring, value copying and AND field filters in Live view and SQL; SQL presets retain original JSON field names, and five preset selectors control log limits
|
|
16
16
|
- **Resource relationships:** a focused Service graph connects Ingress and installed Gateway API routes/listeners to selected Pods, published endpoints, owners and node references, with acceptance and cross-namespace reference evidence, resource links and saved upstream visibility
|
|
17
17
|
- **Fast resource search:** double Shift searches all discoverable resource types, including Secrets, ConfigMaps and custom resources, through a persistent local metadata index with gentle background refresh, type and namespace filters, name-part and typo matching, resource links and explicit AI attachment; search and background indexing can be disabled together in Monitoring settings
|
|
18
18
|
- **Local metric history:** opt-in SQLite storage, up to seven days within a 100 MiB budget, with aggregated ranges and explicit collection gaps
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-logs.js","names":[],"sources":["../../../src/common/power-logs.ts"],"sourcesContent":["export const POWER_LOGS_CHANNEL = \"freelens-power:logs\";\nexport const POWER_LOGS_DEFAULT_LIMITS = {\n liveRows: 10000,\n liveBytes: 16 * 1024 * 1024,\n batchRows: 1000,\n batchBytes: 2 * 1024 * 1024,\n recordBytes: 64 * 1024,\n captureBytes: 256 * 1024 * 1024,\n captureRows: 1000000,\n captures: 3,\n sources: 200,\n resultRows: 1000,\n resultBytes: 2 * 1024 * 1024,\n queryMs: 15000,\n ttlMs: 60 * 60 * 1000,\n historyLines: 1000,\n historyBytes: 1024 * 1024,\n captureSourceLines: 10000,\n captureSourceBytes: 8 * 1024 * 1024,\n requestsPerSecond: 2,\n bytesPerSecond: 1024 * 1024,\n historyConcurrency: 2,\n liveConcurrency: 4,\n} as const;\n\nexport type PowerLogsLimitKey = keyof typeof POWER_LOGS_DEFAULT_LIMITS;\nexport type PowerLogsLimits = { [Key in PowerLogsLimitKey]: number };\nexport const POWER_LOGS_LIMITS: PowerLogsLimits = { ...POWER_LOGS_DEFAULT_LIMITS };\n\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\" | \"unknown\";\nexport type LogWorkloadKind = \"Pod\" | \"Deployment\" | \"DaemonSet\" | \"StatefulSet\" | \"ReplicaSet\" | \"Job\" | \"CronJob\";\nexport interface LogTarget {\n kind: LogWorkloadKind;\n namespace: string;\n name: string;\n uid: string;\n}\nexport interface LogSource {\n namespace: string;\n pod: string;\n podUid: string;\n container: string;\n previous?: boolean;\n}\nexport interface LogRecord {\n timestamp: number | null;\n pod: string;\n podUid: string;\n container: string;\n level: LogLevel;\n message: string;\n fieldsJson: string | null;\n previous?: boolean;\n}\nexport interface LogCapture {\n id: string;\n target: LogTarget;\n createdAt: number;\n expiresAt: number;\n state: \"collecting\" | \"ready\" | \"partial\";\n rows: number;\n bytes: number;\n notes: string[];\n}\nexport type LogCell = string | number | boolean | null;\nexport interface LogQueryResult {\n error?: string;\n columns: string[];\n rows: LogCell[][];\n truncated: boolean;\n elapsedMs: number;\n}\nexport interface LogPartition {\n clusterId: string;\n contextId: string;\n}\nexport type LogRequest = LogPartition &\n (\n | { operation: \"permit\"; lane: \"start\" | \"bytes\"; bytes?: number }\n | { operation: \"stream-open\"; streamId: string; follow: boolean }\n | { operation: \"stream-close\" | \"stream-renew\"; streamId: string }\n | { operation: \"create\"; target: LogTarget }\n | { operation: \"append\"; captureId: string; records: LogRecord[] }\n | { operation: \"finish\"; captureId: string; partial: boolean; notes: string[] }\n | { operation: \"list\" }\n | { operation: \"delete\"; captureId: string }\n | { operation: \"query\"; captureId: string; queryId: string; sql: string }\n | { operation: \"cancel\"; queryId: string }\n | { operation: \"export\"; captureId: string }\n );\n"],"mappings":";AAAA,IAAa,qBAAqB;AAClC,IAAa,4BAA4B;CACvC,UAAU;CACV,WAAW,KAAK,OAAO;CACvB,WAAW;CACX,YAAY,IAAI,OAAO;CACvB,aAAa,KAAK;CAClB,cAAc,MAAM,OAAO;CAC3B,aAAa;CACb,UAAU;CACV,SAAS;CACT,YAAY;CACZ,aAAa,IAAI,OAAO;CACxB,SAAS;CACT,OAAO,OAAU;CACjB,cAAc;CACd,cAAc,OAAO;CACrB,oBAAoB;CACpB,oBAAoB,IAAI,OAAO;CAC/B,mBAAmB;CACnB,gBAAgB,OAAO;CACvB,oBAAoB;CACpB,iBAAiB;AACnB;AAIA,IAAa,oBAAqC,EAAE,GAAG,0BAA0B"}
|
|
1
|
+
{"version":3,"file":"power-logs.js","names":[],"sources":["../../../src/common/power-logs.ts"],"sourcesContent":["export const POWER_LOGS_CHANNEL = \"freelens-power:logs\";\nexport const POWER_LOGS_DEFAULT_LIMITS = {\n liveRows: 10000,\n liveBytes: 16 * 1024 * 1024,\n batchRows: 1000,\n batchBytes: 2 * 1024 * 1024,\n recordBytes: 64 * 1024,\n captureBytes: 256 * 1024 * 1024,\n captureRows: 1000000,\n captures: 3,\n sources: 200,\n resultRows: 1000,\n resultBytes: 2 * 1024 * 1024,\n queryMs: 15000,\n ttlMs: 60 * 60 * 1000,\n historyLines: 1000,\n historyBytes: 1024 * 1024,\n captureSourceLines: 10000,\n captureSourceBytes: 8 * 1024 * 1024,\n requestsPerSecond: 2,\n bytesPerSecond: 1024 * 1024,\n historyConcurrency: 2,\n liveConcurrency: 4,\n} as const;\n\nexport type PowerLogsLimitKey = keyof typeof POWER_LOGS_DEFAULT_LIMITS;\nexport type PowerLogsLimits = { [Key in PowerLogsLimitKey]: number };\nexport const POWER_LOGS_LIMITS: PowerLogsLimits = { ...POWER_LOGS_DEFAULT_LIMITS };\n\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\" | \"unknown\";\nexport type LogWorkloadKind = \"Pod\" | \"Deployment\" | \"DaemonSet\" | \"StatefulSet\" | \"ReplicaSet\" | \"Job\" | \"CronJob\";\nexport interface LogTarget {\n kind: LogWorkloadKind;\n namespace: string;\n name: string;\n uid: string;\n}\nexport interface LogSource {\n namespace: string;\n pod: string;\n podUid: string;\n container: string;\n previous?: boolean;\n}\nexport interface LogRecord {\n timestamp: number | null;\n pod: string;\n podUid: string;\n container: string;\n level: LogLevel;\n message: string;\n fieldsJson: string | null;\n previous?: boolean;\n}\nexport interface LogCapture {\n id: string;\n target: LogTarget;\n createdAt: number;\n expiresAt: number;\n state: \"collecting\" | \"ready\" | \"partial\";\n rows: number;\n bytes: number;\n notes: string[];\n lastSql?: string;\n}\nexport type LogCell = string | number | boolean | null;\nexport interface LogQueryResult {\n error?: string;\n columns: string[];\n rows: LogCell[][];\n truncated: boolean;\n elapsedMs: number;\n}\nexport interface LogPartition {\n clusterId: string;\n contextId: string;\n}\nexport type LogRequest = LogPartition &\n (\n | { operation: \"permit\"; lane: \"start\" | \"bytes\"; bytes?: number }\n | { operation: \"stream-open\"; streamId: string; follow: boolean }\n | { operation: \"stream-close\" | \"stream-renew\"; streamId: string }\n | { operation: \"create\"; target: LogTarget }\n | { operation: \"append\"; captureId: string; records: LogRecord[] }\n | { operation: \"finish\"; captureId: string; partial: boolean; notes: string[] }\n | { operation: \"list\" }\n | { operation: \"delete\"; captureId: string }\n | { operation: \"save-sql\"; captureId: string; sql: string }\n | { operation: \"query\"; captureId: string; queryId: string; sql: string }\n | { operation: \"cancel\"; queryId: string }\n | { operation: \"export\"; captureId: string }\n );\n"],"mappings":";AAAA,IAAa,qBAAqB;AAClC,IAAa,4BAA4B;CACvC,UAAU;CACV,WAAW,KAAK,OAAO;CACvB,WAAW;CACX,YAAY,IAAI,OAAO;CACvB,aAAa,KAAK;CAClB,cAAc,MAAM,OAAO;CAC3B,aAAa;CACb,UAAU;CACV,SAAS;CACT,YAAY;CACZ,aAAa,IAAI,OAAO;CACxB,SAAS;CACT,OAAO,OAAU;CACjB,cAAc;CACd,cAAc,OAAO;CACrB,oBAAoB;CACpB,oBAAoB,IAAI,OAAO;CAC/B,mBAAmB;CACnB,gBAAgB,OAAO;CACvB,oBAAoB;CACpB,iBAAiB;AACnB;AAIA,IAAa,oBAAqC,EAAE,GAAG,0BAA0B"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/common/resource-search-kind-priority.ts
|
|
2
|
+
var COMMON_RESOURCE_KINDS = [
|
|
3
|
+
"Pod",
|
|
4
|
+
"Deployment",
|
|
5
|
+
"Service",
|
|
6
|
+
"StatefulSet",
|
|
7
|
+
"DaemonSet",
|
|
8
|
+
"Job",
|
|
9
|
+
"CronJob",
|
|
10
|
+
"Ingress",
|
|
11
|
+
"Gateway",
|
|
12
|
+
"HTTPRoute",
|
|
13
|
+
"ConfigMap",
|
|
14
|
+
"Secret",
|
|
15
|
+
"Namespace",
|
|
16
|
+
"Node",
|
|
17
|
+
"PersistentVolumeClaim",
|
|
18
|
+
"PersistentVolume"
|
|
19
|
+
];
|
|
20
|
+
//#endregion
|
|
21
|
+
exports.COMMON_RESOURCE_KINDS = COMMON_RESOURCE_KINDS;
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=resource-search-kind-priority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-search-kind-priority.js","names":[],"sources":["../../../src/common/resource-search-kind-priority.ts"],"sourcesContent":["export const COMMON_RESOURCE_KINDS = [\n \"Pod\",\n \"Deployment\",\n \"Service\",\n \"StatefulSet\",\n \"DaemonSet\",\n \"Job\",\n \"CronJob\",\n \"Ingress\",\n \"Gateway\",\n \"HTTPRoute\",\n \"ConfigMap\",\n \"Secret\",\n \"Namespace\",\n \"Node\",\n \"PersistentVolumeClaim\",\n \"PersistentVolume\",\n];\n\nexport function resourceKindPriority(kind: string): number {\n const index = COMMON_RESOURCE_KINDS.indexOf(kind);\n return index < 0 ? COMMON_RESOURCE_KINDS.length : index;\n}\n"],"mappings":";AAAA,IAAa,wBAAwB;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
|
|
@@ -6,6 +6,7 @@ var LogsPreferencesStore = class extends require_extensions.Common.Store.Extensi
|
|
|
6
6
|
enabled = false;
|
|
7
7
|
gentle = true;
|
|
8
8
|
compactRows = false;
|
|
9
|
+
hideResourceNameColumn = false;
|
|
9
10
|
limits = require_power_logs_limits.normalizePowerLogsLimits(void 0);
|
|
10
11
|
constructor() {
|
|
11
12
|
super({
|
|
@@ -14,6 +15,7 @@ var LogsPreferencesStore = class extends require_extensions.Common.Store.Extensi
|
|
|
14
15
|
enabled: false,
|
|
15
16
|
gentle: true,
|
|
16
17
|
compactRows: false,
|
|
18
|
+
hideResourceNameColumn: false,
|
|
17
19
|
limits: require_power_logs_limits.normalizePowerLogsLimits(void 0)
|
|
18
20
|
}
|
|
19
21
|
});
|
|
@@ -21,6 +23,7 @@ var LogsPreferencesStore = class extends require_extensions.Common.Store.Extensi
|
|
|
21
23
|
enabled: require__global_external_mobx.observable,
|
|
22
24
|
gentle: require__global_external_mobx.observable,
|
|
23
25
|
compactRows: require__global_external_mobx.observable,
|
|
26
|
+
hideResourceNameColumn: require__global_external_mobx.observable,
|
|
24
27
|
limits: require__global_external_mobx.observable
|
|
25
28
|
});
|
|
26
29
|
}
|
|
@@ -28,6 +31,7 @@ var LogsPreferencesStore = class extends require_extensions.Common.Store.Extensi
|
|
|
28
31
|
this.enabled = data?.enabled === true;
|
|
29
32
|
this.gentle = data?.gentle !== false;
|
|
30
33
|
this.compactRows = data?.compactRows === true;
|
|
34
|
+
this.hideResourceNameColumn = data?.hideResourceNameColumn === true;
|
|
31
35
|
this.limits = require_power_logs_limits.normalizePowerLogsLimits(data?.limits);
|
|
32
36
|
}
|
|
33
37
|
toJSON() {
|
|
@@ -35,6 +39,7 @@ var LogsPreferencesStore = class extends require_extensions.Common.Store.Extensi
|
|
|
35
39
|
enabled: this.enabled,
|
|
36
40
|
gentle: this.gentle,
|
|
37
41
|
compactRows: this.compactRows,
|
|
42
|
+
hideResourceNameColumn: this.hideResourceNameColumn,
|
|
38
43
|
limits: require_power_logs_limits.normalizePowerLogsLimits(this.limits)
|
|
39
44
|
};
|
|
40
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logs-preferences-store.js","names":[],"sources":["../../../../src/common/store/logs-preferences-store.ts"],"sourcesContent":["import { Common } from \"@freelensapp/extensions\";\nimport { makeObservable, observable } from \"mobx\";\nimport { normalizePowerLogsLimits } from \"../power-logs-limits\";\n\nimport type { PowerLogsLimits } from \"../power-logs\";\n\nexport class LogsPreferencesStore extends Common.Store.ExtensionStore<{\n enabled: boolean;\n gentle: boolean;\n compactRows: boolean;\n limits: PowerLogsLimits;\n}> {\n enabled = false;\n gentle = true;\n compactRows = false;\n limits = normalizePowerLogsLimits(undefined);\n\n constructor() {\n super({\n configName: \"logs-preferences\",\n defaults: {
|
|
1
|
+
{"version":3,"file":"logs-preferences-store.js","names":[],"sources":["../../../../src/common/store/logs-preferences-store.ts"],"sourcesContent":["import { Common } from \"@freelensapp/extensions\";\nimport { makeObservable, observable } from \"mobx\";\nimport { normalizePowerLogsLimits } from \"../power-logs-limits\";\n\nimport type { PowerLogsLimits } from \"../power-logs\";\n\nexport class LogsPreferencesStore extends Common.Store.ExtensionStore<{\n enabled: boolean;\n gentle: boolean;\n compactRows: boolean;\n hideResourceNameColumn: boolean;\n limits: PowerLogsLimits;\n}> {\n enabled = false;\n gentle = true;\n compactRows = false;\n hideResourceNameColumn = false;\n limits = normalizePowerLogsLimits(undefined);\n\n constructor() {\n super({\n configName: \"logs-preferences\",\n defaults: {\n enabled: false,\n gentle: true,\n compactRows: false,\n hideResourceNameColumn: false,\n limits: normalizePowerLogsLimits(undefined),\n },\n });\n makeObservable(this, {\n enabled: observable,\n gentle: observable,\n compactRows: observable,\n hideResourceNameColumn: observable,\n limits: observable,\n });\n }\n\n fromStore(data: {\n enabled?: boolean;\n gentle?: boolean;\n compactRows?: boolean;\n hideResourceNameColumn?: boolean;\n limits?: unknown;\n }): void {\n this.enabled = data?.enabled === true;\n this.gentle = data?.gentle !== false;\n this.compactRows = data?.compactRows === true;\n this.hideResourceNameColumn = data?.hideResourceNameColumn === true;\n this.limits = normalizePowerLogsLimits(data?.limits);\n }\n\n toJSON() {\n return {\n enabled: this.enabled,\n gentle: this.gentle,\n compactRows: this.compactRows,\n hideResourceNameColumn: this.hideResourceNameColumn,\n limits: normalizePowerLogsLimits(this.limits),\n };\n }\n}\n"],"mappings":";;;;AAMA,IAAa,uBAAb,cAA0C,mBAAA,OAAO,MAAM,eAMpD;CACD,UAAU;CACV,SAAS;CACT,cAAc;CACd,yBAAyB;CACzB,SAAS,0BAAA,yBAAyB,KAAA,CAAS;CAE3C,cAAc;EACZ,MAAM;GACJ,YAAY;GACZ,UAAU;IACR,SAAS;IACT,QAAQ;IACR,aAAa;IACb,wBAAwB;IACxB,QAAQ,0BAAA,yBAAyB,KAAA,CAAS;GAC5C;EACF,CAAC;EACD,8BAAA,eAAe,MAAM;GACnB,SAAS,8BAAA;GACT,QAAQ,8BAAA;GACR,aAAa,8BAAA;GACb,wBAAwB,8BAAA;GACxB,QAAQ,8BAAA;EACV,CAAC;CACH;CAEA,UAAU,MAMD;EACP,KAAK,UAAU,MAAM,YAAY;EACjC,KAAK,SAAS,MAAM,WAAW;EAC/B,KAAK,cAAc,MAAM,gBAAgB;EACzC,KAAK,yBAAyB,MAAM,2BAA2B;EAC/D,KAAK,SAAS,0BAAA,yBAAyB,MAAM,MAAM;CACrD;CAEA,SAAS;EACP,OAAO;GACL,SAAS,KAAK;GACd,QAAQ,KAAK;GACb,aAAa,KAAK;GAClB,wBAAwB,KAAK;GAC7B,QAAQ,0BAAA,yBAAyB,KAAK,MAAM;EAC9C;CACF;AACF"}
|
|
@@ -372,6 +372,31 @@ function startDashboardViews() {
|
|
|
372
372
|
}
|
|
373
373
|
};
|
|
374
374
|
owners.set(sender, owner);
|
|
375
|
+
const activeOwner = owner;
|
|
376
|
+
let backgrounded = false;
|
|
377
|
+
const blur = () => {
|
|
378
|
+
backgrounded = true;
|
|
379
|
+
};
|
|
380
|
+
const focus = () => {
|
|
381
|
+
if (!backgrounded) return;
|
|
382
|
+
backgrounded = false;
|
|
383
|
+
if (!dashboardUrl(sender.getURL())) return;
|
|
384
|
+
for (const [id, card] of activeOwner.cards) {
|
|
385
|
+
const configured = configuredCards().find((item) => item.id === id);
|
|
386
|
+
if (!card.visible || card.floating || !configured) continue;
|
|
387
|
+
card.skeleton.view.setVisible(card.loading);
|
|
388
|
+
card.container.setVisible(!card.failed || card.loading);
|
|
389
|
+
if ((configured.refreshEvery10Seconds || configured.autoRefresh) && !card.loading) reload(card, configured.url);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
window.on("blur", blur);
|
|
393
|
+
window.on("focus", focus);
|
|
394
|
+
const cleanup = owner.cleanup;
|
|
395
|
+
owner.cleanup = () => {
|
|
396
|
+
window.removeListener("blur", blur);
|
|
397
|
+
window.removeListener("focus", focus);
|
|
398
|
+
cleanup();
|
|
399
|
+
};
|
|
375
400
|
}
|
|
376
401
|
if (payload && typeof payload === "object" && "hidden" in payload && payload.hidden === true) {
|
|
377
402
|
hide(event.sender);
|
|
@@ -558,6 +583,7 @@ function startDashboardViews() {
|
|
|
558
583
|
applyWebsiteStyles(card);
|
|
559
584
|
}
|
|
560
585
|
card.visible = true;
|
|
586
|
+
card.skeleton.view.setVisible(card.loading);
|
|
561
587
|
card.container.setVisible(!card.failed && (card.loading || card.url === configured.url));
|
|
562
588
|
const zoom = event.sender.getZoomFactor();
|
|
563
589
|
card.container.setBorderRadius(Math.round(12 * zoom));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-views.js","names":[],"sources":["../../src/main/dashboard-views.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { join } from \"node:path\";\nimport {\n app,\n BrowserWindow,\n type IpcMainInvokeEvent,\n ipcMain,\n nativeTheme,\n type Rectangle,\n session,\n shell,\n View,\n type WebContents,\n WebContentsView,\n} from \"electron\";\nimport { reaction } from \"mobx\";\nimport { sanitizeDashboardCards } from \"../common/dashboard\";\nimport { dashboardReadinessScript } from \"../common/dashboard-readiness\";\nimport { dashboardSelectorScript } from \"../common/dashboard-selector\";\nimport {\n DASHBOARD_SITE_ICONS,\n DASHBOARD_VIEWS_LOADING,\n DASHBOARD_VIEWS_PIN,\n DASHBOARD_VIEWS_PINNED,\n type DashboardSiteIcon,\n} from \"../common/dashboard-views\";\nimport { DashboardPreferencesStore } from \"../common/store/dashboard-preferences-store\";\nimport { boundedDashboardIcons, loadDashboardIcons, saveDashboardIcons } from \"./dashboard-icon-cache\";\nimport { observeDashboardNetwork } from \"./dashboard-network\";\n\nconst syncChannel = \"freelens-power:dashboard-views:sync\";\nconst reloadChannel = \"freelens-power:dashboard-views:reload\";\nconst clearChannel = \"freelens-power:dashboard-views:clear\";\nconst dashboardPath = \"/extension/freelens-power/dashboard\";\nconst websiteStyles =\n \"* { scrollbar-width: none !important; } *::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }\";\n\nconst expandedWebsiteStyles =\n \"* { scrollbar-width: auto !important; } *::-webkit-scrollbar { display: revert !important; width: revert !important; height: revert !important; }\";\n\nfunction rendererUrl(value: string, dashboardOnly = false): boolean {\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 (!dashboardOnly || url.pathname === dashboardPath)\n );\n } catch {\n return false;\n }\n}\nfunction dashboardUrl(value: string): boolean {\n return rendererUrl(value, true);\n}\nfunction webUrl(value: string): boolean {\n try {\n return [\"https:\", \"http:\"].includes(new URL(value).protocol);\n } catch {\n return false;\n }\n}\nfunction rectangle(value: unknown): Rectangle | undefined {\n if (!value || typeof value !== \"object\") return;\n const rect = value as Rectangle;\n if (\n ![rect.x, rect.y, rect.width, rect.height].every(\n (n) => Number.isFinite(n) && Number.isInteger(n) && Math.abs(n) <= 32768,\n ) ||\n rect.width <= 0 ||\n rect.height <= 0\n )\n return;\n return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };\n}\n\nfunction createDashboardSkeleton() {\n const view = new View();\n view.setBackgroundColor(nativeTheme.shouldUseDarkColors ? \"#202124\" : \"#ffffff\");\n const bars = Array.from({ length: 4 }, () => {\n const bar = new View();\n bar.setBackgroundColor(nativeTheme.shouldUseDarkColors ? \"#393b40\" : \"#e8e9eb\");\n bar.setBorderRadius(6);\n view.addChildView(bar);\n return bar;\n });\n return {\n view,\n layout: (width: number, height: number) => {\n view.setBounds({ x: 0, y: 0, width, height });\n bars.forEach((bar, index) => {\n bar.setBounds({\n x: 20,\n y: index === 0 ? 20 : 60 + (index - 1) * 24,\n width: Math.max(1, Math.round((width - 40) * (index === 0 ? 0.45 : 1))),\n height: index === 0 ? 20 : index === 3 ? Math.max(30, height - 128) : 12,\n });\n });\n },\n };\n}\n\ntype WebsiteStyleState = {\n view: { webContents: WebContents };\n scrollbars?: boolean;\n websiteStylesKey?: string;\n websiteStylesGeneration?: number;\n};\n\ntype CardView = {\n container: View;\n skeleton: ReturnType<typeof createDashboardSkeleton>;\n view: WebContentsView;\n url: string;\n failed: boolean;\n loading: boolean;\n visible: boolean;\n generation: number;\n stopPreparation?: () => void;\n selector?: string;\n scrollbars?: boolean;\n websiteStylesKey?: string;\n websiteStylesGeneration?: number;\n pending?: Promise<void>;\n requestedNavigation?: boolean;\n nativeNavigation?: { finish: () => void; fail: () => void };\n favicon?: string;\n faviconRequest?: AbortController;\n refreshTimer?: ReturnType<typeof setTimeout>;\n refreshInterval?: number;\n refreshGeneration?: number;\n floating?: BrowserWindow;\n floatingStyles?: WebsiteStyleState;\n floatingPreparation?: {\n refresh: () => Promise<boolean>;\n selectorChanged: () => void;\n stop: () => void;\n isPreparing: () => boolean;\n };\n};\ntype Owner = { window: BrowserWindow; cards: Map<string, CardView>; cleanup: () => void };\n\nasync function applyWebsiteStyles(card: WebsiteStyleState): Promise<void> {\n const contents = card.view.webContents;\n const generation = (card.websiteStylesGeneration ?? 0) + 1;\n card.websiteStylesGeneration = generation;\n const previous = card.websiteStylesKey;\n card.websiteStylesKey = undefined;\n if (contents.isDestroyed()) return;\n if (previous) await contents.removeInsertedCSS(previous).catch(() => {});\n if (card.websiteStylesGeneration !== generation || contents.isDestroyed()) return;\n const key = await contents\n .insertCSS(card.scrollbars ? expandedWebsiteStyles : websiteStyles, { cssOrigin: \"user\" })\n .catch(() => undefined);\n if (!key) return;\n if (card.websiteStylesGeneration === generation && !contents.isDestroyed()) card.websiteStylesKey = key;\n else if (!contents.isDestroyed()) await contents.removeInsertedCSS(key).catch(() => {});\n}\n\nasync function waitForReadiness(\n contents: WebContents,\n _network: ReturnType<typeof observeDashboardNetwork>,\n selector: () => string | undefined,\n current: () => boolean,\n): Promise<void> {\n let deadline: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n (async () => {\n if (!current()) return;\n await contents.mainFrame.executeJavaScript(dashboardSelectorScript(selector())).catch(() => {});\n if (!current()) return;\n await contents.mainFrame.executeJavaScript(dashboardReadinessScript(selector()));\n })().catch(() => new Promise<void>((resolve) => setTimeout(resolve, 200))),\n new Promise<void>((resolve) => {\n deadline = setTimeout(resolve, 8500);\n }),\n ]);\n } finally {\n clearTimeout(deadline);\n }\n}\n\nasync function prepareDashboardPage(contents: WebContents): Promise<void> {\n if (contents.isDestroyed()) return;\n let deadline: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n contents.mainFrame\n .executeJavaScript('document.dispatchEvent(new Event(\"freelens-power:dashboard:preparing\"))')\n .catch(() => {}),\n new Promise<void>((resolve) => {\n deadline = setTimeout(resolve, 250);\n }),\n ]);\n } finally {\n clearTimeout(deadline);\n }\n}\n\nfunction releaseDashboardPage(contents: WebContents): Promise<unknown> {\n if (contents.isDestroyed()) return Promise.resolve();\n return contents.mainFrame\n .executeJavaScript('document.dispatchEvent(new Event(\"freelens-power:dashboard:prepared\"))')\n .catch(() => {});\n}\n\nexport function startDashboardViews(): () => void {\n const websiteSession = session.fromPartition(\"persist:freelens-power-websites\");\n websiteSession.setPermissionRequestHandler((_contents, _permission, callback) => callback(false));\n websiteSession.setPermissionCheckHandler(() => false);\n const owners = new Map<WebContents, Owner>();\n const icons = new Map<string, DashboardSiteIcon>();\n const remove = (owner: Owner, id: string) => {\n const card = owner.cards.get(id);\n if (!card) return;\n owner.cards.delete(id);\n card.nativeNavigation?.fail();\n card.stopPreparation?.();\n card.websiteStylesGeneration = (card.websiteStylesGeneration ?? 0) + 1;\n card.faviconRequest?.abort();\n clearTimeout(card.refreshTimer);\n card.floatingPreparation?.stop();\n card.floating?.destroy();\n card.floating = undefined;\n if (!owner.window.isDestroyed()) owner.window.contentView.removeChildView(card.container);\n if (!card.view.webContents.isDestroyed()) card.view.webContents.close();\n };\n const clear = (sender: WebContents) => {\n const owner = owners.get(sender);\n if (!owner) return;\n owners.delete(sender);\n for (const id of owner.cards.keys()) remove(owner, id);\n owner.cleanup();\n };\n const hide = (sender: WebContents) => {\n for (const card of owners.get(sender)?.cards.values() ?? []) {\n card.visible = false;\n card.container.setVisible(false);\n }\n };\n const configuredCards = () =>\n sanitizeDashboardCards(DashboardPreferencesStore.getInstanceOrCreate<DashboardPreferencesStore>().cards);\n const cachePath = join(app.getPath(\"userData\"), \"freelens-power\", \"dashboard-icons.json\");\n const ready = loadDashboardIcons(cachePath).then((saved) => {\n const configured = configuredCards();\n for (const icon of saved) {\n if (!icons.has(icon.id) && configured.some((card) => card.id === icon.id && card.url === icon.pageUrl))\n icons.set(icon.id, icon);\n }\n });\n let writes = ready;\n const persistIcons = () => {\n writes = writes\n .then(async () => {\n const configured = configuredCards();\n const bounded = boundedDashboardIcons(\n [...icons.values()].filter((icon) =>\n configured.some((card) => card.id === icon.id && card.url === icon.pageUrl),\n ),\n );\n icons.clear();\n for (const icon of bounded) icons.set(icon.id, icon);\n await saveDashboardIcons(cachePath, bounded);\n })\n .catch(() => {});\n };\n persistIcons();\n const publishLoading = (card: CardView) => {\n for (const [sender, owner] of owners) {\n const id = [...owner.cards].find(([, candidate]) => candidate === card)?.[0];\n if (\n !id ||\n sender.isDestroyed() ||\n !dashboardUrl(sender.mainFrame.url) ||\n !dashboardUrl(sender.getURL()) ||\n !configuredCards().some((configured) => configured.id === id && configured.url === card.url)\n )\n continue;\n sender.send(DASHBOARD_VIEWS_LOADING, { id, url: card.url, loading: card.loading, failed: card.failed });\n }\n };\n const reload = (card: CardView, configuredUrl: string, initial = false, navigation?: Promise<void>) => {\n const current = card.view.webContents.getURL();\n const target = !initial && card.url === configuredUrl && webUrl(current) ? current : configuredUrl;\n const prepareExisting = !initial && !navigation && !card.loading && !card.failed && webUrl(current);\n card.faviconRequest?.abort();\n if (!navigation) card.nativeNavigation?.fail();\n card.url = configuredUrl;\n card.failed = false;\n card.loading = true;\n card.stopPreparation?.();\n const contents = card.view.webContents;\n const backgroundThrottling = contents.getBackgroundThrottling();\n if (backgroundThrottling) contents.setBackgroundThrottling(false);\n const network = observeDashboardNetwork(card.view.webContents);\n let stopped = false;\n let documentReady: (() => void) | undefined;\n const stopPreparation = () => {\n if (stopped) return;\n stopped = true;\n network.dispose();\n contents.removeListener(\"destroyed\", stopPreparation);\n if (documentReady) contents.removeListener(\"dom-ready\", documentReady);\n if (!contents.isDestroyed() && backgroundThrottling && !contents.getBackgroundThrottling())\n contents.setBackgroundThrottling(backgroundThrottling);\n if (card.stopPreparation === stopPreparation) card.stopPreparation = undefined;\n };\n card.stopPreparation = stopPreparation;\n contents.once(\"destroyed\", stopPreparation);\n const generation = ++card.generation;\n card.skeleton.view.setVisible(true);\n card.container.setVisible(card.visible && !card.floating);\n publishLoading(card);\n const settled = (failed: boolean) => {\n if (card.generation !== generation || card.view.webContents.isDestroyed()) return;\n card.failed = failed;\n card.loading = false;\n card.skeleton.view.setVisible(false);\n for (const [sender, owner] of owners) {\n if (![...owner.cards.values()].includes(card)) continue;\n card.container.setVisible(card.visible && !card.floating && !failed && dashboardUrl(sender.getURL()));\n }\n publishLoading(card);\n };\n const load = () => {\n if (\n card.generation !== generation ||\n card.view.webContents.isDestroyed() ||\n ![...owners.values()].some((owner) => [...owner.cards.values()].includes(card))\n )\n return Promise.resolve();\n if (navigation) return navigation;\n const navigate = () => {\n if (card.generation !== generation || contents.isDestroyed()) return Promise.resolve();\n card.requestedNavigation = true;\n return contents.loadURL(target).finally(() => {\n if (card.generation === generation) card.requestedNavigation = false;\n });\n };\n return prepareExisting ? prepareDashboardPage(contents).then(navigate) : navigate();\n };\n const ready = new Promise<void>((resolve) => {\n documentReady = resolve;\n contents.once(\"dom-ready\", documentReady);\n });\n card.pending = Promise.race([load(), ready]).then(\n async () => {\n let preparing = true;\n const current = () =>\n preparing &&\n card.generation === generation &&\n !card.view.webContents.isDestroyed() &&\n [...owners.values()].some((owner) => [...owner.cards.values()].includes(card));\n try {\n await waitForReadiness(contents, network, () => card.selector, current);\n if (current()) await releaseDashboardPage(contents);\n } finally {\n preparing = false;\n stopPreparation();\n }\n settled(false);\n },\n () => {\n if (card.generation === generation) void releaseDashboardPage(contents);\n stopPreparation();\n settled(true);\n },\n );\n return card.pending;\n };\n const applySelector = (card: { view: { webContents: WebContents }; selector?: string }) => {\n if (card.view.webContents.isDestroyed()) return Promise.resolve();\n return card.view.webContents.mainFrame.executeJavaScript(dashboardSelectorScript(card.selector)).catch(() => {});\n };\n const refreshTimers = () => {\n const configured = configuredCards();\n for (const [id, icon] of icons) {\n if (!configured.some((card) => card.id === id && card.url === icon.pageUrl)) {\n icons.delete(id);\n persistIcons();\n }\n }\n for (const [sender, owner] of owners) {\n for (const [id, card] of owner.cards) {\n const setting = configured.find((item) => item.id === id);\n if (!setting || setting.url !== card.url) {\n remove(owner, id);\n continue;\n }\n if (card.selector !== setting.selector) {\n card.selector = setting.selector;\n applySelector(card);\n card.floatingPreparation?.selectorChanged();\n }\n if (card.floating && !card.floating.isDestroyed()) card.floating.setTitle(setting.title);\n if (card.floatingStyles) {\n const scrollbars = setting.size === \"l\" || setting.size === \"xl\";\n if (card.floatingStyles.scrollbars !== scrollbars) {\n card.floatingStyles.scrollbars = scrollbars;\n void applyWebsiteStyles(card.floatingStyles);\n }\n }\n const interval = setting.refreshEvery10Seconds ? 10000 : setting.autoRefresh ? 600000 : undefined;\n if (card.refreshInterval === interval) continue;\n clearTimeout(card.refreshTimer);\n card.refreshTimer = undefined;\n card.refreshInterval = interval;\n const refreshGeneration = (card.refreshGeneration ?? 0) + 1;\n card.refreshGeneration = refreshGeneration;\n if (!interval) continue;\n const schedule = () => {\n if (\n owners.get(sender) !== owner ||\n owner.cards.get(id) !== card ||\n card.refreshGeneration !== refreshGeneration\n )\n return;\n if (card.loading && card.pending) {\n void card.pending.then(schedule);\n return;\n }\n card.refreshTimer = setTimeout(tick, interval);\n };\n const tick = async () => {\n try {\n const latest = configuredCards().find((item) => item.id === id);\n if (\n owners.get(sender) !== owner ||\n owner.cards.get(id) !== card ||\n !latest ||\n latest.url !== card.url ||\n (latest.refreshEvery10Seconds ? 10000 : latest.autoRefresh ? 600000 : undefined) !== interval ||\n card.view.webContents.isDestroyed()\n )\n return;\n if (card.loading) {\n await card.pending;\n return;\n }\n if (latest.refreshEvery10Seconds) {\n if (card.floating) {\n if (card.floating.isDestroyed() || card.floatingPreparation?.isPreparing()) return;\n await Promise.all([reload(card, latest.url), card.floatingPreparation?.refresh()]);\n } else if (card.visible && dashboardUrl(sender.getURL())) await reload(card, latest.url);\n } else if (!card.floating && !dashboardUrl(sender.getURL())) await reload(card, latest.url);\n } finally {\n schedule();\n }\n };\n schedule();\n }\n }\n };\n const stopSettingsReaction = reaction(\n () =>\n JSON.stringify(\n configuredCards().map(({ id, title, url, size, autoRefresh, refreshEvery10Seconds, selector }) => ({\n id,\n title,\n url,\n size,\n autoRefresh,\n refreshEvery10Seconds,\n selector,\n })),\n ),\n refreshTimers,\n );\n const trusted = (event: IpcMainInvokeEvent) =>\n event.senderFrame === event.sender.mainFrame &&\n dashboardUrl(event.senderFrame?.url ?? \"\") &&\n dashboardUrl(event.sender.getURL());\n ipcMain.handle(DASHBOARD_SITE_ICONS, async (event) => {\n if (\n event.senderFrame !== event.sender.mainFrame ||\n !rendererUrl(event.senderFrame?.url ?? \"\") ||\n !rendererUrl(event.sender.getURL()) ||\n !BrowserWindow.fromWebContents(event.sender)\n )\n return [];\n await ready;\n return configuredCards().flatMap((card) => {\n const icon = icons.get(card.id);\n return icon?.pageUrl === card.url ? [icon] : [];\n });\n });\n ipcMain.handle(syncChannel, async (event, payload: unknown) => {\n if (!trusted(event)) return;\n const window = BrowserWindow.fromWebContents(event.sender);\n if (!window || window.isDestroyed()) return;\n const requested =\n payload && typeof payload === \"object\" && \"cards\" in payload && Array.isArray(payload.cards) ? payload.cards : [];\n const cards = sanitizeDashboardCards(\n DashboardPreferencesStore.getInstanceOrCreate<DashboardPreferencesStore>().cards,\n );\n let owner = owners.get(event.sender);\n if (!owner) {\n const sender = event.sender;\n const navigation = (_event: unknown, url: string) => {\n if (!dashboardUrl(url)) hide(sender);\n };\n const destroyed = () => clear(sender);\n sender.on(\"did-navigate\", navigation);\n sender.on(\"did-navigate-in-page\", navigation);\n sender.on(\"destroyed\", destroyed);\n owner = {\n window,\n cards: new Map(),\n cleanup: () => {\n sender.removeListener(\"did-navigate\", navigation);\n sender.removeListener(\"did-navigate-in-page\", navigation);\n sender.removeListener(\"destroyed\", destroyed);\n },\n };\n owners.set(sender, owner);\n }\n if (payload && typeof payload === \"object\" && \"hidden\" in payload && payload.hidden === true) {\n hide(event.sender);\n return { failed: [], pinned: [...owner.cards].filter(([, card]) => card.floating).map(([id]) => id) };\n }\n const visible = new Set<string>();\n for (const item of requested.slice(0, cards.length)) {\n if (!item || typeof item !== \"object\" || typeof item.id !== \"string\" || visible.has(item.id)) continue;\n const configured = cards.find((card) => card.id === item.id);\n const bounds = rectangle(item.bounds);\n const clip = rectangle(item.clip);\n if (\n !configured ||\n !bounds ||\n !clip ||\n clip.x < bounds.x ||\n clip.y < bounds.y ||\n clip.x + clip.width > bounds.x + bounds.width ||\n clip.y + clip.height > bounds.y + bounds.height\n )\n continue;\n visible.add(item.id);\n let card = owner.cards.get(item.id);\n if (card?.floating) {\n card.visible = false;\n card.container.setVisible(false);\n continue;\n }\n if (!card) {\n const container = new View();\n container.setBackgroundColor(\"#00000000\");\n const view = new WebContentsView({\n webPreferences: {\n nodeIntegration: false,\n nodeIntegrationInSubFrames: false,\n contextIsolation: true,\n sandbox: true,\n webSecurity: true,\n partition: \"persist:freelens-power-websites\",\n preload: join(__dirname, \"dashboard-preload.js\"),\n },\n });\n view.setBackgroundColor(\"#00000000\");\n view.webContents.setWindowOpenHandler(({ url }) => {\n if (webUrl(url)) void shell.openExternal(url).catch(() => {});\n return { action: \"deny\" };\n });\n const navigation = (event: Electron.Event, url: string) => {\n if (!webUrl(url)) event.preventDefault();\n };\n view.webContents.on(\"will-navigate\", navigation);\n view.webContents.on(\"will-redirect\", navigation);\n view.webContents.on(\"will-frame-navigate\", (event) => {\n if (!webUrl(event.url)) event.preventDefault();\n });\n container.addChildView(view);\n const skeleton = createDashboardSkeleton();\n container.addChildView(skeleton.view);\n window.contentView.addChildView(container);\n card = { container, skeleton, view, url: \"\", failed: false, loading: false, visible: false, generation: 0 };\n owner.cards.set(item.id, card);\n const created = card;\n view.webContents.on(\"before-input-event\", (_event, input: Electron.Input) => {\n if (\n input.type !== \"keyDown\" ||\n input.key !== \"Escape\" ||\n owners.get(event.sender) !== owner ||\n owner.cards.get(item.id) !== created ||\n !trusted(event)\n )\n return;\n event.sender.send(\"freelens-power:dashboard-escape\");\n });\n created.selector = configured.selector;\n view.webContents.on(\"did-start-navigation\", (_event, _url, inPlace, mainFrame) => {\n if (!mainFrame) return;\n if (created.requestedNavigation) {\n created.requestedNavigation = false;\n return;\n }\n created.nativeNavigation?.fail();\n const navigation = new Promise<void>((resolve, reject) => {\n created.nativeNavigation = {\n finish: () => {\n created.nativeNavigation = undefined;\n resolve();\n },\n fail: () => {\n created.nativeNavigation = undefined;\n reject(new Error(\"dashboard navigation failed\"));\n },\n };\n });\n if (inPlace)\n void view.webContents.mainFrame\n .executeJavaScript('document.dispatchEvent(new Event(\"freelens-power:dashboard:preparing\"))')\n .catch(() => {});\n void reload(created, created.url, false, navigation);\n if (inPlace) created.nativeNavigation?.finish();\n });\n view.webContents.on(\"did-finish-load\", () => {\n void applyWebsiteStyles(created);\n void applySelector(created);\n created.nativeNavigation?.finish();\n });\n view.webContents.on(\"did-fail-load\", (_event, code, _description, _url, mainFrame) => {\n if (!mainFrame || (code === -3 && view.webContents.isLoading())) return;\n created.nativeNavigation?.fail();\n });\n view.webContents.on(\"page-favicon-updated\", (_event, favicons: string[]) => {\n const favicon = favicons.find((value) => {\n if (!webUrl(value)) return false;\n const parsed = new URL(value);\n return !parsed.username && !parsed.password;\n });\n if (!favicon || owners.get(event.sender) !== owner || owner.cards.get(item.id) !== created) return;\n created.faviconRequest?.abort();\n const request = new AbortController();\n created.faviconRequest = request;\n created.favicon = favicon;\n const publish = (url: string) => {\n if (\n request.signal.aborted ||\n owners.get(event.sender) !== owner ||\n owner.cards.get(item.id) !== created ||\n event.senderFrame !== event.sender.mainFrame\n )\n return;\n created.favicon = url;\n if (\n url.startsWith(\"data:\") &&\n configuredCards().some((card) => card.id === item.id && card.url === created.url)\n ) {\n icons.set(item.id, { id: item.id, pageUrl: created.url, dataUrl: url });\n persistIcons();\n if (rendererUrl(event.senderFrame?.url ?? \"\") && rendererUrl(event.sender.getURL()))\n event.sender.send(DASHBOARD_SITE_ICONS);\n }\n if (!trusted(event)) return;\n event.sender.send(\"freelens-power:dashboard-site-icon\", { id: item.id, url });\n };\n publish(favicon);\n const timeout = setTimeout(() => request.abort(), 5000);\n void (async () => {\n let current = favicon;\n for (let redirects = 0; redirects <= 5; redirects++) {\n const response = await websiteSession.fetch(current, {\n signal: request.signal,\n redirect: \"manual\",\n credentials: \"include\",\n });\n if (response.status >= 300 && response.status < 400) {\n await response.body?.cancel();\n const location = response.headers.get(\"location\");\n if (!location) return;\n const next = new URL(location, current);\n if (!webUrl(next.href) || next.username || next.password) return;\n current = next.href;\n continue;\n }\n const mime = response.headers.get(\"content-type\")?.split(\";\")[0].trim().toLowerCase();\n if (\n !response.ok ||\n !mime ||\n ![\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"image/x-icon\",\n \"image/vnd.microsoft.icon\",\n \"image/svg+xml\",\n ].includes(mime) ||\n Number(response.headers.get(\"content-length\")) > 524288\n ) {\n await response.body?.cancel();\n return;\n }\n const reader = response.body?.getReader();\n if (!reader) return;\n const chunks: Uint8Array[] = [];\n let size = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.length;\n if (size > 524288) {\n await reader.cancel();\n return;\n }\n chunks.push(value);\n }\n if (size) publish(`data:${mime};base64,${Buffer.concat(chunks).toString(\"base64\")}`);\n return;\n }\n })()\n .catch(() => {})\n .finally(() => clearTimeout(timeout));\n });\n }\n if (card.scrollbars !== (item.scrollbars === true)) {\n card.scrollbars = item.scrollbars === true;\n void applyWebsiteStyles(card);\n }\n card.visible = true;\n card.container.setVisible(!card.failed && (card.loading || card.url === configured.url));\n const zoom = event.sender.getZoomFactor();\n card.container.setBorderRadius(Math.round(12 * zoom));\n card.view.setBorderRadius(Math.round(12 * zoom));\n const scale = (rect: Rectangle): Rectangle => ({\n x: Math.round(rect.x * zoom),\n y: Math.round(rect.y * zoom),\n width: Math.round(rect.width * zoom),\n height: Math.round(rect.height * zoom),\n });\n card.container.setBounds(scale(clip));\n card.skeleton.layout(Math.round(clip.width * zoom), Math.round(clip.height * zoom));\n card.view.setBounds(\n scale({ x: bounds.x - clip.x, y: bounds.y - clip.y, width: bounds.width, height: bounds.height }),\n );\n if (card.url !== configured.url) {\n card.favicon = undefined;\n card.faviconRequest?.abort();\n void reload(card, configured.url, true);\n }\n publishLoading(card);\n if (card.favicon) event.sender.send(\"freelens-power:dashboard-site-icon\", { id: item.id, url: card.favicon });\n }\n for (const [id, card] of owner.cards) {\n if (!cards.some((configured) => configured.id === id)) remove(owner, id);\n else if (!visible.has(id)) {\n card.visible = false;\n card.container.setVisible(false);\n }\n }\n refreshTimers();\n await Promise.all([...owner.cards.values()].map((card) => card.pending));\n return {\n failed: [...owner.cards.entries()].filter(([, card]) => card.failed).map(([id]) => id),\n pinned: [...owner.cards].filter(([, card]) => card.floating).map(([id]) => id),\n };\n });\n const publishPinned = (sender: WebContents, owner: Owner) => {\n if (!sender.isDestroyed())\n sender.send(\n DASHBOARD_VIEWS_PINNED,\n [...owner.cards].filter(([, card]) => card.floating).map(([id]) => id),\n );\n };\n ipcMain.handle(DASHBOARD_VIEWS_PIN, (event, payload: unknown) => {\n if (\n !trusted(event) ||\n !payload ||\n typeof payload !== \"object\" ||\n !(\"id\" in payload) ||\n typeof payload.id !== \"string\" ||\n !((\"pinned\" in payload && typeof payload.pinned === \"boolean\") || (\"focus\" in payload && payload.focus === true))\n )\n return;\n const id = payload.id;\n const owner = owners.get(event.sender);\n const card = owner?.cards.get(payload.id);\n const configured = configuredCards().find((item) => item.id === payload.id);\n if (\n !owner ||\n !card ||\n !configured ||\n card.url !== configured.url ||\n owner.window.isDestroyed() ||\n BrowserWindow.fromWebContents(event.sender) !== owner.window ||\n card.view.webContents.isDestroyed()\n )\n return;\n if (\"focus\" in payload && payload.focus === true) {\n card.floating?.show();\n card.floating?.focus();\n } else if (!(\"pinned\" in payload) || !payload.pinned) {\n card.floating?.close();\n } else if (!card.floating) {\n const floating = new BrowserWindow({\n title: configured.title,\n width: 800,\n height: 600,\n minWidth: 240,\n minHeight: 160,\n resizable: true,\n alwaysOnTop: true,\n autoHideMenuBar: true,\n show: false,\n webPreferences: {\n nodeIntegration: false,\n nodeIntegrationInSubFrames: false,\n contextIsolation: true,\n sandbox: true,\n webSecurity: true,\n partition: \"persist:freelens-power-websites\",\n preload: join(__dirname, \"dashboard-preload.js\"),\n },\n });\n floating.setMenu(null);\n floating.setAlwaysOnTop(true, \"floating\");\n floating.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });\n card.floating = floating;\n card.container.setVisible(false);\n const contents = floating.webContents;\n const floatingStyles: WebsiteStyleState = {\n view: { webContents: contents },\n scrollbars: configured.size === \"l\" || configured.size === \"xl\",\n };\n card.floatingStyles = floatingStyles;\n contents.setWindowOpenHandler(({ url }) => {\n if (webUrl(url)) void shell.openExternal(url).catch(() => {});\n return { action: \"deny\" };\n });\n const navigation = (event: Electron.Event, url: string) => {\n if (!webUrl(url)) event.preventDefault();\n };\n contents.on(\"will-navigate\", navigation);\n contents.on(\"will-redirect\", navigation);\n contents.on(\"will-frame-navigate\", (event) => {\n if (!webUrl(event.url)) event.preventDefault();\n });\n contents.on(\"page-title-updated\", (event) => event.preventDefault());\n contents.on(\"will-prevent-unload\", (event) => event.preventDefault());\n const loadingSkeleton = createDashboardSkeleton();\n const skeleton = loadingSkeleton.view;\n const layoutSkeleton = () => {\n const { width, height } = floating.getContentBounds();\n loadingSkeleton.layout(width, height);\n };\n floating.contentView.addChildView(skeleton);\n floating.on(\"resize\", layoutSkeleton);\n layoutSkeleton();\n let generation = 0;\n let floatingPreparing = false;\n let stopPreparation = () => {};\n let finishLoad: (() => Promise<boolean>) | undefined;\n const begin = () => {\n stopPreparation();\n floatingPreparing = true;\n const token = ++generation;\n const backgroundThrottling = contents.getBackgroundThrottling();\n if (backgroundThrottling) contents.setBackgroundThrottling(false);\n const network = observeDashboardNetwork(contents);\n let active = true;\n let finishing: Promise<boolean> | undefined;\n const current = () => active && generation === token && card.floating === floating && !contents.isDestroyed();\n skeleton.setVisible(true);\n stopPreparation = () => {\n if (!active) return;\n active = false;\n floatingPreparing = false;\n network.dispose();\n if (!contents.isDestroyed() && backgroundThrottling) contents.setBackgroundThrottling(true);\n };\n const stop = stopPreparation;\n const finish = () => {\n finishing ??= (async () => {\n await waitForReadiness(contents, network, () => card.selector, current);\n if (!current()) return false;\n await releaseDashboardPage(contents);\n if (!current()) return false;\n skeleton.setVisible(false);\n stop();\n return false;\n })();\n return finishing;\n };\n finishLoad = finish;\n return {\n finish,\n fail: () => {\n if (current()) {\n void releaseDashboardPage(contents);\n skeleton.setVisible(false);\n stop();\n }\n },\n };\n };\n const refresh = () => {\n const preparation = begin();\n const current = contents.getURL();\n let documentReady: () => void;\n const ready = new Promise<boolean>((resolve) => {\n documentReady = () => {\n void (finishLoad?.() ?? Promise.resolve(false)).then(resolve);\n };\n contents.once(\"dom-ready\", documentReady);\n });\n const loading = prepareDashboardPage(contents)\n .then(() => contents.loadURL(webUrl(current) ? current : configured.url))\n .then(\n () => finishLoad?.() ?? false,\n () => {\n preparation.fail();\n return !contents.isDestroyed();\n },\n );\n return Promise.race([loading, ready]).finally(() => {\n contents.removeListener(\"dom-ready\", documentReady);\n });\n };\n card.floatingPreparation = {\n refresh,\n isPreparing: () => floatingPreparing,\n selectorChanged: () => {\n const preparation = begin();\n void preparation.finish();\n },\n stop: () => {\n ++generation;\n stopPreparation();\n },\n };\n contents.on(\"did-start-navigation\", (_event, _url, inPlace, mainFrame) => {\n if (!mainFrame) return;\n const preparation = begin();\n if (inPlace) void preparation.finish();\n });\n contents.on(\"dom-ready\", () => {\n void applyWebsiteStyles(floatingStyles);\n if (!contents.isDestroyed()) void finishLoad?.();\n });\n contents.on(\"did-finish-load\", () => {\n void applyWebsiteStyles(floatingStyles);\n if (!contents.isDestroyed()) void finishLoad?.();\n });\n contents.on(\"did-fail-load\", (_event, code, _description, _url, mainFrame) => {\n if (!mainFrame || code === -3) return;\n stopPreparation();\n void releaseDashboardPage(contents);\n skeleton.setVisible(false);\n });\n contents.once(\"destroyed\", () => {\n ++generation;\n stopPreparation();\n });\n floating.on(\"closed\", () => {\n if (card.floating !== floating) return;\n card.floatingPreparation?.stop();\n card.floatingPreparation = undefined;\n floatingStyles.websiteStylesGeneration = (floatingStyles.websiteStylesGeneration ?? 0) + 1;\n card.floatingStyles = undefined;\n card.floating = undefined;\n if (owner.cards.get(id) === card) publishPinned(event.sender, owner);\n });\n const initial = begin();\n void contents.loadURL(configured.url).then(() => finishLoad?.(), initial.fail);\n floating.show();\n }\n publishPinned(event.sender, owner);\n return { pinned: [...owner.cards].filter(([, item]) => item.floating).map(([id]) => id) };\n });\n ipcMain.handle(clearChannel, (event) => {\n if (\n event.senderFrame === event.sender.mainFrame &&\n rendererUrl(event.senderFrame?.url ?? \"\") &&\n rendererUrl(event.sender.getURL())\n )\n hide(event.sender);\n });\n ipcMain.handle(reloadChannel, async (event, payload: unknown) => {\n if (\n !trusted(event) ||\n !payload ||\n typeof payload !== \"object\" ||\n !(\"id\" in payload) ||\n typeof payload.id !== \"string\"\n )\n return;\n const owner = owners.get(event.sender);\n const card = owner?.cards.get(payload.id);\n const configured = sanitizeDashboardCards(\n DashboardPreferencesStore.getInstanceOrCreate<DashboardPreferencesStore>().cards,\n ).find((item) => item.id === payload.id);\n if (!card || !configured || card.view.webContents.isDestroyed()) return;\n const [, floatingFailed] = await Promise.all([\n reload(card, configured.url),\n card.floatingPreparation?.refresh() ?? Promise.resolve(false),\n ]);\n return { failed: card.failed || floatingFailed ? [payload.id] : [] };\n });\n return () => {\n stopSettingsReaction();\n ipcMain.removeHandler(DASHBOARD_VIEWS_PIN);\n ipcMain.removeHandler(DASHBOARD_SITE_ICONS);\n ipcMain.removeHandler(syncChannel);\n ipcMain.removeHandler(clearChannel);\n ipcMain.removeHandler(reloadChannel);\n for (const sender of owners.keys()) clear(sender);\n };\n}\n"],"mappings":";;;;;;;;;;;AA8BA,IAAM,cAAc;AACpB,IAAM,gBAAgB;AACtB,IAAM,eAAe;AACrB,IAAM,gBAAgB;AACtB,IAAM,gBACJ;AAEF,IAAM,wBACJ;AAEF,SAAS,YAAY,OAAe,gBAAgB,OAAgB;CAClE,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,KAAK;EACzB,OACE,IAAI,aAAa,aAChB,IAAI,aAAa,2BAA2B,IAAI,SAAS,SAAS,wBAAwB,OAC1F,CAAC,iBAAiB,IAAI,aAAa;CAExC,QAAQ;EACN,OAAO;CACT;AACF;AACA,SAAS,aAAa,OAAwB;CAC5C,OAAO,YAAY,OAAO,IAAI;AAChC;AACA,SAAS,OAAO,OAAwB;CACtC,IAAI;EACF,OAAO,CAAC,UAAU,OAAO,CAAC,CAAC,SAAS,IAAI,IAAI,KAAK,CAAC,CAAC,QAAQ;CAC7D,QAAQ;EACN,OAAO;CACT;AACF;AACA,SAAS,UAAU,OAAuC;CACxD,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU;CACzC,MAAM,OAAO;CACb,IACE,CAAC;EAAC,KAAK;EAAG,KAAK;EAAG,KAAK;EAAO,KAAK;CAAM,CAAC,CAAC,OACxC,MAAM,OAAO,SAAS,CAAC,KAAK,OAAO,UAAU,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,KACrE,KACA,KAAK,SAAS,KACd,KAAK,UAAU,GAEf;CACF,OAAO;EAAE,GAAG,KAAK;EAAG,GAAG,KAAK;EAAG,OAAO,KAAK;EAAO,QAAQ,KAAK;CAAO;AACxE;AAEA,SAAS,0BAA0B;CACjC,MAAM,OAAO,IAAI,SAAA,KAAK;CACtB,KAAK,mBAAmB,SAAA,YAAY,sBAAsB,YAAY,SAAS;CAC/E,MAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,SAAS;EAC3C,MAAM,MAAM,IAAI,SAAA,KAAK;EACrB,IAAI,mBAAmB,SAAA,YAAY,sBAAsB,YAAY,SAAS;EAC9E,IAAI,gBAAgB,CAAC;EACrB,KAAK,aAAa,GAAG;EACrB,OAAO;CACT,CAAC;CACD,OAAO;EACL;EACA,SAAS,OAAe,WAAmB;GACzC,KAAK,UAAU;IAAE,GAAG;IAAG,GAAG;IAAG;IAAO;GAAO,CAAC;GAC5C,KAAK,SAAS,KAAK,UAAU;IAC3B,IAAI,UAAU;KACZ,GAAG;KACH,GAAG,UAAU,IAAI,KAAK,MAAM,QAAQ,KAAK;KACzC,OAAO,KAAK,IAAI,GAAG,KAAK,OAAO,QAAQ,OAAO,UAAU,IAAI,MAAO,EAAE,CAAC;KACtE,QAAQ,UAAU,IAAI,KAAK,UAAU,IAAI,KAAK,IAAI,IAAI,SAAS,GAAG,IAAI;IACxE,CAAC;GACH,CAAC;EACH;CACF;AACF;AA0CA,eAAe,mBAAmB,MAAwC;CACxE,MAAM,WAAW,KAAK,KAAK;CAC3B,MAAM,cAAc,KAAK,2BAA2B,KAAK;CACzD,KAAK,0BAA0B;CAC/B,MAAM,WAAW,KAAK;CACtB,KAAK,mBAAmB,KAAA;CACxB,IAAI,SAAS,YAAY,GAAG;CAC5B,IAAI,UAAU,MAAM,SAAS,kBAAkB,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC;CACvE,IAAI,KAAK,4BAA4B,cAAc,SAAS,YAAY,GAAG;CAC3E,MAAM,MAAM,MAAM,SACf,UAAU,KAAK,aAAa,wBAAwB,eAAe,EAAE,WAAW,OAAO,CAAC,CAAC,CACzF,YAAY,KAAA,CAAS;CACxB,IAAI,CAAC,KAAK;CACV,IAAI,KAAK,4BAA4B,cAAc,CAAC,SAAS,YAAY,GAAG,KAAK,mBAAmB;MAC/F,IAAI,CAAC,SAAS,YAAY,GAAG,MAAM,SAAS,kBAAkB,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;AACxF;AAEA,eAAe,iBACb,UACA,UACA,UACA,SACe;CACf,IAAI;CACJ,IAAI;EACF,MAAM,QAAQ,KAAK,EAChB,YAAY;GACX,IAAI,CAAC,QAAQ,GAAG;GAChB,MAAM,SAAS,UAAU,kBAAkB,2BAAA,wBAAwB,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;GAC9F,IAAI,CAAC,QAAQ,GAAG;GAChB,MAAM,SAAS,UAAU,kBAAkB,4BAAA,yBAAyB,SAAS,CAAC,CAAC;EACjF,EAAA,CAAG,CAAC,CAAC,YAAY,IAAI,SAAe,YAAY,WAAW,SAAS,GAAG,CAAC,CAAC,GACzE,IAAI,SAAe,YAAY;GAC7B,WAAW,WAAW,SAAS,IAAI;EACrC,CAAC,CACH,CAAC;CACH,UAAU;EACR,aAAa,QAAQ;CACvB;AACF;AAEA,eAAe,qBAAqB,UAAsC;CACxE,IAAI,SAAS,YAAY,GAAG;CAC5B,IAAI;CACJ,IAAI;EACF,MAAM,QAAQ,KAAK,CACjB,SAAS,UACN,kBAAkB,2EAAyE,CAAC,CAC5F,YAAY,CAAC,CAAC,GACjB,IAAI,SAAe,YAAY;GAC7B,WAAW,WAAW,SAAS,GAAG;EACpC,CAAC,CACH,CAAC;CACH,UAAU;EACR,aAAa,QAAQ;CACvB;AACF;AAEA,SAAS,qBAAqB,UAAyC;CACrE,IAAI,SAAS,YAAY,GAAG,OAAO,QAAQ,QAAQ;CACnD,OAAO,SAAS,UACb,kBAAkB,0EAAwE,CAAC,CAC3F,YAAY,CAAC,CAAC;AACnB;AAEA,SAAgB,sBAAkC;CAChD,MAAM,iBAAiB,SAAA,QAAQ,cAAc,iCAAiC;CAC9E,eAAe,6BAA6B,WAAW,aAAa,aAAa,SAAS,KAAK,CAAC;CAChG,eAAe,gCAAgC,KAAK;CACpD,MAAM,yBAAS,IAAI,IAAwB;CAC3C,MAAM,wBAAQ,IAAI,IAA+B;CACjD,MAAM,UAAU,OAAc,OAAe;EAC3C,MAAM,OAAO,MAAM,MAAM,IAAI,EAAE;EAC/B,IAAI,CAAC,MAAM;EACX,MAAM,MAAM,OAAO,EAAE;EACrB,KAAK,kBAAkB,KAAK;EAC5B,KAAK,kBAAkB;EACvB,KAAK,2BAA2B,KAAK,2BAA2B,KAAK;EACrE,KAAK,gBAAgB,MAAM;EAC3B,aAAa,KAAK,YAAY;EAC9B,KAAK,qBAAqB,KAAK;EAC/B,KAAK,UAAU,QAAQ;EACvB,KAAK,WAAW,KAAA;EAChB,IAAI,CAAC,MAAM,OAAO,YAAY,GAAG,MAAM,OAAO,YAAY,gBAAgB,KAAK,SAAS;EACxF,IAAI,CAAC,KAAK,KAAK,YAAY,YAAY,GAAG,KAAK,KAAK,YAAY,MAAM;CACxE;CACA,MAAM,SAAS,WAAwB;EACrC,MAAM,QAAQ,OAAO,IAAI,MAAM;EAC/B,IAAI,CAAC,OAAO;EACZ,OAAO,OAAO,MAAM;EACpB,KAAK,MAAM,MAAM,MAAM,MAAM,KAAK,GAAG,OAAO,OAAO,EAAE;EACrD,MAAM,QAAQ;CAChB;CACA,MAAM,QAAQ,WAAwB;EACpC,KAAK,MAAM,QAAQ,OAAO,IAAI,MAAM,CAAC,EAAE,MAAM,OAAO,KAAK,CAAC,GAAG;GAC3D,KAAK,UAAU;GACf,KAAK,UAAU,WAAW,KAAK;EACjC;CACF;CACA,MAAM,wBACJ,kBAAA,uBAAuB,oCAAA,0BAA0B,oBAA+C,CAAC,CAAC,KAAK;CACzG,MAAM,aAAA,GAAA,UAAA,KAAA,CAAiB,SAAA,IAAI,QAAQ,UAAU,GAAG,kBAAkB,sBAAsB;CACxF,MAAM,QAAQ,6BAAA,mBAAmB,SAAS,CAAC,CAAC,MAAM,UAAU;EAC1D,MAAM,aAAa,gBAAgB;EACnC,KAAK,MAAM,QAAQ,OACjB,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,KAAK,WAAW,MAAM,SAAS,KAAK,OAAO,KAAK,MAAM,KAAK,QAAQ,KAAK,OAAO,GACnG,MAAM,IAAI,KAAK,IAAI,IAAI;CAE7B,CAAC;CACD,IAAI,SAAS;CACb,MAAM,qBAAqB;EACzB,SAAS,OACN,KAAK,YAAY;GAChB,MAAM,aAAa,gBAAgB;GACnC,MAAM,UAAU,6BAAA,sBACd,CAAC,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,QAAQ,SAC1B,WAAW,MAAM,SAAS,KAAK,OAAO,KAAK,MAAM,KAAK,QAAQ,KAAK,OAAO,CAC5E,CACF;GACA,MAAM,MAAM;GACZ,KAAK,MAAM,QAAQ,SAAS,MAAM,IAAI,KAAK,IAAI,IAAI;GACnD,MAAM,6BAAA,mBAAmB,WAAW,OAAO;EAC7C,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;CACnB;CACA,aAAa;CACb,MAAM,kBAAkB,SAAmB;EACzC,KAAK,MAAM,CAAC,QAAQ,UAAU,QAAQ;GACpC,MAAM,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,MAAM,GAAG,eAAe,cAAc,IAAI,CAAC,GAAG;GAC1E,IACE,CAAC,MACD,OAAO,YAAY,KACnB,CAAC,aAAa,OAAO,UAAU,GAAG,KAClC,CAAC,aAAa,OAAO,OAAO,CAAC,KAC7B,CAAC,gBAAgB,CAAC,CAAC,MAAM,eAAe,WAAW,OAAO,MAAM,WAAW,QAAQ,KAAK,GAAG,GAE3F;GACF,OAAO,KAAK,wBAAA,yBAAyB;IAAE;IAAI,KAAK,KAAK;IAAK,SAAS,KAAK;IAAS,QAAQ,KAAK;GAAO,CAAC;EACxG;CACF;CACA,MAAM,UAAU,MAAgB,eAAuB,UAAU,OAAO,eAA+B;EACrG,MAAM,UAAU,KAAK,KAAK,YAAY,OAAO;EAC7C,MAAM,SAAS,CAAC,WAAW,KAAK,QAAQ,iBAAiB,OAAO,OAAO,IAAI,UAAU;EACrF,MAAM,kBAAkB,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,WAAW,CAAC,KAAK,UAAU,OAAO,OAAO;EAClG,KAAK,gBAAgB,MAAM;EAC3B,IAAI,CAAC,YAAY,KAAK,kBAAkB,KAAK;EAC7C,KAAK,MAAM;EACX,KAAK,SAAS;EACd,KAAK,UAAU;EACf,KAAK,kBAAkB;EACvB,MAAM,WAAW,KAAK,KAAK;EAC3B,MAAM,uBAAuB,SAAS,wBAAwB;EAC9D,IAAI,sBAAsB,SAAS,wBAAwB,KAAK;EAChE,MAAM,UAAU,0BAAA,wBAAwB,KAAK,KAAK,WAAW;EAC7D,IAAI,UAAU;EACd,IAAI;EACJ,MAAM,wBAAwB;GAC5B,IAAI,SAAS;GACb,UAAU;GACV,QAAQ,QAAQ;GAChB,SAAS,eAAe,aAAa,eAAe;GACpD,IAAI,eAAe,SAAS,eAAe,aAAa,aAAa;GACrE,IAAI,CAAC,SAAS,YAAY,KAAK,wBAAwB,CAAC,SAAS,wBAAwB,GACvF,SAAS,wBAAwB,oBAAoB;GACvD,IAAI,KAAK,oBAAoB,iBAAiB,KAAK,kBAAkB,KAAA;EACvE;EACA,KAAK,kBAAkB;EACvB,SAAS,KAAK,aAAa,eAAe;EAC1C,MAAM,aAAa,EAAE,KAAK;EAC1B,KAAK,SAAS,KAAK,WAAW,IAAI;EAClC,KAAK,UAAU,WAAW,KAAK,WAAW,CAAC,KAAK,QAAQ;EACxD,eAAe,IAAI;EACnB,MAAM,WAAW,WAAoB;GACnC,IAAI,KAAK,eAAe,cAAc,KAAK,KAAK,YAAY,YAAY,GAAG;GAC3E,KAAK,SAAS;GACd,KAAK,UAAU;GACf,KAAK,SAAS,KAAK,WAAW,KAAK;GACnC,KAAK,MAAM,CAAC,QAAQ,UAAU,QAAQ;IACpC,IAAI,CAAC,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG;IAC/C,KAAK,UAAU,WAAW,KAAK,WAAW,CAAC,KAAK,YAAY,CAAC,UAAU,aAAa,OAAO,OAAO,CAAC,CAAC;GACtG;GACA,eAAe,IAAI;EACrB;EACA,MAAM,aAAa;GACjB,IACE,KAAK,eAAe,cACpB,KAAK,KAAK,YAAY,YAAY,KAClC,CAAC,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,GAE9E,OAAO,QAAQ,QAAQ;GACzB,IAAI,YAAY,OAAO;GACvB,MAAM,iBAAiB;IACrB,IAAI,KAAK,eAAe,cAAc,SAAS,YAAY,GAAG,OAAO,QAAQ,QAAQ;IACrF,KAAK,sBAAsB;IAC3B,OAAO,SAAS,QAAQ,MAAM,CAAC,CAAC,cAAc;KAC5C,IAAI,KAAK,eAAe,YAAY,KAAK,sBAAsB;IACjE,CAAC;GACH;GACA,OAAO,kBAAkB,qBAAqB,QAAQ,CAAC,CAAC,KAAK,QAAQ,IAAI,SAAS;EACpF;EACA,MAAM,QAAQ,IAAI,SAAe,YAAY;GAC3C,gBAAgB;GAChB,SAAS,KAAK,aAAa,aAAa;EAC1C,CAAC;EACD,KAAK,UAAU,QAAQ,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAC3C,YAAY;GACV,IAAI,YAAY;GAChB,MAAM,gBACJ,aACA,KAAK,eAAe,cACpB,CAAC,KAAK,KAAK,YAAY,YAAY,KACnC,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC;GAC/E,IAAI;IACF,MAAM,iBAAiB,UAAU,eAAe,KAAK,UAAU,OAAO;IACtE,IAAI,QAAQ,GAAG,MAAM,qBAAqB,QAAQ;GACpD,UAAU;IACR,YAAY;IACZ,gBAAgB;GAClB;GACA,QAAQ,KAAK;EACf,SACM;GACJ,IAAI,KAAK,eAAe,YAAY,qBAA0B,QAAQ;GACtE,gBAAgB;GAChB,QAAQ,IAAI;EACd,CACF;EACA,OAAO,KAAK;CACd;CACA,MAAM,iBAAiB,SAAoE;EACzF,IAAI,KAAK,KAAK,YAAY,YAAY,GAAG,OAAO,QAAQ,QAAQ;EAChE,OAAO,KAAK,KAAK,YAAY,UAAU,kBAAkB,2BAAA,wBAAwB,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;CACjH;CACA,MAAM,sBAAsB;EAC1B,MAAM,aAAa,gBAAgB;EACnC,KAAK,MAAM,CAAC,IAAI,SAAS,OACvB,IAAI,CAAC,WAAW,MAAM,SAAS,KAAK,OAAO,MAAM,KAAK,QAAQ,KAAK,OAAO,GAAG;GAC3E,MAAM,OAAO,EAAE;GACf,aAAa;EACf;EAEF,KAAK,MAAM,CAAC,QAAQ,UAAU,QAC5B,KAAK,MAAM,CAAC,IAAI,SAAS,MAAM,OAAO;GACpC,MAAM,UAAU,WAAW,MAAM,SAAS,KAAK,OAAO,EAAE;GACxD,IAAI,CAAC,WAAW,QAAQ,QAAQ,KAAK,KAAK;IACxC,OAAO,OAAO,EAAE;IAChB;GACF;GACA,IAAI,KAAK,aAAa,QAAQ,UAAU;IACtC,KAAK,WAAW,QAAQ;IACxB,cAAc,IAAI;IAClB,KAAK,qBAAqB,gBAAgB;GAC5C;GACA,IAAI,KAAK,YAAY,CAAC,KAAK,SAAS,YAAY,GAAG,KAAK,SAAS,SAAS,QAAQ,KAAK;GACvF,IAAI,KAAK,gBAAgB;IACvB,MAAM,aAAa,QAAQ,SAAS,OAAO,QAAQ,SAAS;IAC5D,IAAI,KAAK,eAAe,eAAe,YAAY;KACjD,KAAK,eAAe,aAAa;KACjC,mBAAwB,KAAK,cAAc;IAC7C;GACF;GACA,MAAM,WAAW,QAAQ,wBAAwB,MAAQ,QAAQ,cAAc,MAAS,KAAA;GACxF,IAAI,KAAK,oBAAoB,UAAU;GACvC,aAAa,KAAK,YAAY;GAC9B,KAAK,eAAe,KAAA;GACpB,KAAK,kBAAkB;GACvB,MAAM,qBAAqB,KAAK,qBAAqB,KAAK;GAC1D,KAAK,oBAAoB;GACzB,IAAI,CAAC,UAAU;GACf,MAAM,iBAAiB;IACrB,IACE,OAAO,IAAI,MAAM,MAAM,SACvB,MAAM,MAAM,IAAI,EAAE,MAAM,QACxB,KAAK,sBAAsB,mBAE3B;IACF,IAAI,KAAK,WAAW,KAAK,SAAS;KAChC,KAAU,QAAQ,KAAK,QAAQ;KAC/B;IACF;IACA,KAAK,eAAe,WAAW,MAAM,QAAQ;GAC/C;GACA,MAAM,OAAO,YAAY;IACvB,IAAI;KACF,MAAM,SAAS,gBAAgB,CAAC,CAAC,MAAM,SAAS,KAAK,OAAO,EAAE;KAC9D,IACE,OAAO,IAAI,MAAM,MAAM,SACvB,MAAM,MAAM,IAAI,EAAE,MAAM,QACxB,CAAC,UACD,OAAO,QAAQ,KAAK,QACnB,OAAO,wBAAwB,MAAQ,OAAO,cAAc,MAAS,KAAA,OAAe,YACrF,KAAK,KAAK,YAAY,YAAY,GAElC;KACF,IAAI,KAAK,SAAS;MAChB,MAAM,KAAK;MACX;KACF;KACA,IAAI,OAAO;UACL,KAAK,UAAU;OACjB,IAAI,KAAK,SAAS,YAAY,KAAK,KAAK,qBAAqB,YAAY,GAAG;OAC5E,MAAM,QAAQ,IAAI,CAAC,OAAO,MAAM,OAAO,GAAG,GAAG,KAAK,qBAAqB,QAAQ,CAAC,CAAC;MACnF,OAAO,IAAI,KAAK,WAAW,aAAa,OAAO,OAAO,CAAC,GAAG,MAAM,OAAO,MAAM,OAAO,GAAG;KAAA,OAClF,IAAI,CAAC,KAAK,YAAY,CAAC,aAAa,OAAO,OAAO,CAAC,GAAG,MAAM,OAAO,MAAM,OAAO,GAAG;IAC5F,UAAU;KACR,SAAS;IACX;GACF;GACA,SAAS;EACX;CAEJ;CACA,MAAM,uBAAuB,8BAAA,eAEzB,KAAK,UACH,gBAAgB,CAAC,CAAC,KAAK,EAAE,IAAI,OAAO,KAAK,MAAM,aAAa,uBAAuB,gBAAgB;EACjG;EACA;EACA;EACA;EACA;EACA;EACA;CACF,EAAE,CACJ,GACF,aACF;CACA,MAAM,WAAW,UACf,MAAM,gBAAgB,MAAM,OAAO,aACnC,aAAa,MAAM,aAAa,OAAO,EAAE,KACzC,aAAa,MAAM,OAAO,OAAO,CAAC;CACpC,SAAA,QAAQ,OAAO,wBAAA,sBAAsB,OAAO,UAAU;EACpD,IACE,MAAM,gBAAgB,MAAM,OAAO,aACnC,CAAC,YAAY,MAAM,aAAa,OAAO,EAAE,KACzC,CAAC,YAAY,MAAM,OAAO,OAAO,CAAC,KAClC,CAAC,SAAA,cAAc,gBAAgB,MAAM,MAAM,GAE3C,OAAO,CAAC;EACV,MAAM;EACN,OAAO,gBAAgB,CAAC,CAAC,SAAS,SAAS;GACzC,MAAM,OAAO,MAAM,IAAI,KAAK,EAAE;GAC9B,OAAO,MAAM,YAAY,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC;EAChD,CAAC;CACH,CAAC;CACD,SAAA,QAAQ,OAAO,aAAa,OAAO,OAAO,YAAqB;EAC7D,IAAI,CAAC,QAAQ,KAAK,GAAG;EACrB,MAAM,SAAS,SAAA,cAAc,gBAAgB,MAAM,MAAM;EACzD,IAAI,CAAC,UAAU,OAAO,YAAY,GAAG;EACrC,MAAM,YACJ,WAAW,OAAO,YAAY,YAAY,WAAW,WAAW,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;EAClH,MAAM,QAAQ,kBAAA,uBACZ,oCAAA,0BAA0B,oBAA+C,CAAC,CAAC,KAC7E;EACA,IAAI,QAAQ,OAAO,IAAI,MAAM,MAAM;EACnC,IAAI,CAAC,OAAO;GACV,MAAM,SAAS,MAAM;GACrB,MAAM,cAAc,QAAiB,QAAgB;IACnD,IAAI,CAAC,aAAa,GAAG,GAAG,KAAK,MAAM;GACrC;GACA,MAAM,kBAAkB,MAAM,MAAM;GACpC,OAAO,GAAG,gBAAgB,UAAU;GACpC,OAAO,GAAG,wBAAwB,UAAU;GAC5C,OAAO,GAAG,aAAa,SAAS;GAChC,QAAQ;IACN;IACA,uBAAO,IAAI,IAAI;IACf,eAAe;KACb,OAAO,eAAe,gBAAgB,UAAU;KAChD,OAAO,eAAe,wBAAwB,UAAU;KACxD,OAAO,eAAe,aAAa,SAAS;IAC9C;GACF;GACA,OAAO,IAAI,QAAQ,KAAK;EAC1B;EACA,IAAI,WAAW,OAAO,YAAY,YAAY,YAAY,WAAW,QAAQ,WAAW,MAAM;GAC5F,KAAK,MAAM,MAAM;GACjB,OAAO;IAAE,QAAQ,CAAC;IAAG,QAAQ,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,QAAQ,GAAG,UAAU,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE;GAAE;EACtG;EACA,MAAM,0BAAU,IAAI,IAAY;EAChC,KAAK,MAAM,QAAQ,UAAU,MAAM,GAAG,MAAM,MAAM,GAAG;GACnD,IAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,OAAO,YAAY,QAAQ,IAAI,KAAK,EAAE,GAAG;GAC9F,MAAM,aAAa,MAAM,MAAM,SAAS,KAAK,OAAO,KAAK,EAAE;GAC3D,MAAM,SAAS,UAAU,KAAK,MAAM;GACpC,MAAM,OAAO,UAAU,KAAK,IAAI;GAChC,IACE,CAAC,cACD,CAAC,UACD,CAAC,QACD,KAAK,IAAI,OAAO,KAChB,KAAK,IAAI,OAAO,KAChB,KAAK,IAAI,KAAK,QAAQ,OAAO,IAAI,OAAO,SACxC,KAAK,IAAI,KAAK,SAAS,OAAO,IAAI,OAAO,QAEzC;GACF,QAAQ,IAAI,KAAK,EAAE;GACnB,IAAI,OAAO,MAAM,MAAM,IAAI,KAAK,EAAE;GAClC,IAAI,MAAM,UAAU;IAClB,KAAK,UAAU;IACf,KAAK,UAAU,WAAW,KAAK;IAC/B;GACF;GACA,IAAI,CAAC,MAAM;IACT,MAAM,YAAY,IAAI,SAAA,KAAK;IAC3B,UAAU,mBAAmB,WAAW;IACxC,MAAM,OAAO,IAAI,SAAA,gBAAgB,EAC/B,gBAAgB;KACd,iBAAiB;KACjB,4BAA4B;KAC5B,kBAAkB;KAClB,SAAS;KACT,aAAa;KACb,WAAW;KACX,UAAA,GAAA,UAAA,KAAA,CAAc,WAAW,sBAAsB;IACjD,EACF,CAAC;IACD,KAAK,mBAAmB,WAAW;IACnC,KAAK,YAAY,sBAAsB,EAAE,UAAU;KACjD,IAAI,OAAO,GAAG,GAAG,SAAK,MAAM,aAAa,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;KAC5D,OAAO,EAAE,QAAQ,OAAO;IAC1B,CAAC;IACD,MAAM,cAAc,OAAuB,QAAgB;KACzD,IAAI,CAAC,OAAO,GAAG,GAAG,MAAM,eAAe;IACzC;IACA,KAAK,YAAY,GAAG,iBAAiB,UAAU;IAC/C,KAAK,YAAY,GAAG,iBAAiB,UAAU;IAC/C,KAAK,YAAY,GAAG,wBAAwB,UAAU;KACpD,IAAI,CAAC,OAAO,MAAM,GAAG,GAAG,MAAM,eAAe;IAC/C,CAAC;IACD,UAAU,aAAa,IAAI;IAC3B,MAAM,WAAW,wBAAwB;IACzC,UAAU,aAAa,SAAS,IAAI;IACpC,OAAO,YAAY,aAAa,SAAS;IACzC,OAAO;KAAE;KAAW;KAAU;KAAM,KAAK;KAAI,QAAQ;KAAO,SAAS;KAAO,SAAS;KAAO,YAAY;IAAE;IAC1G,MAAM,MAAM,IAAI,KAAK,IAAI,IAAI;IAC7B,MAAM,UAAU;IAChB,KAAK,YAAY,GAAG,uBAAuB,QAAQ,UAA0B;KAC3E,IACE,MAAM,SAAS,aACf,MAAM,QAAQ,YACd,OAAO,IAAI,MAAM,MAAM,MAAM,SAC7B,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,WAC7B,CAAC,QAAQ,KAAK,GAEd;KACF,MAAM,OAAO,KAAK,iCAAiC;IACrD,CAAC;IACD,QAAQ,WAAW,WAAW;IAC9B,KAAK,YAAY,GAAG,yBAAyB,QAAQ,MAAM,SAAS,cAAc;KAChF,IAAI,CAAC,WAAW;KAChB,IAAI,QAAQ,qBAAqB;MAC/B,QAAQ,sBAAsB;MAC9B;KACF;KACA,QAAQ,kBAAkB,KAAK;KAC/B,MAAM,aAAa,IAAI,SAAe,SAAS,WAAW;MACxD,QAAQ,mBAAmB;OACzB,cAAc;QACZ,QAAQ,mBAAmB,KAAA;QAC3B,QAAQ;OACV;OACA,YAAY;QACV,QAAQ,mBAAmB,KAAA;QAC3B,uBAAO,IAAI,MAAM,6BAA6B,CAAC;OACjD;MACF;KACF,CAAC;KACD,IAAI,SACF,KAAU,YAAY,UACnB,kBAAkB,2EAAyE,CAAC,CAC5F,YAAY,CAAC,CAAC;KACnB,OAAY,SAAS,QAAQ,KAAK,OAAO,UAAU;KACnD,IAAI,SAAS,QAAQ,kBAAkB,OAAO;IAChD,CAAC;IACD,KAAK,YAAY,GAAG,yBAAyB;KAC3C,mBAAwB,OAAO;KAC/B,cAAmB,OAAO;KAC1B,QAAQ,kBAAkB,OAAO;IACnC,CAAC;IACD,KAAK,YAAY,GAAG,kBAAkB,QAAQ,MAAM,cAAc,MAAM,cAAc;KACpF,IAAI,CAAC,aAAc,SAAS,MAAM,KAAK,YAAY,UAAU,GAAI;KACjE,QAAQ,kBAAkB,KAAK;IACjC,CAAC;IACD,KAAK,YAAY,GAAG,yBAAyB,QAAQ,aAAuB;KAC1E,MAAM,UAAU,SAAS,MAAM,UAAU;MACvC,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;MAC3B,MAAM,SAAS,IAAI,IAAI,KAAK;MAC5B,OAAO,CAAC,OAAO,YAAY,CAAC,OAAO;KACrC,CAAC;KACD,IAAI,CAAC,WAAW,OAAO,IAAI,MAAM,MAAM,MAAM,SAAS,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,SAAS;KAC5F,QAAQ,gBAAgB,MAAM;KAC9B,MAAM,UAAU,IAAI,gBAAgB;KACpC,QAAQ,iBAAiB;KACzB,QAAQ,UAAU;KAClB,MAAM,WAAW,QAAgB;MAC/B,IACE,QAAQ,OAAO,WACf,OAAO,IAAI,MAAM,MAAM,MAAM,SAC7B,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,WAC7B,MAAM,gBAAgB,MAAM,OAAO,WAEnC;MACF,QAAQ,UAAU;MAClB,IACE,IAAI,WAAW,OAAO,KACtB,gBAAgB,CAAC,CAAC,MAAM,SAAS,KAAK,OAAO,KAAK,MAAM,KAAK,QAAQ,QAAQ,GAAG,GAChF;OACA,MAAM,IAAI,KAAK,IAAI;QAAE,IAAI,KAAK;QAAI,SAAS,QAAQ;QAAK,SAAS;OAAI,CAAC;OACtE,aAAa;OACb,IAAI,YAAY,MAAM,aAAa,OAAO,EAAE,KAAK,YAAY,MAAM,OAAO,OAAO,CAAC,GAChF,MAAM,OAAO,KAAK,wBAAA,oBAAoB;MAC1C;MACA,IAAI,CAAC,QAAQ,KAAK,GAAG;MACrB,MAAM,OAAO,KAAK,sCAAsC;OAAE,IAAI,KAAK;OAAI;MAAI,CAAC;KAC9E;KACA,QAAQ,OAAO;KACf,MAAM,UAAU,iBAAiB,QAAQ,MAAM,GAAG,GAAI;KACtD,CAAM,YAAY;MAChB,IAAI,UAAU;MACd,KAAK,IAAI,YAAY,GAAG,aAAa,GAAG,aAAa;OACnD,MAAM,WAAW,MAAM,eAAe,MAAM,SAAS;QACnD,QAAQ,QAAQ;QAChB,UAAU;QACV,aAAa;OACf,CAAC;OACD,IAAI,SAAS,UAAU,OAAO,SAAS,SAAS,KAAK;QACnD,MAAM,SAAS,MAAM,OAAO;QAC5B,MAAM,WAAW,SAAS,QAAQ,IAAI,UAAU;QAChD,IAAI,CAAC,UAAU;QACf,MAAM,OAAO,IAAI,IAAI,UAAU,OAAO;QACtC,IAAI,CAAC,OAAO,KAAK,IAAI,KAAK,KAAK,YAAY,KAAK,UAAU;QAC1D,UAAU,KAAK;QACf;OACF;OACA,MAAM,OAAO,SAAS,QAAQ,IAAI,cAAc,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,YAAY;OACpF,IACE,CAAC,SAAS,MACV,CAAC,QACD,CAAC;QACC;QACA;QACA;QACA;QACA;QACA;QACA;OACF,CAAC,CAAC,SAAS,IAAI,KACf,OAAO,SAAS,QAAQ,IAAI,gBAAgB,CAAC,IAAI,QACjD;QACA,MAAM,SAAS,MAAM,OAAO;QAC5B;OACF;OACA,MAAM,SAAS,SAAS,MAAM,UAAU;OACxC,IAAI,CAAC,QAAQ;OACb,MAAM,SAAuB,CAAC;OAC9B,IAAI,OAAO;OACX,OAAO,MAAM;QACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;QAC1C,IAAI,MAAM;QACV,QAAQ,MAAM;QACd,IAAI,OAAO,QAAQ;SACjB,MAAM,OAAO,OAAO;SACpB;QACF;QACA,OAAO,KAAK,KAAK;OACnB;OACA,IAAI,MAAM,QAAQ,QAAQ,KAAK,UAAU,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,QAAQ,GAAG;OACnF;MACF;KACF,EAAA,CAAG,CAAC,CACD,YAAY,CAAC,CAAC,CAAC,CACf,cAAc,aAAa,OAAO,CAAC;IACxC,CAAC;GACH;GACA,IAAI,KAAK,gBAAgB,KAAK,eAAe,OAAO;IAClD,KAAK,aAAa,KAAK,eAAe;IACtC,mBAAwB,IAAI;GAC9B;GACA,KAAK,UAAU;GACf,KAAK,UAAU,WAAW,CAAC,KAAK,WAAW,KAAK,WAAW,KAAK,QAAQ,WAAW,IAAI;GACvF,MAAM,OAAO,MAAM,OAAO,cAAc;GACxC,KAAK,UAAU,gBAAgB,KAAK,MAAM,KAAK,IAAI,CAAC;GACpD,KAAK,KAAK,gBAAgB,KAAK,MAAM,KAAK,IAAI,CAAC;GAC/C,MAAM,SAAS,UAAgC;IAC7C,GAAG,KAAK,MAAM,KAAK,IAAI,IAAI;IAC3B,GAAG,KAAK,MAAM,KAAK,IAAI,IAAI;IAC3B,OAAO,KAAK,MAAM,KAAK,QAAQ,IAAI;IACnC,QAAQ,KAAK,MAAM,KAAK,SAAS,IAAI;GACvC;GACA,KAAK,UAAU,UAAU,MAAM,IAAI,CAAC;GACpC,KAAK,SAAS,OAAO,KAAK,MAAM,KAAK,QAAQ,IAAI,GAAG,KAAK,MAAM,KAAK,SAAS,IAAI,CAAC;GAClF,KAAK,KAAK,UACR,MAAM;IAAE,GAAG,OAAO,IAAI,KAAK;IAAG,GAAG,OAAO,IAAI,KAAK;IAAG,OAAO,OAAO;IAAO,QAAQ,OAAO;GAAO,CAAC,CAClG;GACA,IAAI,KAAK,QAAQ,WAAW,KAAK;IAC/B,KAAK,UAAU,KAAA;IACf,KAAK,gBAAgB,MAAM;IAC3B,OAAY,MAAM,WAAW,KAAK,IAAI;GACxC;GACA,eAAe,IAAI;GACnB,IAAI,KAAK,SAAS,MAAM,OAAO,KAAK,sCAAsC;IAAE,IAAI,KAAK;IAAI,KAAK,KAAK;GAAQ,CAAC;EAC9G;EACA,KAAK,MAAM,CAAC,IAAI,SAAS,MAAM,OAC7B,IAAI,CAAC,MAAM,MAAM,eAAe,WAAW,OAAO,EAAE,GAAG,OAAO,OAAO,EAAE;OAClE,IAAI,CAAC,QAAQ,IAAI,EAAE,GAAG;GACzB,KAAK,UAAU;GACf,KAAK,UAAU,WAAW,KAAK;EACjC;EAEF,cAAc;EACd,MAAM,QAAQ,IAAI,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS,KAAK,OAAO,CAAC;EACvE,OAAO;GACL,QAAQ,CAAC,GAAG,MAAM,MAAM,QAAQ,CAAC,CAAC,CAAC,QAAQ,GAAG,UAAU,KAAK,MAAM,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE;GACrF,QAAQ,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,QAAQ,GAAG,UAAU,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE;EAC/E;CACF,CAAC;CACD,MAAM,iBAAiB,QAAqB,UAAiB;EAC3D,IAAI,CAAC,OAAO,YAAY,GACtB,OAAO,KACL,wBAAA,wBACA,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,QAAQ,GAAG,UAAU,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CACvE;CACJ;CACA,SAAA,QAAQ,OAAO,wBAAA,sBAAsB,OAAO,YAAqB;EAC/D,IACE,CAAC,QAAQ,KAAK,KACd,CAAC,WACD,OAAO,YAAY,YACnB,EAAE,QAAQ,YACV,OAAO,QAAQ,OAAO,YACtB,EAAG,YAAY,WAAW,OAAO,QAAQ,WAAW,aAAe,WAAW,WAAW,QAAQ,UAAU,OAE3G;EACF,MAAM,KAAK,QAAQ;EACnB,MAAM,QAAQ,OAAO,IAAI,MAAM,MAAM;EACrC,MAAM,OAAO,OAAO,MAAM,IAAI,QAAQ,EAAE;EACxC,MAAM,aAAa,gBAAgB,CAAC,CAAC,MAAM,SAAS,KAAK,OAAO,QAAQ,EAAE;EAC1E,IACE,CAAC,SACD,CAAC,QACD,CAAC,cACD,KAAK,QAAQ,WAAW,OACxB,MAAM,OAAO,YAAY,KACzB,SAAA,cAAc,gBAAgB,MAAM,MAAM,MAAM,MAAM,UACtD,KAAK,KAAK,YAAY,YAAY,GAElC;EACF,IAAI,WAAW,WAAW,QAAQ,UAAU,MAAM;GAChD,KAAK,UAAU,KAAK;GACpB,KAAK,UAAU,MAAM;EACvB,OAAO,IAAI,EAAE,YAAY,YAAY,CAAC,QAAQ,QAC5C,KAAK,UAAU,MAAM;OAChB,IAAI,CAAC,KAAK,UAAU;GACzB,MAAM,WAAW,IAAI,SAAA,cAAc;IACjC,OAAO,WAAW;IAClB,OAAO;IACP,QAAQ;IACR,UAAU;IACV,WAAW;IACX,WAAW;IACX,aAAa;IACb,iBAAiB;IACjB,MAAM;IACN,gBAAgB;KACd,iBAAiB;KACjB,4BAA4B;KAC5B,kBAAkB;KAClB,SAAS;KACT,aAAa;KACb,WAAW;KACX,UAAA,GAAA,UAAA,KAAA,CAAc,WAAW,sBAAsB;IACjD;GACF,CAAC;GACD,SAAS,QAAQ,IAAI;GACrB,SAAS,eAAe,MAAM,UAAU;GACxC,SAAS,0BAA0B,MAAM,EAAE,qBAAqB,KAAK,CAAC;GACtE,KAAK,WAAW;GAChB,KAAK,UAAU,WAAW,KAAK;GAC/B,MAAM,WAAW,SAAS;GAC1B,MAAM,iBAAoC;IACxC,MAAM,EAAE,aAAa,SAAS;IAC9B,YAAY,WAAW,SAAS,OAAO,WAAW,SAAS;GAC7D;GACA,KAAK,iBAAiB;GACtB,SAAS,sBAAsB,EAAE,UAAU;IACzC,IAAI,OAAO,GAAG,GAAG,SAAK,MAAM,aAAa,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5D,OAAO,EAAE,QAAQ,OAAO;GAC1B,CAAC;GACD,MAAM,cAAc,OAAuB,QAAgB;IACzD,IAAI,CAAC,OAAO,GAAG,GAAG,MAAM,eAAe;GACzC;GACA,SAAS,GAAG,iBAAiB,UAAU;GACvC,SAAS,GAAG,iBAAiB,UAAU;GACvC,SAAS,GAAG,wBAAwB,UAAU;IAC5C,IAAI,CAAC,OAAO,MAAM,GAAG,GAAG,MAAM,eAAe;GAC/C,CAAC;GACD,SAAS,GAAG,uBAAuB,UAAU,MAAM,eAAe,CAAC;GACnE,SAAS,GAAG,wBAAwB,UAAU,MAAM,eAAe,CAAC;GACpE,MAAM,kBAAkB,wBAAwB;GAChD,MAAM,WAAW,gBAAgB;GACjC,MAAM,uBAAuB;IAC3B,MAAM,EAAE,OAAO,WAAW,SAAS,iBAAiB;IACpD,gBAAgB,OAAO,OAAO,MAAM;GACtC;GACA,SAAS,YAAY,aAAa,QAAQ;GAC1C,SAAS,GAAG,UAAU,cAAc;GACpC,eAAe;GACf,IAAI,aAAa;GACjB,IAAI,oBAAoB;GACxB,IAAI,wBAAwB,CAAC;GAC7B,IAAI;GACJ,MAAM,cAAc;IAClB,gBAAgB;IAChB,oBAAoB;IACpB,MAAM,QAAQ,EAAE;IAChB,MAAM,uBAAuB,SAAS,wBAAwB;IAC9D,IAAI,sBAAsB,SAAS,wBAAwB,KAAK;IAChE,MAAM,UAAU,0BAAA,wBAAwB,QAAQ;IAChD,IAAI,SAAS;IACb,IAAI;IACJ,MAAM,gBAAgB,UAAU,eAAe,SAAS,KAAK,aAAa,YAAY,CAAC,SAAS,YAAY;IAC5G,SAAS,WAAW,IAAI;IACxB,wBAAwB;KACtB,IAAI,CAAC,QAAQ;KACb,SAAS;KACT,oBAAoB;KACpB,QAAQ,QAAQ;KAChB,IAAI,CAAC,SAAS,YAAY,KAAK,sBAAsB,SAAS,wBAAwB,IAAI;IAC5F;IACA,MAAM,OAAO;IACb,MAAM,eAAe;KACnB,eAAe,YAAY;MACzB,MAAM,iBAAiB,UAAU,eAAe,KAAK,UAAU,OAAO;MACtE,IAAI,CAAC,QAAQ,GAAG,OAAO;MACvB,MAAM,qBAAqB,QAAQ;MACnC,IAAI,CAAC,QAAQ,GAAG,OAAO;MACvB,SAAS,WAAW,KAAK;MACzB,KAAK;MACL,OAAO;KACT,EAAA,CAAG;KACH,OAAO;IACT;IACA,aAAa;IACb,OAAO;KACL;KACA,YAAY;MACV,IAAI,QAAQ,GAAG;OACb,qBAA0B,QAAQ;OAClC,SAAS,WAAW,KAAK;OACzB,KAAK;MACP;KACF;IACF;GACF;GACA,MAAM,gBAAgB;IACpB,MAAM,cAAc,MAAM;IAC1B,MAAM,UAAU,SAAS,OAAO;IAChC,IAAI;IACJ,MAAM,QAAQ,IAAI,SAAkB,YAAY;KAC9C,sBAAsB;MACpB,CAAM,aAAa,KAAK,QAAQ,QAAQ,KAAK,EAAA,CAAG,KAAK,OAAO;KAC9D;KACA,SAAS,KAAK,aAAa,aAAa;IAC1C,CAAC;IACD,MAAM,UAAU,qBAAqB,QAAQ,CAAC,CAC3C,WAAW,SAAS,QAAQ,OAAO,OAAO,IAAI,UAAU,WAAW,GAAG,CAAC,CAAC,CACxE,WACO,aAAa,KAAK,aAClB;KACJ,YAAY,KAAK;KACjB,OAAO,CAAC,SAAS,YAAY;IAC/B,CACF;IACF,OAAO,QAAQ,KAAK,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,cAAc;KAClD,SAAS,eAAe,aAAa,aAAa;IACpD,CAAC;GACH;GACA,KAAK,sBAAsB;IACzB;IACA,mBAAmB;IACnB,uBAAuB;KAErB,MAAK,CAAA,CAAY,OAAO;IAC1B;IACA,YAAY;KACV,EAAE;KACF,gBAAgB;IAClB;GACF;GACA,SAAS,GAAG,yBAAyB,QAAQ,MAAM,SAAS,cAAc;IACxE,IAAI,CAAC,WAAW;IAChB,MAAM,cAAc,MAAM;IAC1B,IAAI,SAAS,YAAiB,OAAO;GACvC,CAAC;GACD,SAAS,GAAG,mBAAmB;IAC7B,mBAAwB,cAAc;IACtC,IAAI,CAAC,SAAS,YAAY,GAAG,aAAkB;GACjD,CAAC;GACD,SAAS,GAAG,yBAAyB;IACnC,mBAAwB,cAAc;IACtC,IAAI,CAAC,SAAS,YAAY,GAAG,aAAkB;GACjD,CAAC;GACD,SAAS,GAAG,kBAAkB,QAAQ,MAAM,cAAc,MAAM,cAAc;IAC5E,IAAI,CAAC,aAAa,SAAS,IAAI;IAC/B,gBAAgB;IAChB,qBAA0B,QAAQ;IAClC,SAAS,WAAW,KAAK;GAC3B,CAAC;GACD,SAAS,KAAK,mBAAmB;IAC/B,EAAE;IACF,gBAAgB;GAClB,CAAC;GACD,SAAS,GAAG,gBAAgB;IAC1B,IAAI,KAAK,aAAa,UAAU;IAChC,KAAK,qBAAqB,KAAK;IAC/B,KAAK,sBAAsB,KAAA;IAC3B,eAAe,2BAA2B,eAAe,2BAA2B,KAAK;IACzF,KAAK,iBAAiB,KAAA;IACtB,KAAK,WAAW,KAAA;IAChB,IAAI,MAAM,MAAM,IAAI,EAAE,MAAM,MAAM,cAAc,MAAM,QAAQ,KAAK;GACrE,CAAC;GACD,MAAM,UAAU,MAAM;GACtB,SAAc,QAAQ,WAAW,GAAG,CAAC,CAAC,WAAW,aAAa,GAAG,QAAQ,IAAI;GAC7E,SAAS,KAAK;EAChB;EACA,cAAc,MAAM,QAAQ,KAAK;EACjC,OAAO,EAAE,QAAQ,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,QAAQ,GAAG,UAAU,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE;CAC1F,CAAC;CACD,SAAA,QAAQ,OAAO,eAAe,UAAU;EACtC,IACE,MAAM,gBAAgB,MAAM,OAAO,aACnC,YAAY,MAAM,aAAa,OAAO,EAAE,KACxC,YAAY,MAAM,OAAO,OAAO,CAAC,GAEjC,KAAK,MAAM,MAAM;CACrB,CAAC;CACD,SAAA,QAAQ,OAAO,eAAe,OAAO,OAAO,YAAqB;EAC/D,IACE,CAAC,QAAQ,KAAK,KACd,CAAC,WACD,OAAO,YAAY,YACnB,EAAE,QAAQ,YACV,OAAO,QAAQ,OAAO,UAEtB;EAEF,MAAM,OADQ,OAAO,IAAI,MAAM,MAClB,CAAA,EAAO,MAAM,IAAI,QAAQ,EAAE;EACxC,MAAM,aAAa,kBAAA,uBACjB,oCAAA,0BAA0B,oBAA+C,CAAC,CAAC,KAC7E,CAAC,CAAC,MAAM,SAAS,KAAK,OAAO,QAAQ,EAAE;EACvC,IAAI,CAAC,QAAQ,CAAC,cAAc,KAAK,KAAK,YAAY,YAAY,GAAG;EACjE,MAAM,GAAG,kBAAkB,MAAM,QAAQ,IAAI,CAC3C,OAAO,MAAM,WAAW,GAAG,GAC3B,KAAK,qBAAqB,QAAQ,KAAK,QAAQ,QAAQ,KAAK,CAC9D,CAAC;EACD,OAAO,EAAE,QAAQ,KAAK,UAAU,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE;CACrE,CAAC;CACD,aAAa;EACX,qBAAqB;EACrB,SAAA,QAAQ,cAAc,wBAAA,mBAAmB;EACzC,SAAA,QAAQ,cAAc,wBAAA,oBAAoB;EAC1C,SAAA,QAAQ,cAAc,WAAW;EACjC,SAAA,QAAQ,cAAc,YAAY;EAClC,SAAA,QAAQ,cAAc,aAAa;EACnC,KAAK,MAAM,UAAU,OAAO,KAAK,GAAG,MAAM,MAAM;CAClD;AACF"}
|
|
1
|
+
{"version":3,"file":"dashboard-views.js","names":[],"sources":["../../src/main/dashboard-views.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { join } from \"node:path\";\nimport {\n app,\n BrowserWindow,\n type IpcMainInvokeEvent,\n ipcMain,\n nativeTheme,\n type Rectangle,\n session,\n shell,\n View,\n type WebContents,\n WebContentsView,\n} from \"electron\";\nimport { reaction } from \"mobx\";\nimport { sanitizeDashboardCards } from \"../common/dashboard\";\nimport { dashboardReadinessScript } from \"../common/dashboard-readiness\";\nimport { dashboardSelectorScript } from \"../common/dashboard-selector\";\nimport {\n DASHBOARD_SITE_ICONS,\n DASHBOARD_VIEWS_LOADING,\n DASHBOARD_VIEWS_PIN,\n DASHBOARD_VIEWS_PINNED,\n type DashboardSiteIcon,\n} from \"../common/dashboard-views\";\nimport { DashboardPreferencesStore } from \"../common/store/dashboard-preferences-store\";\nimport { boundedDashboardIcons, loadDashboardIcons, saveDashboardIcons } from \"./dashboard-icon-cache\";\nimport { observeDashboardNetwork } from \"./dashboard-network\";\n\nconst syncChannel = \"freelens-power:dashboard-views:sync\";\nconst reloadChannel = \"freelens-power:dashboard-views:reload\";\nconst clearChannel = \"freelens-power:dashboard-views:clear\";\nconst dashboardPath = \"/extension/freelens-power/dashboard\";\nconst websiteStyles =\n \"* { scrollbar-width: none !important; } *::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }\";\n\nconst expandedWebsiteStyles =\n \"* { scrollbar-width: auto !important; } *::-webkit-scrollbar { display: revert !important; width: revert !important; height: revert !important; }\";\n\nfunction rendererUrl(value: string, dashboardOnly = false): boolean {\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 (!dashboardOnly || url.pathname === dashboardPath)\n );\n } catch {\n return false;\n }\n}\nfunction dashboardUrl(value: string): boolean {\n return rendererUrl(value, true);\n}\nfunction webUrl(value: string): boolean {\n try {\n return [\"https:\", \"http:\"].includes(new URL(value).protocol);\n } catch {\n return false;\n }\n}\nfunction rectangle(value: unknown): Rectangle | undefined {\n if (!value || typeof value !== \"object\") return;\n const rect = value as Rectangle;\n if (\n ![rect.x, rect.y, rect.width, rect.height].every(\n (n) => Number.isFinite(n) && Number.isInteger(n) && Math.abs(n) <= 32768,\n ) ||\n rect.width <= 0 ||\n rect.height <= 0\n )\n return;\n return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };\n}\n\nfunction createDashboardSkeleton() {\n const view = new View();\n view.setBackgroundColor(nativeTheme.shouldUseDarkColors ? \"#202124\" : \"#ffffff\");\n const bars = Array.from({ length: 4 }, () => {\n const bar = new View();\n bar.setBackgroundColor(nativeTheme.shouldUseDarkColors ? \"#393b40\" : \"#e8e9eb\");\n bar.setBorderRadius(6);\n view.addChildView(bar);\n return bar;\n });\n return {\n view,\n layout: (width: number, height: number) => {\n view.setBounds({ x: 0, y: 0, width, height });\n bars.forEach((bar, index) => {\n bar.setBounds({\n x: 20,\n y: index === 0 ? 20 : 60 + (index - 1) * 24,\n width: Math.max(1, Math.round((width - 40) * (index === 0 ? 0.45 : 1))),\n height: index === 0 ? 20 : index === 3 ? Math.max(30, height - 128) : 12,\n });\n });\n },\n };\n}\n\ntype WebsiteStyleState = {\n view: { webContents: WebContents };\n scrollbars?: boolean;\n websiteStylesKey?: string;\n websiteStylesGeneration?: number;\n};\n\ntype CardView = {\n container: View;\n skeleton: ReturnType<typeof createDashboardSkeleton>;\n view: WebContentsView;\n url: string;\n failed: boolean;\n loading: boolean;\n visible: boolean;\n generation: number;\n stopPreparation?: () => void;\n selector?: string;\n scrollbars?: boolean;\n websiteStylesKey?: string;\n websiteStylesGeneration?: number;\n pending?: Promise<void>;\n requestedNavigation?: boolean;\n nativeNavigation?: { finish: () => void; fail: () => void };\n favicon?: string;\n faviconRequest?: AbortController;\n refreshTimer?: ReturnType<typeof setTimeout>;\n refreshInterval?: number;\n refreshGeneration?: number;\n floating?: BrowserWindow;\n floatingStyles?: WebsiteStyleState;\n floatingPreparation?: {\n refresh: () => Promise<boolean>;\n selectorChanged: () => void;\n stop: () => void;\n isPreparing: () => boolean;\n };\n};\ntype Owner = { window: BrowserWindow; cards: Map<string, CardView>; cleanup: () => void };\n\nasync function applyWebsiteStyles(card: WebsiteStyleState): Promise<void> {\n const contents = card.view.webContents;\n const generation = (card.websiteStylesGeneration ?? 0) + 1;\n card.websiteStylesGeneration = generation;\n const previous = card.websiteStylesKey;\n card.websiteStylesKey = undefined;\n if (contents.isDestroyed()) return;\n if (previous) await contents.removeInsertedCSS(previous).catch(() => {});\n if (card.websiteStylesGeneration !== generation || contents.isDestroyed()) return;\n const key = await contents\n .insertCSS(card.scrollbars ? expandedWebsiteStyles : websiteStyles, { cssOrigin: \"user\" })\n .catch(() => undefined);\n if (!key) return;\n if (card.websiteStylesGeneration === generation && !contents.isDestroyed()) card.websiteStylesKey = key;\n else if (!contents.isDestroyed()) await contents.removeInsertedCSS(key).catch(() => {});\n}\n\nasync function waitForReadiness(\n contents: WebContents,\n _network: ReturnType<typeof observeDashboardNetwork>,\n selector: () => string | undefined,\n current: () => boolean,\n): Promise<void> {\n let deadline: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n (async () => {\n if (!current()) return;\n await contents.mainFrame.executeJavaScript(dashboardSelectorScript(selector())).catch(() => {});\n if (!current()) return;\n await contents.mainFrame.executeJavaScript(dashboardReadinessScript(selector()));\n })().catch(() => new Promise<void>((resolve) => setTimeout(resolve, 200))),\n new Promise<void>((resolve) => {\n deadline = setTimeout(resolve, 8500);\n }),\n ]);\n } finally {\n clearTimeout(deadline);\n }\n}\n\nasync function prepareDashboardPage(contents: WebContents): Promise<void> {\n if (contents.isDestroyed()) return;\n let deadline: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n contents.mainFrame\n .executeJavaScript('document.dispatchEvent(new Event(\"freelens-power:dashboard:preparing\"))')\n .catch(() => {}),\n new Promise<void>((resolve) => {\n deadline = setTimeout(resolve, 250);\n }),\n ]);\n } finally {\n clearTimeout(deadline);\n }\n}\n\nfunction releaseDashboardPage(contents: WebContents): Promise<unknown> {\n if (contents.isDestroyed()) return Promise.resolve();\n return contents.mainFrame\n .executeJavaScript('document.dispatchEvent(new Event(\"freelens-power:dashboard:prepared\"))')\n .catch(() => {});\n}\n\nexport function startDashboardViews(): () => void {\n const websiteSession = session.fromPartition(\"persist:freelens-power-websites\");\n websiteSession.setPermissionRequestHandler((_contents, _permission, callback) => callback(false));\n websiteSession.setPermissionCheckHandler(() => false);\n const owners = new Map<WebContents, Owner>();\n const icons = new Map<string, DashboardSiteIcon>();\n const remove = (owner: Owner, id: string) => {\n const card = owner.cards.get(id);\n if (!card) return;\n owner.cards.delete(id);\n card.nativeNavigation?.fail();\n card.stopPreparation?.();\n card.websiteStylesGeneration = (card.websiteStylesGeneration ?? 0) + 1;\n card.faviconRequest?.abort();\n clearTimeout(card.refreshTimer);\n card.floatingPreparation?.stop();\n card.floating?.destroy();\n card.floating = undefined;\n if (!owner.window.isDestroyed()) owner.window.contentView.removeChildView(card.container);\n if (!card.view.webContents.isDestroyed()) card.view.webContents.close();\n };\n const clear = (sender: WebContents) => {\n const owner = owners.get(sender);\n if (!owner) return;\n owners.delete(sender);\n for (const id of owner.cards.keys()) remove(owner, id);\n owner.cleanup();\n };\n const hide = (sender: WebContents) => {\n for (const card of owners.get(sender)?.cards.values() ?? []) {\n card.visible = false;\n card.container.setVisible(false);\n }\n };\n const configuredCards = () =>\n sanitizeDashboardCards(DashboardPreferencesStore.getInstanceOrCreate<DashboardPreferencesStore>().cards);\n const cachePath = join(app.getPath(\"userData\"), \"freelens-power\", \"dashboard-icons.json\");\n const ready = loadDashboardIcons(cachePath).then((saved) => {\n const configured = configuredCards();\n for (const icon of saved) {\n if (!icons.has(icon.id) && configured.some((card) => card.id === icon.id && card.url === icon.pageUrl))\n icons.set(icon.id, icon);\n }\n });\n let writes = ready;\n const persistIcons = () => {\n writes = writes\n .then(async () => {\n const configured = configuredCards();\n const bounded = boundedDashboardIcons(\n [...icons.values()].filter((icon) =>\n configured.some((card) => card.id === icon.id && card.url === icon.pageUrl),\n ),\n );\n icons.clear();\n for (const icon of bounded) icons.set(icon.id, icon);\n await saveDashboardIcons(cachePath, bounded);\n })\n .catch(() => {});\n };\n persistIcons();\n const publishLoading = (card: CardView) => {\n for (const [sender, owner] of owners) {\n const id = [...owner.cards].find(([, candidate]) => candidate === card)?.[0];\n if (\n !id ||\n sender.isDestroyed() ||\n !dashboardUrl(sender.mainFrame.url) ||\n !dashboardUrl(sender.getURL()) ||\n !configuredCards().some((configured) => configured.id === id && configured.url === card.url)\n )\n continue;\n sender.send(DASHBOARD_VIEWS_LOADING, { id, url: card.url, loading: card.loading, failed: card.failed });\n }\n };\n const reload = (card: CardView, configuredUrl: string, initial = false, navigation?: Promise<void>) => {\n const current = card.view.webContents.getURL();\n const target = !initial && card.url === configuredUrl && webUrl(current) ? current : configuredUrl;\n const prepareExisting = !initial && !navigation && !card.loading && !card.failed && webUrl(current);\n card.faviconRequest?.abort();\n if (!navigation) card.nativeNavigation?.fail();\n card.url = configuredUrl;\n card.failed = false;\n card.loading = true;\n card.stopPreparation?.();\n const contents = card.view.webContents;\n const backgroundThrottling = contents.getBackgroundThrottling();\n if (backgroundThrottling) contents.setBackgroundThrottling(false);\n const network = observeDashboardNetwork(card.view.webContents);\n let stopped = false;\n let documentReady: (() => void) | undefined;\n const stopPreparation = () => {\n if (stopped) return;\n stopped = true;\n network.dispose();\n contents.removeListener(\"destroyed\", stopPreparation);\n if (documentReady) contents.removeListener(\"dom-ready\", documentReady);\n if (!contents.isDestroyed() && backgroundThrottling && !contents.getBackgroundThrottling())\n contents.setBackgroundThrottling(backgroundThrottling);\n if (card.stopPreparation === stopPreparation) card.stopPreparation = undefined;\n };\n card.stopPreparation = stopPreparation;\n contents.once(\"destroyed\", stopPreparation);\n const generation = ++card.generation;\n card.skeleton.view.setVisible(true);\n card.container.setVisible(card.visible && !card.floating);\n publishLoading(card);\n const settled = (failed: boolean) => {\n if (card.generation !== generation || card.view.webContents.isDestroyed()) return;\n card.failed = failed;\n card.loading = false;\n card.skeleton.view.setVisible(false);\n for (const [sender, owner] of owners) {\n if (![...owner.cards.values()].includes(card)) continue;\n card.container.setVisible(card.visible && !card.floating && !failed && dashboardUrl(sender.getURL()));\n }\n publishLoading(card);\n };\n const load = () => {\n if (\n card.generation !== generation ||\n card.view.webContents.isDestroyed() ||\n ![...owners.values()].some((owner) => [...owner.cards.values()].includes(card))\n )\n return Promise.resolve();\n if (navigation) return navigation;\n const navigate = () => {\n if (card.generation !== generation || contents.isDestroyed()) return Promise.resolve();\n card.requestedNavigation = true;\n return contents.loadURL(target).finally(() => {\n if (card.generation === generation) card.requestedNavigation = false;\n });\n };\n return prepareExisting ? prepareDashboardPage(contents).then(navigate) : navigate();\n };\n const ready = new Promise<void>((resolve) => {\n documentReady = resolve;\n contents.once(\"dom-ready\", documentReady);\n });\n card.pending = Promise.race([load(), ready]).then(\n async () => {\n let preparing = true;\n const current = () =>\n preparing &&\n card.generation === generation &&\n !card.view.webContents.isDestroyed() &&\n [...owners.values()].some((owner) => [...owner.cards.values()].includes(card));\n try {\n await waitForReadiness(contents, network, () => card.selector, current);\n if (current()) await releaseDashboardPage(contents);\n } finally {\n preparing = false;\n stopPreparation();\n }\n settled(false);\n },\n () => {\n if (card.generation === generation) void releaseDashboardPage(contents);\n stopPreparation();\n settled(true);\n },\n );\n return card.pending;\n };\n const applySelector = (card: { view: { webContents: WebContents }; selector?: string }) => {\n if (card.view.webContents.isDestroyed()) return Promise.resolve();\n return card.view.webContents.mainFrame.executeJavaScript(dashboardSelectorScript(card.selector)).catch(() => {});\n };\n const refreshTimers = () => {\n const configured = configuredCards();\n for (const [id, icon] of icons) {\n if (!configured.some((card) => card.id === id && card.url === icon.pageUrl)) {\n icons.delete(id);\n persistIcons();\n }\n }\n for (const [sender, owner] of owners) {\n for (const [id, card] of owner.cards) {\n const setting = configured.find((item) => item.id === id);\n if (!setting || setting.url !== card.url) {\n remove(owner, id);\n continue;\n }\n if (card.selector !== setting.selector) {\n card.selector = setting.selector;\n applySelector(card);\n card.floatingPreparation?.selectorChanged();\n }\n if (card.floating && !card.floating.isDestroyed()) card.floating.setTitle(setting.title);\n if (card.floatingStyles) {\n const scrollbars = setting.size === \"l\" || setting.size === \"xl\";\n if (card.floatingStyles.scrollbars !== scrollbars) {\n card.floatingStyles.scrollbars = scrollbars;\n void applyWebsiteStyles(card.floatingStyles);\n }\n }\n const interval = setting.refreshEvery10Seconds ? 10000 : setting.autoRefresh ? 600000 : undefined;\n if (card.refreshInterval === interval) continue;\n clearTimeout(card.refreshTimer);\n card.refreshTimer = undefined;\n card.refreshInterval = interval;\n const refreshGeneration = (card.refreshGeneration ?? 0) + 1;\n card.refreshGeneration = refreshGeneration;\n if (!interval) continue;\n const schedule = () => {\n if (\n owners.get(sender) !== owner ||\n owner.cards.get(id) !== card ||\n card.refreshGeneration !== refreshGeneration\n )\n return;\n if (card.loading && card.pending) {\n void card.pending.then(schedule);\n return;\n }\n card.refreshTimer = setTimeout(tick, interval);\n };\n const tick = async () => {\n try {\n const latest = configuredCards().find((item) => item.id === id);\n if (\n owners.get(sender) !== owner ||\n owner.cards.get(id) !== card ||\n !latest ||\n latest.url !== card.url ||\n (latest.refreshEvery10Seconds ? 10000 : latest.autoRefresh ? 600000 : undefined) !== interval ||\n card.view.webContents.isDestroyed()\n )\n return;\n if (card.loading) {\n await card.pending;\n return;\n }\n if (latest.refreshEvery10Seconds) {\n if (card.floating) {\n if (card.floating.isDestroyed() || card.floatingPreparation?.isPreparing()) return;\n await Promise.all([reload(card, latest.url), card.floatingPreparation?.refresh()]);\n } else if (card.visible && dashboardUrl(sender.getURL())) await reload(card, latest.url);\n } else if (!card.floating && !dashboardUrl(sender.getURL())) await reload(card, latest.url);\n } finally {\n schedule();\n }\n };\n schedule();\n }\n }\n };\n const stopSettingsReaction = reaction(\n () =>\n JSON.stringify(\n configuredCards().map(({ id, title, url, size, autoRefresh, refreshEvery10Seconds, selector }) => ({\n id,\n title,\n url,\n size,\n autoRefresh,\n refreshEvery10Seconds,\n selector,\n })),\n ),\n refreshTimers,\n );\n const trusted = (event: IpcMainInvokeEvent) =>\n event.senderFrame === event.sender.mainFrame &&\n dashboardUrl(event.senderFrame?.url ?? \"\") &&\n dashboardUrl(event.sender.getURL());\n ipcMain.handle(DASHBOARD_SITE_ICONS, async (event) => {\n if (\n event.senderFrame !== event.sender.mainFrame ||\n !rendererUrl(event.senderFrame?.url ?? \"\") ||\n !rendererUrl(event.sender.getURL()) ||\n !BrowserWindow.fromWebContents(event.sender)\n )\n return [];\n await ready;\n return configuredCards().flatMap((card) => {\n const icon = icons.get(card.id);\n return icon?.pageUrl === card.url ? [icon] : [];\n });\n });\n ipcMain.handle(syncChannel, async (event, payload: unknown) => {\n if (!trusted(event)) return;\n const window = BrowserWindow.fromWebContents(event.sender);\n if (!window || window.isDestroyed()) return;\n const requested =\n payload && typeof payload === \"object\" && \"cards\" in payload && Array.isArray(payload.cards) ? payload.cards : [];\n const cards = sanitizeDashboardCards(\n DashboardPreferencesStore.getInstanceOrCreate<DashboardPreferencesStore>().cards,\n );\n let owner = owners.get(event.sender);\n if (!owner) {\n const sender = event.sender;\n const navigation = (_event: unknown, url: string) => {\n if (!dashboardUrl(url)) hide(sender);\n };\n const destroyed = () => clear(sender);\n sender.on(\"did-navigate\", navigation);\n sender.on(\"did-navigate-in-page\", navigation);\n sender.on(\"destroyed\", destroyed);\n owner = {\n window,\n cards: new Map(),\n cleanup: () => {\n sender.removeListener(\"did-navigate\", navigation);\n sender.removeListener(\"did-navigate-in-page\", navigation);\n sender.removeListener(\"destroyed\", destroyed);\n },\n };\n owners.set(sender, owner);\n const activeOwner = owner;\n let backgrounded = false;\n const blur = () => {\n backgrounded = true;\n };\n const focus = () => {\n if (!backgrounded) return;\n backgrounded = false;\n if (!dashboardUrl(sender.getURL())) return;\n for (const [id, card] of activeOwner.cards) {\n const configured = configuredCards().find((item) => item.id === id);\n if (!card.visible || card.floating || !configured) continue;\n card.skeleton.view.setVisible(card.loading);\n card.container.setVisible(!card.failed || card.loading);\n if ((configured.refreshEvery10Seconds || configured.autoRefresh) && !card.loading)\n void reload(card, configured.url);\n }\n };\n window.on(\"blur\", blur);\n window.on(\"focus\", focus);\n const cleanup = owner.cleanup;\n owner.cleanup = () => {\n window.removeListener(\"blur\", blur);\n window.removeListener(\"focus\", focus);\n cleanup();\n };\n }\n if (payload && typeof payload === \"object\" && \"hidden\" in payload && payload.hidden === true) {\n hide(event.sender);\n return { failed: [], pinned: [...owner.cards].filter(([, card]) => card.floating).map(([id]) => id) };\n }\n const visible = new Set<string>();\n for (const item of requested.slice(0, cards.length)) {\n if (!item || typeof item !== \"object\" || typeof item.id !== \"string\" || visible.has(item.id)) continue;\n const configured = cards.find((card) => card.id === item.id);\n const bounds = rectangle(item.bounds);\n const clip = rectangle(item.clip);\n if (\n !configured ||\n !bounds ||\n !clip ||\n clip.x < bounds.x ||\n clip.y < bounds.y ||\n clip.x + clip.width > bounds.x + bounds.width ||\n clip.y + clip.height > bounds.y + bounds.height\n )\n continue;\n visible.add(item.id);\n let card = owner.cards.get(item.id);\n if (card?.floating) {\n card.visible = false;\n card.container.setVisible(false);\n continue;\n }\n if (!card) {\n const container = new View();\n container.setBackgroundColor(\"#00000000\");\n const view = new WebContentsView({\n webPreferences: {\n nodeIntegration: false,\n nodeIntegrationInSubFrames: false,\n contextIsolation: true,\n sandbox: true,\n webSecurity: true,\n partition: \"persist:freelens-power-websites\",\n preload: join(__dirname, \"dashboard-preload.js\"),\n },\n });\n view.setBackgroundColor(\"#00000000\");\n view.webContents.setWindowOpenHandler(({ url }) => {\n if (webUrl(url)) void shell.openExternal(url).catch(() => {});\n return { action: \"deny\" };\n });\n const navigation = (event: Electron.Event, url: string) => {\n if (!webUrl(url)) event.preventDefault();\n };\n view.webContents.on(\"will-navigate\", navigation);\n view.webContents.on(\"will-redirect\", navigation);\n view.webContents.on(\"will-frame-navigate\", (event) => {\n if (!webUrl(event.url)) event.preventDefault();\n });\n container.addChildView(view);\n const skeleton = createDashboardSkeleton();\n container.addChildView(skeleton.view);\n window.contentView.addChildView(container);\n card = { container, skeleton, view, url: \"\", failed: false, loading: false, visible: false, generation: 0 };\n owner.cards.set(item.id, card);\n const created = card;\n view.webContents.on(\"before-input-event\", (_event, input: Electron.Input) => {\n if (\n input.type !== \"keyDown\" ||\n input.key !== \"Escape\" ||\n owners.get(event.sender) !== owner ||\n owner.cards.get(item.id) !== created ||\n !trusted(event)\n )\n return;\n event.sender.send(\"freelens-power:dashboard-escape\");\n });\n created.selector = configured.selector;\n view.webContents.on(\"did-start-navigation\", (_event, _url, inPlace, mainFrame) => {\n if (!mainFrame) return;\n if (created.requestedNavigation) {\n created.requestedNavigation = false;\n return;\n }\n created.nativeNavigation?.fail();\n const navigation = new Promise<void>((resolve, reject) => {\n created.nativeNavigation = {\n finish: () => {\n created.nativeNavigation = undefined;\n resolve();\n },\n fail: () => {\n created.nativeNavigation = undefined;\n reject(new Error(\"dashboard navigation failed\"));\n },\n };\n });\n if (inPlace)\n void view.webContents.mainFrame\n .executeJavaScript('document.dispatchEvent(new Event(\"freelens-power:dashboard:preparing\"))')\n .catch(() => {});\n void reload(created, created.url, false, navigation);\n if (inPlace) created.nativeNavigation?.finish();\n });\n view.webContents.on(\"did-finish-load\", () => {\n void applyWebsiteStyles(created);\n void applySelector(created);\n created.nativeNavigation?.finish();\n });\n view.webContents.on(\"did-fail-load\", (_event, code, _description, _url, mainFrame) => {\n if (!mainFrame || (code === -3 && view.webContents.isLoading())) return;\n created.nativeNavigation?.fail();\n });\n view.webContents.on(\"page-favicon-updated\", (_event, favicons: string[]) => {\n const favicon = favicons.find((value) => {\n if (!webUrl(value)) return false;\n const parsed = new URL(value);\n return !parsed.username && !parsed.password;\n });\n if (!favicon || owners.get(event.sender) !== owner || owner.cards.get(item.id) !== created) return;\n created.faviconRequest?.abort();\n const request = new AbortController();\n created.faviconRequest = request;\n created.favicon = favicon;\n const publish = (url: string) => {\n if (\n request.signal.aborted ||\n owners.get(event.sender) !== owner ||\n owner.cards.get(item.id) !== created ||\n event.senderFrame !== event.sender.mainFrame\n )\n return;\n created.favicon = url;\n if (\n url.startsWith(\"data:\") &&\n configuredCards().some((card) => card.id === item.id && card.url === created.url)\n ) {\n icons.set(item.id, { id: item.id, pageUrl: created.url, dataUrl: url });\n persistIcons();\n if (rendererUrl(event.senderFrame?.url ?? \"\") && rendererUrl(event.sender.getURL()))\n event.sender.send(DASHBOARD_SITE_ICONS);\n }\n if (!trusted(event)) return;\n event.sender.send(\"freelens-power:dashboard-site-icon\", { id: item.id, url });\n };\n publish(favicon);\n const timeout = setTimeout(() => request.abort(), 5000);\n void (async () => {\n let current = favicon;\n for (let redirects = 0; redirects <= 5; redirects++) {\n const response = await websiteSession.fetch(current, {\n signal: request.signal,\n redirect: \"manual\",\n credentials: \"include\",\n });\n if (response.status >= 300 && response.status < 400) {\n await response.body?.cancel();\n const location = response.headers.get(\"location\");\n if (!location) return;\n const next = new URL(location, current);\n if (!webUrl(next.href) || next.username || next.password) return;\n current = next.href;\n continue;\n }\n const mime = response.headers.get(\"content-type\")?.split(\";\")[0].trim().toLowerCase();\n if (\n !response.ok ||\n !mime ||\n ![\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"image/x-icon\",\n \"image/vnd.microsoft.icon\",\n \"image/svg+xml\",\n ].includes(mime) ||\n Number(response.headers.get(\"content-length\")) > 524288\n ) {\n await response.body?.cancel();\n return;\n }\n const reader = response.body?.getReader();\n if (!reader) return;\n const chunks: Uint8Array[] = [];\n let size = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.length;\n if (size > 524288) {\n await reader.cancel();\n return;\n }\n chunks.push(value);\n }\n if (size) publish(`data:${mime};base64,${Buffer.concat(chunks).toString(\"base64\")}`);\n return;\n }\n })()\n .catch(() => {})\n .finally(() => clearTimeout(timeout));\n });\n }\n if (card.scrollbars !== (item.scrollbars === true)) {\n card.scrollbars = item.scrollbars === true;\n void applyWebsiteStyles(card);\n }\n card.visible = true;\n card.skeleton.view.setVisible(card.loading);\n card.container.setVisible(!card.failed && (card.loading || card.url === configured.url));\n const zoom = event.sender.getZoomFactor();\n card.container.setBorderRadius(Math.round(12 * zoom));\n card.view.setBorderRadius(Math.round(12 * zoom));\n const scale = (rect: Rectangle): Rectangle => ({\n x: Math.round(rect.x * zoom),\n y: Math.round(rect.y * zoom),\n width: Math.round(rect.width * zoom),\n height: Math.round(rect.height * zoom),\n });\n card.container.setBounds(scale(clip));\n card.skeleton.layout(Math.round(clip.width * zoom), Math.round(clip.height * zoom));\n card.view.setBounds(\n scale({ x: bounds.x - clip.x, y: bounds.y - clip.y, width: bounds.width, height: bounds.height }),\n );\n if (card.url !== configured.url) {\n card.favicon = undefined;\n card.faviconRequest?.abort();\n void reload(card, configured.url, true);\n }\n publishLoading(card);\n if (card.favicon) event.sender.send(\"freelens-power:dashboard-site-icon\", { id: item.id, url: card.favicon });\n }\n for (const [id, card] of owner.cards) {\n if (!cards.some((configured) => configured.id === id)) remove(owner, id);\n else if (!visible.has(id)) {\n card.visible = false;\n card.container.setVisible(false);\n }\n }\n refreshTimers();\n await Promise.all([...owner.cards.values()].map((card) => card.pending));\n return {\n failed: [...owner.cards.entries()].filter(([, card]) => card.failed).map(([id]) => id),\n pinned: [...owner.cards].filter(([, card]) => card.floating).map(([id]) => id),\n };\n });\n const publishPinned = (sender: WebContents, owner: Owner) => {\n if (!sender.isDestroyed())\n sender.send(\n DASHBOARD_VIEWS_PINNED,\n [...owner.cards].filter(([, card]) => card.floating).map(([id]) => id),\n );\n };\n ipcMain.handle(DASHBOARD_VIEWS_PIN, (event, payload: unknown) => {\n if (\n !trusted(event) ||\n !payload ||\n typeof payload !== \"object\" ||\n !(\"id\" in payload) ||\n typeof payload.id !== \"string\" ||\n !((\"pinned\" in payload && typeof payload.pinned === \"boolean\") || (\"focus\" in payload && payload.focus === true))\n )\n return;\n const id = payload.id;\n const owner = owners.get(event.sender);\n const card = owner?.cards.get(payload.id);\n const configured = configuredCards().find((item) => item.id === payload.id);\n if (\n !owner ||\n !card ||\n !configured ||\n card.url !== configured.url ||\n owner.window.isDestroyed() ||\n BrowserWindow.fromWebContents(event.sender) !== owner.window ||\n card.view.webContents.isDestroyed()\n )\n return;\n if (\"focus\" in payload && payload.focus === true) {\n card.floating?.show();\n card.floating?.focus();\n } else if (!(\"pinned\" in payload) || !payload.pinned) {\n card.floating?.close();\n } else if (!card.floating) {\n const floating = new BrowserWindow({\n title: configured.title,\n width: 800,\n height: 600,\n minWidth: 240,\n minHeight: 160,\n resizable: true,\n alwaysOnTop: true,\n autoHideMenuBar: true,\n show: false,\n webPreferences: {\n nodeIntegration: false,\n nodeIntegrationInSubFrames: false,\n contextIsolation: true,\n sandbox: true,\n webSecurity: true,\n partition: \"persist:freelens-power-websites\",\n preload: join(__dirname, \"dashboard-preload.js\"),\n },\n });\n floating.setMenu(null);\n floating.setAlwaysOnTop(true, \"floating\");\n floating.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });\n card.floating = floating;\n card.container.setVisible(false);\n const contents = floating.webContents;\n const floatingStyles: WebsiteStyleState = {\n view: { webContents: contents },\n scrollbars: configured.size === \"l\" || configured.size === \"xl\",\n };\n card.floatingStyles = floatingStyles;\n contents.setWindowOpenHandler(({ url }) => {\n if (webUrl(url)) void shell.openExternal(url).catch(() => {});\n return { action: \"deny\" };\n });\n const navigation = (event: Electron.Event, url: string) => {\n if (!webUrl(url)) event.preventDefault();\n };\n contents.on(\"will-navigate\", navigation);\n contents.on(\"will-redirect\", navigation);\n contents.on(\"will-frame-navigate\", (event) => {\n if (!webUrl(event.url)) event.preventDefault();\n });\n contents.on(\"page-title-updated\", (event) => event.preventDefault());\n contents.on(\"will-prevent-unload\", (event) => event.preventDefault());\n const loadingSkeleton = createDashboardSkeleton();\n const skeleton = loadingSkeleton.view;\n const layoutSkeleton = () => {\n const { width, height } = floating.getContentBounds();\n loadingSkeleton.layout(width, height);\n };\n floating.contentView.addChildView(skeleton);\n floating.on(\"resize\", layoutSkeleton);\n layoutSkeleton();\n let generation = 0;\n let floatingPreparing = false;\n let stopPreparation = () => {};\n let finishLoad: (() => Promise<boolean>) | undefined;\n const begin = () => {\n stopPreparation();\n floatingPreparing = true;\n const token = ++generation;\n const backgroundThrottling = contents.getBackgroundThrottling();\n if (backgroundThrottling) contents.setBackgroundThrottling(false);\n const network = observeDashboardNetwork(contents);\n let active = true;\n let finishing: Promise<boolean> | undefined;\n const current = () => active && generation === token && card.floating === floating && !contents.isDestroyed();\n skeleton.setVisible(true);\n stopPreparation = () => {\n if (!active) return;\n active = false;\n floatingPreparing = false;\n network.dispose();\n if (!contents.isDestroyed() && backgroundThrottling) contents.setBackgroundThrottling(true);\n };\n const stop = stopPreparation;\n const finish = () => {\n finishing ??= (async () => {\n await waitForReadiness(contents, network, () => card.selector, current);\n if (!current()) return false;\n await releaseDashboardPage(contents);\n if (!current()) return false;\n skeleton.setVisible(false);\n stop();\n return false;\n })();\n return finishing;\n };\n finishLoad = finish;\n return {\n finish,\n fail: () => {\n if (current()) {\n void releaseDashboardPage(contents);\n skeleton.setVisible(false);\n stop();\n }\n },\n };\n };\n const refresh = () => {\n const preparation = begin();\n const current = contents.getURL();\n let documentReady: () => void;\n const ready = new Promise<boolean>((resolve) => {\n documentReady = () => {\n void (finishLoad?.() ?? Promise.resolve(false)).then(resolve);\n };\n contents.once(\"dom-ready\", documentReady);\n });\n const loading = prepareDashboardPage(contents)\n .then(() => contents.loadURL(webUrl(current) ? current : configured.url))\n .then(\n () => finishLoad?.() ?? false,\n () => {\n preparation.fail();\n return !contents.isDestroyed();\n },\n );\n return Promise.race([loading, ready]).finally(() => {\n contents.removeListener(\"dom-ready\", documentReady);\n });\n };\n card.floatingPreparation = {\n refresh,\n isPreparing: () => floatingPreparing,\n selectorChanged: () => {\n const preparation = begin();\n void preparation.finish();\n },\n stop: () => {\n ++generation;\n stopPreparation();\n },\n };\n contents.on(\"did-start-navigation\", (_event, _url, inPlace, mainFrame) => {\n if (!mainFrame) return;\n const preparation = begin();\n if (inPlace) void preparation.finish();\n });\n contents.on(\"dom-ready\", () => {\n void applyWebsiteStyles(floatingStyles);\n if (!contents.isDestroyed()) void finishLoad?.();\n });\n contents.on(\"did-finish-load\", () => {\n void applyWebsiteStyles(floatingStyles);\n if (!contents.isDestroyed()) void finishLoad?.();\n });\n contents.on(\"did-fail-load\", (_event, code, _description, _url, mainFrame) => {\n if (!mainFrame || code === -3) return;\n stopPreparation();\n void releaseDashboardPage(contents);\n skeleton.setVisible(false);\n });\n contents.once(\"destroyed\", () => {\n ++generation;\n stopPreparation();\n });\n floating.on(\"closed\", () => {\n if (card.floating !== floating) return;\n card.floatingPreparation?.stop();\n card.floatingPreparation = undefined;\n floatingStyles.websiteStylesGeneration = (floatingStyles.websiteStylesGeneration ?? 0) + 1;\n card.floatingStyles = undefined;\n card.floating = undefined;\n if (owner.cards.get(id) === card) publishPinned(event.sender, owner);\n });\n const initial = begin();\n void contents.loadURL(configured.url).then(() => finishLoad?.(), initial.fail);\n floating.show();\n }\n publishPinned(event.sender, owner);\n return { pinned: [...owner.cards].filter(([, item]) => item.floating).map(([id]) => id) };\n });\n ipcMain.handle(clearChannel, (event) => {\n if (\n event.senderFrame === event.sender.mainFrame &&\n rendererUrl(event.senderFrame?.url ?? \"\") &&\n rendererUrl(event.sender.getURL())\n )\n hide(event.sender);\n });\n ipcMain.handle(reloadChannel, async (event, payload: unknown) => {\n if (\n !trusted(event) ||\n !payload ||\n typeof payload !== \"object\" ||\n !(\"id\" in payload) ||\n typeof payload.id !== \"string\"\n )\n return;\n const owner = owners.get(event.sender);\n const card = owner?.cards.get(payload.id);\n const configured = sanitizeDashboardCards(\n DashboardPreferencesStore.getInstanceOrCreate<DashboardPreferencesStore>().cards,\n ).find((item) => item.id === payload.id);\n if (!card || !configured || card.view.webContents.isDestroyed()) return;\n const [, floatingFailed] = await Promise.all([\n reload(card, configured.url),\n card.floatingPreparation?.refresh() ?? Promise.resolve(false),\n ]);\n return { failed: card.failed || floatingFailed ? [payload.id] : [] };\n });\n return () => {\n stopSettingsReaction();\n ipcMain.removeHandler(DASHBOARD_VIEWS_PIN);\n ipcMain.removeHandler(DASHBOARD_SITE_ICONS);\n ipcMain.removeHandler(syncChannel);\n ipcMain.removeHandler(clearChannel);\n ipcMain.removeHandler(reloadChannel);\n for (const sender of owners.keys()) clear(sender);\n };\n}\n"],"mappings":"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"}
|
|
@@ -93,6 +93,11 @@ var PowerLogsDatabase = class {
|
|
|
93
93
|
this.remove(request.captureId);
|
|
94
94
|
return true;
|
|
95
95
|
}
|
|
96
|
+
if (request.operation === "save-sql") {
|
|
97
|
+
if (typeof request.sql !== "string" || request.sql.length > 65536 || request.sql.includes("\0")) throw new Error("Invalid SQL draft");
|
|
98
|
+
capture.meta.lastSql = request.sql;
|
|
99
|
+
return { ...capture.meta };
|
|
100
|
+
}
|
|
96
101
|
if (request.operation === "export") throw new Error("Export requires trusted destination");
|
|
97
102
|
if (request.operation === "append") {
|
|
98
103
|
if (capture.meta.state !== "collecting") throw new Error("Capture is complete");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-logs-database.js","names":[],"sources":["../../src/main/power-logs-database.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { appendFileSync, chmodSync, createReadStream, mkdirSync, rmSync, truncateSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { POWER_LOGS_LIMITS as L, type LogCapture, type LogRequest } from \"../common/power-logs\";\n\nimport type { PowerLogsQuerySnapshot } from \"./power-logs-query\";\n\ntype Capture = {\n meta: LogCapture;\n partition: string;\n directory: string;\n spool: string;\n database: string;\n limits: { captureBytes: number; captureRows: number };\n};\nexport class PowerLogsDatabase {\n private captures = new Map<string, Capture>();\n private closed = false;\n private timer: ReturnType<typeof setInterval>;\n constructor(private directory: string) {\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n chmodSync(directory, 0o700);\n this.timer = setInterval(() => this.expire(), 30000);\n this.timer.unref();\n }\n private partition(request: LogRequest): string {\n return JSON.stringify([request.clusterId, request.contextId]);\n }\n private expire(): void {\n for (const [id, capture] of this.captures) {\n if (capture.meta.expiresAt > Date.now()) continue;\n try {\n this.remove(id);\n } catch {\n // retain expired entries within the capture limit until a later sweep can remove locked files\n }\n }\n }\n private remove(id: string): void {\n const capture = this.captures.get(id);\n if (!capture) return;\n try {\n rmSync(capture.directory, { recursive: true, force: true });\n } catch {\n throw new Error(\"Log capture storage is busy or unavailable\");\n }\n this.captures.delete(id);\n }\n exportPath(request: LogRequest & { captureId: string }): string {\n return this.capture(request).spool;\n }\n private capture(request: LogRequest & { captureId: string }): Capture {\n const capture = this.captures.get(request.captureId);\n if (!capture || capture.partition !== this.partition(request) || capture.meta.expiresAt <= Date.now())\n throw new Error(\"Capture unavailable\");\n return capture;\n }\n async run(request: LogRequest): Promise<unknown> {\n if (this.closed) throw new Error(\"Log storage unavailable\");\n this.expire();\n if (request.operation === \"permit\" || \"streamId\" in request) throw new Error(\"Log permits require main process\");\n const partition = this.partition(request);\n if (request.operation === \"cancel\") return true;\n if (request.operation === \"list\")\n return [...this.captures.values()]\n .filter((c) => c.partition === partition && c.meta.expiresAt > Date.now())\n .map((c) => ({ ...c.meta }));\n if (request.operation === \"create\") {\n if (this.captures.size >= L.captures) throw new Error(\"Capture limit reached\");\n const id = randomUUID(),\n directory = join(this.directory, id),\n now = Date.now();\n mkdirSync(directory, { mode: 0o700 });\n const spool = join(directory, \"logs.ndjson\");\n writeFileSync(spool, \"\", { mode: 0o600 });\n const meta: LogCapture = {\n id,\n target: request.target,\n createdAt: now,\n expiresAt: now + L.ttlMs,\n state: \"collecting\",\n rows: 0,\n bytes: 0,\n notes: [],\n };\n this.captures.set(id, {\n meta,\n partition,\n directory,\n spool,\n database: join(directory, \"logs.sqlite\"),\n limits: { captureBytes: L.captureBytes, captureRows: L.captureRows },\n });\n return { ...meta };\n }\n const capture = this.capture(request);\n if (request.operation === \"delete\") {\n this.remove(request.captureId);\n return true;\n }\n if (request.operation === \"export\") throw new Error(\"Export requires trusted destination\");\n if (request.operation === \"append\") {\n if (capture.meta.state !== \"collecting\") throw new Error(\"Capture is complete\");\n if (request.records.length > L.batchRows) throw new Error(\"Log batch exceeds limit\");\n const data = request.records\n .map((record) => JSON.stringify({ ...record, previous: record.previous === true }) + \"\\n\")\n .join(\"\");\n const bytes = Buffer.byteLength(data);\n if (\n bytes > L.batchBytes ||\n capture.meta.bytes + bytes > capture.limits.captureBytes ||\n capture.meta.rows + request.records.length > capture.limits.captureRows\n )\n throw new Error(\"Capture limit reached\");\n try {\n appendFileSync(capture.spool, data);\n } catch {\n truncateSync(capture.spool, capture.meta.bytes);\n throw new Error(\"Log capture storage unavailable\");\n }\n capture.meta.rows += request.records.length;\n capture.meta.bytes += bytes;\n return { ...capture.meta };\n }\n if (request.operation === \"finish\") {\n if (capture.meta.state !== \"collecting\") return { ...capture.meta };\n let database: DatabaseSync | undefined;\n try {\n writeFileSync(capture.database, \"\", { mode: 0o600 });\n database = new DatabaseSync(capture.database, { allowExtension: false });\n database.exec(\n \"PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA cache_size=-16384; PRAGMA temp_store=MEMORY\",\n );\n database.exec(\n \"CREATE TABLE logs(timestamp INTEGER, pod TEXT, podUid TEXT, container TEXT, level TEXT, message TEXT, fieldsJson TEXT, previous INTEGER CHECK(previous IN (0,1)))\",\n );\n const insert = database.prepare(\"INSERT INTO logs VALUES (?, ?, ?, ?, ?, ?, ?, ?)\");\n const input = createReadStream(capture.spool);\n const lines = createInterface({ input, crlfDelay: Infinity });\n let count = 0;\n database.exec(\"BEGIN\");\n try {\n for await (const line of lines) {\n const record = JSON.parse(line);\n insert.run(\n record.timestamp,\n record.pod,\n record.podUid,\n record.container,\n record.level,\n record.message,\n record.fieldsJson,\n record.previous ? 1 : 0,\n );\n if (++count % 5000 === 0) {\n database.exec(\"COMMIT; BEGIN\");\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n database.exec(\"COMMIT\");\n } finally {\n lines.close();\n input.destroy();\n }\n database.exec(\"CREATE INDEX logs_timestamp ON logs(timestamp)\");\n } catch {\n database?.close();\n database = undefined;\n rmSync(capture.database, { force: true });\n throw new Error(\"Log capture storage unavailable\");\n } finally {\n database?.close();\n }\n capture.meta.state = request.partial ? \"partial\" : \"ready\";\n capture.meta.notes = request.notes;\n return { ...capture.meta };\n }\n if (capture.meta.state === \"collecting\") throw new Error(\"Finish capture before querying\");\n return {\n database: capture.database,\n captureId: capture.meta.id,\n expiresAt: capture.meta.expiresAt,\n } satisfies PowerLogsQuerySnapshot;\n }\n close(): void {\n this.closed = true;\n clearInterval(this.timer);\n this.captures.clear();\n rmSync(this.directory, { recursive: true, force: true });\n }\n}\n"],"mappings":";;;;;;;AAiBA,IAAa,oBAAb,MAA+B;CAIT;CAHpB,2BAAmB,IAAI,IAAqB;CAC5C,SAAiB;CACjB;CACA,YAAY,WAA2B;EAAnB,KAAA,YAAA;EAClB,CAAA,GAAA,QAAA,UAAA,CAAU,WAAW;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EACrD,CAAA,GAAA,QAAA,UAAA,CAAU,WAAW,GAAK;EAC1B,KAAK,QAAQ,kBAAkB,KAAK,OAAO,GAAG,GAAK;EACnD,KAAK,MAAM,MAAM;CACnB;CACA,UAAkB,SAA6B;EAC7C,OAAO,KAAK,UAAU,CAAC,QAAQ,WAAW,QAAQ,SAAS,CAAC;CAC9D;CACA,SAAuB;EACrB,KAAK,MAAM,CAAC,IAAI,YAAY,KAAK,UAAU;GACzC,IAAI,QAAQ,KAAK,YAAY,KAAK,IAAI,GAAG;GACzC,IAAI;IACF,KAAK,OAAO,EAAE;GAChB,QAAQ,CAER;EACF;CACF;CACA,OAAe,IAAkB;EAC/B,MAAM,UAAU,KAAK,SAAS,IAAI,EAAE;EACpC,IAAI,CAAC,SAAS;EACd,IAAI;GACF,CAAA,GAAA,QAAA,OAAA,CAAO,QAAQ,WAAW;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;EAC5D,QAAQ;GACN,MAAM,IAAI,MAAM,4CAA4C;EAC9D;EACA,KAAK,SAAS,OAAO,EAAE;CACzB;CACA,WAAW,SAAqD;EAC9D,OAAO,KAAK,QAAQ,OAAO,CAAC,CAAC;CAC/B;CACA,QAAgB,SAAsD;EACpE,MAAM,UAAU,KAAK,SAAS,IAAI,QAAQ,SAAS;EACnD,IAAI,CAAC,WAAW,QAAQ,cAAc,KAAK,UAAU,OAAO,KAAK,QAAQ,KAAK,aAAa,KAAK,IAAI,GAClG,MAAM,IAAI,MAAM,qBAAqB;EACvC,OAAO;CACT;CACA,MAAM,IAAI,SAAuC;EAC/C,IAAI,KAAK,QAAQ,MAAM,IAAI,MAAM,yBAAyB;EAC1D,KAAK,OAAO;EACZ,IAAI,QAAQ,cAAc,YAAY,cAAc,SAAS,MAAM,IAAI,MAAM,kCAAkC;EAC/G,MAAM,YAAY,KAAK,UAAU,OAAO;EACxC,IAAI,QAAQ,cAAc,UAAU,OAAO;EAC3C,IAAI,QAAQ,cAAc,QACxB,OAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,CAAC,CAC/B,QAAQ,MAAM,EAAE,cAAc,aAAa,EAAE,KAAK,YAAY,KAAK,IAAI,CAAC,CAAC,CACzE,KAAK,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE;EAC/B,IAAI,QAAQ,cAAc,UAAU;GAClC,IAAI,KAAK,SAAS,QAAQ,mBAAA,kBAAE,UAAU,MAAM,IAAI,MAAM,uBAAuB;GAC7E,MAAM,MAAA,GAAA,YAAA,WAAA,CAAgB,GACpB,aAAA,GAAA,UAAA,KAAA,CAAiB,KAAK,WAAW,EAAE,GACnC,MAAM,KAAK,IAAI;GACjB,CAAA,GAAA,QAAA,UAAA,CAAU,WAAW,EAAE,MAAM,IAAM,CAAC;GACpC,MAAM,SAAA,GAAA,UAAA,KAAA,CAAa,WAAW,aAAa;GAC3C,CAAA,GAAA,QAAA,cAAA,CAAc,OAAO,IAAI,EAAE,MAAM,IAAM,CAAC;GACxC,MAAM,OAAmB;IACvB;IACA,QAAQ,QAAQ;IAChB,WAAW;IACX,WAAW,MAAM,mBAAA,kBAAE;IACnB,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO,CAAC;GACV;GACA,KAAK,SAAS,IAAI,IAAI;IACpB;IACA;IACA;IACA;IACA,WAAA,GAAA,UAAA,KAAA,CAAe,WAAW,aAAa;IACvC,QAAQ;KAAE,cAAc,mBAAA,kBAAE;KAAc,aAAa,mBAAA,kBAAE;IAAY;GACrE,CAAC;GACD,OAAO,EAAE,GAAG,KAAK;EACnB;EACA,MAAM,UAAU,KAAK,QAAQ,OAAO;EACpC,IAAI,QAAQ,cAAc,UAAU;GAClC,KAAK,OAAO,QAAQ,SAAS;GAC7B,OAAO;EACT;EACA,IAAI,QAAQ,cAAc,UAAU,MAAM,IAAI,MAAM,qCAAqC;EACzF,IAAI,QAAQ,cAAc,UAAU;GAClC,IAAI,QAAQ,KAAK,UAAU,cAAc,MAAM,IAAI,MAAM,qBAAqB;GAC9E,IAAI,QAAQ,QAAQ,SAAS,mBAAA,kBAAE,WAAW,MAAM,IAAI,MAAM,yBAAyB;GACnF,MAAM,OAAO,QAAQ,QAClB,KAAK,WAAW,KAAK,UAAU;IAAE,GAAG;IAAQ,UAAU,OAAO,aAAa;GAAK,CAAC,IAAI,IAAI,CAAC,CACzF,KAAK,EAAE;GACV,MAAM,QAAQ,OAAO,WAAW,IAAI;GACpC,IACE,QAAQ,mBAAA,kBAAE,cACV,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,OAAO,gBAC5C,QAAQ,KAAK,OAAO,QAAQ,QAAQ,SAAS,QAAQ,OAAO,aAE5D,MAAM,IAAI,MAAM,uBAAuB;GACzC,IAAI;IACF,CAAA,GAAA,QAAA,eAAA,CAAe,QAAQ,OAAO,IAAI;GACpC,QAAQ;IACN,CAAA,GAAA,QAAA,aAAA,CAAa,QAAQ,OAAO,QAAQ,KAAK,KAAK;IAC9C,MAAM,IAAI,MAAM,iCAAiC;GACnD;GACA,QAAQ,KAAK,QAAQ,QAAQ,QAAQ;GACrC,QAAQ,KAAK,SAAS;GACtB,OAAO,EAAE,GAAG,QAAQ,KAAK;EAC3B;EACA,IAAI,QAAQ,cAAc,UAAU;GAClC,IAAI,QAAQ,KAAK,UAAU,cAAc,OAAO,EAAE,GAAG,QAAQ,KAAK;GAClE,IAAI;GACJ,IAAI;IACF,CAAA,GAAA,QAAA,cAAA,CAAc,QAAQ,UAAU,IAAI,EAAE,MAAM,IAAM,CAAC;IACnD,WAAW,IAAI,YAAA,aAAa,QAAQ,UAAU,EAAE,gBAAgB,MAAM,CAAC;IACvE,SAAS,KACP,qGACF;IACA,SAAS,KACP,mKACF;IACA,MAAM,SAAS,SAAS,QAAQ,kDAAkD;IAClF,MAAM,SAAA,GAAA,QAAA,iBAAA,CAAyB,QAAQ,KAAK;IAC5C,MAAM,SAAA,GAAA,cAAA,gBAAA,CAAwB;KAAE;KAAO,WAAW;IAAS,CAAC;IAC5D,IAAI,QAAQ;IACZ,SAAS,KAAK,OAAO;IACrB,IAAI;KACF,WAAW,MAAM,QAAQ,OAAO;MAC9B,MAAM,SAAS,KAAK,MAAM,IAAI;MAC9B,OAAO,IACL,OAAO,WACP,OAAO,KACP,OAAO,QACP,OAAO,WACP,OAAO,OACP,OAAO,SACP,OAAO,YACP,OAAO,WAAW,IAAI,CACxB;MACA,IAAI,EAAE,QAAQ,QAAS,GAAG;OACxB,SAAS,KAAK,eAAe;OAC7B,MAAM,IAAI,SAAe,YAAY,aAAa,OAAO,CAAC;MAC5D;KACF;KACA,SAAS,KAAK,QAAQ;IACxB,UAAU;KACR,MAAM,MAAM;KACZ,MAAM,QAAQ;IAChB;IACA,SAAS,KAAK,gDAAgD;GAChE,QAAQ;IACN,UAAU,MAAM;IAChB,WAAW,KAAA;IACX,CAAA,GAAA,QAAA,OAAA,CAAO,QAAQ,UAAU,EAAE,OAAO,KAAK,CAAC;IACxC,MAAM,IAAI,MAAM,iCAAiC;GACnD,UAAU;IACR,UAAU,MAAM;GAClB;GACA,QAAQ,KAAK,QAAQ,QAAQ,UAAU,YAAY;GACnD,QAAQ,KAAK,QAAQ,QAAQ;GAC7B,OAAO,EAAE,GAAG,QAAQ,KAAK;EAC3B;EACA,IAAI,QAAQ,KAAK,UAAU,cAAc,MAAM,IAAI,MAAM,gCAAgC;EACzF,OAAO;GACL,UAAU,QAAQ;GAClB,WAAW,QAAQ,KAAK;GACxB,WAAW,QAAQ,KAAK;EAC1B;CACF;CACA,QAAc;EACZ,KAAK,SAAS;EACd,cAAc,KAAK,KAAK;EACxB,KAAK,SAAS,MAAM;EACpB,CAAA,GAAA,QAAA,OAAA,CAAO,KAAK,WAAW;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACzD;AACF"}
|
|
1
|
+
{"version":3,"file":"power-logs-database.js","names":[],"sources":["../../src/main/power-logs-database.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { appendFileSync, chmodSync, createReadStream, mkdirSync, rmSync, truncateSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { POWER_LOGS_LIMITS as L, type LogCapture, type LogRequest } from \"../common/power-logs\";\n\nimport type { PowerLogsQuerySnapshot } from \"./power-logs-query\";\n\ntype Capture = {\n meta: LogCapture;\n partition: string;\n directory: string;\n spool: string;\n database: string;\n limits: { captureBytes: number; captureRows: number };\n};\nexport class PowerLogsDatabase {\n private captures = new Map<string, Capture>();\n private closed = false;\n private timer: ReturnType<typeof setInterval>;\n constructor(private directory: string) {\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n chmodSync(directory, 0o700);\n this.timer = setInterval(() => this.expire(), 30000);\n this.timer.unref();\n }\n private partition(request: LogRequest): string {\n return JSON.stringify([request.clusterId, request.contextId]);\n }\n private expire(): void {\n for (const [id, capture] of this.captures) {\n if (capture.meta.expiresAt > Date.now()) continue;\n try {\n this.remove(id);\n } catch {\n // retain expired entries within the capture limit until a later sweep can remove locked files\n }\n }\n }\n private remove(id: string): void {\n const capture = this.captures.get(id);\n if (!capture) return;\n try {\n rmSync(capture.directory, { recursive: true, force: true });\n } catch {\n throw new Error(\"Log capture storage is busy or unavailable\");\n }\n this.captures.delete(id);\n }\n exportPath(request: LogRequest & { captureId: string }): string {\n return this.capture(request).spool;\n }\n private capture(request: LogRequest & { captureId: string }): Capture {\n const capture = this.captures.get(request.captureId);\n if (!capture || capture.partition !== this.partition(request) || capture.meta.expiresAt <= Date.now())\n throw new Error(\"Capture unavailable\");\n return capture;\n }\n async run(request: LogRequest): Promise<unknown> {\n if (this.closed) throw new Error(\"Log storage unavailable\");\n this.expire();\n if (request.operation === \"permit\" || \"streamId\" in request) throw new Error(\"Log permits require main process\");\n const partition = this.partition(request);\n if (request.operation === \"cancel\") return true;\n if (request.operation === \"list\")\n return [...this.captures.values()]\n .filter((c) => c.partition === partition && c.meta.expiresAt > Date.now())\n .map((c) => ({ ...c.meta }));\n if (request.operation === \"create\") {\n if (this.captures.size >= L.captures) throw new Error(\"Capture limit reached\");\n const id = randomUUID(),\n directory = join(this.directory, id),\n now = Date.now();\n mkdirSync(directory, { mode: 0o700 });\n const spool = join(directory, \"logs.ndjson\");\n writeFileSync(spool, \"\", { mode: 0o600 });\n const meta: LogCapture = {\n id,\n target: request.target,\n createdAt: now,\n expiresAt: now + L.ttlMs,\n state: \"collecting\",\n rows: 0,\n bytes: 0,\n notes: [],\n };\n this.captures.set(id, {\n meta,\n partition,\n directory,\n spool,\n database: join(directory, \"logs.sqlite\"),\n limits: { captureBytes: L.captureBytes, captureRows: L.captureRows },\n });\n return { ...meta };\n }\n const capture = this.capture(request);\n if (request.operation === \"delete\") {\n this.remove(request.captureId);\n return true;\n }\n if (request.operation === \"save-sql\") {\n if (typeof request.sql !== \"string\" || request.sql.length > 65536 || request.sql.includes(\"\\0\"))\n throw new Error(\"Invalid SQL draft\");\n capture.meta.lastSql = request.sql;\n return { ...capture.meta };\n }\n if (request.operation === \"export\") throw new Error(\"Export requires trusted destination\");\n if (request.operation === \"append\") {\n if (capture.meta.state !== \"collecting\") throw new Error(\"Capture is complete\");\n if (request.records.length > L.batchRows) throw new Error(\"Log batch exceeds limit\");\n const data = request.records\n .map((record) => JSON.stringify({ ...record, previous: record.previous === true }) + \"\\n\")\n .join(\"\");\n const bytes = Buffer.byteLength(data);\n if (\n bytes > L.batchBytes ||\n capture.meta.bytes + bytes > capture.limits.captureBytes ||\n capture.meta.rows + request.records.length > capture.limits.captureRows\n )\n throw new Error(\"Capture limit reached\");\n try {\n appendFileSync(capture.spool, data);\n } catch {\n truncateSync(capture.spool, capture.meta.bytes);\n throw new Error(\"Log capture storage unavailable\");\n }\n capture.meta.rows += request.records.length;\n capture.meta.bytes += bytes;\n return { ...capture.meta };\n }\n if (request.operation === \"finish\") {\n if (capture.meta.state !== \"collecting\") return { ...capture.meta };\n let database: DatabaseSync | undefined;\n try {\n writeFileSync(capture.database, \"\", { mode: 0o600 });\n database = new DatabaseSync(capture.database, { allowExtension: false });\n database.exec(\n \"PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA cache_size=-16384; PRAGMA temp_store=MEMORY\",\n );\n database.exec(\n \"CREATE TABLE logs(timestamp INTEGER, pod TEXT, podUid TEXT, container TEXT, level TEXT, message TEXT, fieldsJson TEXT, previous INTEGER CHECK(previous IN (0,1)))\",\n );\n const insert = database.prepare(\"INSERT INTO logs VALUES (?, ?, ?, ?, ?, ?, ?, ?)\");\n const input = createReadStream(capture.spool);\n const lines = createInterface({ input, crlfDelay: Infinity });\n let count = 0;\n database.exec(\"BEGIN\");\n try {\n for await (const line of lines) {\n const record = JSON.parse(line);\n insert.run(\n record.timestamp,\n record.pod,\n record.podUid,\n record.container,\n record.level,\n record.message,\n record.fieldsJson,\n record.previous ? 1 : 0,\n );\n if (++count % 5000 === 0) {\n database.exec(\"COMMIT; BEGIN\");\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n database.exec(\"COMMIT\");\n } finally {\n lines.close();\n input.destroy();\n }\n database.exec(\"CREATE INDEX logs_timestamp ON logs(timestamp)\");\n } catch {\n database?.close();\n database = undefined;\n rmSync(capture.database, { force: true });\n throw new Error(\"Log capture storage unavailable\");\n } finally {\n database?.close();\n }\n capture.meta.state = request.partial ? \"partial\" : \"ready\";\n capture.meta.notes = request.notes;\n return { ...capture.meta };\n }\n if (capture.meta.state === \"collecting\") throw new Error(\"Finish capture before querying\");\n return {\n database: capture.database,\n captureId: capture.meta.id,\n expiresAt: capture.meta.expiresAt,\n } satisfies PowerLogsQuerySnapshot;\n }\n close(): void {\n this.closed = true;\n clearInterval(this.timer);\n this.captures.clear();\n rmSync(this.directory, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}
|
|
@@ -97,6 +97,14 @@ function validateLogRequest(input) {
|
|
|
97
97
|
...scoped,
|
|
98
98
|
operation: r.operation
|
|
99
99
|
};
|
|
100
|
+
if (r.operation === "save-sql") {
|
|
101
|
+
if (!text(r.sql, 65536)) return fail();
|
|
102
|
+
return {
|
|
103
|
+
...scoped,
|
|
104
|
+
operation: "save-sql",
|
|
105
|
+
sql: r.sql
|
|
106
|
+
};
|
|
107
|
+
}
|
|
100
108
|
if (r.operation === "query") {
|
|
101
109
|
if (!identifier(r.queryId) || !text(r.sql, 65536) || !r.sql.trim()) return fail();
|
|
102
110
|
return {
|