@gemigo/extension-sdk 0.2.0 → 0.2.1

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.
@@ -18,160 +18,160 @@ var NativeEventType;
18
18
  (function(e) {
19
19
  e.Message = "message";
20
20
  })(NativeEventType ||= {});
21
- var createDestructor_default = (e, f) => {
22
- let p = [], m = !1;
21
+ var createDestructor_default = (e, v) => {
22
+ let y = [], b = !1;
23
23
  return {
24
- destroy(h) {
25
- m || (m = !0, f(`${e}: Destroying connection`), p.forEach((e) => {
26
- e(h);
24
+ destroy(x) {
25
+ b || (b = !0, v(`${e}: Destroying connection`), y.forEach((e) => {
26
+ e(x);
27
27
  }));
28
28
  },
29
29
  onDestroy(e) {
30
- m ? e() : p.push(e);
30
+ b ? e() : y.push(e);
31
31
  }
32
32
  };
33
- }, createLogger_default = (e) => (...f) => {
34
- e && console.log("[Penpal]", ...f);
33
+ }, createLogger_default = (e) => (...v) => {
34
+ e && console.log("[Penpal]", ...v);
35
35
  };
36
- const serializeError = ({ name: e, message: f, stack: p }) => ({
36
+ const serializeError = ({ name: e, message: v, stack: y }) => ({
37
37
  name: e,
38
- message: f,
39
- stack: p
38
+ message: v,
39
+ stack: y
40
40
  }), deserializeError = (e) => {
41
- let f = /* @__PURE__ */ Error();
42
- return Object.keys(e).forEach((p) => f[p] = e[p]), f;
41
+ let v = /* @__PURE__ */ Error();
42
+ return Object.keys(e).forEach((y) => v[y] = e[y]), v;
43
43
  };
44
- var connectCallReceiver_default = (p, g, _) => {
45
- let { localName: y, local: b, remote: x, originForSending: S, originForReceiving: C } = p, w = !1, T = (p) => {
46
- if (p.source !== x || p.data.penpal !== MessageType.Call) return;
47
- if (C !== "*" && p.origin !== C) {
48
- _(`${y} received message from origin ${p.origin} which did not match expected origin ${C}`);
44
+ var connectCallReceiver_default = (y, S, C) => {
45
+ let { localName: T, local: E, remote: D, originForSending: O, originForReceiving: k } = y, A = !1, j = (y) => {
46
+ if (y.source !== D || y.data.penpal !== MessageType.Call) return;
47
+ if (k !== "*" && y.origin !== k) {
48
+ C(`${T} received message from origin ${y.origin} which did not match expected origin ${k}`);
49
49
  return;
50
50
  }
51
- let { methodName: h, args: b, id: T } = p.data;
52
- _(`${y}: Received ${h}() call`);
53
- let E = (p) => (g) => {
54
- if (_(`${y}: Sending ${h}() reply`), w) {
55
- _(`${y}: Unable to send ${h}() reply due to destroyed connection`);
51
+ let { methodName: x, args: E, id: j } = y.data;
52
+ C(`${T}: Received ${x}() call`);
53
+ let M = (y) => (S) => {
54
+ if (C(`${T}: Sending ${x}() reply`), A) {
55
+ C(`${T}: Unable to send ${x}() reply due to destroyed connection`);
56
56
  return;
57
57
  }
58
- let b = {
58
+ let E = {
59
59
  penpal: MessageType.Reply,
60
- id: T,
61
- resolution: p,
62
- returnValue: g
60
+ id: j,
61
+ resolution: y,
62
+ returnValue: S
63
63
  };
64
- p === Resolution.Rejected && g instanceof Error && (b.returnValue = serializeError(g), b.returnValueIsError = !0);
64
+ y === Resolution.Rejected && S instanceof Error && (E.returnValue = serializeError(S), E.returnValueIsError = !0);
65
65
  try {
66
- x.postMessage(b, S);
67
- } catch (p) {
68
- if (p.name === NativeErrorName.DataCloneError) {
69
- let m = {
66
+ D.postMessage(E, O);
67
+ } catch (y) {
68
+ if (y.name === NativeErrorName.DataCloneError) {
69
+ let b = {
70
70
  penpal: MessageType.Reply,
71
- id: T,
71
+ id: j,
72
72
  resolution: Resolution.Rejected,
73
- returnValue: serializeError(p),
73
+ returnValue: serializeError(y),
74
74
  returnValueIsError: !0
75
75
  };
76
- x.postMessage(m, S);
76
+ D.postMessage(b, O);
77
77
  }
78
- throw p;
78
+ throw y;
79
79
  }
80
80
  };
81
- new Promise((e) => e(g[h].apply(g, b))).then(E(Resolution.Fulfilled), E(Resolution.Rejected));
81
+ new Promise((e) => e(S[x].apply(S, E))).then(M(Resolution.Fulfilled), M(Resolution.Rejected));
82
82
  };
83
- return b.addEventListener(NativeEventType.Message, T), () => {
84
- w = !0, b.removeEventListener(NativeEventType.Message, T);
83
+ return E.addEventListener(NativeEventType.Message, j), () => {
84
+ A = !0, E.removeEventListener(NativeEventType.Message, j);
85
85
  };
86
- }, id = 0, generateId_default = () => ++id, KEY_PATH_DELIMITER = ".", keyPathToSegments = (e) => e ? e.split(KEY_PATH_DELIMITER) : [], segmentsToKeyPath = (e) => e.join(KEY_PATH_DELIMITER), createKeyPath = (e, f) => {
87
- let p = keyPathToSegments(f || "");
88
- return p.push(e), segmentsToKeyPath(p);
86
+ }, id = 0, generateId_default = () => ++id, KEY_PATH_DELIMITER = ".", keyPathToSegments = (e) => e ? e.split(KEY_PATH_DELIMITER) : [], segmentsToKeyPath = (e) => e.join(KEY_PATH_DELIMITER), createKeyPath = (e, v) => {
87
+ let y = keyPathToSegments(v || "");
88
+ return y.push(e), segmentsToKeyPath(y);
89
89
  };
90
- const setAtKeyPath = (e, f, p) => {
91
- let m = keyPathToSegments(f);
92
- return m.reduce((e, f, h) => (e[f] === void 0 && (e[f] = {}), h === m.length - 1 && (e[f] = p), e[f]), e), e;
93
- }, serializeMethods = (e, f) => {
94
- let p = {};
95
- return Object.keys(e).forEach((m) => {
96
- let h = e[m], g = createKeyPath(m, f);
97
- typeof h == "object" && Object.assign(p, serializeMethods(h, g)), typeof h == "function" && (p[g] = h);
98
- }), p;
90
+ const setAtKeyPath = (e, v, y) => {
91
+ let b = keyPathToSegments(v);
92
+ return b.reduce((e, v, x) => (e[v] === void 0 && (e[v] = {}), x === b.length - 1 && (e[v] = y), e[v]), e), e;
93
+ }, serializeMethods = (e, v) => {
94
+ let y = {};
95
+ return Object.keys(e).forEach((b) => {
96
+ let x = e[b], S = createKeyPath(b, v);
97
+ typeof x == "object" && Object.assign(y, serializeMethods(x, S)), typeof x == "function" && (y[S] = x);
98
+ }), y;
99
99
  }, deserializeMethods = (e) => {
100
- let f = {};
101
- for (let p in e) setAtKeyPath(f, p, e[p]);
102
- return f;
100
+ let v = {};
101
+ for (let y in e) setAtKeyPath(v, y, e[y]);
102
+ return v;
103
103
  };
104
- var connectCallSender_default = (m, g, _, v, b) => {
105
- let { localName: x, local: C, remote: w, originForSending: T, originForReceiving: E } = g, D = !1;
106
- b(`${x}: Connecting call sender`);
107
- let O = (m) => (...g) => {
108
- b(`${x}: Sending ${m}() call`);
109
- let _;
104
+ var connectCallSender_default = (b, S, C, w, E) => {
105
+ let { localName: D, local: k, remote: A, originForSending: j, originForReceiving: M } = S, N = !1;
106
+ E(`${D}: Connecting call sender`);
107
+ let P = (b) => (...S) => {
108
+ E(`${D}: Sending ${b}() call`);
109
+ let C;
110
110
  try {
111
- w.closed && (_ = !0);
111
+ A.closed && (C = !0);
112
112
  } catch {
113
- _ = !0;
113
+ C = !0;
114
114
  }
115
- if (_ && v(), D) {
116
- let e = /* @__PURE__ */ Error(`Unable to send ${m}() call due to destroyed connection`);
115
+ if (C && w(), N) {
116
+ let e = /* @__PURE__ */ Error(`Unable to send ${b}() call due to destroyed connection`);
117
117
  throw e.code = ErrorCode.ConnectionDestroyed, e;
118
118
  }
119
- return new Promise((p, _) => {
120
- let v = generateId_default(), D = (g) => {
121
- if (g.source !== w || g.data.penpal !== MessageType.Reply || g.data.id !== v) return;
122
- if (E !== "*" && g.origin !== E) {
123
- b(`${x} received message from origin ${g.origin} which did not match expected origin ${E}`);
119
+ return new Promise((y, C) => {
120
+ let w = generateId_default(), N = (S) => {
121
+ if (S.source !== A || S.data.penpal !== MessageType.Reply || S.data.id !== w) return;
122
+ if (M !== "*" && S.origin !== M) {
123
+ E(`${D} received message from origin ${S.origin} which did not match expected origin ${M}`);
124
124
  return;
125
125
  }
126
- let S = g.data;
127
- b(`${x}: Received ${m}() reply`), C.removeEventListener(NativeEventType.Message, D);
128
- let T = S.returnValue;
129
- S.returnValueIsError && (T = deserializeError(T)), (S.resolution === Resolution.Fulfilled ? p : _)(T);
126
+ let O = S.data;
127
+ E(`${D}: Received ${b}() reply`), k.removeEventListener(NativeEventType.Message, N);
128
+ let j = O.returnValue;
129
+ O.returnValueIsError && (j = deserializeError(j)), (O.resolution === Resolution.Fulfilled ? y : C)(j);
130
130
  };
131
- C.addEventListener(NativeEventType.Message, D);
132
- let O = {
131
+ k.addEventListener(NativeEventType.Message, N);
132
+ let P = {
133
133
  penpal: MessageType.Call,
134
- id: v,
135
- methodName: m,
136
- args: g
134
+ id: w,
135
+ methodName: b,
136
+ args: S
137
137
  };
138
- w.postMessage(O, T);
138
+ A.postMessage(P, j);
139
139
  });
140
- }, k = _.reduce((e, f) => (e[f] = O(f), e), {});
141
- return Object.assign(m, deserializeMethods(k)), () => {
142
- D = !0;
140
+ }, F = C.reduce((e, v) => (e[v] = P(v), e), {});
141
+ return Object.assign(b, deserializeMethods(F)), () => {
142
+ N = !0;
143
143
  };
144
- }, startConnectionTimeout_default = (e, f) => {
145
- let m;
146
- return e !== void 0 && (m = window.setTimeout(() => {
147
- let m = /* @__PURE__ */ Error(`Connection timed out after ${e}ms`);
148
- m.code = ErrorCode.ConnectionTimeout, f(m);
144
+ }, startConnectionTimeout_default = (e, v) => {
145
+ let b;
146
+ return e !== void 0 && (b = window.setTimeout(() => {
147
+ let b = /* @__PURE__ */ Error(`Connection timed out after ${e}ms`);
148
+ b.code = ErrorCode.ConnectionTimeout, v(b);
149
149
  }, e)), () => {
150
- clearTimeout(m);
150
+ clearTimeout(b);
151
151
  };
152
- }, handleSynAckMessageFactory_default = (f, p, m, h) => {
153
- let { destroy: g, onDestroy: _ } = m;
154
- return (m) => {
155
- if (!(f instanceof RegExp ? f.test(m.origin) : f === "*" || f === m.origin)) {
156
- h(`Child: Handshake - Received SYN-ACK from origin ${m.origin} which did not match expected origin ${f}`);
152
+ }, handleSynAckMessageFactory_default = (v, y, b, x) => {
153
+ let { destroy: S, onDestroy: C } = b;
154
+ return (b) => {
155
+ if (!(v instanceof RegExp ? v.test(b.origin) : v === "*" || v === b.origin)) {
156
+ x(`Child: Handshake - Received SYN-ACK from origin ${b.origin} which did not match expected origin ${v}`);
157
157
  return;
158
158
  }
159
- h("Child: Handshake - Received SYN-ACK, responding with ACK");
160
- let v = m.origin === "null" ? "*" : m.origin, y = {
159
+ x("Child: Handshake - Received SYN-ACK, responding with ACK");
160
+ let w = b.origin === "null" ? "*" : b.origin, T = {
161
161
  penpal: MessageType.Ack,
162
- methodNames: Object.keys(p)
162
+ methodNames: Object.keys(y)
163
163
  };
164
- window.parent.postMessage(y, v);
165
- let x = {
164
+ window.parent.postMessage(T, w);
165
+ let D = {
166
166
  localName: "Child",
167
167
  local: window,
168
168
  remote: window.parent,
169
- originForSending: v,
170
- originForReceiving: m.origin
169
+ originForSending: w,
170
+ originForReceiving: b.origin
171
171
  };
172
- _(connectCallReceiver_default(x, p, h));
173
- let S = {};
174
- return _(connectCallSender_default(S, x, m.data.methodNames, g, h)), S;
172
+ C(connectCallReceiver_default(D, y, x));
173
+ let O = {};
174
+ return C(connectCallSender_default(O, D, b.data.methodNames, S, x)), O;
175
175
  };
176
176
  }, areGlobalsAccessible = () => {
177
177
  try {
@@ -180,29 +180,29 @@ var connectCallSender_default = (m, g, _, v, b) => {
180
180
  return !1;
181
181
  }
182
182
  return !0;
183
- }, connectToParent_default = (f = {}) => {
184
- let { parentOrigin: p = "*", methods: m = {}, timeout: v, debug: y = !1 } = f, b = createLogger_default(y), x = createDestructor_default("Child", b), { destroy: S, onDestroy: C } = x, w = handleSynAckMessageFactory_default(p, serializeMethods(m), x, b), T = () => {
185
- b("Child: Handshake - Sending SYN");
186
- let f = { penpal: MessageType.Syn }, m = p instanceof RegExp ? "*" : p;
187
- window.parent.postMessage(f, m);
183
+ }, connectToParent_default = (v = {}) => {
184
+ let { parentOrigin: y = "*", methods: b = {}, timeout: w, debug: T = !1 } = v, E = createLogger_default(T), D = createDestructor_default("Child", E), { destroy: O, onDestroy: k } = D, A = handleSynAckMessageFactory_default(y, serializeMethods(b), D, E), j = () => {
185
+ E("Child: Handshake - Sending SYN");
186
+ let v = { penpal: MessageType.Syn }, b = y instanceof RegExp ? "*" : y;
187
+ window.parent.postMessage(v, b);
188
188
  };
189
189
  return {
190
- promise: new Promise((f, p) => {
191
- let m = startConnectionTimeout_default(v, S), g = (p) => {
192
- if (areGlobalsAccessible() && !(p.source !== parent || !p.data) && p.data.penpal === MessageType.SynAck) {
193
- let e = w(p);
194
- e && (window.removeEventListener(NativeEventType.Message, g), m(), f(e));
190
+ promise: new Promise((v, y) => {
191
+ let b = startConnectionTimeout_default(w, O), S = (y) => {
192
+ if (areGlobalsAccessible() && !(y.source !== parent || !y.data) && y.data.penpal === MessageType.SynAck) {
193
+ let e = A(y);
194
+ e && (window.removeEventListener(NativeEventType.Message, S), b(), v(e));
195
195
  }
196
196
  };
197
- window.addEventListener(NativeEventType.Message, g), T(), C((e) => {
198
- window.removeEventListener(NativeEventType.Message, g), e && p(e);
197
+ window.addEventListener(NativeEventType.Message, S), j(), k((e) => {
198
+ window.removeEventListener(NativeEventType.Message, S), e && y(e);
199
199
  });
200
200
  }),
201
201
  destroy() {
202
- S();
202
+ O();
203
203
  }
204
204
  };
205
- }, eventHandlers = { contextMenu: [] }, connectionPromise = null;
205
+ }, connectionPromise = null;
206
206
  function isInIframe() {
207
207
  try {
208
208
  return window.self !== window.top;
@@ -210,38 +210,86 @@ function isInIframe() {
210
210
  return !0;
211
211
  }
212
212
  }
213
- function getHost() {
214
- return connectionPromise || (isInIframe() || console.warn("[GemiGo SDK] Not running in iframe. SDK calls will verify connection forever."), connectionPromise = connectToParent_default({ methods: { onContextMenuEvent(e) {
215
- eventHandlers.contextMenu.forEach((f) => {
216
- try {
217
- f(e);
218
- } catch (e) {
219
- console.error("[GemiGo SDK] Error in contextMenu handler:", e);
220
- }
221
- });
222
- } } }).promise, connectionPromise);
213
+ function getHost(e) {
214
+ if (connectionPromise) return connectionPromise;
215
+ isInIframe() || console.warn("[GemiGo SDK] Not running in iframe. SDK calls will not work.");
216
+ let v = {};
217
+ return e && Object.assign(v, e), connectionPromise = connectToParent_default({ methods: v }).promise, connectionPromise;
218
+ }
219
+ function initConnection(e) {
220
+ getHost(e).catch((e) => {
221
+ console.debug("[GemiGo SDK] Auto-connect waiting...", e);
222
+ });
223
+ }
224
+ var handlers = {
225
+ contextMenu: [],
226
+ selectionChange: []
227
+ };
228
+ function on(e, v) {
229
+ return handlers[e].push(v), () => {
230
+ let y = handlers[e].indexOf(v);
231
+ y > -1 && handlers[e].splice(y, 1);
232
+ };
233
+ }
234
+ function emit(e, v) {
235
+ handlers[e].forEach((y) => {
236
+ try {
237
+ y(v);
238
+ } catch (v) {
239
+ console.error(`[GemiGo SDK] Error in ${e} handler:`, v);
240
+ }
241
+ });
242
+ }
243
+ function getChildMethods() {
244
+ return {
245
+ onContextMenuEvent(e) {
246
+ emit("contextMenu", e);
247
+ },
248
+ onSelectionChange(e, v) {
249
+ handlers.selectionChange.forEach((y) => {
250
+ try {
251
+ y(e, v);
252
+ } catch (e) {
253
+ console.error("[GemiGo SDK] Error in selectionChange handler:", e);
254
+ }
255
+ });
256
+ }
257
+ };
223
258
  }
224
- var sdkInstance = {
259
+ const extensionAPI = {
225
260
  getPageInfo: () => getHost().then((e) => e.getPageInfo()),
226
- notify: (e, f) => getHost().then((p) => p.notify({
227
- title: e,
228
- message: f
261
+ getPageHTML: () => getHost().then((e) => e.getPageHTML()),
262
+ getPageText: () => getHost().then((e) => e.getPageText()),
263
+ getSelection: () => getHost().then((e) => e.getSelection()),
264
+ extractArticle: () => getHost().then((e) => e.extractArticle()),
265
+ highlight: (e, v) => getHost().then((y) => y.highlight(e, v)),
266
+ removeHighlight: (e) => getHost().then((v) => v.removeHighlight(e)),
267
+ insertWidget: (e, v = "bottom-right") => getHost().then((y) => y.insertWidget({
268
+ html: e,
269
+ position: v
229
270
  })),
230
- extension: {
231
- getPageHTML: () => getHost().then((e) => e.getPageHTML()),
232
- getPageText: () => getHost().then((e) => e.getPageText()),
233
- getSelection: () => getHost().then((e) => e.getSelection()),
234
- highlight: (e, f) => getHost().then((p) => p.highlight(e, f)),
235
- extractArticle: () => getHost().then((e) => e.extractArticle()),
236
- captureVisible: () => getHost().then((e) => e.captureVisible()),
237
- getContextMenuEvent: () => getHost().then((e) => e.getContextMenuEvent()),
238
- onContextMenu(e) {
239
- getHost(), eventHandlers.contextMenu.push(e);
240
- }
241
- }
271
+ updateWidget: (e, v) => getHost().then((y) => y.updateWidget(e, v)),
272
+ removeWidget: (e) => getHost().then((v) => v.removeWidget(e)),
273
+ injectCSS: (e) => getHost().then((v) => v.injectCSS(e)),
274
+ removeCSS: (e) => getHost().then((v) => v.removeCSS(e)),
275
+ extractLinks: () => getHost().then((e) => e.extractLinks()),
276
+ extractImages: () => getHost().then((e) => e.extractImages()),
277
+ queryElement: (e, v) => getHost().then((y) => y.queryElement(e, v)),
278
+ captureVisible: () => getHost().then((e) => e.captureVisible()),
279
+ getContextMenuEvent: () => getHost().then((e) => e.getContextMenuEvent()),
280
+ onContextMenu: (e) => (getHost(), on("contextMenu", e)),
281
+ onSelectionChange: (e) => (getHost(), on("selectionChange", e))
242
282
  };
243
- getHost().catch((e) => {
244
- console.debug("[GemiGo SDK] Auto-connect waiting...", e);
245
- });
246
- var src_default = sdkInstance;
247
- export { src_default as default };
283
+ function createSDK() {
284
+ return {
285
+ notify: (e, v) => getHost().then((y) => y.notify({
286
+ title: e,
287
+ message: v
288
+ })),
289
+ extension: extensionAPI
290
+ };
291
+ }
292
+ var sdk = createSDK();
293
+ initConnection(getChildMethods());
294
+ var sdk_default = sdk;
295
+ export { sdk_default as default };
@@ -1 +1 @@
1
- (function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.gemigo=t())})(this,function(){var e;(function(e){e.Call=`call`,e.Reply=`reply`,e.Syn=`syn`,e.SynAck=`synAck`,e.Ack=`ack`})(e||={});var t;(function(e){e.Fulfilled=`fulfilled`,e.Rejected=`rejected`})(t||={});var n;(function(e){e.ConnectionDestroyed=`ConnectionDestroyed`,e.ConnectionTimeout=`ConnectionTimeout`,e.NoIframeSrc=`NoIframeSrc`})(n||={});var r;(function(e){e.DataCloneError=`DataCloneError`})(r||={});var i;(function(e){e.Message=`message`})(i||={});var a=(e,t)=>{let n=[],r=!1;return{destroy(i){r||(r=!0,t(`${e}: Destroying connection`),n.forEach(e=>{e(i)}))},onDestroy(e){r?e():n.push(e)}}},o=e=>(...t)=>{e&&console.log(`[Penpal]`,...t)};let s=({name:e,message:t,stack:n})=>({name:e,message:t,stack:n}),c=e=>{let t=Error();return Object.keys(e).forEach(n=>t[n]=e[n]),t};var l=(n,a,o)=>{let{localName:c,local:l,remote:u,originForSending:d,originForReceiving:f}=n,p=!1,m=n=>{if(n.source!==u||n.data.penpal!==e.Call)return;if(f!==`*`&&n.origin!==f){o(`${c} received message from origin ${n.origin} which did not match expected origin ${f}`);return}let{methodName:i,args:l,id:m}=n.data;o(`${c}: Received ${i}() call`);let h=n=>a=>{if(o(`${c}: Sending ${i}() reply`),p){o(`${c}: Unable to send ${i}() reply due to destroyed connection`);return}let l={penpal:e.Reply,id:m,resolution:n,returnValue:a};n===t.Rejected&&a instanceof Error&&(l.returnValue=s(a),l.returnValueIsError=!0);try{u.postMessage(l,d)}catch(n){if(n.name===r.DataCloneError){let r={penpal:e.Reply,id:m,resolution:t.Rejected,returnValue:s(n),returnValueIsError:!0};u.postMessage(r,d)}throw n}};new Promise(e=>e(a[i].apply(a,l))).then(h(t.Fulfilled),h(t.Rejected))};return l.addEventListener(i.Message,m),()=>{p=!0,l.removeEventListener(i.Message,m)}},u=0,d=()=>++u,f=`.`,p=e=>e?e.split(f):[],m=e=>e.join(f),h=(e,t)=>{let n=p(t||``);return n.push(e),m(n)};let g=(e,t,n)=>{let r=p(t);return r.reduce((e,t,i)=>(e[t]===void 0&&(e[t]={}),i===r.length-1&&(e[t]=n),e[t]),e),e},_=(e,t)=>{let n={};return Object.keys(e).forEach(r=>{let i=e[r],a=h(r,t);typeof i==`object`&&Object.assign(n,_(i,a)),typeof i==`function`&&(n[a]=i)}),n},v=e=>{let t={};for(let n in e)g(t,n,e[n]);return t};var y=(r,a,o,s,l)=>{let{localName:u,local:f,remote:p,originForSending:m,originForReceiving:h}=a,g=!1;l(`${u}: Connecting call sender`);let _=r=>(...a)=>{l(`${u}: Sending ${r}() call`);let o;try{p.closed&&(o=!0)}catch{o=!0}if(o&&s(),g){let e=Error(`Unable to send ${r}() call due to destroyed connection`);throw e.code=n.ConnectionDestroyed,e}return new Promise((n,o)=>{let s=d(),g=a=>{if(a.source!==p||a.data.penpal!==e.Reply||a.data.id!==s)return;if(h!==`*`&&a.origin!==h){l(`${u} received message from origin ${a.origin} which did not match expected origin ${h}`);return}let d=a.data;l(`${u}: Received ${r}() reply`),f.removeEventListener(i.Message,g);let m=d.returnValue;d.returnValueIsError&&(m=c(m)),(d.resolution===t.Fulfilled?n:o)(m)};f.addEventListener(i.Message,g);let _={penpal:e.Call,id:s,methodName:r,args:a};p.postMessage(_,m)})},y=o.reduce((e,t)=>(e[t]=_(t),e),{});return Object.assign(r,v(y)),()=>{g=!0}},b=(e,t)=>{let r;return e!==void 0&&(r=window.setTimeout(()=>{let r=Error(`Connection timed out after ${e}ms`);r.code=n.ConnectionTimeout,t(r)},e)),()=>{clearTimeout(r)}},x=(t,n,r,i)=>{let{destroy:a,onDestroy:o}=r;return r=>{if(!(t instanceof RegExp?t.test(r.origin):t===`*`||t===r.origin)){i(`Child: Handshake - Received SYN-ACK from origin ${r.origin} which did not match expected origin ${t}`);return}i(`Child: Handshake - Received SYN-ACK, responding with ACK`);let s=r.origin===`null`?`*`:r.origin,c={penpal:e.Ack,methodNames:Object.keys(n)};window.parent.postMessage(c,s);let u={localName:`Child`,local:window,remote:window.parent,originForSending:s,originForReceiving:r.origin};o(l(u,n,i));let d={};return o(y(d,u,r.data.methodNames,a,i)),d}},S=()=>{try{clearTimeout()}catch{return!1}return!0},C=(t={})=>{let{parentOrigin:n=`*`,methods:r={},timeout:s,debug:c=!1}=t,l=o(c),u=a(`Child`,l),{destroy:d,onDestroy:f}=u,p=x(n,_(r),u,l),m=()=>{l(`Child: Handshake - Sending SYN`);let t={penpal:e.Syn},r=n instanceof RegExp?`*`:n;window.parent.postMessage(t,r)};return{promise:new Promise((t,n)=>{let r=b(s,d),a=n=>{if(S()&&!(n.source!==parent||!n.data)&&n.data.penpal===e.SynAck){let e=p(n);e&&(window.removeEventListener(i.Message,a),r(),t(e))}};window.addEventListener(i.Message,a),m(),f(e=>{window.removeEventListener(i.Message,a),e&&n(e)})}),destroy(){d()}}},w={contextMenu:[]},T=null;function E(){try{return window.self!==window.top}catch{return!0}}function D(){return T||(E()||console.warn(`[GemiGo SDK] Not running in iframe. SDK calls will verify connection forever.`),T=C({methods:{onContextMenuEvent(e){w.contextMenu.forEach(t=>{try{t(e)}catch(e){console.error(`[GemiGo SDK] Error in contextMenu handler:`,e)}})}}}).promise,T)}return D().catch(e=>{console.debug(`[GemiGo SDK] Auto-connect waiting...`,e)}),{getPageInfo:()=>D().then(e=>e.getPageInfo()),notify:(e,t)=>D().then(n=>n.notify({title:e,message:t})),extension:{getPageHTML:()=>D().then(e=>e.getPageHTML()),getPageText:()=>D().then(e=>e.getPageText()),getSelection:()=>D().then(e=>e.getSelection()),highlight:(e,t)=>D().then(n=>n.highlight(e,t)),extractArticle:()=>D().then(e=>e.extractArticle()),captureVisible:()=>D().then(e=>e.captureVisible()),getContextMenuEvent:()=>D().then(e=>e.getContextMenuEvent()),onContextMenu(e){D(),w.contextMenu.push(e)}}}});
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.gemigo=t())})(this,function(){var e;(function(e){e.Call=`call`,e.Reply=`reply`,e.Syn=`syn`,e.SynAck=`synAck`,e.Ack=`ack`})(e||={});var t;(function(e){e.Fulfilled=`fulfilled`,e.Rejected=`rejected`})(t||={});var n;(function(e){e.ConnectionDestroyed=`ConnectionDestroyed`,e.ConnectionTimeout=`ConnectionTimeout`,e.NoIframeSrc=`NoIframeSrc`})(n||={});var r;(function(e){e.DataCloneError=`DataCloneError`})(r||={});var i;(function(e){e.Message=`message`})(i||={});var a=(e,t)=>{let n=[],r=!1;return{destroy(i){r||(r=!0,t(`${e}: Destroying connection`),n.forEach(e=>{e(i)}))},onDestroy(e){r?e():n.push(e)}}},o=e=>(...t)=>{e&&console.log(`[Penpal]`,...t)};let s=({name:e,message:t,stack:n})=>({name:e,message:t,stack:n}),c=e=>{let t=Error();return Object.keys(e).forEach(n=>t[n]=e[n]),t};var l=(n,a,o)=>{let{localName:c,local:l,remote:u,originForSending:d,originForReceiving:f}=n,p=!1,m=n=>{if(n.source!==u||n.data.penpal!==e.Call)return;if(f!==`*`&&n.origin!==f){o(`${c} received message from origin ${n.origin} which did not match expected origin ${f}`);return}let{methodName:i,args:l,id:m}=n.data;o(`${c}: Received ${i}() call`);let h=n=>a=>{if(o(`${c}: Sending ${i}() reply`),p){o(`${c}: Unable to send ${i}() reply due to destroyed connection`);return}let l={penpal:e.Reply,id:m,resolution:n,returnValue:a};n===t.Rejected&&a instanceof Error&&(l.returnValue=s(a),l.returnValueIsError=!0);try{u.postMessage(l,d)}catch(n){if(n.name===r.DataCloneError){let r={penpal:e.Reply,id:m,resolution:t.Rejected,returnValue:s(n),returnValueIsError:!0};u.postMessage(r,d)}throw n}};new Promise(e=>e(a[i].apply(a,l))).then(h(t.Fulfilled),h(t.Rejected))};return l.addEventListener(i.Message,m),()=>{p=!0,l.removeEventListener(i.Message,m)}},u=0,d=()=>++u,f=`.`,p=e=>e?e.split(f):[],m=e=>e.join(f),h=(e,t)=>{let n=p(t||``);return n.push(e),m(n)};let g=(e,t,n)=>{let r=p(t);return r.reduce((e,t,i)=>(e[t]===void 0&&(e[t]={}),i===r.length-1&&(e[t]=n),e[t]),e),e},_=(e,t)=>{let n={};return Object.keys(e).forEach(r=>{let i=e[r],a=h(r,t);typeof i==`object`&&Object.assign(n,_(i,a)),typeof i==`function`&&(n[a]=i)}),n},v=e=>{let t={};for(let n in e)g(t,n,e[n]);return t};var y=(r,a,o,s,l)=>{let{localName:u,local:f,remote:p,originForSending:m,originForReceiving:h}=a,g=!1;l(`${u}: Connecting call sender`);let _=r=>(...a)=>{l(`${u}: Sending ${r}() call`);let o;try{p.closed&&(o=!0)}catch{o=!0}if(o&&s(),g){let e=Error(`Unable to send ${r}() call due to destroyed connection`);throw e.code=n.ConnectionDestroyed,e}return new Promise((n,o)=>{let s=d(),g=a=>{if(a.source!==p||a.data.penpal!==e.Reply||a.data.id!==s)return;if(h!==`*`&&a.origin!==h){l(`${u} received message from origin ${a.origin} which did not match expected origin ${h}`);return}let d=a.data;l(`${u}: Received ${r}() reply`),f.removeEventListener(i.Message,g);let m=d.returnValue;d.returnValueIsError&&(m=c(m)),(d.resolution===t.Fulfilled?n:o)(m)};f.addEventListener(i.Message,g);let _={penpal:e.Call,id:s,methodName:r,args:a};p.postMessage(_,m)})},y=o.reduce((e,t)=>(e[t]=_(t),e),{});return Object.assign(r,v(y)),()=>{g=!0}},b=(e,t)=>{let r;return e!==void 0&&(r=window.setTimeout(()=>{let r=Error(`Connection timed out after ${e}ms`);r.code=n.ConnectionTimeout,t(r)},e)),()=>{clearTimeout(r)}},x=(t,n,r,i)=>{let{destroy:a,onDestroy:o}=r;return r=>{if(!(t instanceof RegExp?t.test(r.origin):t===`*`||t===r.origin)){i(`Child: Handshake - Received SYN-ACK from origin ${r.origin} which did not match expected origin ${t}`);return}i(`Child: Handshake - Received SYN-ACK, responding with ACK`);let s=r.origin===`null`?`*`:r.origin,c={penpal:e.Ack,methodNames:Object.keys(n)};window.parent.postMessage(c,s);let u={localName:`Child`,local:window,remote:window.parent,originForSending:s,originForReceiving:r.origin};o(l(u,n,i));let d={};return o(y(d,u,r.data.methodNames,a,i)),d}},S=()=>{try{clearTimeout()}catch{return!1}return!0},C=(t={})=>{let{parentOrigin:n=`*`,methods:r={},timeout:s,debug:c=!1}=t,l=o(c),u=a(`Child`,l),{destroy:d,onDestroy:f}=u,p=x(n,_(r),u,l),m=()=>{l(`Child: Handshake - Sending SYN`);let t={penpal:e.Syn},r=n instanceof RegExp?`*`:n;window.parent.postMessage(t,r)};return{promise:new Promise((t,n)=>{let r=b(s,d),a=n=>{if(S()&&!(n.source!==parent||!n.data)&&n.data.penpal===e.SynAck){let e=p(n);e&&(window.removeEventListener(i.Message,a),r(),t(e))}};window.addEventListener(i.Message,a),m(),f(e=>{window.removeEventListener(i.Message,a),e&&n(e)})}),destroy(){d()}}},w=null;function T(){try{return window.self!==window.top}catch{return!0}}function E(e){if(w)return w;T()||console.warn(`[GemiGo SDK] Not running in iframe. SDK calls will not work.`);let t={};return e&&Object.assign(t,e),w=C({methods:t}).promise,w}function D(e){E(e).catch(e=>{console.debug(`[GemiGo SDK] Auto-connect waiting...`,e)})}var O={contextMenu:[],selectionChange:[]};function k(e,t){return O[e].push(t),()=>{let n=O[e].indexOf(t);n>-1&&O[e].splice(n,1)}}function A(e,t){O[e].forEach(n=>{try{n(t)}catch(t){console.error(`[GemiGo SDK] Error in ${e} handler:`,t)}})}function j(){return{onContextMenuEvent(e){A(`contextMenu`,e)},onSelectionChange(e,t){O.selectionChange.forEach(n=>{try{n(e,t)}catch(e){console.error(`[GemiGo SDK] Error in selectionChange handler:`,e)}})}}}let M={getPageInfo:()=>E().then(e=>e.getPageInfo()),getPageHTML:()=>E().then(e=>e.getPageHTML()),getPageText:()=>E().then(e=>e.getPageText()),getSelection:()=>E().then(e=>e.getSelection()),extractArticle:()=>E().then(e=>e.extractArticle()),highlight:(e,t)=>E().then(n=>n.highlight(e,t)),removeHighlight:e=>E().then(t=>t.removeHighlight(e)),insertWidget:(e,t=`bottom-right`)=>E().then(n=>n.insertWidget({html:e,position:t})),updateWidget:(e,t)=>E().then(n=>n.updateWidget(e,t)),removeWidget:e=>E().then(t=>t.removeWidget(e)),injectCSS:e=>E().then(t=>t.injectCSS(e)),removeCSS:e=>E().then(t=>t.removeCSS(e)),extractLinks:()=>E().then(e=>e.extractLinks()),extractImages:()=>E().then(e=>e.extractImages()),queryElement:(e,t)=>E().then(n=>n.queryElement(e,t)),captureVisible:()=>E().then(e=>e.captureVisible()),getContextMenuEvent:()=>E().then(e=>e.getContextMenuEvent()),onContextMenu:e=>(E(),k(`contextMenu`,e)),onSelectionChange:e=>(E(),k(`selectionChange`,e))};function N(){return{notify:(e,t)=>E().then(n=>n.notify({title:e,message:t})),extension:M}}var P=N();return D(j()),P});
package/dist/index.d.ts CHANGED
@@ -6,10 +6,9 @@
6
6
  * Usage (CDN):
7
7
  * <script src="https://unpkg.com/@gemigo/extension-sdk/dist/gemigo-extension-sdk.umd.js"></script>
8
8
  * <script>
9
- * // No connect() needed - just use the global gemigo object directly
10
9
  * gemigo.getPageInfo().then(console.log);
11
10
  *
12
- * gemigo.on('contextMenu', (event) => {
11
+ * gemigo.extension.onContextMenu((event) => {
13
12
  * console.log('Context menu clicked:', event);
14
13
  * });
15
14
  * </script>
@@ -19,64 +18,6 @@
19
18
  *
20
19
  * const pageInfo = await gemigo.getPageInfo();
21
20
  */
22
- type ContextMenuEventHandler = (event: {
23
- menuId: string;
24
- selectionText?: string;
25
- pageUrl?: string;
26
- }) => void;
27
- export interface GemigoSDK {
28
- /** Get current page information */
29
- getPageInfo(): Promise<{
30
- url: string;
31
- title: string;
32
- favIconUrl?: string;
33
- }>;
34
- /** Send system notification */
35
- notify(title: string, message: string): Promise<{
36
- success: boolean;
37
- }>;
38
- /** Extension-specific APIs */
39
- extension: {
40
- /** Get full page HTML */
41
- getPageHTML(): Promise<string>;
42
- /** Get page text content */
43
- getPageText(): Promise<string>;
44
- /** Get selected text on page */
45
- getSelection(): Promise<string>;
46
- /** Highlight elements matching selector */
47
- highlight(selector: string, color?: string): Promise<{
48
- success: boolean;
49
- count?: number;
50
- }>;
51
- /** Extract article content from page */
52
- extractArticle(): Promise<{
53
- success: boolean;
54
- title?: string;
55
- content?: string;
56
- excerpt?: string;
57
- url?: string;
58
- }>;
59
- /** Capture visible tab screenshot */
60
- captureVisible(): Promise<{
61
- success: boolean;
62
- dataUrl?: string;
63
- error?: string;
64
- }>;
65
- /** Get pending context menu event (if any) */
66
- getContextMenuEvent(): Promise<{
67
- success: boolean;
68
- event?: {
69
- menuId: string;
70
- selectionText?: string;
71
- pageUrl?: string;
72
- };
73
- }>;
74
- /** Register context menu event handler */
75
- onContextMenu(handler: ContextMenuEventHandler): void;
76
- };
77
- }
78
- /**
79
- * Proxy handler to auto-connect on method calls
80
- */
81
- declare const sdkInstance: GemigoSDK;
82
- export default sdkInstance;
21
+ export * from './types';
22
+ export { default } from './sdk';
23
+ export type { SDKInstance } from './sdk';
package/dist/sdk.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { extensionAPI } from './apis';
2
+ /**
3
+ * SDK interface for current implementation
4
+ * (Subset of full GemigoSDK - only extension platform is implemented)
5
+ */
6
+ export interface SDKInstance {
7
+ /** Send system notification */
8
+ notify(title: string, message: string): Promise<{
9
+ success: boolean;
10
+ }>;
11
+ /** Extension-specific APIs */
12
+ extension: typeof extensionAPI;
13
+ }
14
+ declare const sdk: SDKInstance;
15
+ export default sdk;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * AI API types
3
+ */
4
+ /** Chat message */
5
+ export interface ChatMessage {
6
+ role: 'user' | 'assistant' | 'system';
7
+ content: string;
8
+ }
9
+ /** Chat response */
10
+ export interface ChatResponse {
11
+ role: 'assistant';
12
+ content: string;
13
+ }
14
+ /** Translation options */
15
+ export interface TranslateOptions {
16
+ /** Source language (auto-detect if not specified) */
17
+ from?: string;
18
+ /** Target language (required) */
19
+ to: string;
20
+ }
21
+ /** Translation result */
22
+ export interface TranslateResult {
23
+ text: string;
24
+ from: string;
25
+ to: string;
26
+ }
27
+ /** AI API for cloud model integration */
28
+ export interface AIAPI {
29
+ /**
30
+ * Multi-turn conversation
31
+ * @param messages - Conversation history
32
+ * @returns Assistant response
33
+ */
34
+ chat(messages: ChatMessage[]): Promise<ChatResponse>;
35
+ /**
36
+ * Summarize text content
37
+ * @param text - Text to summarize
38
+ * @returns Summary text
39
+ */
40
+ summarize(text: string): Promise<string>;
41
+ /**
42
+ * Translate text
43
+ * @param text - Text to translate
44
+ * @param options - Translation options
45
+ * @returns Translation result
46
+ */
47
+ translate(text: string, options: TranslateOptions): Promise<TranslateResult>;
48
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Clipboard API types
3
+ */
4
+ /** Clipboard content */
5
+ export interface ClipboardContent {
6
+ text?: string;
7
+ image?: Blob;
8
+ }
9
+ /** Clipboard change callback */
10
+ export type ClipboardChangeCallback = (content: ClipboardContent) => void;
11
+ /** Clipboard API */
12
+ export interface ClipboardAPI {
13
+ /**
14
+ * Read text from clipboard
15
+ * @returns Clipboard text content
16
+ */
17
+ readText(): Promise<string>;
18
+ /**
19
+ * Write text to clipboard
20
+ * @param text - Text to write
21
+ */
22
+ writeText(text: string): Promise<void>;
23
+ /**
24
+ * Read image from clipboard
25
+ * @returns Image blob or null
26
+ */
27
+ readImage(): Promise<Blob | null>;
28
+ /**
29
+ * Write image to clipboard
30
+ * @param blob - Image blob to write
31
+ */
32
+ writeImage(blob: Blob): Promise<void>;
33
+ /**
34
+ * Listen for clipboard content changes (desktop only)
35
+ * @param callback - Change callback
36
+ * @returns Unsubscribe function
37
+ */
38
+ onChange(callback: ClipboardChangeCallback): () => void;
39
+ }