@pixpilot/chrome-lifecycle 0.1.0 → 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,3 +1,130 @@
1
1
  # @pixpilot/chrome-lifecycle
2
2
 
3
- ## Usage Add usage instructions here.
3
+ Utilities for managing Chrome extension lifecycle events.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @pixpilot/chrome-lifecycle
9
+ ```
10
+
11
+ ## API
12
+
13
+ ### Window Events
14
+
15
+ #### `onWindowClose(windowId, callback)`
16
+
17
+ Registers a callback when a specific Chrome window closes.
18
+
19
+ ```typescript
20
+ import { onWindowClose } from '@pixpilot/chrome-lifecycle';
21
+
22
+ const unsubscribe = onWindowClose(windowId, () => {
23
+ console.log('Window closed');
24
+ });
25
+
26
+ // Stop listening
27
+ unsubscribe();
28
+ ```
29
+
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
+ ```
121
+
122
+ **Callback data:**
123
+
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 |
129
+
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.0",
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",
@@ -30,10 +30,10 @@
30
30
  "tsdown": "^0.15.9",
31
31
  "typescript": "^5.9.3",
32
32
  "@internal/eslint-config": "0.3.0",
33
- "@internal/prettier-config": "0.1.0",
34
33
  "@internal/tsdown-config": "0.1.0",
34
+ "@internal/tsconfig": "0.1.0",
35
35
  "@internal/vitest-config": "0.1.0",
36
- "@internal/tsconfig": "0.1.0"
36
+ "@internal/prettier-config": "0.1.0"
37
37
  },
38
38
  "prettier": "@internal/prettier-config",
39
39
  "scripts": {