@stringpush/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -0
- package/dist/chunk-FROJCNV7.umd.cjs +153 -0
- package/dist/chunk-FROJCNV7.umd.cjs.map +1 -0
- package/dist/chunk-X3WTVBZ6.mjs +153 -0
- package/dist/chunk-X3WTVBZ6.mjs.map +1 -0
- package/dist/edit-launcher-DB2DJSQJ.umd.cjs +250 -0
- package/dist/edit-launcher-DB2DJSQJ.umd.cjs.map +1 -0
- package/dist/edit-launcher-PTZ5BIO2.mjs +250 -0
- package/dist/edit-launcher-PTZ5BIO2.mjs.map +1 -0
- package/dist/index.d.cts +78 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.mjs +260 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.umd.cjs +260 -0
- package/dist/index.umd.cjs.map +1 -0
- package/dist/overlay-7KC2RRGB.mjs +1039 -0
- package/dist/overlay-7KC2RRGB.mjs.map +1 -0
- package/dist/overlay-MLOXYRPA.umd.cjs +1039 -0
- package/dist/overlay-MLOXYRPA.umd.cjs.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
var _chunkFROJCNV7umdcjs = require('./chunk-FROJCNV7.umd.cjs');
|
|
6
|
+
|
|
7
|
+
// src/edit-launcher/constants.ts
|
|
8
|
+
var LAUNCHER_ROOT_ID = "translation-edit-launcher-root";
|
|
9
|
+
var LAUNCHER_CHUNK_MARKER = "translation-edit-launcher-chunk-v1";
|
|
10
|
+
|
|
11
|
+
// src/edit-launcher/index.ts
|
|
12
|
+
function tokenMatchesInit(token, options) {
|
|
13
|
+
const claims = _chunkFROJCNV7umdcjs.decodeEditTokenClaims.call(void 0, token);
|
|
14
|
+
if (!claims) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return claims.applicationId === options.applicationId && claims.environmentName === options.environment;
|
|
18
|
+
}
|
|
19
|
+
function storedEditToken(options) {
|
|
20
|
+
const token = _chunkFROJCNV7umdcjs.readEditTokenFromStorage.call(void 0, );
|
|
21
|
+
if (!token || !tokenMatchesInit(token, options)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return token;
|
|
25
|
+
}
|
|
26
|
+
function mountEditLauncher(context) {
|
|
27
|
+
if (typeof document === "undefined") {
|
|
28
|
+
return { destroy: () => {
|
|
29
|
+
} };
|
|
30
|
+
}
|
|
31
|
+
_optionalChain([document, 'access', _ => _.getElementById, 'call', _2 => _2(LAUNCHER_ROOT_ID), 'optionalAccess', _3 => _3.remove, 'call', _4 => _4()]);
|
|
32
|
+
const host = document.createElement("div");
|
|
33
|
+
host.id = LAUNCHER_ROOT_ID;
|
|
34
|
+
host.setAttribute("data-translation-edit-launcher", "true");
|
|
35
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
36
|
+
const style = document.createElement("style");
|
|
37
|
+
style.textContent = `
|
|
38
|
+
:host { all: initial; }
|
|
39
|
+
.wrap { font-family: system-ui, -apple-system, Segoe UI, sans-serif; font-size: 14px; }
|
|
40
|
+
.edit-bar {
|
|
41
|
+
position: fixed;
|
|
42
|
+
left: 0;
|
|
43
|
+
right: 0;
|
|
44
|
+
bottom: 0;
|
|
45
|
+
z-index: 2147483645;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: space-between;
|
|
49
|
+
gap: 12px;
|
|
50
|
+
padding: 12px 20px;
|
|
51
|
+
background: #fff;
|
|
52
|
+
border-top: 1px solid #e2e8f0;
|
|
53
|
+
box-shadow: 0 -4px 24px rgba(15, 23, 42, 0.08);
|
|
54
|
+
}
|
|
55
|
+
.edit-bar__left {
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
gap: 10px;
|
|
59
|
+
min-width: 0;
|
|
60
|
+
}
|
|
61
|
+
.edit-mode-pill {
|
|
62
|
+
display: inline-flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 6px;
|
|
65
|
+
padding: 4px 10px;
|
|
66
|
+
border-radius: 999px;
|
|
67
|
+
background: #d1fae5;
|
|
68
|
+
color: #047857;
|
|
69
|
+
font-size: 12px;
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
}
|
|
73
|
+
.edit-mode-pill::before {
|
|
74
|
+
content: "";
|
|
75
|
+
width: 6px;
|
|
76
|
+
height: 6px;
|
|
77
|
+
border-radius: 50%;
|
|
78
|
+
background: #10b981;
|
|
79
|
+
}
|
|
80
|
+
.edit-bar__hint {
|
|
81
|
+
color: #64748b;
|
|
82
|
+
font-size: 13px;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
text-overflow: ellipsis;
|
|
86
|
+
}
|
|
87
|
+
.fab {
|
|
88
|
+
flex-shrink: 0;
|
|
89
|
+
border: none;
|
|
90
|
+
border-radius: 8px;
|
|
91
|
+
padding: 10px 18px;
|
|
92
|
+
background: #4338ca;
|
|
93
|
+
color: #fff;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
font: inherit;
|
|
96
|
+
font-size: 14px;
|
|
97
|
+
font-weight: 600;
|
|
98
|
+
box-shadow: 0 2px 8px rgba(67, 56, 202, 0.28);
|
|
99
|
+
}
|
|
100
|
+
.fab:hover { background: #3730a3; }
|
|
101
|
+
.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
|
+
.marker { display: none; }
|
|
129
|
+
`;
|
|
130
|
+
const wrap = document.createElement("div");
|
|
131
|
+
wrap.className = "wrap";
|
|
132
|
+
const marker = document.createElement("span");
|
|
133
|
+
marker.className = "marker";
|
|
134
|
+
marker.textContent = LAUNCHER_CHUNK_MARKER;
|
|
135
|
+
const bar = document.createElement("div");
|
|
136
|
+
bar.className = "edit-bar";
|
|
137
|
+
const left = document.createElement("div");
|
|
138
|
+
left.className = "edit-bar__left";
|
|
139
|
+
const pill = document.createElement("span");
|
|
140
|
+
pill.className = "edit-mode-pill";
|
|
141
|
+
pill.textContent = "Edit mode active";
|
|
142
|
+
pill.hidden = true;
|
|
143
|
+
const hint = document.createElement("span");
|
|
144
|
+
hint.className = "edit-bar__hint";
|
|
145
|
+
hint.textContent = "Translate copy on this page";
|
|
146
|
+
left.append(pill, hint);
|
|
147
|
+
const fab = document.createElement("button");
|
|
148
|
+
fab.type = "button";
|
|
149
|
+
fab.className = "fab";
|
|
150
|
+
fab.textContent = "Translate";
|
|
151
|
+
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");
|
|
158
|
+
const syncFabLabel = () => {
|
|
159
|
+
if (context.isEditModeActive()) {
|
|
160
|
+
fab.textContent = "Stop editing";
|
|
161
|
+
fab.classList.add("is-editing");
|
|
162
|
+
pill.hidden = false;
|
|
163
|
+
hint.hidden = true;
|
|
164
|
+
panel.hidden = true;
|
|
165
|
+
} else {
|
|
166
|
+
fab.textContent = "Translate";
|
|
167
|
+
fab.classList.remove("is-editing");
|
|
168
|
+
pill.hidden = true;
|
|
169
|
+
hint.hidden = false;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const showError = (message) => {
|
|
173
|
+
errorEl.hidden = false;
|
|
174
|
+
errorEl.textContent = message;
|
|
175
|
+
};
|
|
176
|
+
const clearError = () => {
|
|
177
|
+
errorEl.hidden = true;
|
|
178
|
+
errorEl.textContent = "";
|
|
179
|
+
};
|
|
180
|
+
fab.addEventListener("click", () => {
|
|
181
|
+
void (async () => {
|
|
182
|
+
clearError();
|
|
183
|
+
if (context.isEditModeActive()) {
|
|
184
|
+
context.disableEditMode();
|
|
185
|
+
syncFabLabel();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const existing = storedEditToken(context.initOptions);
|
|
189
|
+
if (existing) {
|
|
190
|
+
try {
|
|
191
|
+
await context.enableEditMode();
|
|
192
|
+
syncFabLabel();
|
|
193
|
+
} catch (err) {
|
|
194
|
+
showError(err instanceof Error ? err.message : String(err));
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
panel.hidden = false;
|
|
199
|
+
})();
|
|
200
|
+
});
|
|
201
|
+
signInBtn.addEventListener("click", () => {
|
|
202
|
+
void (async () => {
|
|
203
|
+
clearError();
|
|
204
|
+
const { signInUrl, requestEditToken } = context.launcherOptions;
|
|
205
|
+
if (requestEditToken) {
|
|
206
|
+
signInBtn.disabled = true;
|
|
207
|
+
try {
|
|
208
|
+
const token = await requestEditToken();
|
|
209
|
+
_chunkFROJCNV7umdcjs.persistEditToken.call(void 0, token);
|
|
210
|
+
await context.enableEditMode();
|
|
211
|
+
panel.hidden = true;
|
|
212
|
+
syncFabLabel();
|
|
213
|
+
} catch (err) {
|
|
214
|
+
showError(err instanceof Error ? err.message : String(err));
|
|
215
|
+
} finally {
|
|
216
|
+
signInBtn.disabled = false;
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
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.");
|
|
233
|
+
})();
|
|
234
|
+
});
|
|
235
|
+
wrap.append(marker, bar, panel);
|
|
236
|
+
shadow.append(style, wrap);
|
|
237
|
+
document.body.append(host);
|
|
238
|
+
syncFabLabel();
|
|
239
|
+
return {
|
|
240
|
+
destroy: () => {
|
|
241
|
+
host.remove();
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/mitopalov/work/translations/packages/sdk/dist/edit-launcher-DB2DJSQJ.umd.cjs","../src/edit-launcher/constants.ts","../src/edit-launcher/index.ts"],"names":[],"mappings":"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","file":"/home/mitopalov/work/translations/packages/sdk/dist/edit-launcher-DB2DJSQJ.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; auth + enableEditMode happen on user action.\n */\nimport { decodeEditTokenClaims } from \"../edit-token-decode.js\";\nimport {\n persistEditToken,\n readEditTokenFromStorage,\n} from \"../edit-token.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 and optional sign-in panel in a shadow root.\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 .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 .panel {\n position: fixed;\n bottom: 72px;\n left: 20px;\n z-index: 2147483645;\n width: min(300px, calc(100vw - 40px));\n padding: 14px;\n border-radius: 12px;\n background: #fff;\n color: #0f172a;\n border: 1px solid #e2e8f0;\n box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);\n }\n .panel p { margin: 0 0 10px; line-height: 1.45; font-size: 13px; color: #475569; }\n .panel button {\n width: 100%;\n border: none;\n border-radius: 8px;\n padding: 9px 12px;\n cursor: pointer;\n font: inherit;\n font-weight: 600;\n }\n .panel .primary { background: #4338ca; color: #fff; }\n .panel .primary:disabled { opacity: 0.6; cursor: wait; }\n .panel .error { margin-top: 8px; color: #b91c1c; font-size: 12px; }\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\n const panel = document.createElement(\"div\");\n panel.className = \"panel\";\n panel.hidden = true;\n panel.innerHTML =\n '<p>Sign in to edit copy on this page.</p><button type=\"button\" class=\"primary\">Sign in</button><p class=\"error\" hidden></p>';\n\n const signInBtn = panel.querySelector<HTMLButtonElement>(\".primary\")!;\n const errorEl = panel.querySelector<HTMLParagraphElement>(\".error\")!;\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 panel.hidden = true;\n } else {\n fab.textContent = \"Translate\";\n fab.classList.remove(\"is-editing\");\n pill.hidden = true;\n hint.hidden = false;\n }\n };\n\n const showError = (message: string): void => {\n errorEl.hidden = false;\n errorEl.textContent = message;\n };\n\n const clearError = (): void => {\n errorEl.hidden = true;\n errorEl.textContent = \"\";\n };\n\n fab.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n if (context.isEditModeActive()) {\n context.disableEditMode();\n syncFabLabel();\n return;\n }\n\n const existing = storedEditToken(context.initOptions);\n if (existing) {\n try {\n await context.enableEditMode();\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n }\n return;\n }\n\n panel.hidden = false;\n })();\n });\n\n signInBtn.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n const { signInUrl, requestEditToken } = context.launcherOptions;\n\n if (requestEditToken) {\n signInBtn.disabled = true;\n try {\n const token = await requestEditToken();\n persistEditToken(token);\n await context.enableEditMode();\n panel.hidden = true;\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n } finally {\n signInBtn.disabled = false;\n }\n return;\n }\n\n if (signInUrl) {\n const params = new URLSearchParams();\n params.set(\n \"returnUrl\",\n window.location.href.split(\"#\")[0] ?? window.location.href,\n );\n params.set(\"applicationId\", context.initOptions.applicationId);\n params.set(\"environment\", context.initOptions.environment);\n const separator = signInUrl.includes(\"?\") ? \"&\" : \"?\";\n window.location.href = `${signInUrl}${separator}${params.toString()}`;\n return;\n }\n\n showError(\"Edit launcher auth is not configured.\");\n })();\n });\n\n wrap.append(marker, bar, panel);\n shadow.append(style, wrap);\n document.body.append(host);\n syncFabLabel();\n\n return {\n destroy: () => {\n host.remove();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeEditTokenClaims,
|
|
3
|
+
persistEditToken,
|
|
4
|
+
readEditTokenFromStorage
|
|
5
|
+
} from "./chunk-X3WTVBZ6.mjs";
|
|
6
|
+
|
|
7
|
+
// src/edit-launcher/constants.ts
|
|
8
|
+
var LAUNCHER_ROOT_ID = "translation-edit-launcher-root";
|
|
9
|
+
var LAUNCHER_CHUNK_MARKER = "translation-edit-launcher-chunk-v1";
|
|
10
|
+
|
|
11
|
+
// src/edit-launcher/index.ts
|
|
12
|
+
function tokenMatchesInit(token, options) {
|
|
13
|
+
const claims = decodeEditTokenClaims(token);
|
|
14
|
+
if (!claims) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return claims.applicationId === options.applicationId && claims.environmentName === options.environment;
|
|
18
|
+
}
|
|
19
|
+
function storedEditToken(options) {
|
|
20
|
+
const token = readEditTokenFromStorage();
|
|
21
|
+
if (!token || !tokenMatchesInit(token, options)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return token;
|
|
25
|
+
}
|
|
26
|
+
function mountEditLauncher(context) {
|
|
27
|
+
if (typeof document === "undefined") {
|
|
28
|
+
return { destroy: () => {
|
|
29
|
+
} };
|
|
30
|
+
}
|
|
31
|
+
document.getElementById(LAUNCHER_ROOT_ID)?.remove();
|
|
32
|
+
const host = document.createElement("div");
|
|
33
|
+
host.id = LAUNCHER_ROOT_ID;
|
|
34
|
+
host.setAttribute("data-translation-edit-launcher", "true");
|
|
35
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
36
|
+
const style = document.createElement("style");
|
|
37
|
+
style.textContent = `
|
|
38
|
+
:host { all: initial; }
|
|
39
|
+
.wrap { font-family: system-ui, -apple-system, Segoe UI, sans-serif; font-size: 14px; }
|
|
40
|
+
.edit-bar {
|
|
41
|
+
position: fixed;
|
|
42
|
+
left: 0;
|
|
43
|
+
right: 0;
|
|
44
|
+
bottom: 0;
|
|
45
|
+
z-index: 2147483645;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: space-between;
|
|
49
|
+
gap: 12px;
|
|
50
|
+
padding: 12px 20px;
|
|
51
|
+
background: #fff;
|
|
52
|
+
border-top: 1px solid #e2e8f0;
|
|
53
|
+
box-shadow: 0 -4px 24px rgba(15, 23, 42, 0.08);
|
|
54
|
+
}
|
|
55
|
+
.edit-bar__left {
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
gap: 10px;
|
|
59
|
+
min-width: 0;
|
|
60
|
+
}
|
|
61
|
+
.edit-mode-pill {
|
|
62
|
+
display: inline-flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 6px;
|
|
65
|
+
padding: 4px 10px;
|
|
66
|
+
border-radius: 999px;
|
|
67
|
+
background: #d1fae5;
|
|
68
|
+
color: #047857;
|
|
69
|
+
font-size: 12px;
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
}
|
|
73
|
+
.edit-mode-pill::before {
|
|
74
|
+
content: "";
|
|
75
|
+
width: 6px;
|
|
76
|
+
height: 6px;
|
|
77
|
+
border-radius: 50%;
|
|
78
|
+
background: #10b981;
|
|
79
|
+
}
|
|
80
|
+
.edit-bar__hint {
|
|
81
|
+
color: #64748b;
|
|
82
|
+
font-size: 13px;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
text-overflow: ellipsis;
|
|
86
|
+
}
|
|
87
|
+
.fab {
|
|
88
|
+
flex-shrink: 0;
|
|
89
|
+
border: none;
|
|
90
|
+
border-radius: 8px;
|
|
91
|
+
padding: 10px 18px;
|
|
92
|
+
background: #4338ca;
|
|
93
|
+
color: #fff;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
font: inherit;
|
|
96
|
+
font-size: 14px;
|
|
97
|
+
font-weight: 600;
|
|
98
|
+
box-shadow: 0 2px 8px rgba(67, 56, 202, 0.28);
|
|
99
|
+
}
|
|
100
|
+
.fab:hover { background: #3730a3; }
|
|
101
|
+
.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
|
+
.marker { display: none; }
|
|
129
|
+
`;
|
|
130
|
+
const wrap = document.createElement("div");
|
|
131
|
+
wrap.className = "wrap";
|
|
132
|
+
const marker = document.createElement("span");
|
|
133
|
+
marker.className = "marker";
|
|
134
|
+
marker.textContent = LAUNCHER_CHUNK_MARKER;
|
|
135
|
+
const bar = document.createElement("div");
|
|
136
|
+
bar.className = "edit-bar";
|
|
137
|
+
const left = document.createElement("div");
|
|
138
|
+
left.className = "edit-bar__left";
|
|
139
|
+
const pill = document.createElement("span");
|
|
140
|
+
pill.className = "edit-mode-pill";
|
|
141
|
+
pill.textContent = "Edit mode active";
|
|
142
|
+
pill.hidden = true;
|
|
143
|
+
const hint = document.createElement("span");
|
|
144
|
+
hint.className = "edit-bar__hint";
|
|
145
|
+
hint.textContent = "Translate copy on this page";
|
|
146
|
+
left.append(pill, hint);
|
|
147
|
+
const fab = document.createElement("button");
|
|
148
|
+
fab.type = "button";
|
|
149
|
+
fab.className = "fab";
|
|
150
|
+
fab.textContent = "Translate";
|
|
151
|
+
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");
|
|
158
|
+
const syncFabLabel = () => {
|
|
159
|
+
if (context.isEditModeActive()) {
|
|
160
|
+
fab.textContent = "Stop editing";
|
|
161
|
+
fab.classList.add("is-editing");
|
|
162
|
+
pill.hidden = false;
|
|
163
|
+
hint.hidden = true;
|
|
164
|
+
panel.hidden = true;
|
|
165
|
+
} else {
|
|
166
|
+
fab.textContent = "Translate";
|
|
167
|
+
fab.classList.remove("is-editing");
|
|
168
|
+
pill.hidden = true;
|
|
169
|
+
hint.hidden = false;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const showError = (message) => {
|
|
173
|
+
errorEl.hidden = false;
|
|
174
|
+
errorEl.textContent = message;
|
|
175
|
+
};
|
|
176
|
+
const clearError = () => {
|
|
177
|
+
errorEl.hidden = true;
|
|
178
|
+
errorEl.textContent = "";
|
|
179
|
+
};
|
|
180
|
+
fab.addEventListener("click", () => {
|
|
181
|
+
void (async () => {
|
|
182
|
+
clearError();
|
|
183
|
+
if (context.isEditModeActive()) {
|
|
184
|
+
context.disableEditMode();
|
|
185
|
+
syncFabLabel();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const existing = storedEditToken(context.initOptions);
|
|
189
|
+
if (existing) {
|
|
190
|
+
try {
|
|
191
|
+
await context.enableEditMode();
|
|
192
|
+
syncFabLabel();
|
|
193
|
+
} catch (err) {
|
|
194
|
+
showError(err instanceof Error ? err.message : String(err));
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
panel.hidden = false;
|
|
199
|
+
})();
|
|
200
|
+
});
|
|
201
|
+
signInBtn.addEventListener("click", () => {
|
|
202
|
+
void (async () => {
|
|
203
|
+
clearError();
|
|
204
|
+
const { signInUrl, requestEditToken } = context.launcherOptions;
|
|
205
|
+
if (requestEditToken) {
|
|
206
|
+
signInBtn.disabled = true;
|
|
207
|
+
try {
|
|
208
|
+
const token = await requestEditToken();
|
|
209
|
+
persistEditToken(token);
|
|
210
|
+
await context.enableEditMode();
|
|
211
|
+
panel.hidden = true;
|
|
212
|
+
syncFabLabel();
|
|
213
|
+
} catch (err) {
|
|
214
|
+
showError(err instanceof Error ? err.message : String(err));
|
|
215
|
+
} finally {
|
|
216
|
+
signInBtn.disabled = false;
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
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.");
|
|
233
|
+
})();
|
|
234
|
+
});
|
|
235
|
+
wrap.append(marker, bar, panel);
|
|
236
|
+
shadow.append(style, wrap);
|
|
237
|
+
document.body.append(host);
|
|
238
|
+
syncFabLabel();
|
|
239
|
+
return {
|
|
240
|
+
destroy: () => {
|
|
241
|
+
host.remove();
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
export {
|
|
246
|
+
LAUNCHER_CHUNK_MARKER,
|
|
247
|
+
LAUNCHER_ROOT_ID,
|
|
248
|
+
mountEditLauncher
|
|
249
|
+
};
|
|
250
|
+
//# sourceMappingURL=edit-launcher-PTZ5BIO2.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; auth + enableEditMode happen on user action.\n */\nimport { decodeEditTokenClaims } from \"../edit-token-decode.js\";\nimport {\n persistEditToken,\n readEditTokenFromStorage,\n} from \"../edit-token.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 and optional sign-in panel in a shadow root.\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 .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 .panel {\n position: fixed;\n bottom: 72px;\n left: 20px;\n z-index: 2147483645;\n width: min(300px, calc(100vw - 40px));\n padding: 14px;\n border-radius: 12px;\n background: #fff;\n color: #0f172a;\n border: 1px solid #e2e8f0;\n box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);\n }\n .panel p { margin: 0 0 10px; line-height: 1.45; font-size: 13px; color: #475569; }\n .panel button {\n width: 100%;\n border: none;\n border-radius: 8px;\n padding: 9px 12px;\n cursor: pointer;\n font: inherit;\n font-weight: 600;\n }\n .panel .primary { background: #4338ca; color: #fff; }\n .panel .primary:disabled { opacity: 0.6; cursor: wait; }\n .panel .error { margin-top: 8px; color: #b91c1c; font-size: 12px; }\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\n const panel = document.createElement(\"div\");\n panel.className = \"panel\";\n panel.hidden = true;\n panel.innerHTML =\n '<p>Sign in to edit copy on this page.</p><button type=\"button\" class=\"primary\">Sign in</button><p class=\"error\" hidden></p>';\n\n const signInBtn = panel.querySelector<HTMLButtonElement>(\".primary\")!;\n const errorEl = panel.querySelector<HTMLParagraphElement>(\".error\")!;\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 panel.hidden = true;\n } else {\n fab.textContent = \"Translate\";\n fab.classList.remove(\"is-editing\");\n pill.hidden = true;\n hint.hidden = false;\n }\n };\n\n const showError = (message: string): void => {\n errorEl.hidden = false;\n errorEl.textContent = message;\n };\n\n const clearError = (): void => {\n errorEl.hidden = true;\n errorEl.textContent = \"\";\n };\n\n fab.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n if (context.isEditModeActive()) {\n context.disableEditMode();\n syncFabLabel();\n return;\n }\n\n const existing = storedEditToken(context.initOptions);\n if (existing) {\n try {\n await context.enableEditMode();\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n }\n return;\n }\n\n panel.hidden = false;\n })();\n });\n\n signInBtn.addEventListener(\"click\", () => {\n void (async () => {\n clearError();\n const { signInUrl, requestEditToken } = context.launcherOptions;\n\n if (requestEditToken) {\n signInBtn.disabled = true;\n try {\n const token = await requestEditToken();\n persistEditToken(token);\n await context.enableEditMode();\n panel.hidden = true;\n syncFabLabel();\n } catch (err) {\n showError(err instanceof Error ? err.message : String(err));\n } finally {\n signInBtn.disabled = false;\n }\n return;\n }\n\n if (signInUrl) {\n const params = new URLSearchParams();\n params.set(\n \"returnUrl\",\n window.location.href.split(\"#\")[0] ?? window.location.href,\n );\n params.set(\"applicationId\", context.initOptions.applicationId);\n params.set(\"environment\", context.initOptions.environment);\n const separator = signInUrl.includes(\"?\") ? \"&\" : \"?\";\n window.location.href = `${signInUrl}${separator}${params.toString()}`;\n return;\n }\n\n showError(\"Edit launcher auth is not configured.\");\n })();\n });\n\n wrap.append(marker, bar, panel);\n shadow.append(style, wrap);\n document.body.append(host);\n syncFabLabel();\n\n return {\n destroy: () => {\n host.remove();\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { RuntimeInitOptions, TranslateValues } from '@stringpush/runtime-core';
|
|
2
|
+
export { Environment, ManifestLocaleEntry, MissingKeyFallback, RetryOptions, TranslateValues, TranslationCatalog, TranslationManifest } from '@stringpush/runtime-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Public SDK types — stable contract for customer apps (M1-SDK-01).
|
|
6
|
+
*
|
|
7
|
+
* Intent: runtime fields from @stringpush/runtime-core; overlay/edit fields stay on @stringpush/sdk.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type InitOptions = RuntimeInitOptions & {
|
|
11
|
+
/** Short-lived edit JWT from `POST /v1/auth/edit-session` (overlay + realtime). */
|
|
12
|
+
editToken?: string;
|
|
13
|
+
/** WebSocket gateway URL (defaults from `apiBaseUrl`, e.g. ws://localhost:3001 in local dev). */
|
|
14
|
+
realtimeUrl?: string;
|
|
15
|
+
/**
|
|
16
|
+
* When true (default), `?translation_edit=1` plus a resolvable edit token auto-calls `enableEditMode()`.
|
|
17
|
+
* When only `?translation_edit=1` is present, use {@link EditLauncherOptions} instead (FAB, no auto-enable).
|
|
18
|
+
*/
|
|
19
|
+
autoEnableEditFromQuery?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Staging overlay launcher: `?translation_edit=1` shows a FAB; sign-in then `enableEditMode()` on click.
|
|
22
|
+
* Intent: avoids accidental overlay and keeps JWT out of the visible URL when using hash/callback auth.
|
|
23
|
+
*/
|
|
24
|
+
editLauncher?: EditLauncherOptions;
|
|
25
|
+
};
|
|
26
|
+
/** Configures the staging edit launcher FAB (M2-SDK-05). */
|
|
27
|
+
type EditLauncherOptions = {
|
|
28
|
+
/** When set, require `?translation_edit_key=` to match before showing the launcher. */
|
|
29
|
+
armingKey?: string;
|
|
30
|
+
/** Redirect here to sign in; `returnUrl` is appended. */
|
|
31
|
+
signInUrl?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Mint an edit JWT in-process (local demo only). Ignored when `signInUrl` is set.
|
|
34
|
+
* Intent: never ship admin/dev secrets in production customer bundles.
|
|
35
|
+
*/
|
|
36
|
+
requestEditToken?: () => Promise<string>;
|
|
37
|
+
};
|
|
38
|
+
/** Options for {@link enableEditMode}. */
|
|
39
|
+
type EnableEditModeOptions = {
|
|
40
|
+
editToken?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Maps DOM nodes to translation keys for overlay edit mode (M2-SDK-02).
|
|
45
|
+
*
|
|
46
|
+
* Intent: host `registerResolver` first; `data-i18n-key` on the element or an ancestor is the fallback.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
declare const I18N_KEY_ATTR = "data-i18n-key";
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @stringpush/sdk — plain JS runtime: load manifest + bundles, expose `t()` (ICU in M1-SDK-03).
|
|
53
|
+
*
|
|
54
|
+
* Intent: customer-facing surface; runtime catalog/manifest delegated to @stringpush/runtime-core.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the SDK: fetch manifest and the active locale bundle into an in-memory catalog.
|
|
59
|
+
*
|
|
60
|
+
* Intent: entry point for customer apps; triggers `onTranslationsUpdated` when the first catalog is ready.
|
|
61
|
+
*/
|
|
62
|
+
declare function init(options: InitOptions): Promise<void>;
|
|
63
|
+
declare function destroy(): void;
|
|
64
|
+
declare function getLocale(): string;
|
|
65
|
+
/**
|
|
66
|
+
* Switch active locale and reload its bundle from the manifest.
|
|
67
|
+
*/
|
|
68
|
+
declare function setLocale(locale: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Translate a key using the loaded catalog and ICU MessageFormat.
|
|
71
|
+
*/
|
|
72
|
+
declare function t(key: string, values?: TranslateValues): string;
|
|
73
|
+
declare function loadNamespace(_namespace: string): Promise<void>;
|
|
74
|
+
declare function enableEditMode(options?: EnableEditModeOptions): Promise<void>;
|
|
75
|
+
declare function disableEditMode(): void;
|
|
76
|
+
declare function registerResolver(resolver: (element: Element) => string | null): () => void;
|
|
77
|
+
|
|
78
|
+
export { type EditLauncherOptions, type EnableEditModeOptions, I18N_KEY_ATTR, type InitOptions, destroy, disableEditMode, enableEditMode, getLocale, init, loadNamespace, registerResolver, setLocale, t };
|