@klauswuestefeld/jux-web 2026.3.6 → 2026.3.16

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,4 +1,7 @@
1
1
  export declare let chosenLanguage: any;
2
2
  export declare const reloadWithLanguageOverride: (lang: string) => void;
3
3
  export declare const getTranslation: (k: string) => string;
4
+ export declare const formatTextToMarkdown: (container: HTMLElement, text: string) => void;
5
+ export declare const createFormattedParagraph: (text: string) => HTMLParagraphElement;
6
+ export declare const applyFormattedTranslation: (element: HTMLElement, key: string) => void;
4
7
  export declare const initLanguage: (translations_: any, defaultLanguage?: string) => void;
package/dist/jux-web.js CHANGED
@@ -6,7 +6,7 @@ var juxWeb = (function (exports) {
6
6
  if (prefix !== undefined) {
7
7
  throw 'prefix was already set to ' + prefix;
8
8
  }
9
- if (!s) {
9
+ if (!s && s !== '') {
10
10
  throw 'prefix argument is ' + s + ', use empty string if you want no prefix.';
11
11
  }
12
12
  prefix = s;
@@ -36,6 +36,61 @@ var juxWeb = (function (exports) {
36
36
  };
37
37
  // @ts-ignore
38
38
  const getTranslation = (k) => translations[k] || k;
39
+ const formatTextToMarkdown = (container, text) => {
40
+ // Split the text on bold markers (**...**), <br>, markdown-style links [text](url),
41
+ // and semantic color tags like {warning|text}.
42
+ // The regex uses a capture group to include the delimiters in the results.
43
+ const parts = text.split(/(\*\*.*?\*\*|<br>|\[.*?\]\(https?:\/\/.*?\)|\{[a-z0-9-]+\|.*?\})/gi);
44
+ parts.forEach(part => {
45
+ if (!part)
46
+ return;
47
+ if (part === '<br>') {
48
+ container.appendChild(document.createElement('br'));
49
+ return;
50
+ }
51
+ if (part.startsWith('**') && part.endsWith('**')) {
52
+ const bold = document.createElement('b');
53
+ bold.textContent = part.slice(2, -2);
54
+ container.appendChild(bold);
55
+ return;
56
+ }
57
+ if (part.startsWith('[') && part.includes('](') && part.endsWith(')')) {
58
+ const match = part.match(/\[(.*?)\]\((https?:\/\/.*?)\)/);
59
+ if (match) {
60
+ const [, label, href] = match;
61
+ const link = document.createElement('a');
62
+ link.href = href;
63
+ link.textContent = label;
64
+ link.target = '_blank';
65
+ link.rel = 'noopener noreferrer';
66
+ container.appendChild(link);
67
+ return;
68
+ }
69
+ }
70
+ if (part.startsWith('{') && part.includes('|') && part.endsWith('}')) {
71
+ const match = part.match(/\{([a-z0-9-]+)\|(.*?)\}/i);
72
+ if (match) {
73
+ const [, className, text] = match;
74
+ const coloredText = document.createElement('span');
75
+ coloredText.className = `${className.toLowerCase()}`;
76
+ formatTextToMarkdown(coloredText, text);
77
+ container.appendChild(coloredText);
78
+ return;
79
+ }
80
+ }
81
+ container.appendChild(document.createTextNode(part));
82
+ });
83
+ };
84
+ const createFormattedParagraph = (text) => {
85
+ const paragraph = document.createElement('p');
86
+ formatTextToMarkdown(paragraph, text);
87
+ return paragraph;
88
+ };
89
+ const applyFormattedTranslation = (element, key) => {
90
+ const wrapper = document.createElement('span');
91
+ formatTextToMarkdown(wrapper, getTranslation(key));
92
+ element.replaceChildren(...Array.from(wrapper.childNodes));
93
+ };
39
94
  const initLanguage = (translations_, defaultLanguage = 'en') => {
40
95
  exports.chosenLanguage = getLocalStorageItem(OVERRIDE_KEY) || getBrowserLanguage();
41
96
  translations = translations_[exports.chosenLanguage] || translations_[defaultLanguage];
@@ -1045,6 +1100,8 @@ var juxWeb = (function (exports) {
1045
1100
  container.style.justifyContent = 'center';
1046
1101
  container.style.margin = '0 auto';
1047
1102
  container.style.paddingTop = '0';
1103
+ container.style.paddingInline = '36px';
1104
+ container.style.boxSizing = 'border-box';
1048
1105
  };
1049
1106
  const applyBtnsContainerStyles = (container) => {
1050
1107
  container.style.display = 'flex';
@@ -1141,14 +1198,19 @@ var juxWeb = (function (exports) {
1141
1198
  };
1142
1199
  const appendLoginTypes = (loginPage, btnsContainer, clientApp, onUserLogin, onLoginError, loginTypes, backgroundImg, explanation, handleSSOLogin, handlers) => {
1143
1200
  const upperLoginTypes = loginTypes.map(str => str.toUpperCase());
1201
+ if (upperLoginTypes.length === 0) {
1202
+ applyFormattedTranslation(explanation, 'sign-in-no-login-type');
1203
+ btnsContainer.replaceChildren();
1204
+ return;
1205
+ }
1144
1206
  const onlyAuth = upperLoginTypes.length > 0 &&
1145
1207
  upperLoginTypes.every(type => type === 'AUTH-PASSWORD');
1146
- explanation.textContent = getTranslation('sign-in-explanation');
1208
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1147
1209
  if (upperLoginTypes.includes('AUTH-PASSWORD2') || onlyAuth) {
1148
1210
  btnsContainer.replaceChildren();
1149
1211
  btnsContainer.appendChild(authPasswordForm(clientApp, loginPage, onUserLogin, onLoginError, backgroundImg, handlers));
1150
1212
  upperLoginTypes.pop();
1151
- explanation.textContent = getTranslation('sign-in-email');
1213
+ applyFormattedTranslation(explanation, 'sign-in-email');
1152
1214
  if (!onlyAuth) {
1153
1215
  btnsContainer.appendChild(backButton('Back', () => {
1154
1216
  btnsContainer.replaceChildren();
@@ -1187,7 +1249,7 @@ var juxWeb = (function (exports) {
1187
1249
  const salutation = document.createElement('sign-in-salutation');
1188
1250
  salutation.textContent = getTranslation('sign-in-salutation');
1189
1251
  const explanation = document.createElement('sign-in-explanation');
1190
- explanation.textContent = getTranslation('sign-in-explanation');
1252
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1191
1253
  const msg = document.createElement('sign-in-msg');
1192
1254
  msg.textContent = getTranslation('sign-in-msg-general');
1193
1255
  content.push(salutation, explanation, msg);
@@ -14838,7 +14900,7 @@ var juxWeb = (function (exports) {
14838
14900
  email.style.marginBottom = '10px';
14839
14901
  const howTo = document.createElement('p');
14840
14902
  howTo.id = 'magic-link-how-to';
14841
- howTo.innerHTML = getTranslation(howTo.id);
14903
+ applyFormattedTranslation(howTo, howTo.id);
14842
14904
  howTo.style.padding = '0 30px';
14843
14905
  howTo.style.textAlign = 'center';
14844
14906
  const btn = document.createElement('button');
@@ -14873,7 +14935,7 @@ var juxWeb = (function (exports) {
14873
14935
  email.style.marginBottom = '10px';
14874
14936
  const info = document.createElement('p');
14875
14937
  info.id = 'magic-link-fail-info';
14876
- info.innerHTML = getTranslation(failInfo || info.id);
14938
+ applyFormattedTranslation(info, failInfo || info.id);
14877
14939
  info.style.padding = '0 30px';
14878
14940
  info.style.textAlign = 'center';
14879
14941
  const back = document.createElement('a');
@@ -14951,9 +15013,9 @@ var juxWeb = (function (exports) {
14951
15013
  window.juxWebGlobal.fetchUserBackendUrl = fetchUserBackendUrl;
14952
15014
  window.juxWebGlobal.magicLinkRequestEndpoint = magicLinkRequestEndpoint;
14953
15015
  window.juxWebGlobal.magicLinkAuthEndpoint = magicLinkAuthEndpoint;
14954
- const supportedLoginTypesArray = Array.isArray(supportedLoginTypes)
15016
+ const supportedLoginTypesArray = (Array.isArray(supportedLoginTypes)
14955
15017
  ? supportedLoginTypes
14956
- : [supportedLoginTypes];
15018
+ : [supportedLoginTypes]).filter((loginType) => typeof loginType === 'string' && loginType.trim().length > 0);
14957
15019
  const signInToken = extractTokenFromWindowLocation('sign-in') || extractTokenFromWindowLocation('magic-link');
14958
15020
  if (signInToken) {
14959
15021
  const onOpenMagicLink = () => {
@@ -15102,6 +15164,8 @@ var juxWeb = (function (exports) {
15102
15164
 
15103
15165
  exports.clearSession = clearSession;
15104
15166
  exports.command = command;
15167
+ exports.createFormattedParagraph = createFormattedParagraph;
15168
+ exports.formatTextToMarkdown = formatTextToMarkdown;
15105
15169
  exports.getCurrentUser = getCurrentUser;
15106
15170
  exports.getTranslation = getTranslation;
15107
15171
  exports.handleJuxEvent = handleJuxEvent;
package/dist/main.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { chosenLanguage, reloadWithLanguageOverride, initLanguage, getTranslation } from './jux/language';
1
+ export { chosenLanguage, reloadWithLanguageOverride, initLanguage, getTranslation, formatTextToMarkdown, createFormattedParagraph } from './jux/language';
2
2
  export { loginButton } from './login/login-button';
3
3
  export { loginPage } from './login/login-page';
4
4
  export { handleMagicLinkRequest, initSession, onMicrosoftSignIn } from './login/session';
package/dist/main.esm.js CHANGED
@@ -5,7 +5,7 @@ const setLocalStoragePrefix = (s) => {
5
5
  if (prefix !== undefined) {
6
6
  throw 'prefix was already set to ' + prefix;
7
7
  }
8
- if (!s) {
8
+ if (!s && s !== '') {
9
9
  throw 'prefix argument is ' + s + ', use empty string if you want no prefix.';
10
10
  }
11
11
  prefix = s;
@@ -35,6 +35,61 @@ const reloadWithLanguageOverride = (lang) => {
35
35
  };
36
36
  // @ts-ignore
37
37
  const getTranslation = (k) => translations[k] || k;
38
+ const formatTextToMarkdown = (container, text) => {
39
+ // Split the text on bold markers (**...**), <br>, markdown-style links [text](url),
40
+ // and semantic color tags like {warning|text}.
41
+ // The regex uses a capture group to include the delimiters in the results.
42
+ const parts = text.split(/(\*\*.*?\*\*|<br>|\[.*?\]\(https?:\/\/.*?\)|\{[a-z0-9-]+\|.*?\})/gi);
43
+ parts.forEach(part => {
44
+ if (!part)
45
+ return;
46
+ if (part === '<br>') {
47
+ container.appendChild(document.createElement('br'));
48
+ return;
49
+ }
50
+ if (part.startsWith('**') && part.endsWith('**')) {
51
+ const bold = document.createElement('b');
52
+ bold.textContent = part.slice(2, -2);
53
+ container.appendChild(bold);
54
+ return;
55
+ }
56
+ if (part.startsWith('[') && part.includes('](') && part.endsWith(')')) {
57
+ const match = part.match(/\[(.*?)\]\((https?:\/\/.*?)\)/);
58
+ if (match) {
59
+ const [, label, href] = match;
60
+ const link = document.createElement('a');
61
+ link.href = href;
62
+ link.textContent = label;
63
+ link.target = '_blank';
64
+ link.rel = 'noopener noreferrer';
65
+ container.appendChild(link);
66
+ return;
67
+ }
68
+ }
69
+ if (part.startsWith('{') && part.includes('|') && part.endsWith('}')) {
70
+ const match = part.match(/\{([a-z0-9-]+)\|(.*?)\}/i);
71
+ if (match) {
72
+ const [, className, text] = match;
73
+ const coloredText = document.createElement('span');
74
+ coloredText.className = `${className.toLowerCase()}`;
75
+ formatTextToMarkdown(coloredText, text);
76
+ container.appendChild(coloredText);
77
+ return;
78
+ }
79
+ }
80
+ container.appendChild(document.createTextNode(part));
81
+ });
82
+ };
83
+ const createFormattedParagraph = (text) => {
84
+ const paragraph = document.createElement('p');
85
+ formatTextToMarkdown(paragraph, text);
86
+ return paragraph;
87
+ };
88
+ const applyFormattedTranslation = (element, key) => {
89
+ const wrapper = document.createElement('span');
90
+ formatTextToMarkdown(wrapper, getTranslation(key));
91
+ element.replaceChildren(...Array.from(wrapper.childNodes));
92
+ };
38
93
  const initLanguage = (translations_, defaultLanguage = 'en') => {
39
94
  chosenLanguage = getLocalStorageItem(OVERRIDE_KEY) || getBrowserLanguage();
40
95
  translations = translations_[chosenLanguage] || translations_[defaultLanguage];
@@ -1044,6 +1099,8 @@ const applyContainerStyles = (container) => {
1044
1099
  container.style.justifyContent = 'center';
1045
1100
  container.style.margin = '0 auto';
1046
1101
  container.style.paddingTop = '0';
1102
+ container.style.paddingInline = '36px';
1103
+ container.style.boxSizing = 'border-box';
1047
1104
  };
1048
1105
  const applyBtnsContainerStyles = (container) => {
1049
1106
  container.style.display = 'flex';
@@ -1140,14 +1197,19 @@ const onEmailLoginRequest = (clientApp, loginPage, onUserLogin, backgroundImage,
1140
1197
  };
1141
1198
  const appendLoginTypes = (loginPage, btnsContainer, clientApp, onUserLogin, onLoginError, loginTypes, backgroundImg, explanation, handleSSOLogin, handlers) => {
1142
1199
  const upperLoginTypes = loginTypes.map(str => str.toUpperCase());
1200
+ if (upperLoginTypes.length === 0) {
1201
+ applyFormattedTranslation(explanation, 'sign-in-no-login-type');
1202
+ btnsContainer.replaceChildren();
1203
+ return;
1204
+ }
1143
1205
  const onlyAuth = upperLoginTypes.length > 0 &&
1144
1206
  upperLoginTypes.every(type => type === 'AUTH-PASSWORD');
1145
- explanation.textContent = getTranslation('sign-in-explanation');
1207
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1146
1208
  if (upperLoginTypes.includes('AUTH-PASSWORD2') || onlyAuth) {
1147
1209
  btnsContainer.replaceChildren();
1148
1210
  btnsContainer.appendChild(authPasswordForm(clientApp, loginPage, onUserLogin, onLoginError, backgroundImg, handlers));
1149
1211
  upperLoginTypes.pop();
1150
- explanation.textContent = getTranslation('sign-in-email');
1212
+ applyFormattedTranslation(explanation, 'sign-in-email');
1151
1213
  if (!onlyAuth) {
1152
1214
  btnsContainer.appendChild(backButton('Back', () => {
1153
1215
  btnsContainer.replaceChildren();
@@ -1186,7 +1248,7 @@ const loginPage = (clientApp, backgroundImg, onUserLogin, onLoginError, loginTyp
1186
1248
  const salutation = document.createElement('sign-in-salutation');
1187
1249
  salutation.textContent = getTranslation('sign-in-salutation');
1188
1250
  const explanation = document.createElement('sign-in-explanation');
1189
- explanation.textContent = getTranslation('sign-in-explanation');
1251
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1190
1252
  const msg = document.createElement('sign-in-msg');
1191
1253
  msg.textContent = getTranslation('sign-in-msg-general');
1192
1254
  content.push(salutation, explanation, msg);
@@ -1518,7 +1580,7 @@ const magicLinkRequestedPage = (backgroundImg, onReturn) => {
1518
1580
  email.style.marginBottom = '10px';
1519
1581
  const howTo = document.createElement('p');
1520
1582
  howTo.id = 'magic-link-how-to';
1521
- howTo.innerHTML = getTranslation(howTo.id);
1583
+ applyFormattedTranslation(howTo, howTo.id);
1522
1584
  howTo.style.padding = '0 30px';
1523
1585
  howTo.style.textAlign = 'center';
1524
1586
  const btn = document.createElement('button');
@@ -1553,7 +1615,7 @@ const unauthorizedMagicLinkRequestPage = (backgroundImg, onReturn, failInfo) =>
1553
1615
  email.style.marginBottom = '10px';
1554
1616
  const info = document.createElement('p');
1555
1617
  info.id = 'magic-link-fail-info';
1556
- info.innerHTML = getTranslation(failInfo || info.id);
1618
+ applyFormattedTranslation(info, failInfo || info.id);
1557
1619
  info.style.padding = '0 30px';
1558
1620
  info.style.textAlign = 'center';
1559
1621
  const back = document.createElement('a');
@@ -1631,9 +1693,9 @@ const initSession = ({ clientApp, supportedLoginTypes, onUserLogin, onLoginError
1631
1693
  window.juxWebGlobal.fetchUserBackendUrl = fetchUserBackendUrl;
1632
1694
  window.juxWebGlobal.magicLinkRequestEndpoint = magicLinkRequestEndpoint;
1633
1695
  window.juxWebGlobal.magicLinkAuthEndpoint = magicLinkAuthEndpoint;
1634
- const supportedLoginTypesArray = Array.isArray(supportedLoginTypes)
1696
+ const supportedLoginTypesArray = (Array.isArray(supportedLoginTypes)
1635
1697
  ? supportedLoginTypes
1636
- : [supportedLoginTypes];
1698
+ : [supportedLoginTypes]).filter((loginType) => typeof loginType === 'string' && loginType.trim().length > 0);
1637
1699
  const signInToken = extractTokenFromWindowLocation('sign-in') || extractTokenFromWindowLocation('magic-link');
1638
1700
  if (signInToken) {
1639
1701
  const onOpenMagicLink = () => {
@@ -1780,4 +1842,4 @@ const command = (element, endpoint, onResult, params, onError, onRedirect) => {
1780
1842
  element.dispatchEvent(new JuxEvent('command', 'POST', endpoint, onResult, params, onError, onRedirect));
1781
1843
  };
1782
1844
 
1783
- export { chosenLanguage, clearSession, command, getCurrentUser, getTranslation, handleJuxEvent, handleMagicLinkRequest, initLanguage, initSession, loginButton, loginPage, magicLinkModal, onMicrosoftSignIn, query, reloadWithLanguageOverride, requestMagicLink, resetJuxWebGlobal, setLocalStoragePrefix, upload };
1845
+ export { chosenLanguage, clearSession, command, createFormattedParagraph, formatTextToMarkdown, getCurrentUser, getTranslation, handleJuxEvent, handleMagicLinkRequest, initLanguage, initSession, loginButton, loginPage, magicLinkModal, onMicrosoftSignIn, query, reloadWithLanguageOverride, requestMagicLink, resetJuxWebGlobal, setLocalStoragePrefix, upload };
package/dist/main.js CHANGED
@@ -29,7 +29,7 @@ const setLocalStoragePrefix = (s) => {
29
29
  if (prefix !== undefined) {
30
30
  throw 'prefix was already set to ' + prefix;
31
31
  }
32
- if (!s) {
32
+ if (!s && s !== '') {
33
33
  throw 'prefix argument is ' + s + ', use empty string if you want no prefix.';
34
34
  }
35
35
  prefix = s;
@@ -59,6 +59,61 @@ const reloadWithLanguageOverride = (lang) => {
59
59
  };
60
60
  // @ts-ignore
61
61
  const getTranslation = (k) => translations[k] || k;
62
+ const formatTextToMarkdown = (container, text) => {
63
+ // Split the text on bold markers (**...**), <br>, markdown-style links [text](url),
64
+ // and semantic color tags like {warning|text}.
65
+ // The regex uses a capture group to include the delimiters in the results.
66
+ const parts = text.split(/(\*\*.*?\*\*|<br>|\[.*?\]\(https?:\/\/.*?\)|\{[a-z0-9-]+\|.*?\})/gi);
67
+ parts.forEach(part => {
68
+ if (!part)
69
+ return;
70
+ if (part === '<br>') {
71
+ container.appendChild(document.createElement('br'));
72
+ return;
73
+ }
74
+ if (part.startsWith('**') && part.endsWith('**')) {
75
+ const bold = document.createElement('b');
76
+ bold.textContent = part.slice(2, -2);
77
+ container.appendChild(bold);
78
+ return;
79
+ }
80
+ if (part.startsWith('[') && part.includes('](') && part.endsWith(')')) {
81
+ const match = part.match(/\[(.*?)\]\((https?:\/\/.*?)\)/);
82
+ if (match) {
83
+ const [, label, href] = match;
84
+ const link = document.createElement('a');
85
+ link.href = href;
86
+ link.textContent = label;
87
+ link.target = '_blank';
88
+ link.rel = 'noopener noreferrer';
89
+ container.appendChild(link);
90
+ return;
91
+ }
92
+ }
93
+ if (part.startsWith('{') && part.includes('|') && part.endsWith('}')) {
94
+ const match = part.match(/\{([a-z0-9-]+)\|(.*?)\}/i);
95
+ if (match) {
96
+ const [, className, text] = match;
97
+ const coloredText = document.createElement('span');
98
+ coloredText.className = `${className.toLowerCase()}`;
99
+ formatTextToMarkdown(coloredText, text);
100
+ container.appendChild(coloredText);
101
+ return;
102
+ }
103
+ }
104
+ container.appendChild(document.createTextNode(part));
105
+ });
106
+ };
107
+ const createFormattedParagraph = (text) => {
108
+ const paragraph = document.createElement('p');
109
+ formatTextToMarkdown(paragraph, text);
110
+ return paragraph;
111
+ };
112
+ const applyFormattedTranslation = (element, key) => {
113
+ const wrapper = document.createElement('span');
114
+ formatTextToMarkdown(wrapper, getTranslation(key));
115
+ element.replaceChildren(...Array.from(wrapper.childNodes));
116
+ };
62
117
  const initLanguage = (translations_, defaultLanguage = 'en') => {
63
118
  exports.chosenLanguage = getLocalStorageItem(OVERRIDE_KEY) || getBrowserLanguage();
64
119
  translations = translations_[exports.chosenLanguage] || translations_[defaultLanguage];
@@ -1068,6 +1123,8 @@ const applyContainerStyles = (container) => {
1068
1123
  container.style.justifyContent = 'center';
1069
1124
  container.style.margin = '0 auto';
1070
1125
  container.style.paddingTop = '0';
1126
+ container.style.paddingInline = '36px';
1127
+ container.style.boxSizing = 'border-box';
1071
1128
  };
1072
1129
  const applyBtnsContainerStyles = (container) => {
1073
1130
  container.style.display = 'flex';
@@ -1164,14 +1221,19 @@ const onEmailLoginRequest = (clientApp, loginPage, onUserLogin, backgroundImage,
1164
1221
  };
1165
1222
  const appendLoginTypes = (loginPage, btnsContainer, clientApp, onUserLogin, onLoginError, loginTypes, backgroundImg, explanation, handleSSOLogin, handlers) => {
1166
1223
  const upperLoginTypes = loginTypes.map(str => str.toUpperCase());
1224
+ if (upperLoginTypes.length === 0) {
1225
+ applyFormattedTranslation(explanation, 'sign-in-no-login-type');
1226
+ btnsContainer.replaceChildren();
1227
+ return;
1228
+ }
1167
1229
  const onlyAuth = upperLoginTypes.length > 0 &&
1168
1230
  upperLoginTypes.every(type => type === 'AUTH-PASSWORD');
1169
- explanation.textContent = getTranslation('sign-in-explanation');
1231
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1170
1232
  if (upperLoginTypes.includes('AUTH-PASSWORD2') || onlyAuth) {
1171
1233
  btnsContainer.replaceChildren();
1172
1234
  btnsContainer.appendChild(authPasswordForm(clientApp, loginPage, onUserLogin, onLoginError, backgroundImg, handlers));
1173
1235
  upperLoginTypes.pop();
1174
- explanation.textContent = getTranslation('sign-in-email');
1236
+ applyFormattedTranslation(explanation, 'sign-in-email');
1175
1237
  if (!onlyAuth) {
1176
1238
  btnsContainer.appendChild(backButton('Back', () => {
1177
1239
  btnsContainer.replaceChildren();
@@ -1210,7 +1272,7 @@ const loginPage = (clientApp, backgroundImg, onUserLogin, onLoginError, loginTyp
1210
1272
  const salutation = document.createElement('sign-in-salutation');
1211
1273
  salutation.textContent = getTranslation('sign-in-salutation');
1212
1274
  const explanation = document.createElement('sign-in-explanation');
1213
- explanation.textContent = getTranslation('sign-in-explanation');
1275
+ applyFormattedTranslation(explanation, 'sign-in-explanation');
1214
1276
  const msg = document.createElement('sign-in-msg');
1215
1277
  msg.textContent = getTranslation('sign-in-msg-general');
1216
1278
  content.push(salutation, explanation, msg);
@@ -1542,7 +1604,7 @@ const magicLinkRequestedPage = (backgroundImg, onReturn) => {
1542
1604
  email.style.marginBottom = '10px';
1543
1605
  const howTo = document.createElement('p');
1544
1606
  howTo.id = 'magic-link-how-to';
1545
- howTo.innerHTML = getTranslation(howTo.id);
1607
+ applyFormattedTranslation(howTo, howTo.id);
1546
1608
  howTo.style.padding = '0 30px';
1547
1609
  howTo.style.textAlign = 'center';
1548
1610
  const btn = document.createElement('button');
@@ -1577,7 +1639,7 @@ const unauthorizedMagicLinkRequestPage = (backgroundImg, onReturn, failInfo) =>
1577
1639
  email.style.marginBottom = '10px';
1578
1640
  const info = document.createElement('p');
1579
1641
  info.id = 'magic-link-fail-info';
1580
- info.innerHTML = getTranslation(failInfo || info.id);
1642
+ applyFormattedTranslation(info, failInfo || info.id);
1581
1643
  info.style.padding = '0 30px';
1582
1644
  info.style.textAlign = 'center';
1583
1645
  const back = document.createElement('a');
@@ -1655,9 +1717,9 @@ const initSession = ({ clientApp, supportedLoginTypes, onUserLogin, onLoginError
1655
1717
  window.juxWebGlobal.fetchUserBackendUrl = fetchUserBackendUrl;
1656
1718
  window.juxWebGlobal.magicLinkRequestEndpoint = magicLinkRequestEndpoint;
1657
1719
  window.juxWebGlobal.magicLinkAuthEndpoint = magicLinkAuthEndpoint;
1658
- const supportedLoginTypesArray = Array.isArray(supportedLoginTypes)
1720
+ const supportedLoginTypesArray = (Array.isArray(supportedLoginTypes)
1659
1721
  ? supportedLoginTypes
1660
- : [supportedLoginTypes];
1722
+ : [supportedLoginTypes]).filter((loginType) => typeof loginType === 'string' && loginType.trim().length > 0);
1661
1723
  const signInToken = extractTokenFromWindowLocation('sign-in') || extractTokenFromWindowLocation('magic-link');
1662
1724
  if (signInToken) {
1663
1725
  const onOpenMagicLink = () => {
@@ -1806,6 +1868,8 @@ const command = (element, endpoint, onResult, params, onError, onRedirect) => {
1806
1868
 
1807
1869
  exports.clearSession = clearSession;
1808
1870
  exports.command = command;
1871
+ exports.createFormattedParagraph = createFormattedParagraph;
1872
+ exports.formatTextToMarkdown = formatTextToMarkdown;
1809
1873
  exports.getCurrentUser = getCurrentUser;
1810
1874
  exports.getTranslation = getTranslation;
1811
1875
  exports.handleJuxEvent = handleJuxEvent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klauswuestefeld/jux-web",
3
- "version": "2026.3.6",
3
+ "version": "2026.3.16",
4
4
  "author": "Jux.House",
5
5
  "license": "Copyrighted",
6
6
  "description": "",