@jam.dev/recording-links 0.2.0 → 0.3.0-electron.5

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
@@ -183,6 +183,354 @@ type RecorderSingleton = {
183
183
  };
184
184
  ```
185
185
 
186
+ ## Electron
187
+
188
+ Jam Recording Links can be integrated into your Electron app to enable seamless
189
+ bug reporting with screen recordings.
190
+
191
+ ### 1. Initialize Jam Electron (Required)
192
+
193
+ Initialize the SDK in your main process before `app.whenReady()`:
194
+
195
+ ```typescript
196
+ import { app, BrowserWindow } from "electron";
197
+ import * as jam from "@jam.dev/recording-links/electron";
198
+
199
+ jam.initialize({
200
+ // Optional: Customize the session if using non-default or multiple sessions.
201
+ // A Recorder can only capture logs from windows belonging to the same session.
202
+ // Defaults to session.defaultSession if not provided.
203
+ // defaultSession: session.defaultSession,
204
+
205
+ // Optional: Customize the display media request handler.
206
+ // By default, Jam installs a handler that auto-selects capture sources.
207
+ // Set to null if you want to handle display media requests yourself.
208
+ // defaultDisplayMediaRequestHandler: null,
209
+
210
+ openRecorderWindow(session) {
211
+ return new BrowserWindow({
212
+ width: 1000,
213
+ height: 700,
214
+ webPreferences: {
215
+ // Required to allow loading Jam's remote recorder scripts from *.jam.dev.
216
+ // Alternative: Use session.webRequest.onHeadersReceived() to set CSP headers
217
+ // allowing *.jam.dev for default-src, script-src, and worker-src.
218
+ allowRunningInsecureContent: true,
219
+ nodeIntegration: false,
220
+ contextIsolation: true,
221
+ sandbox: true,
222
+ },
223
+ });
224
+ },
225
+ async loadRecorderPage(win, data) {
226
+ // Load your app with jam-* query parameters
227
+ return win.loadURL(`https://my-app.com${data.search}`);
228
+ // or: return win.loadFile("index.html", data);
229
+ },
230
+ });
231
+ ```
232
+
233
+ **What this does:**
234
+ - Installs display media request handler for screen capture on the default session
235
+ - Configures window creation and content loading for recorder
236
+ - Manages recorder window lifecycle per session
237
+
238
+ ---
239
+
240
+ ### 2. Handle Incoming Jam Links (Recommended)
241
+
242
+ **Register your app's protocol** (if not already done):
243
+
244
+ Add to your `package.json` electron-builder config:
245
+
246
+ ```json
247
+ {
248
+ "build": {
249
+ "protocols": {
250
+ "name": "my-app",
251
+ "schemes": ["myapp"]
252
+ }
253
+ }
254
+ }
255
+ ```
256
+
257
+ **Wire up protocol handlers** to open Jam recordings:
258
+
259
+ ```typescript
260
+ import { app } from "electron";
261
+ import * as jam from "@jam.dev/recording-links/electron";
262
+
263
+ // macOS: Handle protocol URLs
264
+ app.on("open-url", (event, url) => {
265
+ event.preventDefault();
266
+ const [cleanUrl, recorderWindow] = jam.openUrl(url);
267
+
268
+ if (recorderWindow) {
269
+ recorderWindow.focus();
270
+ }
271
+ // cleanUrl has jam-* params removed for your main window
272
+ });
273
+
274
+ // Windows/Linux: Handle second instance
275
+ // Note: Remember to implement app.requestSingleInstanceLock() in your app
276
+ app.on("second-instance", (event, commandLine) => {
277
+ const url = commandLine.find((arg) => arg.startsWith("myapp://"));
278
+ if (url) {
279
+ const [cleanUrl, recorderWindow] = jam.openUrl(url);
280
+ if (recorderWindow) {
281
+ recorderWindow.focus();
282
+ }
283
+ }
284
+ });
285
+ ```
286
+
287
+ **Result:** Links like `myapp://open?jam-recording=abc123` will launch your app and open the recorder.
288
+
289
+ ---
290
+
291
+ ### 3. Enable In-App Recording (Optional)
292
+
293
+ Add a menu item to trigger recordings from within your app:
294
+
295
+ ```typescript
296
+ import { Menu } from "electron";
297
+ import * as jam from "@jam.dev/recording-links/electron";
298
+
299
+ const template = [
300
+ {
301
+ label: "Help",
302
+ submenu: [
303
+ {
304
+ label: "Report a Bug",
305
+ click: () => {
306
+ jam.openRecorder("your-recording-id-here");
307
+ },
308
+ },
309
+ ],
310
+ },
311
+ ];
312
+
313
+ Menu.setApplicationMenu(Menu.buildFromTemplate(template));
314
+ ```
315
+
316
+ **Test:** Click "Report a Bug" → Recorder window opens and screen capture starts.
317
+
318
+ ---
319
+
320
+ ### API Reference
321
+
322
+ #### `initialize(config)`
323
+
324
+ Initializes the Jam SDK for Electron. Must be called before using other Jam functions.
325
+
326
+ **Parameters:**
327
+
328
+ - `config.defaultSession?: Session` - The session to install display media handler on. Defaults to `session.defaultSession`. If using multiple sessions, install handlers on each session.
329
+
330
+ - `config.defaultDisplayMediaRequestHandler?: DisplayMediaRequestHandler | null` - The display media request handler to install. In an upcoming version, this will introduce a "screen or window picker" UI to the Recorder window. Right now, it uses the OS picker on Mac, and default-selects the first window it sees in Windows + Linux. Set to `null` to handle display media requests yourself (see advanced example below).
331
+
332
+ - `config.openRecorderWindow(session: Session): BrowserWindow` **(required)** - Function that creates and returns a BrowserWindow for the recorder. Called once per session when opening a recorder.
333
+
334
+ - `config.loadRecorderPage(win: BrowserWindow, data: IJamData): Promise<void>` **(required)** - Function that loads your app with Jam recording parameters into the recorder window.
335
+
336
+ **Advanced Example with Custom Display Media Handler:**
337
+
338
+ ```typescript
339
+ import { BrowserWindow, desktopCapturer, session, webContents } from "electron";
340
+ import * as jam from "@jam.dev/recording-links/electron";
341
+
342
+ jam.initialize({
343
+ defaultDisplayMediaRequestHandler: null, // Don't install default handler
344
+ // ... other config
345
+ });
346
+
347
+ // Install custom handler per session
348
+ session.defaultSession.setDisplayMediaRequestHandler(
349
+ async (request, callback) => {
350
+ const frame = request.frame;
351
+ const contents = frame ? webContents.fromFrame(frame) : null;
352
+ const win = contents ? BrowserWindow.fromWebContents(contents) : null;
353
+
354
+ if (jam.isJamRecorder(win)) {
355
+ const sources = await desktopCapturer.getSources({
356
+ types: ["screen", "window"],
357
+ });
358
+ jam.handleDisplayMediaRequest(sources, callback, win);
359
+ } else {
360
+ // Your app's own display media handling
361
+ callback({});
362
+ }
363
+ },
364
+ { useSystemPicker: true },
365
+ );
366
+ ```
367
+
368
+ #### `openRecorder(init, session?)`
369
+
370
+ Opens a Jam recorder window. If a recorder window is already open for the session, focuses it and loads the new recording.
371
+
372
+ **Parameters:**
373
+
374
+ - `init: string | { recordingId: string; jamTitle?: string } | URLSearchParams` - Recording ID, data object, or URL parameters
375
+ - `session?: Session` - Optional session (defaults to `defaultSession` from initialize)
376
+
377
+ **Returns:** `BrowserWindow` - The recorder window (returns immediately, page loads asynchronously)
378
+
379
+ **Examples:**
380
+
381
+ ```typescript
382
+ // Open by recording ID
383
+ jam.openRecorder("abc123");
384
+
385
+ // Open with title
386
+ jam.openRecorder({ recordingId: "abc123", jamTitle: "Bug Report" });
387
+
388
+ // Open from URL parameters
389
+ const params = new URLSearchParams("jam-recording=abc123&jam-title=Bug");
390
+ jam.openRecorder(params);
391
+ ```
392
+
393
+ #### `openUrl(url, session?)`
394
+
395
+ Parses a URL, extracts Jam recording parameters, and opens the recorder if parameters are found. Returns the cleaned URL (with Jam params removed) and the recorder window if opened.
396
+
397
+ **Parameters:**
398
+
399
+ - `url: string | URL` - URL to parse (e.g., from protocol handler)
400
+ - `session?: Session` - Optional session (defaults to `defaultSession` from initialize)
401
+
402
+ **Returns:** `[string, BrowserWindow | null]` - Tuple of cleaned URL and recorder window (or null)
403
+
404
+ **Example:**
405
+
406
+ ```typescript
407
+ app.on("open-url", (event, url) => {
408
+ event.preventDefault();
409
+ const [cleanUrl, recorderWindow] = jam.openUrl(url);
410
+
411
+ if (recorderWindow) {
412
+ recorderWindow.focus();
413
+ } else {
414
+ // Open your main window with cleanUrl
415
+ }
416
+ });
417
+ ```
418
+
419
+ #### `isJamRecorder(win)`
420
+
421
+ Checks if a BrowserWindow is a Jam recorder window.
422
+
423
+ **Parameters:**
424
+
425
+ - `win: BrowserWindow | null` - Window to check
426
+
427
+ **Returns:** `boolean` - True if window is a Jam recorder
428
+
429
+ **Example:**
430
+
431
+ ```typescript
432
+ import { BrowserWindow } from "electron";
433
+ import * as jam from "@jam.dev/recording-links/electron";
434
+
435
+ const win = BrowserWindow.getFocusedWindow();
436
+ if (jam.isJamRecorder(win)) {
437
+ console.log("This is a Jam recorder window");
438
+ }
439
+ ```
440
+
441
+ #### `handleDisplayMediaRequest(sources, callback, requester?)`
442
+
443
+ Helper function for custom display media request handlers. Filters sources (excludes recorder window and DevTools), selects the best source, and calls the callback.
444
+
445
+ In an upcoming version, this function will implement a handshake with the Recorder window to display a "screen or window picker" UI. Right now, it auto-selects the first app window (or first screen if no windows are available).
446
+
447
+ **Parameters:**
448
+
449
+ - `sources: DesktopCapturerSource[]` - Sources from `desktopCapturer.getSources()`
450
+ - `callback: DisplayMediaRequestHandlerCallback` - Callback from display media request handler
451
+ - `requester?: BrowserWindow | null` - The requesting window (to exclude from sources)
452
+
453
+ **Example:**
454
+
455
+ ```typescript
456
+ import { BrowserWindow, desktopCapturer, session, webContents } from "electron";
457
+ import * as jam from "@jam.dev/recording-links/electron";
458
+
459
+ session.defaultSession.setDisplayMediaRequestHandler(
460
+ async (request, callback) => {
461
+ const sources = await desktopCapturer.getSources({
462
+ types: ["screen", "window"],
463
+ });
464
+
465
+ const frame = request.frame;
466
+ const win = frame
467
+ ? BrowserWindow.fromWebContents(webContents.fromFrame(frame))
468
+ : null;
469
+
470
+ jam.handleDisplayMediaRequest(sources, callback, win);
471
+ },
472
+ );
473
+ ```
474
+
475
+ #### `IJamData` Interface
476
+
477
+ Data structure representing Jam recording parameters.
478
+
479
+ ```typescript
480
+ interface IJamData {
481
+ recordingId: string; // Jam recording ID
482
+ jamTitle: string | undefined | null; // Optional recording title
483
+ state: string | undefined | null; // Optional JWT state
484
+ searchParams: URLSearchParams; // jam-* query parameters
485
+ search: string; // Query string with '?' prefix (or empty)
486
+ }
487
+ ```
488
+
489
+ ---
490
+
491
+ ## What Jam Recording Links Enables for Your Electron App
492
+
493
+ Users can report bugs with one click:
494
+
495
+ - From your app menu → Instant screen recording starts
496
+ - From Jam Recording Links (via Slack, email) → App launches and recording begins
497
+ - Zero configuration of screen capture or window management
498
+
499
+ ## What the SDK Provides
500
+
501
+ ### Window Architecture
502
+
503
+ - Creates separate recorder window when `jam-*` query parameters detected
504
+ - Maintains main window alongside recorder (multi-window pattern)
505
+ - Auto-focuses or reuses existing recorder window if already open
506
+ - Prevents recorder window from closing while recording
507
+
508
+ ### Screen Capture
509
+
510
+ - Filters capture sources to exclude recorder window (prevents recursive capture)
511
+ - Auto-excludes DevTools windows from source list
512
+ - Configures `desktopCapturer` with appropriate permissions
513
+ - TEMPORARY: Implements source selection strategy: prefer app windows, then screens
514
+ - WILL BE REPLACED with user source picker in future release
515
+
516
+ ### Protocol Integration
517
+
518
+ - Parses `yourapp://open?jam-recording=ID` format URLs
519
+ - Routes protocol requests to dual-window opener
520
+ - Handles cross-platform differences (macOS `open-url` vs Windows/Linux `second-instance`)
521
+ - Validates and extracts `jam-*` query parameters
522
+
523
+ ### Developer Configuration
524
+
525
+ **You provide:**
526
+
527
+ - **Protocol scheme**: Your app's custom URL scheme (e.g., "notion", "slack")
528
+ - **Web URLs**: Dev server (e.g., `http://localhost:3000`) and production path
529
+ - **Recording IDs**: Which Jam recording to open for bug reports (coming soon: dynamic values via API)
530
+ - **Trigger UI**: Where to place menu items or buttons in your app
531
+
532
+ **SDK handles everything else.**
533
+
186
534
  ## Contributing
187
535
 
188
536
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, build commands, and release process.
@@ -0,0 +1,193 @@
1
+ import { BrowserWindow, type DesktopCapturerSource, type DisplayMediaRequestHandlerHandlerRequest, type Session, type WebFrameMain } from "electron";
2
+ type DisplayMediaRequestHandler = (request: DisplayMediaRequestHandlerHandlerRequest, callback: DisplayMediaRequestHandlerCallback) => void | Promise<void>;
3
+ type DisplayMediaRequestHandlerCallback = (streams: {
4
+ video?: WebFrameMain | {
5
+ id: string;
6
+ name: string;
7
+ } | undefined;
8
+ audio?: "loopback" | "loopbackWithMute" | WebFrameMain | undefined;
9
+ enableLocalEcho?: boolean | undefined;
10
+ }) => void | Promise<void>;
11
+ /**
12
+ * Initialize the Jam SDK for Electron.
13
+ *
14
+ * @example Basic setup
15
+ * ```typescript
16
+ * import { app, BrowserWindow } from 'electron';
17
+ * import * as jam from '@jam.dev/recording-links/electron';
18
+ *
19
+ * jam.initialize({
20
+ * openRecorderWindow(session) {
21
+ * return new BrowserWindow({
22
+ * width: 1000,
23
+ * height: 700,
24
+ * webPreferences: {
25
+ * allowRunningInsecureContent: true,
26
+ * nodeIntegration: false,
27
+ * contextIsolation: true,
28
+ * sandbox: true,
29
+ * },
30
+ * });
31
+ * },
32
+ * async loadRecorderPage(win, data) {
33
+ * await win.loadURL(`https://my-app.com${data.searchParams.toString()}`);
34
+ * },
35
+ * });
36
+ * ```
37
+ *
38
+ * @example Multi-session app with custom display media handler
39
+ * ```typescript
40
+ * import { session, desktopCapturer, BrowserWindow } from 'electron';
41
+ * import * as jam from '@jam.dev/recording-links/electron';
42
+ *
43
+ * jam.initialize({
44
+ * defaultDisplayMediaRequestHandler: null, // Use custom handler per session
45
+ * openRecorderWindow(ses) {
46
+ * const win = new BrowserWindow({
47
+ * width: 1000,
48
+ * height: 700,
49
+ * webPreferences: {
50
+ * allowRunningInsecureContent: true,
51
+ * nodeIntegration: false,
52
+ * contextIsolation: true,
53
+ * sandbox: true,
54
+ * },
55
+ * });
56
+ * win.on('closed', () => console.log('Recorder closed'));
57
+ * return win;
58
+ * },
59
+ * async loadRecorderPage(win, data) {
60
+ * const recordingId = data.searchParams.get('jam-recording');
61
+ * await win.loadURL(`https://my-app.com/recorder?id=${recordingId}`);
62
+ * },
63
+ * });
64
+ *
65
+ * // Install handler on a specific session
66
+ * const mySession = session.fromPartition('persist:my-session');
67
+ * mySession.setDisplayMediaRequestHandler(async (request, callback) => {
68
+ * const frame = request.frame;
69
+ * const win = frame ? BrowserWindow.fromWebContents(frame.webContents) : null;
70
+ *
71
+ * if (jam.isJamRecorder(win)) {
72
+ * const sources = await desktopCapturer.getSources({ types: ['screen', 'window'] });
73
+ * jam.handleDisplayMediaRequest(sources, callback, win);
74
+ * } else {
75
+ * // Your app's default behavior
76
+ * callback({});
77
+ * }
78
+ * });
79
+ * ```
80
+ */
81
+ export declare function initialize(config: {
82
+ /**
83
+ * The session upon which we will install Jam's display media request handler.
84
+ * If your app uses multiple sessions, please install a handler on each session
85
+ * for which Jam is installed; then in your handler, call
86
+ * `jam.handleDisplayMediaRequest(request, callback)`.
87
+ *
88
+ * @default session.defaultSession
89
+ */
90
+ defaultSession?: Session | undefined;
91
+ /**
92
+ * Jam.js for Electron requires setting a display media request handler,
93
+ * which defines the app's implementation for
94
+ * `navigator.mediaDevices.getDisplayMedia(...)` calls.
95
+ *
96
+ * By default, we use:
97
+ *
98
+ * ```
99
+ * defaultSession.setDisplayMediaRequestHandler(
100
+ * (request, callback) => {
101
+ * if (request.frame.url === "https://jam.dev") {
102
+ * jam.handleDisplayMediaRequest(sources, callback);
103
+ * }
104
+ * }),
105
+ * { useSystemPicker: true },
106
+ * );
107
+ * ```
108
+ *
109
+ * If your app manages multiple sessions, or sets its own display media
110
+ * request handler on the session, set this to `null` and either:
111
+ *
112
+ * - Select the default `source` and call the `callback` yourself; or
113
+ * - Call `jam.handleDisplayMediaRequest(sources, callback)` in your handler
114
+ * when the request originates from a Jam-originating frame
115
+ */
116
+ defaultDisplayMediaRequestHandler?: DisplayMediaRequestHandler | null;
117
+ /**
118
+ * A function that takes one argument (a `Session` object) and opens a
119
+ * BrowserWindow we will use for recording.
120
+ *
121
+ * The window will be cached for reuse; when calling `loadRecorder`, Jam will
122
+ * only call this function if a previously-opened Recorder window for this
123
+ * session has been closed.
124
+ */
125
+ openRecorderWindow(ses: Session): BrowserWindow;
126
+ /**
127
+ * A function that takes two arguments—a `BrowserWindow` (opened by
128
+ * `openRecorderWindow`) and an `IJamData` object used to route the window
129
+ * to the proper recorder configuration.
130
+ */
131
+ loadRecorderPage(win: BrowserWindow, data: IJamData): Promise<void>;
132
+ }): Promise<void>;
133
+ /**
134
+ * Opens a Jam recorder window for the given recording ID or data.
135
+ *
136
+ * @param init - Recording ID string, data object, or URLSearchParams
137
+ * @param ses - Optional session (defaults to defaultSession from initialize)
138
+ * @returns The recorder BrowserWindow
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * import * as jam from '@jam.dev/recording-links/electron';
143
+ *
144
+ * // Open by recording ID
145
+ * jam.openRecorder('abc123');
146
+ *
147
+ * // Open with title
148
+ * jam.openRecorder({ recordingId: 'abc123', title: 'Bug Report' });
149
+ *
150
+ * // Open with URLSearchParams (from protocol handler)
151
+ * const params = new URLSearchParams('jam-recording=abc123&jam-title=Bug+Report');
152
+ * jam.openRecorder(params);
153
+ * ```
154
+ */
155
+ export declare function openRecorder(init: string | JamData | ConstructorParameters<typeof JamData>[0], ses?: Session): BrowserWindow;
156
+ /**
157
+ * Syntactic sugar for opening the recorder from a URL.
158
+ * Returns a tuple of the un-jammed URL and the recorder window, if opened.
159
+ */
160
+ export declare function openUrl(url: string | URL, ses?: Session): [string, BrowserWindow | null];
161
+ /**
162
+ * Data structure representing Jam recording parameters.
163
+ */
164
+ export interface IJamData {
165
+ /** Jam recording ID */
166
+ readonly recordingId: string;
167
+ /** Optional recording title */
168
+ readonly title: string | undefined | null;
169
+ /**
170
+ * URLSearchParams containing jam-* query parameters.
171
+ * Follows the same convention as URL.searchParams.
172
+ */
173
+ readonly searchParams: URLSearchParams;
174
+ /**
175
+ * Query string with leading '?' (or empty string if no params).
176
+ * Follows the same convention as URL.search - includes the '?' prefix when params exist.
177
+ */
178
+ readonly search: string;
179
+ }
180
+ declare class JamData implements IJamData {
181
+ readonly recordingId: string;
182
+ readonly title: string | undefined | null;
183
+ get searchParams(): URLSearchParams;
184
+ get search(): string;
185
+ constructor(init: URLSearchParams | {
186
+ recordingId: string;
187
+ title?: string | null;
188
+ });
189
+ }
190
+ export declare function isJamRecorder(win: BrowserWindow | null): boolean;
191
+ export declare function handleDisplayMediaRequest(sources: DesktopCapturerSource[], callback: DisplayMediaRequestHandlerCallback, requester?: BrowserWindow | null): void;
192
+ export {};
193
+ //# sourceMappingURL=electron.d.ts.map
@@ -0,0 +1 @@
1
+ import{app as e,session as r,webContents as t,BrowserWindow as n,desktopCapturer as o}from"electron";const s={defaultSession:null,windows:new Map,openRecorderWindow(){throw new Error("Not initialized")},loadRecorderPage(){throw new Error("Not initialized")}},i=(e,r)=>{const s=e.frame,i=s?t.fromFrame(s):null,a=i?n.fromWebContents(i):null;u(a)?o.getSources({types:["screen","window"]}).then(e=>f(e,r,a)).catch(e=>{r({})}):r({})};async function a(t){if(!e.isReady())return e.whenReady().then(()=>a(t));const{defaultSession:n=r.defaultSession,defaultDisplayMediaRequestHandler:o=i}=t;s.defaultSession=n,s.openRecorderWindow=t.openRecorderWindow,s.loadRecorderPage=t.loadRecorderPage,o&&n.setDisplayMediaRequestHandler(o,{useSystemPicker:!0})}function d(e,r){const t=r??s.defaultSession;if(null===t)throw new Error("Cannot open recorder: no `session` found or provided");let n=s.windows.get(t);n||(n=s.openRecorderWindow(t),s.windows.set(t,n),n.on("closed",()=>s.windows.delete(t)));const o="string"==typeof e?new l({recordingId:e}):e instanceof l?e:new l(e);return s.loadRecorderPage(n,o).catch(e=>{}),n.isMinimized()&&n.restore(),n.focus(),n}function c(e,r){const t="string"==typeof e?new URL(e):e,n=new URLSearchParams;for(const[e,r]of t.searchParams.entries())e.startsWith("jam-")&&(n.set(e,r),t.searchParams.delete(e));return[t.href,n.has("jam-recording")?d(new l(n),r):null]}class l{get searchParams(){const e=new URLSearchParams;return e.set("jam-recording",this.recordingId),this.title&&e.set("jam-title",this.title),e}get search(){const e=this.searchParams.toString();return e?`?${e}`:""}constructor(e){if(e instanceof URLSearchParams){const r=e.get("jam-recording");if(!r)throw new Error("Missing jam-recording parameter");this.recordingId=r,this.title=e.get("jam-title")}else this.recordingId=e.recordingId,this.title=e.title||null}}function u(e){for(const r of s.windows.values())if(r===e)return!0;return!1}function f(e,r,t){const n=t?.getMediaSourceId(),o=e.filter(e=>!e.name.includes("DevTools")&&e.id!==n),s=o.find(e=>e.id.startsWith("window:")),i=o.find(e=>e.id.startsWith("screen:")),a=s||i||o[0];r(a?{video:a,audio:"loopback"}:{})}export{f as handleDisplayMediaRequest,a as initialize,u as isJamRecorder,d as openRecorder,c as openUrl};//# sourceMappingURL=electron.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jam.dev/recording-links",
3
- "version": "0.2.0",
3
+ "version": "0.3.0-electron.5",
4
4
  "description": "Capture bug reports from your users with the Jam recording links SDK",
5
5
  "keywords": [
6
6
  "jam",
@@ -29,6 +29,10 @@
29
29
  "./lib/sdk": {
30
30
  "types": "./lib/sdk.d.ts",
31
31
  "import": "./lib/sdk.js"
32
+ },
33
+ "./electron": {
34
+ "types": "./lib/electron.d.ts",
35
+ "import": "./lib/electron.js"
32
36
  }
33
37
  },
34
38
  "files": [
@@ -63,5 +67,8 @@
63
67
  "tslib": "^2.8.1",
64
68
  "typescript": "^5.8.2",
65
69
  "vitest": "^2.1.8"
70
+ },
71
+ "optionalDependencies": {
72
+ "electron": "^39.2.3"
66
73
  }
67
74
  }