@stringpush/sdk 0.2.0 → 0.3.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.
@@ -1,8 +1,16 @@
1
1
  import {
2
+ adminOriginFromSignInUrl,
3
+ buildOverlayGrantUrl,
4
+ clearEditToken,
2
5
  decodeEditTokenClaims,
6
+ listenForOverlayGrant,
7
+ newRequestId,
8
+ openOverlayGrantPopup,
3
9
  persistEditToken,
4
- readEditTokenFromStorage
5
- } from "./chunk-X3WTVBZ6.mjs";
10
+ readEditTokenFromStorage,
11
+ redirectToOverlayGrant,
12
+ watchPopupClosed
13
+ } from "./chunk-YYR2BH3L.mjs";
6
14
 
7
15
  // src/edit-launcher/constants.ts
8
16
  var LAUNCHER_ROOT_ID = "translation-edit-launcher-root";
@@ -84,6 +92,7 @@ function mountEditLauncher(context) {
84
92
  overflow: hidden;
85
93
  text-overflow: ellipsis;
86
94
  }
95
+ .edit-bar__hint.is-error { color: #b91c1c; }
87
96
  .fab {
88
97
  flex-shrink: 0;
89
98
  border: none;
@@ -99,32 +108,6 @@ function mountEditLauncher(context) {
99
108
  }
100
109
  .fab:hover { background: #3730a3; }
101
110
  .fab.is-editing { background: #0f766e; box-shadow: 0 2px 8px rgba(15, 118, 110, 0.28); }
102
- .panel {
103
- position: fixed;
104
- bottom: 72px;
105
- left: 20px;
106
- z-index: 2147483645;
107
- width: min(300px, calc(100vw - 40px));
108
- padding: 14px;
109
- border-radius: 12px;
110
- background: #fff;
111
- color: #0f172a;
112
- border: 1px solid #e2e8f0;
113
- box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
114
- }
115
- .panel p { margin: 0 0 10px; line-height: 1.45; font-size: 13px; color: #475569; }
116
- .panel button {
117
- width: 100%;
118
- border: none;
119
- border-radius: 8px;
120
- padding: 9px 12px;
121
- cursor: pointer;
122
- font: inherit;
123
- font-weight: 600;
124
- }
125
- .panel .primary { background: #4338ca; color: #fff; }
126
- .panel .primary:disabled { opacity: 0.6; cursor: wait; }
127
- .panel .error { margin-top: 8px; color: #b91c1c; font-size: 12px; }
128
111
  .marker { display: none; }
129
112
  `;
130
113
  const wrap = document.createElement("div");
@@ -149,95 +132,156 @@ function mountEditLauncher(context) {
149
132
  fab.className = "fab";
150
133
  fab.textContent = "Translate";
151
134
  bar.append(left, fab);
152
- const panel = document.createElement("div");
153
- panel.className = "panel";
154
- panel.hidden = true;
155
- panel.innerHTML = '<p>Sign in to edit copy on this page.</p><button type="button" class="primary">Sign in</button><p class="error" hidden></p>';
156
- const signInBtn = panel.querySelector(".primary");
157
- const errorEl = panel.querySelector(".error");
135
+ wrap.append(marker, bar);
136
+ shadow.append(style, wrap);
137
+ document.body.append(host);
138
+ const signInUrl = context.launcherOptions.signInUrl;
139
+ const signInMode = context.launcherOptions.signInMode ?? "popup";
140
+ let currentRequestId = null;
141
+ let grantPopup = null;
142
+ let stopPopupWatch = null;
143
+ const defaultHint = "Translate copy on this page";
158
144
  const syncFabLabel = () => {
159
145
  if (context.isEditModeActive()) {
160
146
  fab.textContent = "Stop editing";
161
147
  fab.classList.add("is-editing");
162
148
  pill.hidden = false;
163
149
  hint.hidden = true;
164
- panel.hidden = true;
150
+ hint.classList.remove("is-error");
165
151
  } else {
166
152
  fab.textContent = "Translate";
167
153
  fab.classList.remove("is-editing");
168
154
  pill.hidden = true;
169
155
  hint.hidden = false;
156
+ if (!hint.classList.contains("is-error")) {
157
+ hint.textContent = defaultHint;
158
+ }
170
159
  }
171
160
  };
172
- const showError = (message) => {
173
- errorEl.hidden = false;
174
- errorEl.textContent = message;
161
+ const clearGrantAttempt = () => {
162
+ currentRequestId = null;
163
+ stopPopupWatch?.();
164
+ stopPopupWatch = null;
165
+ grantPopup = null;
175
166
  };
176
- const clearError = () => {
177
- errorEl.hidden = true;
178
- errorEl.textContent = "";
167
+ const showInlineError = (message) => {
168
+ hint.hidden = false;
169
+ hint.textContent = message;
170
+ hint.classList.add("is-error");
171
+ };
172
+ const clearInlineStatus = () => {
173
+ hint.classList.remove("is-error");
174
+ hint.textContent = defaultHint;
175
+ };
176
+ const openGrantPopup = () => {
177
+ if (!signInUrl) {
178
+ showInlineError("Edit launcher auth is not configured.");
179
+ return;
180
+ }
181
+ if (grantPopup && !grantPopup.closed) {
182
+ grantPopup.focus();
183
+ hint.hidden = false;
184
+ hint.textContent = "Complete sign-in in the popup.";
185
+ return;
186
+ }
187
+ const returnUrl = window.location.href.split("#")[0] ?? window.location.href;
188
+ currentRequestId = newRequestId();
189
+ const grantUrl = buildOverlayGrantUrl(signInUrl, {
190
+ returnUrl,
191
+ applicationId: context.initOptions.applicationId,
192
+ environment: context.initOptions.environment,
193
+ mode: signInMode,
194
+ requestId: currentRequestId
195
+ });
196
+ if (signInMode === "redirect") {
197
+ redirectToOverlayGrant(grantUrl);
198
+ return;
199
+ }
200
+ const popup = openOverlayGrantPopup(grantUrl);
201
+ if (!popup) {
202
+ redirectToOverlayGrant(grantUrl);
203
+ return;
204
+ }
205
+ grantPopup = popup;
206
+ hint.hidden = false;
207
+ hint.textContent = "Complete sign-in in the popup.";
208
+ stopPopupWatch = watchPopupClosed(popup, () => {
209
+ if (currentRequestId) {
210
+ clearGrantAttempt();
211
+ if (!context.isEditModeActive()) {
212
+ showInlineError("Sign-in window closed before finishing.");
213
+ }
214
+ }
215
+ });
179
216
  };
217
+ let stopOverlayGrantListener = null;
218
+ if (signInUrl && signInMode === "popup") {
219
+ const adminOrigin = adminOriginFromSignInUrl(signInUrl);
220
+ stopOverlayGrantListener = listenForOverlayGrant(
221
+ adminOrigin,
222
+ () => currentRequestId,
223
+ {
224
+ onGranted: (token) => {
225
+ void (async () => {
226
+ clearInlineStatus();
227
+ persistEditToken(token);
228
+ try {
229
+ await context.enableEditMode();
230
+ clearGrantAttempt();
231
+ syncFabLabel();
232
+ } catch (err) {
233
+ clearEditToken();
234
+ showInlineError(err instanceof Error ? err.message : String(err));
235
+ openGrantPopup();
236
+ }
237
+ })();
238
+ },
239
+ onError: (message) => {
240
+ clearGrantAttempt();
241
+ showInlineError(message);
242
+ }
243
+ }
244
+ );
245
+ }
180
246
  fab.addEventListener("click", () => {
181
247
  void (async () => {
182
- clearError();
183
248
  if (context.isEditModeActive()) {
184
249
  context.disableEditMode();
250
+ clearInlineStatus();
185
251
  syncFabLabel();
186
252
  return;
187
253
  }
254
+ clearInlineStatus();
188
255
  const existing = storedEditToken(context.initOptions);
189
256
  if (existing) {
190
257
  try {
191
258
  await context.enableEditMode();
192
259
  syncFabLabel();
193
- } catch (err) {
194
- showError(err instanceof Error ? err.message : String(err));
260
+ return;
261
+ } catch {
262
+ clearEditToken();
195
263
  }
196
- return;
197
264
  }
198
- panel.hidden = false;
199
- })();
200
- });
201
- signInBtn.addEventListener("click", () => {
202
- void (async () => {
203
- clearError();
204
- const { signInUrl, requestEditToken } = context.launcherOptions;
265
+ const { requestEditToken } = context.launcherOptions;
205
266
  if (requestEditToken) {
206
- signInBtn.disabled = true;
207
267
  try {
208
268
  const token = await requestEditToken();
209
269
  persistEditToken(token);
210
270
  await context.enableEditMode();
211
- panel.hidden = true;
212
271
  syncFabLabel();
213
272
  } catch (err) {
214
- showError(err instanceof Error ? err.message : String(err));
215
- } finally {
216
- signInBtn.disabled = false;
273
+ showInlineError(err instanceof Error ? err.message : String(err));
217
274
  }
218
275
  return;
219
276
  }
220
- if (signInUrl) {
221
- const params = new URLSearchParams();
222
- params.set(
223
- "returnUrl",
224
- window.location.href.split("#")[0] ?? window.location.href
225
- );
226
- params.set("applicationId", context.initOptions.applicationId);
227
- params.set("environment", context.initOptions.environment);
228
- const separator = signInUrl.includes("?") ? "&" : "?";
229
- window.location.href = `${signInUrl}${separator}${params.toString()}`;
230
- return;
231
- }
232
- showError("Edit launcher auth is not configured.");
277
+ openGrantPopup();
233
278
  })();
234
279
  });
235
- wrap.append(marker, bar, panel);
236
- shadow.append(style, wrap);
237
- document.body.append(host);
238
280
  syncFabLabel();
239
281
  return {
240
282
  destroy: () => {
283
+ stopOverlayGrantListener?.();
284
+ clearGrantAttempt();
241
285
  host.remove();
242
286
  }
243
287
  };
@@ -247,4 +291,4 @@ export {
247
291
  LAUNCHER_ROOT_ID,
248
292
  mountEditLauncher
249
293
  };
250
- //# sourceMappingURL=edit-launcher-PTZ5BIO2.mjs.map
294
+ //# sourceMappingURL=edit-launcher-YJB2FBO7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/edit-launcher/constants.ts","../src/edit-launcher/index.ts"],"sourcesContent":["/**\n * DOM and bundle-split markers for the lazy-loaded edit launcher chunk (M2-SDK-05).\n *\n * Intent: stable ids/markers keep overlay UI out of the default SDK entry until edit mode activates.\n */\nexport const LAUNCHER_ROOT_ID = \"translation-edit-launcher-root\";\n\n/** Marker string used in bundle-split tests to keep launcher out of the main entry. */\nexport const LAUNCHER_CHUNK_MARKER = \"translation-edit-launcher-chunk-v1\";\n","/**\n * Lazy-loaded bottom bar launcher for staging overlay (M2-SDK-05, UI-09).\n *\n * Intent: `?translation_edit=1` arms the UI only; Translate opens popup grant or enables from stored token.\n */\nimport {\n clearEditToken,\n persistEditToken,\n readEditTokenFromStorage,\n} from \"../edit-token.js\";\nimport { decodeEditTokenClaims } from \"../edit-token-decode.js\";\nimport {\n adminOriginFromSignInUrl,\n buildOverlayGrantUrl,\n listenForOverlayGrant,\n newRequestId,\n openOverlayGrantPopup,\n redirectToOverlayGrant,\n watchPopupClosed,\n type OverlayGrantMode,\n} from \"./sign-in-popup.js\";\nimport type { EditLauncherOptions, InitOptions } from \"../types.js\";\nimport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport type EditLauncherMountContext = {\n initOptions: InitOptions;\n launcherOptions: EditLauncherOptions;\n isEditModeActive: () => boolean;\n enableEditMode: () => Promise<void>;\n disableEditMode: () => void;\n};\n\nexport type EditLauncherHandle = {\n destroy: () => void;\n};\n\nfunction tokenMatchesInit(token: string, options: InitOptions): boolean {\n const claims = decodeEditTokenClaims(token);\n if (!claims) {\n return false;\n }\n return (\n claims.applicationId === options.applicationId &&\n claims.environmentName === options.environment\n );\n}\n\nfunction storedEditToken(options: InitOptions): string | null {\n const token = readEditTokenFromStorage();\n if (!token || !tokenMatchesInit(token, options)) {\n return null;\n }\n return token;\n}\n\n/**\n * Mounts bottom edit bar in a shadow root; Translate is the single auth gesture.\n */\nexport function mountEditLauncher(context: EditLauncherMountContext): EditLauncherHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n document.getElementById(LAUNCHER_ROOT_ID)?.remove();\n\n const host = document.createElement(\"div\");\n host.id = LAUNCHER_ROOT_ID;\n host.setAttribute(\"data-translation-edit-launcher\", \"true\");\n\n const shadow = host.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = `\n :host { all: initial; }\n .wrap { font-family: system-ui, -apple-system, Segoe UI, sans-serif; font-size: 14px; }\n .edit-bar {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2147483645;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 20px;\n background: #fff;\n border-top: 1px solid #e2e8f0;\n box-shadow: 0 -4px 24px rgba(15, 23, 42, 0.08);\n }\n .edit-bar__left {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n }\n .edit-mode-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: 999px;\n background: #d1fae5;\n color: #047857;\n font-size: 12px;\n font-weight: 600;\n white-space: nowrap;\n }\n .edit-mode-pill::before {\n content: \"\";\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #10b981;\n }\n .edit-bar__hint {\n color: #64748b;\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .edit-bar__hint.is-error { color: #b91c1c; }\n .fab {\n flex-shrink: 0;\n border: none;\n border-radius: 8px;\n padding: 10px 18px;\n background: #4338ca;\n color: #fff;\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 600;\n box-shadow: 0 2px 8px rgba(67, 56, 202, 0.28);\n }\n .fab:hover { background: #3730a3; }\n .fab.is-editing { background: #0f766e; box-shadow: 0 2px 8px rgba(15, 118, 110, 0.28); }\n .marker { display: none; }\n `;\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"wrap\";\n\n const marker = document.createElement(\"span\");\n marker.className = \"marker\";\n marker.textContent = LAUNCHER_CHUNK_MARKER;\n\n const bar = document.createElement(\"div\");\n bar.className = \"edit-bar\";\n\n const left = document.createElement(\"div\");\n left.className = \"edit-bar__left\";\n\n const pill = document.createElement(\"span\");\n pill.className = \"edit-mode-pill\";\n pill.textContent = \"Edit mode active\";\n pill.hidden = true;\n\n const hint = document.createElement(\"span\");\n hint.className = \"edit-bar__hint\";\n hint.textContent = \"Translate copy on this page\";\n\n left.append(pill, hint);\n\n const fab = document.createElement(\"button\");\n fab.type = \"button\";\n fab.className = \"fab\";\n fab.textContent = \"Translate\";\n\n bar.append(left, fab);\n wrap.append(marker, bar);\n shadow.append(style, wrap);\n document.body.append(host);\n\n const signInUrl = context.launcherOptions.signInUrl;\n const signInMode: OverlayGrantMode = context.launcherOptions.signInMode ?? \"popup\";\n\n let currentRequestId: string | null = null;\n let grantPopup: Window | null = null;\n let stopPopupWatch: (() => void) | null = null;\n\n const defaultHint = \"Translate copy on this page\";\n\n const syncFabLabel = (): void => {\n if (context.isEditModeActive()) {\n fab.textContent = \"Stop editing\";\n fab.classList.add(\"is-editing\");\n pill.hidden = false;\n hint.hidden = true;\n hint.classList.remove(\"is-error\");\n } else {\n fab.textContent = \"Translate\";\n fab.classList.remove(\"is-editing\");\n pill.hidden = true;\n hint.hidden = false;\n if (!hint.classList.contains(\"is-error\")) {\n hint.textContent = defaultHint;\n }\n }\n };\n\n const clearGrantAttempt = (): void => {\n currentRequestId = null;\n stopPopupWatch?.();\n stopPopupWatch = null;\n grantPopup = null;\n };\n\n const showInlineError = (message: string): void => {\n hint.hidden = false;\n hint.textContent = message;\n hint.classList.add(\"is-error\");\n };\n\n const clearInlineStatus = (): void => {\n hint.classList.remove(\"is-error\");\n hint.textContent = defaultHint;\n };\n\n const openGrantPopup = (): void => {\n if (!signInUrl) {\n showInlineError(\"Edit launcher auth is not configured.\");\n return;\n }\n\n if (grantPopup && !grantPopup.closed) {\n grantPopup.focus();\n hint.hidden = false;\n hint.textContent = \"Complete sign-in in the popup.\";\n return;\n }\n\n const returnUrl = window.location.href.split(\"#\")[0] ?? window.location.href;\n currentRequestId = newRequestId();\n const grantUrl = buildOverlayGrantUrl(signInUrl, {\n returnUrl,\n applicationId: context.initOptions.applicationId,\n environment: context.initOptions.environment,\n mode: signInMode,\n requestId: currentRequestId,\n });\n\n if (signInMode === \"redirect\") {\n redirectToOverlayGrant(grantUrl);\n return;\n }\n\n const popup = openOverlayGrantPopup(grantUrl);\n if (!popup) {\n redirectToOverlayGrant(grantUrl);\n return;\n }\n\n grantPopup = popup;\n hint.hidden = false;\n hint.textContent = \"Complete sign-in in the popup.\";\n stopPopupWatch = watchPopupClosed(popup, () => {\n if (currentRequestId) {\n clearGrantAttempt();\n if (!context.isEditModeActive()) {\n showInlineError(\"Sign-in window closed before finishing.\");\n }\n }\n });\n };\n\n let stopOverlayGrantListener: (() => void) | null = null;\n if (signInUrl && signInMode === \"popup\") {\n const adminOrigin = adminOriginFromSignInUrl(signInUrl);\n stopOverlayGrantListener = listenForOverlayGrant(\n adminOrigin,\n () => currentRequestId,\n {\n onGranted: (token) => {\n void (async () => {\n clearInlineStatus();\n persistEditToken(token);\n try {\n await context.enableEditMode();\n clearGrantAttempt();\n syncFabLabel();\n } catch (err) {\n clearEditToken();\n showInlineError(err instanceof Error ? err.message : String(err));\n openGrantPopup();\n }\n })();\n },\n onError: (message) => {\n clearGrantAttempt();\n showInlineError(message);\n },\n },\n );\n }\n\n fab.addEventListener(\"click\", () => {\n void (async () => {\n if (context.isEditModeActive()) {\n context.disableEditMode();\n clearInlineStatus();\n syncFabLabel();\n return;\n }\n\n clearInlineStatus();\n\n const existing = storedEditToken(context.initOptions);\n if (existing) {\n try {\n await context.enableEditMode();\n syncFabLabel();\n return;\n } catch {\n clearEditToken();\n }\n }\n\n const { requestEditToken } = context.launcherOptions;\n if (requestEditToken) {\n try {\n const token = await requestEditToken();\n persistEditToken(token);\n await context.enableEditMode();\n syncFabLabel();\n } catch (err) {\n showInlineError(err instanceof Error ? err.message : String(err));\n }\n return;\n }\n\n openGrantPopup();\n })();\n });\n\n syncFabLabel();\n\n return {\n destroy: () => {\n stopOverlayGrantListener?.();\n clearGrantAttempt();\n host.remove();\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,15 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkFROJCNV7umdcjs = require('./chunk-FROJCNV7.umd.cjs');
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+ var _chunkD2H2PTN2umdcjs = require('./chunk-D2H2PTN2.umd.cjs');
6
14
 
7
15
  // src/edit-launcher/constants.ts
8
16
  var LAUNCHER_ROOT_ID = "translation-edit-launcher-root";
@@ -10,14 +18,14 @@ var LAUNCHER_CHUNK_MARKER = "translation-edit-launcher-chunk-v1";
10
18
 
11
19
  // src/edit-launcher/index.ts
12
20
  function tokenMatchesInit(token, options) {
13
- const claims = _chunkFROJCNV7umdcjs.decodeEditTokenClaims.call(void 0, token);
21
+ const claims = _chunkD2H2PTN2umdcjs.decodeEditTokenClaims.call(void 0, token);
14
22
  if (!claims) {
15
23
  return false;
16
24
  }
17
25
  return claims.applicationId === options.applicationId && claims.environmentName === options.environment;
18
26
  }
19
27
  function storedEditToken(options) {
20
- const token = _chunkFROJCNV7umdcjs.readEditTokenFromStorage.call(void 0, );
28
+ const token = _chunkD2H2PTN2umdcjs.readEditTokenFromStorage.call(void 0, );
21
29
  if (!token || !tokenMatchesInit(token, options)) {
22
30
  return null;
23
31
  }
@@ -84,6 +92,7 @@ function mountEditLauncher(context) {
84
92
  overflow: hidden;
85
93
  text-overflow: ellipsis;
86
94
  }
95
+ .edit-bar__hint.is-error { color: #b91c1c; }
87
96
  .fab {
88
97
  flex-shrink: 0;
89
98
  border: none;
@@ -99,32 +108,6 @@ function mountEditLauncher(context) {
99
108
  }
100
109
  .fab:hover { background: #3730a3; }
101
110
  .fab.is-editing { background: #0f766e; box-shadow: 0 2px 8px rgba(15, 118, 110, 0.28); }
102
- .panel {
103
- position: fixed;
104
- bottom: 72px;
105
- left: 20px;
106
- z-index: 2147483645;
107
- width: min(300px, calc(100vw - 40px));
108
- padding: 14px;
109
- border-radius: 12px;
110
- background: #fff;
111
- color: #0f172a;
112
- border: 1px solid #e2e8f0;
113
- box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
114
- }
115
- .panel p { margin: 0 0 10px; line-height: 1.45; font-size: 13px; color: #475569; }
116
- .panel button {
117
- width: 100%;
118
- border: none;
119
- border-radius: 8px;
120
- padding: 9px 12px;
121
- cursor: pointer;
122
- font: inherit;
123
- font-weight: 600;
124
- }
125
- .panel .primary { background: #4338ca; color: #fff; }
126
- .panel .primary:disabled { opacity: 0.6; cursor: wait; }
127
- .panel .error { margin-top: 8px; color: #b91c1c; font-size: 12px; }
128
111
  .marker { display: none; }
129
112
  `;
130
113
  const wrap = document.createElement("div");
@@ -149,95 +132,156 @@ function mountEditLauncher(context) {
149
132
  fab.className = "fab";
150
133
  fab.textContent = "Translate";
151
134
  bar.append(left, fab);
152
- const panel = document.createElement("div");
153
- panel.className = "panel";
154
- panel.hidden = true;
155
- panel.innerHTML = '<p>Sign in to edit copy on this page.</p><button type="button" class="primary">Sign in</button><p class="error" hidden></p>';
156
- const signInBtn = panel.querySelector(".primary");
157
- const errorEl = panel.querySelector(".error");
135
+ wrap.append(marker, bar);
136
+ shadow.append(style, wrap);
137
+ document.body.append(host);
138
+ const signInUrl = context.launcherOptions.signInUrl;
139
+ const signInMode = _nullishCoalesce(context.launcherOptions.signInMode, () => ( "popup"));
140
+ let currentRequestId = null;
141
+ let grantPopup = null;
142
+ let stopPopupWatch = null;
143
+ const defaultHint = "Translate copy on this page";
158
144
  const syncFabLabel = () => {
159
145
  if (context.isEditModeActive()) {
160
146
  fab.textContent = "Stop editing";
161
147
  fab.classList.add("is-editing");
162
148
  pill.hidden = false;
163
149
  hint.hidden = true;
164
- panel.hidden = true;
150
+ hint.classList.remove("is-error");
165
151
  } else {
166
152
  fab.textContent = "Translate";
167
153
  fab.classList.remove("is-editing");
168
154
  pill.hidden = true;
169
155
  hint.hidden = false;
156
+ if (!hint.classList.contains("is-error")) {
157
+ hint.textContent = defaultHint;
158
+ }
170
159
  }
171
160
  };
172
- const showError = (message) => {
173
- errorEl.hidden = false;
174
- errorEl.textContent = message;
161
+ const clearGrantAttempt = () => {
162
+ currentRequestId = null;
163
+ _optionalChain([stopPopupWatch, 'optionalCall', _5 => _5()]);
164
+ stopPopupWatch = null;
165
+ grantPopup = null;
175
166
  };
176
- const clearError = () => {
177
- errorEl.hidden = true;
178
- errorEl.textContent = "";
167
+ const showInlineError = (message) => {
168
+ hint.hidden = false;
169
+ hint.textContent = message;
170
+ hint.classList.add("is-error");
179
171
  };
172
+ const clearInlineStatus = () => {
173
+ hint.classList.remove("is-error");
174
+ hint.textContent = defaultHint;
175
+ };
176
+ const openGrantPopup = () => {
177
+ if (!signInUrl) {
178
+ showInlineError("Edit launcher auth is not configured.");
179
+ return;
180
+ }
181
+ if (grantPopup && !grantPopup.closed) {
182
+ grantPopup.focus();
183
+ hint.hidden = false;
184
+ hint.textContent = "Complete sign-in in the popup.";
185
+ return;
186
+ }
187
+ const returnUrl = _nullishCoalesce(window.location.href.split("#")[0], () => ( window.location.href));
188
+ currentRequestId = _chunkD2H2PTN2umdcjs.newRequestId.call(void 0, );
189
+ const grantUrl = _chunkD2H2PTN2umdcjs.buildOverlayGrantUrl.call(void 0, signInUrl, {
190
+ returnUrl,
191
+ applicationId: context.initOptions.applicationId,
192
+ environment: context.initOptions.environment,
193
+ mode: signInMode,
194
+ requestId: currentRequestId
195
+ });
196
+ if (signInMode === "redirect") {
197
+ _chunkD2H2PTN2umdcjs.redirectToOverlayGrant.call(void 0, grantUrl);
198
+ return;
199
+ }
200
+ const popup = _chunkD2H2PTN2umdcjs.openOverlayGrantPopup.call(void 0, grantUrl);
201
+ if (!popup) {
202
+ _chunkD2H2PTN2umdcjs.redirectToOverlayGrant.call(void 0, grantUrl);
203
+ return;
204
+ }
205
+ grantPopup = popup;
206
+ hint.hidden = false;
207
+ hint.textContent = "Complete sign-in in the popup.";
208
+ stopPopupWatch = _chunkD2H2PTN2umdcjs.watchPopupClosed.call(void 0, popup, () => {
209
+ if (currentRequestId) {
210
+ clearGrantAttempt();
211
+ if (!context.isEditModeActive()) {
212
+ showInlineError("Sign-in window closed before finishing.");
213
+ }
214
+ }
215
+ });
216
+ };
217
+ let stopOverlayGrantListener = null;
218
+ if (signInUrl && signInMode === "popup") {
219
+ const adminOrigin = _chunkD2H2PTN2umdcjs.adminOriginFromSignInUrl.call(void 0, signInUrl);
220
+ stopOverlayGrantListener = _chunkD2H2PTN2umdcjs.listenForOverlayGrant.call(void 0,
221
+ adminOrigin,
222
+ () => currentRequestId,
223
+ {
224
+ onGranted: (token) => {
225
+ void (async () => {
226
+ clearInlineStatus();
227
+ _chunkD2H2PTN2umdcjs.persistEditToken.call(void 0, token);
228
+ try {
229
+ await context.enableEditMode();
230
+ clearGrantAttempt();
231
+ syncFabLabel();
232
+ } catch (err) {
233
+ _chunkD2H2PTN2umdcjs.clearEditToken.call(void 0, );
234
+ showInlineError(err instanceof Error ? err.message : String(err));
235
+ openGrantPopup();
236
+ }
237
+ })();
238
+ },
239
+ onError: (message) => {
240
+ clearGrantAttempt();
241
+ showInlineError(message);
242
+ }
243
+ }
244
+ );
245
+ }
180
246
  fab.addEventListener("click", () => {
181
247
  void (async () => {
182
- clearError();
183
248
  if (context.isEditModeActive()) {
184
249
  context.disableEditMode();
250
+ clearInlineStatus();
185
251
  syncFabLabel();
186
252
  return;
187
253
  }
254
+ clearInlineStatus();
188
255
  const existing = storedEditToken(context.initOptions);
189
256
  if (existing) {
190
257
  try {
191
258
  await context.enableEditMode();
192
259
  syncFabLabel();
193
- } catch (err) {
194
- showError(err instanceof Error ? err.message : String(err));
260
+ return;
261
+ } catch (e) {
262
+ _chunkD2H2PTN2umdcjs.clearEditToken.call(void 0, );
195
263
  }
196
- return;
197
264
  }
198
- panel.hidden = false;
199
- })();
200
- });
201
- signInBtn.addEventListener("click", () => {
202
- void (async () => {
203
- clearError();
204
- const { signInUrl, requestEditToken } = context.launcherOptions;
265
+ const { requestEditToken } = context.launcherOptions;
205
266
  if (requestEditToken) {
206
- signInBtn.disabled = true;
207
267
  try {
208
268
  const token = await requestEditToken();
209
- _chunkFROJCNV7umdcjs.persistEditToken.call(void 0, token);
269
+ _chunkD2H2PTN2umdcjs.persistEditToken.call(void 0, token);
210
270
  await context.enableEditMode();
211
- panel.hidden = true;
212
271
  syncFabLabel();
213
272
  } catch (err) {
214
- showError(err instanceof Error ? err.message : String(err));
215
- } finally {
216
- signInBtn.disabled = false;
273
+ showInlineError(err instanceof Error ? err.message : String(err));
217
274
  }
218
275
  return;
219
276
  }
220
- if (signInUrl) {
221
- const params = new URLSearchParams();
222
- params.set(
223
- "returnUrl",
224
- _nullishCoalesce(window.location.href.split("#")[0], () => ( window.location.href))
225
- );
226
- params.set("applicationId", context.initOptions.applicationId);
227
- params.set("environment", context.initOptions.environment);
228
- const separator = signInUrl.includes("?") ? "&" : "?";
229
- window.location.href = `${signInUrl}${separator}${params.toString()}`;
230
- return;
231
- }
232
- showError("Edit launcher auth is not configured.");
277
+ openGrantPopup();
233
278
  })();
234
279
  });
235
- wrap.append(marker, bar, panel);
236
- shadow.append(style, wrap);
237
- document.body.append(host);
238
280
  syncFabLabel();
239
281
  return {
240
282
  destroy: () => {
283
+ _optionalChain([stopOverlayGrantListener, 'optionalCall', _6 => _6()]);
284
+ clearGrantAttempt();
241
285
  host.remove();
242
286
  }
243
287
  };
@@ -247,4 +291,4 @@ function mountEditLauncher(context) {
247
291
 
248
292
 
249
293
  exports.LAUNCHER_CHUNK_MARKER = LAUNCHER_CHUNK_MARKER; exports.LAUNCHER_ROOT_ID = LAUNCHER_ROOT_ID; exports.mountEditLauncher = mountEditLauncher;
250
- //# sourceMappingURL=edit-launcher-DB2DJSQJ.umd.cjs.map
294
+ //# sourceMappingURL=edit-launcher-Z5AGCAQB.umd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/translations/translations/packages/sdk/dist/edit-launcher-Z5AGCAQB.umd.cjs","../src/edit-launcher/constants.ts","../src/edit-launcher/index.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,+DAAiC;AACjC;AACA;ACTO,IAAM,iBAAA,EAAmB,gCAAA;AAGzB,IAAM,sBAAA,EAAwB,oCAAA;ADSrC;AACA;AEoBA,SAAS,gBAAA,CAAiB,KAAA,EAAe,OAAA,EAA+B;AACtE,EAAA,MAAM,OAAA,EAAS,wDAAA,KAA2B,CAAA;AAC1C,EAAA,GAAA,CAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OACE,MAAA,CAAO,cAAA,IAAkB,OAAA,CAAQ,cAAA,GACjC,MAAA,CAAO,gBAAA,IAAoB,OAAA,CAAQ,WAAA;AAEvC;AAEA,SAAS,eAAA,CAAgB,OAAA,EAAqC;AAC5D,EAAA,MAAM,MAAA,EAAQ,2DAAA,CAAyB;AACvC,EAAA,GAAA,CAAI,CAAC,MAAA,GAAS,CAAC,gBAAA,CAAiB,KAAA,EAAO,OAAO,CAAA,EAAG;AAC/C,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AAKO,SAAS,iBAAA,CAAkB,OAAA,EAAuD;AACvF,EAAA,GAAA,CAAI,OAAO,SAAA,IAAa,WAAA,EAAa;AACnC,IAAA,OAAO,EAAE,OAAA,EAAS,CAAA,EAAA,GAAM;AAAA,IAAC,EAAE,CAAA;AAAA,EAC7B;AAEA,kBAAA,QAAA,mBAAS,cAAA,mBAAe,gBAAgB,CAAA,6BAAG,MAAA,mBAAO,GAAA;AAElD,EAAA,MAAM,KAAA,EAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACzC,EAAA,IAAA,CAAK,GAAA,EAAK,gBAAA;AACV,EAAA,IAAA,CAAK,YAAA,CAAa,gCAAA,EAAkC,MAAM,CAAA;AAE1D,EAAA,MAAM,OAAA,EAAS,IAAA,CAAK,YAAA,CAAa,EAAE,IAAA,EAAM,OAAO,CAAC,CAAA;AACjD,EAAA,MAAM,MAAA,EAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,EAAA,KAAA,CAAM,YAAA,EAAc,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAqEpB,EAAA,MAAM,KAAA,EAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACzC,EAAA,IAAA,CAAK,UAAA,EAAY,MAAA;AAEjB,EAAA,MAAM,OAAA,EAAS,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC5C,EAAA,MAAA,CAAO,UAAA,EAAY,QAAA;AACnB,EAAA,MAAA,CAAO,YAAA,EAAc,qBAAA;AAErB,EAAA,MAAM,IAAA,EAAM,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACxC,EAAA,GAAA,CAAI,UAAA,EAAY,UAAA;AAEhB,EAAA,MAAM,KAAA,EAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACzC,EAAA,IAAA,CAAK,UAAA,EAAY,gBAAA;AAEjB,EAAA,MAAM,KAAA,EAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,UAAA,EAAY,gBAAA;AACjB,EAAA,IAAA,CAAK,YAAA,EAAc,kBAAA;AACnB,EAAA,IAAA,CAAK,OAAA,EAAS,IAAA;AAEd,EAAA,MAAM,KAAA,EAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,UAAA,EAAY,gBAAA;AACjB,EAAA,IAAA,CAAK,YAAA,EAAc,6BAAA;AAEnB,EAAA,IAAA,CAAK,MAAA,CAAO,IAAA,EAAM,IAAI,CAAA;AAEtB,EAAA,MAAM,IAAA,EAAM,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC3C,EAAA,GAAA,CAAI,KAAA,EAAO,QAAA;AACX,EAAA,GAAA,CAAI,UAAA,EAAY,KAAA;AAChB,EAAA,GAAA,CAAI,YAAA,EAAc,WAAA;AAElB,EAAA,GAAA,CAAI,MAAA,CAAO,IAAA,EAAM,GAAG,CAAA;AACpB,EAAA,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,GAAG,CAAA;AACvB,EAAA,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,IAAI,CAAA;AACzB,EAAA,QAAA,CAAS,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA;AAEzB,EAAA,MAAM,UAAA,EAAY,OAAA,CAAQ,eAAA,CAAgB,SAAA;AAC1C,EAAA,MAAM,WAAA,mBAA+B,OAAA,CAAQ,eAAA,CAAgB,UAAA,UAAc,SAAA;AAE3E,EAAA,IAAI,iBAAA,EAAkC,IAAA;AACtC,EAAA,IAAI,WAAA,EAA4B,IAAA;AAChC,EAAA,IAAI,eAAA,EAAsC,IAAA;AAE1C,EAAA,MAAM,YAAA,EAAc,6BAAA;AAEpB,EAAA,MAAM,aAAA,EAAe,CAAA,EAAA,GAAY;AAC/B,IAAA,GAAA,CAAI,OAAA,CAAQ,gBAAA,CAAiB,CAAA,EAAG;AAC9B,MAAA,GAAA,CAAI,YAAA,EAAc,cAAA;AAClB,MAAA,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,YAAY,CAAA;AAC9B,MAAA,IAAA,CAAK,OAAA,EAAS,KAAA;AACd,MAAA,IAAA,CAAK,OAAA,EAAS,IAAA;AACd,MAAA,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,UAAU,CAAA;AAAA,IAClC,EAAA,KAAO;AACL,MAAA,GAAA,CAAI,YAAA,EAAc,WAAA;AAClB,MAAA,GAAA,CAAI,SAAA,CAAU,MAAA,CAAO,YAAY,CAAA;AACjC,MAAA,IAAA,CAAK,OAAA,EAAS,IAAA;AACd,MAAA,IAAA,CAAK,OAAA,EAAS,KAAA;AACd,MAAA,GAAA,CAAI,CAAC,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,UAAU,CAAA,EAAG;AACxC,QAAA,IAAA,CAAK,YAAA,EAAc,WAAA;AAAA,MACrB;AAAA,IACF;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,kBAAA,EAAoB,CAAA,EAAA,GAAY;AACpC,IAAA,iBAAA,EAAmB,IAAA;AACnB,oBAAA,cAAA,0BAAA,CAAiB,GAAA;AACjB,IAAA,eAAA,EAAiB,IAAA;AACjB,IAAA,WAAA,EAAa,IAAA;AAAA,EACf,CAAA;AAEA,EAAA,MAAM,gBAAA,EAAkB,CAAC,OAAA,EAAA,GAA0B;AACjD,IAAA,IAAA,CAAK,OAAA,EAAS,KAAA;AACd,IAAA,IAAA,CAAK,YAAA,EAAc,OAAA;AACnB,IAAA,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,UAAU,CAAA;AAAA,EAC/B,CAAA;AAEA,EAAA,MAAM,kBAAA,EAAoB,CAAA,EAAA,GAAY;AACpC,IAAA,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,UAAU,CAAA;AAChC,IAAA,IAAA,CAAK,YAAA,EAAc,WAAA;AAAA,EACrB,CAAA;AAEA,EAAA,MAAM,eAAA,EAAiB,CAAA,EAAA,GAAY;AACjC,IAAA,GAAA,CAAI,CAAC,SAAA,EAAW;AACd,MAAA,eAAA,CAAgB,uCAAuC,CAAA;AACvD,MAAA,MAAA;AAAA,IACF;AAEA,IAAA,GAAA,CAAI,WAAA,GAAc,CAAC,UAAA,CAAW,MAAA,EAAQ;AACpC,MAAA,UAAA,CAAW,KAAA,CAAM,CAAA;AACjB,MAAA,IAAA,CAAK,OAAA,EAAS,KAAA;AACd,MAAA,IAAA,CAAK,YAAA,EAAc,gCAAA;AACnB,MAAA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,mBAAY,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,UAAK,MAAA,CAAO,QAAA,CAAS,MAAA;AACxE,IAAA,iBAAA,EAAmB,+CAAA,CAAa;AAChC,IAAA,MAAM,SAAA,EAAW,uDAAA,SAAqB,EAAW;AAAA,MAC/C,SAAA;AAAA,MACA,aAAA,EAAe,OAAA,CAAQ,WAAA,CAAY,aAAA;AAAA,MACnC,WAAA,EAAa,OAAA,CAAQ,WAAA,CAAY,WAAA;AAAA,MACjC,IAAA,EAAM,UAAA;AAAA,MACN,SAAA,EAAW;AAAA,IACb,CAAC,CAAA;AAED,IAAA,GAAA,CAAI,WAAA,IAAe,UAAA,EAAY;AAC7B,MAAA,yDAAA,QAA+B,CAAA;AAC/B,MAAA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,EAAQ,wDAAA,QAA8B,CAAA;AAC5C,IAAA,GAAA,CAAI,CAAC,KAAA,EAAO;AACV,MAAA,yDAAA,QAA+B,CAAA;AAC/B,MAAA,MAAA;AAAA,IACF;AAEA,IAAA,WAAA,EAAa,KAAA;AACb,IAAA,IAAA,CAAK,OAAA,EAAS,KAAA;AACd,IAAA,IAAA,CAAK,YAAA,EAAc,gCAAA;AACnB,IAAA,eAAA,EAAiB,mDAAA,KAAiB,EAAO,CAAA,EAAA,GAAM;AAC7C,MAAA,GAAA,CAAI,gBAAA,EAAkB;AACpB,QAAA,iBAAA,CAAkB,CAAA;AAClB,QAAA,GAAA,CAAI,CAAC,OAAA,CAAQ,gBAAA,CAAiB,CAAA,EAAG;AAC/B,UAAA,eAAA,CAAgB,yCAAyC,CAAA;AAAA,QAC3D;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,IAAI,yBAAA,EAAgD,IAAA;AACpD,EAAA,GAAA,CAAI,UAAA,GAAa,WAAA,IAAe,OAAA,EAAS;AACvC,IAAA,MAAM,YAAA,EAAc,2DAAA,SAAkC,CAAA;AACtD,IAAA,yBAAA,EAA2B,wDAAA;AAAA,MACzB,WAAA;AAAA,MACA,CAAA,EAAA,GAAM,gBAAA;AAAA,MACN;AAAA,QACE,SAAA,EAAW,CAAC,KAAA,EAAA,GAAU;AACpB,UAAA,KAAA,CAAM,MAAA,CAAA,EAAA,GAAY;AAChB,YAAA,iBAAA,CAAkB,CAAA;AAClB,YAAA,mDAAA,KAAsB,CAAA;AACtB,YAAA,IAAI;AACF,cAAA,MAAM,OAAA,CAAQ,cAAA,CAAe,CAAA;AAC7B,cAAA,iBAAA,CAAkB,CAAA;AAClB,cAAA,YAAA,CAAa,CAAA;AAAA,YACf,EAAA,MAAA,CAAS,GAAA,EAAK;AACZ,cAAA,iDAAA,CAAe;AACf,cAAA,eAAA,CAAgB,IAAA,WAAe,MAAA,EAAQ,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,GAAG,CAAC,CAAA;AAChE,cAAA,cAAA,CAAe,CAAA;AAAA,YACjB;AAAA,UACF,CAAA,CAAA,CAAG,CAAA;AAAA,QACL,CAAA;AAAA,QACA,OAAA,EAAS,CAAC,OAAA,EAAA,GAAY;AACpB,UAAA,iBAAA,CAAkB,CAAA;AAClB,UAAA,eAAA,CAAgB,OAAO,CAAA;AAAA,QACzB;AAAA,MACF;AAAA,IACF,CAAA;AAAA,EACF;AAEA,EAAA,GAAA,CAAI,gBAAA,CAAiB,OAAA,EAAS,CAAA,EAAA,GAAM;AAClC,IAAA,KAAA,CAAM,MAAA,CAAA,EAAA,GAAY;AAChB,MAAA,GAAA,CAAI,OAAA,CAAQ,gBAAA,CAAiB,CAAA,EAAG;AAC9B,QAAA,OAAA,CAAQ,eAAA,CAAgB,CAAA;AACxB,QAAA,iBAAA,CAAkB,CAAA;AAClB,QAAA,YAAA,CAAa,CAAA;AACb,QAAA,MAAA;AAAA,MACF;AAEA,MAAA,iBAAA,CAAkB,CAAA;AAElB,MAAA,MAAM,SAAA,EAAW,eAAA,CAAgB,OAAA,CAAQ,WAAW,CAAA;AACpD,MAAA,GAAA,CAAI,QAAA,EAAU;AACZ,QAAA,IAAI;AACF,UAAA,MAAM,OAAA,CAAQ,cAAA,CAAe,CAAA;AAC7B,UAAA,YAAA,CAAa,CAAA;AACb,UAAA,MAAA;AAAA,QACF,EAAA,UAAQ;AACN,UAAA,iDAAA,CAAe;AAAA,QACjB;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,iBAAiB,EAAA,EAAI,OAAA,CAAQ,eAAA;AACrC,MAAA,GAAA,CAAI,gBAAA,EAAkB;AACpB,QAAA,IAAI;AACF,UAAA,MAAM,MAAA,EAAQ,MAAM,gBAAA,CAAiB,CAAA;AACrC,UAAA,mDAAA,KAAsB,CAAA;AACtB,UAAA,MAAM,OAAA,CAAQ,cAAA,CAAe,CAAA;AAC7B,UAAA,YAAA,CAAa,CAAA;AAAA,QACf,EAAA,MAAA,CAAS,GAAA,EAAK;AACZ,UAAA,eAAA,CAAgB,IAAA,WAAe,MAAA,EAAQ,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,QAClE;AACA,QAAA,MAAA;AAAA,MACF;AAEA,MAAA,cAAA,CAAe,CAAA;AAAA,IACjB,CAAA,CAAA,CAAG,CAAA;AAAA,EACL,CAAC,CAAA;AAED,EAAA,YAAA,CAAa,CAAA;AAEb,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,CAAA,EAAA,GAAM;AACb,sBAAA,wBAAA,0BAAA,CAA2B,GAAA;AAC3B,MAAA,iBAAA,CAAkB,CAAA;AAClB,MAAA,IAAA,CAAK,MAAA,CAAO,CAAA;AAAA,IACd;AAAA,EACF,CAAA;AACF;AF1DA;AACE;AACA;AACA;AACF,kJAAC","file":"/home/runner/work/translations/translations/packages/sdk/dist/edit-launcher-Z5AGCAQB.umd.cjs","sourcesContent":[null,"/**\n * DOM and bundle-split markers for the lazy-loaded edit launcher chunk (M2-SDK-05).\n *\n * Intent: stable ids/markers keep overlay UI out of the default SDK entry until edit mode activates.\n */\nexport const LAUNCHER_ROOT_ID = \"translation-edit-launcher-root\";\n\n/** Marker string used in bundle-split tests to keep launcher out of the main entry. */\nexport const LAUNCHER_CHUNK_MARKER = \"translation-edit-launcher-chunk-v1\";\n","/**\n * Lazy-loaded bottom bar launcher for staging overlay (M2-SDK-05, UI-09).\n *\n * Intent: `?translation_edit=1` arms the UI only; Translate opens popup grant or enables from stored token.\n */\nimport {\n clearEditToken,\n persistEditToken,\n readEditTokenFromStorage,\n} from \"../edit-token.js\";\nimport { decodeEditTokenClaims } from \"../edit-token-decode.js\";\nimport {\n adminOriginFromSignInUrl,\n buildOverlayGrantUrl,\n listenForOverlayGrant,\n newRequestId,\n openOverlayGrantPopup,\n redirectToOverlayGrant,\n watchPopupClosed,\n type OverlayGrantMode,\n} from \"./sign-in-popup.js\";\nimport type { EditLauncherOptions, InitOptions } from \"../types.js\";\nimport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport { LAUNCHER_CHUNK_MARKER, LAUNCHER_ROOT_ID } from \"./constants.js\";\n\nexport type EditLauncherMountContext = {\n initOptions: InitOptions;\n launcherOptions: EditLauncherOptions;\n isEditModeActive: () => boolean;\n enableEditMode: () => Promise<void>;\n disableEditMode: () => void;\n};\n\nexport type EditLauncherHandle = {\n destroy: () => void;\n};\n\nfunction tokenMatchesInit(token: string, options: InitOptions): boolean {\n const claims = decodeEditTokenClaims(token);\n if (!claims) {\n return false;\n }\n return (\n claims.applicationId === options.applicationId &&\n claims.environmentName === options.environment\n );\n}\n\nfunction storedEditToken(options: InitOptions): string | null {\n const token = readEditTokenFromStorage();\n if (!token || !tokenMatchesInit(token, options)) {\n return null;\n }\n return token;\n}\n\n/**\n * Mounts bottom edit bar in a shadow root; Translate is the single auth gesture.\n */\nexport function mountEditLauncher(context: EditLauncherMountContext): EditLauncherHandle {\n if (typeof document === \"undefined\") {\n return { destroy: () => {} };\n }\n\n document.getElementById(LAUNCHER_ROOT_ID)?.remove();\n\n const host = document.createElement(\"div\");\n host.id = LAUNCHER_ROOT_ID;\n host.setAttribute(\"data-translation-edit-launcher\", \"true\");\n\n const shadow = host.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = `\n :host { all: initial; }\n .wrap { font-family: system-ui, -apple-system, Segoe UI, sans-serif; font-size: 14px; }\n .edit-bar {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2147483645;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 20px;\n background: #fff;\n border-top: 1px solid #e2e8f0;\n box-shadow: 0 -4px 24px rgba(15, 23, 42, 0.08);\n }\n .edit-bar__left {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n }\n .edit-mode-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: 999px;\n background: #d1fae5;\n color: #047857;\n font-size: 12px;\n font-weight: 600;\n white-space: nowrap;\n }\n .edit-mode-pill::before {\n content: \"\";\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #10b981;\n }\n .edit-bar__hint {\n color: #64748b;\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .edit-bar__hint.is-error { color: #b91c1c; }\n .fab {\n flex-shrink: 0;\n border: none;\n border-radius: 8px;\n padding: 10px 18px;\n background: #4338ca;\n color: #fff;\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n font-weight: 600;\n box-shadow: 0 2px 8px rgba(67, 56, 202, 0.28);\n }\n .fab:hover { background: #3730a3; }\n .fab.is-editing { background: #0f766e; box-shadow: 0 2px 8px rgba(15, 118, 110, 0.28); }\n .marker { display: none; }\n `;\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"wrap\";\n\n const marker = document.createElement(\"span\");\n marker.className = \"marker\";\n marker.textContent = LAUNCHER_CHUNK_MARKER;\n\n const bar = document.createElement(\"div\");\n bar.className = \"edit-bar\";\n\n const left = document.createElement(\"div\");\n left.className = \"edit-bar__left\";\n\n const pill = document.createElement(\"span\");\n pill.className = \"edit-mode-pill\";\n pill.textContent = \"Edit mode active\";\n pill.hidden = true;\n\n const hint = document.createElement(\"span\");\n hint.className = \"edit-bar__hint\";\n hint.textContent = \"Translate copy on this page\";\n\n left.append(pill, hint);\n\n const fab = document.createElement(\"button\");\n fab.type = \"button\";\n fab.className = \"fab\";\n fab.textContent = \"Translate\";\n\n bar.append(left, fab);\n wrap.append(marker, bar);\n shadow.append(style, wrap);\n document.body.append(host);\n\n const signInUrl = context.launcherOptions.signInUrl;\n const signInMode: OverlayGrantMode = context.launcherOptions.signInMode ?? \"popup\";\n\n let currentRequestId: string | null = null;\n let grantPopup: Window | null = null;\n let stopPopupWatch: (() => void) | null = null;\n\n const defaultHint = \"Translate copy on this page\";\n\n const syncFabLabel = (): void => {\n if (context.isEditModeActive()) {\n fab.textContent = \"Stop editing\";\n fab.classList.add(\"is-editing\");\n pill.hidden = false;\n hint.hidden = true;\n hint.classList.remove(\"is-error\");\n } else {\n fab.textContent = \"Translate\";\n fab.classList.remove(\"is-editing\");\n pill.hidden = true;\n hint.hidden = false;\n if (!hint.classList.contains(\"is-error\")) {\n hint.textContent = defaultHint;\n }\n }\n };\n\n const clearGrantAttempt = (): void => {\n currentRequestId = null;\n stopPopupWatch?.();\n stopPopupWatch = null;\n grantPopup = null;\n };\n\n const showInlineError = (message: string): void => {\n hint.hidden = false;\n hint.textContent = message;\n hint.classList.add(\"is-error\");\n };\n\n const clearInlineStatus = (): void => {\n hint.classList.remove(\"is-error\");\n hint.textContent = defaultHint;\n };\n\n const openGrantPopup = (): void => {\n if (!signInUrl) {\n showInlineError(\"Edit launcher auth is not configured.\");\n return;\n }\n\n if (grantPopup && !grantPopup.closed) {\n grantPopup.focus();\n hint.hidden = false;\n hint.textContent = \"Complete sign-in in the popup.\";\n return;\n }\n\n const returnUrl = window.location.href.split(\"#\")[0] ?? window.location.href;\n currentRequestId = newRequestId();\n const grantUrl = buildOverlayGrantUrl(signInUrl, {\n returnUrl,\n applicationId: context.initOptions.applicationId,\n environment: context.initOptions.environment,\n mode: signInMode,\n requestId: currentRequestId,\n });\n\n if (signInMode === \"redirect\") {\n redirectToOverlayGrant(grantUrl);\n return;\n }\n\n const popup = openOverlayGrantPopup(grantUrl);\n if (!popup) {\n redirectToOverlayGrant(grantUrl);\n return;\n }\n\n grantPopup = popup;\n hint.hidden = false;\n hint.textContent = \"Complete sign-in in the popup.\";\n stopPopupWatch = watchPopupClosed(popup, () => {\n if (currentRequestId) {\n clearGrantAttempt();\n if (!context.isEditModeActive()) {\n showInlineError(\"Sign-in window closed before finishing.\");\n }\n }\n });\n };\n\n let stopOverlayGrantListener: (() => void) | null = null;\n if (signInUrl && signInMode === \"popup\") {\n const adminOrigin = adminOriginFromSignInUrl(signInUrl);\n stopOverlayGrantListener = listenForOverlayGrant(\n adminOrigin,\n () => currentRequestId,\n {\n onGranted: (token) => {\n void (async () => {\n clearInlineStatus();\n persistEditToken(token);\n try {\n await context.enableEditMode();\n clearGrantAttempt();\n syncFabLabel();\n } catch (err) {\n clearEditToken();\n showInlineError(err instanceof Error ? err.message : String(err));\n openGrantPopup();\n }\n })();\n },\n onError: (message) => {\n clearGrantAttempt();\n showInlineError(message);\n },\n },\n );\n }\n\n fab.addEventListener(\"click\", () => {\n void (async () => {\n if (context.isEditModeActive()) {\n context.disableEditMode();\n clearInlineStatus();\n syncFabLabel();\n return;\n }\n\n clearInlineStatus();\n\n const existing = storedEditToken(context.initOptions);\n if (existing) {\n try {\n await context.enableEditMode();\n syncFabLabel();\n return;\n } catch {\n clearEditToken();\n }\n }\n\n const { requestEditToken } = context.launcherOptions;\n if (requestEditToken) {\n try {\n const token = await requestEditToken();\n persistEditToken(token);\n await context.enableEditMode();\n syncFabLabel();\n } catch (err) {\n showInlineError(err instanceof Error ? err.message : String(err));\n }\n return;\n }\n\n openGrantPopup();\n })();\n });\n\n syncFabLabel();\n\n return {\n destroy: () => {\n stopOverlayGrantListener?.();\n clearGrantAttempt();\n host.remove();\n },\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -29,6 +29,11 @@ type EditLauncherOptions = {
29
29
  armingKey?: string;
30
30
  /** Redirect here to sign in; `returnUrl` is appended. */
31
31
  signInUrl?: string;
32
+ /**
33
+ * `popup` opens admin overlay-grant in a new window (default); `redirect` navigates away from the app.
34
+ * Intent: popup keeps translators on the page they are editing.
35
+ */
36
+ signInMode?: "popup" | "redirect";
32
37
  /**
33
38
  * Mint an edit JWT in-process (local demo only). Ignored when `signInUrl` is set.
34
39
  * Intent: never ship admin/dev secrets in production customer bundles.
package/dist/index.d.ts CHANGED
@@ -29,6 +29,11 @@ type EditLauncherOptions = {
29
29
  armingKey?: string;
30
30
  /** Redirect here to sign in; `returnUrl` is appended. */
31
31
  signInUrl?: string;
32
+ /**
33
+ * `popup` opens admin overlay-grant in a new window (default); `redirect` navigates away from the app.
34
+ * Intent: popup keeps translators on the page they are editing.
35
+ */
36
+ signInMode?: "popup" | "redirect";
32
37
  /**
33
38
  * Mint an edit JWT in-process (local demo only). Ignored when `signInUrl` is set.
34
39
  * Intent: never ship admin/dev secrets in production customer bundles.