@pixpilot/chrome-lifecycle 0.1.1 → 0.2.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 CHANGED
@@ -1,30 +1,130 @@
1
1
  # @pixpilot/chrome-lifecycle
2
2
 
3
- A utility package for managing Chrome extension lifecycle events.
3
+ Utilities for managing Chrome extension lifecycle events.
4
4
 
5
- ## onWindowClose
5
+ ## Installation
6
6
 
7
- Registers a callback to be executed when a specific Chrome window is closed. This is useful for triggering alerts, saving data, or cleaning up resources when a specific popup or window is removed.
7
+ ```bash
8
+ npm install @pixpilot/chrome-lifecycle
9
+ ```
10
+
11
+ ## API
12
+
13
+ ### Window Events
14
+
15
+ #### `onWindowClose(windowId, callback)`
8
16
 
9
- ### Usage
17
+ Registers a callback when a specific Chrome window closes.
10
18
 
11
19
  ```typescript
12
20
  import { onWindowClose } from '@pixpilot/chrome-lifecycle';
13
21
 
14
22
  const unsubscribe = onWindowClose(windowId, () => {
15
- console.log('Window was closed!');
16
- // Perform cleanup or other actions
23
+ console.log('Window closed');
17
24
  });
18
25
 
19
- // Later, to stop listening:
26
+ // Stop listening
20
27
  unsubscribe();
21
28
  ```
22
29
 
23
- ### Parameters
30
+ | Parameter | Type | Description |
31
+ | ---------- | ------------ | ---------------------------- |
32
+ | `windowId` | `number` | Chrome window ID to watch |
33
+ | `callback` | `() => void` | Function to execute on close |
34
+
35
+ **Returns:** Unsubscribe function
36
+
37
+ ---
38
+
39
+ ### Side Panel State Manager
40
+
41
+ Tracks whether side panels are visible or hidden across different windows.
42
+
43
+ #### Setup
44
+
45
+ **Background script (service worker):**
46
+
47
+ ```typescript
48
+ import { initSidePanelStateManager } from '@pixpilot/chrome-lifecycle';
49
+
50
+ // Initialize once at startup
51
+ initSidePanelStateManager();
52
+ ```
53
+
54
+ **Side panel script (frontend):**
55
+
56
+ ```typescript
57
+ import { initializeSidePanelStateTracker } from '@pixpilot/chrome-lifecycle';
58
+
59
+ // Initialize when side panel loads
60
+ const cleanup = initializeSidePanelStateTracker();
61
+ ```
62
+
63
+ #### Functions
64
+
65
+ ##### `initSidePanelStateManager()`
66
+
67
+ Initializes the backend state manager. Must be called once in your background script before using other side panel functions. Subsequent calls log a warning and are ignored.
68
+
69
+ ```typescript
70
+ import { initSidePanelStateManager } from '@pixpilot/chrome-lifecycle';
71
+
72
+ initSidePanelStateManager();
73
+ ```
74
+
75
+ ##### `initializeSidePanelStateTracker()`
76
+
77
+ Initializes the frontend tracker in your side panel. Sets up visibility tracking and heartbeat to keep the connection alive.
78
+
79
+ ```typescript
80
+ import { initializeSidePanelStateTracker } from '@pixpilot/chrome-lifecycle';
81
+
82
+ const cleanup = initializeSidePanelStateTracker();
83
+ ```
84
+
85
+ **Returns:** Cleanup function to remove listeners and disconnect
86
+
87
+ ##### `isWindowSidePanelVisible(windowId)`
88
+
89
+ Returns `true` if the side panel is visible for the given window.
90
+
91
+ ```typescript
92
+ import { isWindowSidePanelVisible } from '@pixpilot/chrome-lifecycle';
93
+
94
+ const isVisible = isWindowSidePanelVisible(windowId);
95
+ ```
96
+
97
+ ##### `getSidePanelStateForWindow(windowId)`
98
+
99
+ Returns the current state (`'visible'` | `'hidden'` | `undefined`) for the given window.
100
+
101
+ ```typescript
102
+ import { getSidePanelStateForWindow } from '@pixpilot/chrome-lifecycle';
103
+
104
+ const state = getSidePanelStateForWindow(windowId);
105
+ ```
106
+
107
+ ##### `onSidePanelStateChange(listener)`
108
+
109
+ Listens for side panel state changes across all windows.
110
+
111
+ ```typescript
112
+ import { onSidePanelStateChange } from '@pixpilot/chrome-lifecycle';
113
+
114
+ const unsubscribe = onSidePanelStateChange(({ windowId, state, reason }) => {
115
+ console.log(`Window ${windowId}: ${state}`);
116
+ });
117
+
118
+ // Stop listening
119
+ unsubscribe();
120
+ ```
24
121
 
25
- - `windowId` (number): The Chrome window ID to watch
26
- - `callback` (function): Function to execute when the window closes
122
+ **Callback data:**
27
123
 
28
- ### Returns
124
+ | Property | Type | Description |
125
+ | ---------- | ------------------------- | ------------------------- |
126
+ | `windowId` | `number` | Chrome window ID |
127
+ | `state` | `'visible'` \| `'hidden'` | Current side panel state |
128
+ | `reason` | `string` | What triggered the change |
29
129
 
30
- A function to unsubscribe (remove) the listener.
130
+ **Returns:** Unsubscribe function
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- const e=new Map;let t=!1;function n(){t||(t=!0,chrome.windows.onRemoved.addListener(t=>{let n=e.get(t);n&&(n.forEach(e=>{e()}),e.delete(t))}))}function r(t,r){n();let i=e.get(t);return i||(i=new Set,e.set(t,i)),i.add(r),()=>{let n=e.get(t);n&&(n.delete(r),n.size===0&&e.delete(t))}}exports.onWindowClose=r;
1
+ const e=new Map;let t=!1;function n(){t||(t=!0,chrome.windows.onRemoved.addListener(t=>{let n=e.get(t);n&&(n.forEach(e=>{e()}),e.delete(t))}))}function r(t,r){n();let i=e.get(t);return i||(i=new Set,e.set(t,i)),i.add(r),()=>{let n=e.get(t);n&&(n.delete(r),n.size===0&&e.delete(t))}}const i=new Map,a=new Set;let o=!1;function s(){if(!o)throw Error(`Side panel state manager must be initialized with initSidePanelStateManager() before use.`)}function c(){o||(o=!0,chrome.action.onClicked.addListener(e=>{let t=i.get(e.windowId);t&&t.state===`visible`?t.port&&(i.delete(e.windowId),t.port.postMessage({type:`close-side-panel`})):chrome.sidePanel.open({windowId:e.windowId}).catch(console.error)}),chrome.runtime.onConnect.addListener(e=>{e.name===chrome.runtime.id&&(e.onMessage.addListener(t=>{t.type===`side-panel-state-tracker`&&t.state&&u({port:e,state:t.state,reason:t.reason??`unknown`,windowId:t.windowId,type:t.type})}),e.onDisconnect.addListener(e=>{Array.from(i.entries()).forEach(([t,n])=>{n.port&&n.port===e&&u({port:void 0,state:`hidden`,reason:`port-disconnected`,windowId:t,type:`side-panel-state-tracker`})})}))}))}function l(e){if(e.type!==`side-panel-state-tracker`)return;let t={state:e.state,reason:e.reason,windowId:e.windowId};a.forEach(e=>{try{e(t)}catch(e){console.error(`Error in side panel state listener:`,e)}})}function u(e){let{windowId:t,state:n}=e;if(n===`hidden`){i.delete(t),l(e);return}i.set(t,e),l(e)}function d(e){return s(),i.get(e)?.state}function f(e){return s(),d(e)===`visible`}function p(e){return s(),a.add(e),()=>{a.delete(e)}}exports.getSidePanelStateForWindow=d,exports.initSidePanelStateManager=c,exports.isWindowSidePanelVisible=f,exports.onSidePanelStateChange=p,exports.onWindowClose=r;
package/dist/index.d.cts CHANGED
@@ -17,4 +17,38 @@ type CloseCallback = () => void;
17
17
  */
18
18
  declare function onWindowClose(windowId: number, callback: CloseCallback): () => void;
19
19
  //#endregion
20
- export { onWindowClose };
20
+ //#region src/types/side-panel.d.ts
21
+ type SidePanelState = 'visible' | 'hidden';
22
+ interface BaseSidePanelMessage {
23
+ windowId: number;
24
+ timestamp?: number;
25
+ }
26
+ interface SidePanelStateData extends BaseSidePanelMessage {
27
+ type: 'side-panel-heartbeat' | 'side-panel-state-tracker' | 'side-panel-state-open';
28
+ state: SidePanelState;
29
+ reason: string;
30
+ }
31
+ type SidePanelStateChangeData = Omit<SidePanelStateData, 'timestamp' | 'type'>;
32
+ //#endregion
33
+ //#region src/sidepanel-state-manager.d.ts
34
+ type SidePanelStateListener = (data: SidePanelStateChangeData) => void;
35
+ /**
36
+ * Initializes the side panel state manager.
37
+ * Sets up Chrome event listeners for action clicks and runtime connections.
38
+ * This function should be called once before using other functions in this module.
39
+ * Subsequent calls will log a warning and do nothing.
40
+ */
41
+ declare function initSidePanelStateManager(): void;
42
+ declare function getSidePanelStateForWindow(windowId: number): SidePanelState | undefined;
43
+ declare function isWindowSidePanelVisible(windowId: number): boolean;
44
+ /**
45
+ * Adds a listener for side panel state changes.
46
+ * The listener will be called whenever the side panel state changes (visible/hidden).
47
+ * Note: Heartbeat messages do not trigger listeners, and timestamp is excluded from the data.
48
+ *
49
+ * @param listener - Callback function that receives state change data
50
+ * @returns Unsubscribe function to remove the listener
51
+ */
52
+ declare function onSidePanelStateChange(listener: SidePanelStateListener): () => void;
53
+ //#endregion
54
+ export { getSidePanelStateForWindow, initSidePanelStateManager, isWindowSidePanelVisible, onSidePanelStateChange, onWindowClose };
package/dist/index.d.ts CHANGED
@@ -17,4 +17,38 @@ type CloseCallback = () => void;
17
17
  */
18
18
  declare function onWindowClose(windowId: number, callback: CloseCallback): () => void;
19
19
  //#endregion
20
- export { onWindowClose };
20
+ //#region src/types/side-panel.d.ts
21
+ type SidePanelState = 'visible' | 'hidden';
22
+ interface BaseSidePanelMessage {
23
+ windowId: number;
24
+ timestamp?: number;
25
+ }
26
+ interface SidePanelStateData extends BaseSidePanelMessage {
27
+ type: 'side-panel-heartbeat' | 'side-panel-state-tracker' | 'side-panel-state-open';
28
+ state: SidePanelState;
29
+ reason: string;
30
+ }
31
+ type SidePanelStateChangeData = Omit<SidePanelStateData, 'timestamp' | 'type'>;
32
+ //#endregion
33
+ //#region src/sidepanel-state-manager.d.ts
34
+ type SidePanelStateListener = (data: SidePanelStateChangeData) => void;
35
+ /**
36
+ * Initializes the side panel state manager.
37
+ * Sets up Chrome event listeners for action clicks and runtime connections.
38
+ * This function should be called once before using other functions in this module.
39
+ * Subsequent calls will log a warning and do nothing.
40
+ */
41
+ declare function initSidePanelStateManager(): void;
42
+ declare function getSidePanelStateForWindow(windowId: number): SidePanelState | undefined;
43
+ declare function isWindowSidePanelVisible(windowId: number): boolean;
44
+ /**
45
+ * Adds a listener for side panel state changes.
46
+ * The listener will be called whenever the side panel state changes (visible/hidden).
47
+ * Note: Heartbeat messages do not trigger listeners, and timestamp is excluded from the data.
48
+ *
49
+ * @param listener - Callback function that receives state change data
50
+ * @returns Unsubscribe function to remove the listener
51
+ */
52
+ declare function onSidePanelStateChange(listener: SidePanelStateListener): () => void;
53
+ //#endregion
54
+ export { getSidePanelStateForWindow, initSidePanelStateManager, isWindowSidePanelVisible, onSidePanelStateChange, onWindowClose };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- const e=new Map;let t=!1;function n(){t||(t=!0,chrome.windows.onRemoved.addListener(t=>{let n=e.get(t);n&&(n.forEach(e=>{e()}),e.delete(t))}))}function r(t,r){n();let i=e.get(t);return i||(i=new Set,e.set(t,i)),i.add(r),()=>{let n=e.get(t);n&&(n.delete(r),n.size===0&&e.delete(t))}}export{r as onWindowClose};
1
+ const e=new Map;let t=!1;function n(){t||(t=!0,chrome.windows.onRemoved.addListener(t=>{let n=e.get(t);n&&(n.forEach(e=>{e()}),e.delete(t))}))}function r(t,r){n();let i=e.get(t);return i||(i=new Set,e.set(t,i)),i.add(r),()=>{let n=e.get(t);n&&(n.delete(r),n.size===0&&e.delete(t))}}const i=new Map,a=new Set;let o=!1;function s(){if(!o)throw Error(`Side panel state manager must be initialized with initSidePanelStateManager() before use.`)}function c(){o||(o=!0,chrome.action.onClicked.addListener(e=>{let t=i.get(e.windowId);t&&t.state===`visible`?t.port&&(i.delete(e.windowId),t.port.postMessage({type:`close-side-panel`})):chrome.sidePanel.open({windowId:e.windowId}).catch(console.error)}),chrome.runtime.onConnect.addListener(e=>{e.name===chrome.runtime.id&&(e.onMessage.addListener(t=>{t.type===`side-panel-state-tracker`&&t.state&&u({port:e,state:t.state,reason:t.reason??`unknown`,windowId:t.windowId,type:t.type})}),e.onDisconnect.addListener(e=>{Array.from(i.entries()).forEach(([t,n])=>{n.port&&n.port===e&&u({port:void 0,state:`hidden`,reason:`port-disconnected`,windowId:t,type:`side-panel-state-tracker`})})}))}))}function l(e){if(e.type!==`side-panel-state-tracker`)return;let t={state:e.state,reason:e.reason,windowId:e.windowId};a.forEach(e=>{try{e(t)}catch(e){console.error(`Error in side panel state listener:`,e)}})}function u(e){let{windowId:t,state:n}=e;if(n===`hidden`){i.delete(t),l(e);return}i.set(t,e),l(e)}function d(e){return s(),i.get(e)?.state}function f(e){return s(),d(e)===`visible`}function p(e){return s(),a.add(e),()=>{a.delete(e)}}export{d as getSidePanelStateForWindow,c as initSidePanelStateManager,f as isWindowSidePanelVisible,p as onSidePanelStateChange,r as onWindowClose};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pixpilot/chrome-lifecycle",
3
3
  "type": "module",
4
- "version": "0.1.1",
4
+ "version": "0.2.0",
5
5
  "description": "Lifecycle management utilities for Chrome extensions.",
6
6
  "author": "m.doaie <m.doaie@hotmail.com>",
7
7
  "license": "MIT",
@@ -29,11 +29,11 @@
29
29
  "eslint": "^9.38.0",
30
30
  "tsdown": "^0.15.9",
31
31
  "typescript": "^5.9.3",
32
- "@internal/prettier-config": "0.1.0",
33
- "@internal/tsdown-config": "0.1.0",
34
32
  "@internal/eslint-config": "0.3.0",
33
+ "@internal/tsdown-config": "0.1.0",
35
34
  "@internal/tsconfig": "0.1.0",
36
- "@internal/vitest-config": "0.1.0"
35
+ "@internal/vitest-config": "0.1.0",
36
+ "@internal/prettier-config": "0.1.0"
37
37
  },
38
38
  "prettier": "@internal/prettier-config",
39
39
  "scripts": {