@codenotch/codenotch.react 1.0.82 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeEditor.d.ts +1 -1
- package/dist/components/CodeEditor.d.ts.map +1 -1
- package/dist/components/CodeEditor.js +66 -64
- package/dist/index.d.ts +67 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +134 -113
- package/dist/models/Codenotch.d.ts +28 -131
- package/dist/models/Codenotch.d.ts.map +1 -1
- package/package.json +45 -44
- package/src/components/CodeEditor.tsx +204 -203
- package/src/index.ts +399 -385
- package/src/models/Codenotch.ts +29 -148
- package/README.md +0 -145
- package/dist/components/Auml.d.ts +0 -46
- package/dist/components/Auml.d.ts.map +0 -1
- package/dist/components/Auml.js +0 -208
- package/dist/models/AppManifestModels.d.ts +0 -46
- package/dist/models/AppManifestModels.d.ts.map +0 -1
- package/dist/models/AppManifestModels.js +0 -2
- package/dist/models/ProjectManifestModels.d.ts +0 -126
- package/dist/models/ProjectManifestModels.d.ts.map +0 -1
- package/dist/models/ProjectManifestModels.js +0 -158
- package/dist/utils/I18nUtils.d.ts +0 -7
- package/dist/utils/I18nUtils.d.ts.map +0 -1
- package/dist/utils/I18nUtils.js +0 -37
- package/src/core/ProcessUtils.ts +0 -86
- package/src/core/SignalR.ts +0 -375
package/src/index.ts
CHANGED
|
@@ -1,386 +1,400 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { IDisposable } from "./models/Misc";
|
|
5
|
-
import { ICodenotchApi, ICodenotchDialog, ICodenotchEnv, IProcessResult } from "./models/Codenotch";
|
|
6
|
-
import { v4 } from "uuid";
|
|
7
|
-
import CodeEditor from "./components/CodeEditor";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
1
|
+
import { createElement, useSyncExternalStore, type ComponentType, type ComponentProps, type FunctionComponent, type ReactNode, type RefAttributes } from "react";
|
|
2
|
+
import { flushSync } from "react-dom";
|
|
3
|
+
import { createRoot, Root } from "react-dom/client";
|
|
4
|
+
import { IDisposable } from "./models/Misc";
|
|
5
|
+
import { ICodenotchApi, ICodenotchDialog, ICodenotchEnv, IProcessResult, WithCodenotchProps } from "./models/Codenotch";
|
|
6
|
+
import { v4 } from "uuid";
|
|
7
|
+
import CodeEditor from "./components/CodeEditor";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The Codenotch runtime environment, populated by {@link init}.
|
|
11
|
+
* Prefer reading it through `useCodenotch().env`.
|
|
12
|
+
*/
|
|
13
|
+
const env: ICodenotchEnv = {};
|
|
14
|
+
|
|
15
|
+
const MISSING_CLUSTER_URL_ERROR = "Codenotch cluster URL is not defined. Please set it in the Codenotch configuration.";
|
|
16
|
+
const MISSING_SERVICE_NAME_ERROR = "Codenotch service name is not defined. Please set it in the Codenotch configuration.";
|
|
17
|
+
const MISSING_TENANT_NAME_ERROR = "Codenotch tenant name is not defined. Please set it in the Codenotch configuration.";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Change tracking. `env` is a plain mutable object; every mutation done through
|
|
21
|
+
// the public API (init, setTheme, setLanguage) goes through notify(), which
|
|
22
|
+
// rotates the API object (so React sees a new reference) and wakes up the
|
|
23
|
+
// subscribers: useCodenotch() via useSyncExternalStore, withCodenotch(),
|
|
24
|
+
// and any listener registered with onCodenotchChange().
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
const listeners = new Set<() => void>();
|
|
27
|
+
let currentApi: ICodenotchApi;
|
|
28
|
+
|
|
29
|
+
function notify(): void {
|
|
30
|
+
currentApi = createApi();
|
|
31
|
+
listeners.forEach((listener) => {
|
|
32
|
+
try { listener(); }
|
|
33
|
+
catch (error) { console.error("Error in Codenotch change listener:", error); }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function subscribe(listener: () => void): () => void {
|
|
38
|
+
listeners.add(listener);
|
|
39
|
+
return () => { listeners.delete(listener); };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Initialize the Codenotch environment from the given key-value pairs.
|
|
44
|
+
*
|
|
45
|
+
* This is normally called once, by the page hosting the application, with the
|
|
46
|
+
* environment variables injected by the Codenotch runtime (URL parameters or
|
|
47
|
+
* any other source). It must be called before `useCodenotch()` is used.
|
|
48
|
+
*
|
|
49
|
+
* Recognized key formats:
|
|
50
|
+
* The first language found becomes the current language if none is set.
|
|
51
|
+
* - `GLOBAL.<name>` — JSON value assigned to `window[name]` (or `globalThis`).
|
|
52
|
+
* - `appManifest` / `projectManifest` — parsed as JSON into the environment.
|
|
53
|
+
* - anything else — copied as-is onto {@link env}.
|
|
54
|
+
*
|
|
55
|
+
* The UI theme is resolved from the `_theme` URL parameter, or from
|
|
56
|
+
* `prefers-color-scheme` as a fallback.
|
|
57
|
+
*
|
|
58
|
+
* @param envVariables An object containing the Codenotch environment variables as key-value pairs.
|
|
59
|
+
* @example
|
|
60
|
+
* import { init } from 'codenotch-react';
|
|
61
|
+
* init({
|
|
62
|
+
* clusterUrl: 'https://cluster.example.com',
|
|
63
|
+
* serviceName: 'myproject',
|
|
64
|
+
* tenantName: 'acme'
|
|
65
|
+
* });
|
|
66
|
+
*/
|
|
67
|
+
function init(envVariables: any): void {
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// Ensure envVariables is an object
|
|
71
|
+
if (typeof envVariables === 'object' && envVariables !== null) {
|
|
72
|
+
|
|
73
|
+
Object.keys(envVariables).forEach((key) => {
|
|
74
|
+
try {
|
|
75
|
+
if (key.startsWith('GLOBAL.')) {
|
|
76
|
+
// Global variable, we set it directly on the window object
|
|
77
|
+
let globalKey = key.substring('GLOBAL.'.length);
|
|
78
|
+
let globalObject = typeof window !== 'undefined' ? window : globalThis;
|
|
79
|
+
(globalObject as any)[globalKey] = JSON.parse(envVariables[key]);
|
|
80
|
+
console.log(`Codenotch global variable ${globalKey} set!`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
(env as any)[key] = envVariables[key];
|
|
85
|
+
|
|
86
|
+
if (['appManifest', 'projectManifest'].includes(key)) {
|
|
87
|
+
// Must be Json
|
|
88
|
+
try {
|
|
89
|
+
(env as any)[key] = JSON.parse(envVariables[key]);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.warn(`Codenotch environment variable ${key} is not a valid JSON string. Using raw value.`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`Codenotch environment variable ${key} set to ${(env as any)[key]}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error(`Error processing Codenotch environment variable ${key}:`, error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Get theme like AEC does
|
|
106
|
+
try {
|
|
107
|
+
let urlParams = Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
|
108
|
+
if (urlParams["_theme"]) {
|
|
109
|
+
let theme = urlParams["_theme"].toLowerCase();
|
|
110
|
+
if (theme === "light" || theme === "dark") {
|
|
111
|
+
env.theme = theme;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
115
|
+
env.theme = 'dark';
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
env.theme = 'light';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
notify();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Build the API object bound to {@link env}. Every method reads `env` lazily,
|
|
129
|
+
* so an instance never goes stale; a new one is created by {@link notify} only
|
|
130
|
+
* to give React a fresh reference when the environment changes.
|
|
131
|
+
*/
|
|
132
|
+
function createApi(): ICodenotchApi {
|
|
133
|
+
const api: ICodenotchApi = {
|
|
134
|
+
env: env,
|
|
135
|
+
uuid: () => v4(),
|
|
136
|
+
getProjectFileUrl: (relativePath: string): string => {
|
|
137
|
+
if (env.clusterUrl === undefined) throw new Error(MISSING_CLUSTER_URL_ERROR);
|
|
138
|
+
if (env.serviceName === undefined) throw new Error(MISSING_SERVICE_NAME_ERROR);
|
|
139
|
+
relativePath = relativePath.replace(/\\/g, "/");
|
|
140
|
+
if (relativePath.startsWith("/")) relativePath = relativePath.substring(1);
|
|
141
|
+
let url = `${env.clusterUrl}/${env.serviceName}/project/file?name=""&path=${encodeURIComponent(relativePath)}`;
|
|
142
|
+
return url;
|
|
143
|
+
},
|
|
144
|
+
getProjectFile: async (relativePath: string): Promise<string | undefined> => {
|
|
145
|
+
let url = api.getProjectFileUrl(relativePath);
|
|
146
|
+
let resp = await fetch(url);
|
|
147
|
+
if (resp.status.toString().startsWith('2') === false) {
|
|
148
|
+
throw new Error(`Failed to fetch file content: ${resp.status} ${resp.statusText}`);
|
|
149
|
+
}
|
|
150
|
+
let content = await resp.text();
|
|
151
|
+
return content;
|
|
152
|
+
},
|
|
153
|
+
requestCnql: async (cnql: string, verbose?: boolean): Promise<any> => {
|
|
154
|
+
if (env.clusterUrl === undefined) throw new Error(MISSING_CLUSTER_URL_ERROR);
|
|
155
|
+
if (env.serviceName === undefined) throw new Error(MISSING_SERVICE_NAME_ERROR);
|
|
156
|
+
|
|
157
|
+
const request: RequestInit = {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
mode: 'cors',
|
|
160
|
+
credentials: 'same-origin',
|
|
161
|
+
headers: { 'Content-Type': 'application/json' },
|
|
162
|
+
body: `"${cnql.replace(/\"/g, '\\\"')}"`
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
if (`${env.accessToken ?? ''}`.trim() !== "") {
|
|
166
|
+
if (env.tenantName === undefined) throw new Error(MISSING_TENANT_NAME_ERROR);
|
|
167
|
+
request.headers = {
|
|
168
|
+
...request.headers, [`${env.tenantName}AccessToken`]: env.accessToken!
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const response = await fetch(`${env.clusterUrl}/${env.serviceName}/cnql${verbose ? "?v=true" : ""}`, request);
|
|
173
|
+
if (!response.ok) {
|
|
174
|
+
const error = await response.text();
|
|
175
|
+
throw new Error(error);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return await response.json();
|
|
179
|
+
},
|
|
180
|
+
startProcess: async <TProcess, TInput, TOutput>(process: TProcess, inputs: TInput): Promise<IProcessResult<TOutput>> => {
|
|
181
|
+
throw new Error('Not implemented.');
|
|
182
|
+
},
|
|
183
|
+
getUrlParams: () => {
|
|
184
|
+
let result: { [key: string]: string } = {};
|
|
185
|
+
if (typeof window !== 'undefined') {
|
|
186
|
+
const params = new URL(window.location.href).searchParams;
|
|
187
|
+
params.forEach((value: string, key: string) => {
|
|
188
|
+
if (result[key] === undefined) {
|
|
189
|
+
result[key] = value;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
},
|
|
195
|
+
showDialog: (node: ReactNode): ICodenotchDialog => {
|
|
196
|
+
let dialogId = "codenotch-dialog-" + Math.random().toString(36).substring(2, 9);
|
|
197
|
+
let root: Root | undefined = undefined;
|
|
198
|
+
let result: ICodenotchDialog = {
|
|
199
|
+
id: dialogId,
|
|
200
|
+
close: () => {
|
|
201
|
+
// Unmount the React tree first so effects are cleaned up and nothing leaks,
|
|
202
|
+
// then drop the <dialog> element itself.
|
|
203
|
+
root?.unmount();
|
|
204
|
+
root = undefined;
|
|
205
|
+
let dialogElement = document.getElementById(dialogId);
|
|
206
|
+
if (dialogElement) {
|
|
207
|
+
dialogElement.remove();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Find "cn-dialog-container" element in the page
|
|
213
|
+
let container = document.getElementById("cn-dialog-container");
|
|
214
|
+
if (container === null) {
|
|
215
|
+
container = document.createElement("div");
|
|
216
|
+
container.id = "cn-dialog-container";
|
|
217
|
+
container.style.zIndex = "9999";
|
|
218
|
+
container.style.position = "fixed";
|
|
219
|
+
container.style.top = "0";
|
|
220
|
+
container.style.left = "0";
|
|
221
|
+
container.style.minWidth = "100vw";
|
|
222
|
+
container.style.minHeight = "100vh";
|
|
223
|
+
container.style.maxWidth = "100vw";
|
|
224
|
+
container.style.maxHeight = "100vh";
|
|
225
|
+
container.style.display = "flex";
|
|
226
|
+
container.style.alignItems = "center";
|
|
227
|
+
container.style.justifyContent = "center";
|
|
228
|
+
container.style.pointerEvents = 'none';
|
|
229
|
+
document.body.appendChild(container);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let dialogElement = document.createElement('dialog');
|
|
233
|
+
dialogElement.id = dialogId;
|
|
234
|
+
dialogElement.style.zIndex = "9999";
|
|
235
|
+
dialogElement.style.position = "fixed";
|
|
236
|
+
dialogElement.style.top = "0";
|
|
237
|
+
dialogElement.style.left = "0";
|
|
238
|
+
dialogElement.style.minWidth = "100vw";
|
|
239
|
+
dialogElement.style.minHeight = "100vh";
|
|
240
|
+
dialogElement.style.maxWidth = "100vw";
|
|
241
|
+
dialogElement.style.maxHeight = "100vh";
|
|
242
|
+
dialogElement.style.display = "flex";
|
|
243
|
+
dialogElement.style.alignItems = "center";
|
|
244
|
+
dialogElement.style.justifyContent = "center";
|
|
245
|
+
dialogElement.style.pointerEvents = 'all';
|
|
246
|
+
dialogElement.style.background = '#78787822';
|
|
247
|
+
dialogElement.style.backdropFilter = 'blur(2px)';
|
|
248
|
+
|
|
249
|
+
container.appendChild(dialogElement);
|
|
250
|
+
|
|
251
|
+
// React 19: ReactDOM.render() is gone. Render synchronously through a dedicated root
|
|
252
|
+
// so the content is in the DOM before showModal() moves the focus into the dialog.
|
|
253
|
+
root = createRoot(dialogElement);
|
|
254
|
+
flushSync(() => root!.render(node));
|
|
255
|
+
dialogElement.showModal();
|
|
256
|
+
|
|
257
|
+
return result;
|
|
258
|
+
},
|
|
259
|
+
setTheme: (theme: 'light' | 'dark') => {
|
|
260
|
+
if (env.theme === theme) return;
|
|
261
|
+
if (env.theme) document.documentElement.classList.remove(env.theme);
|
|
262
|
+
|
|
263
|
+
env.theme = theme;
|
|
264
|
+
document.documentElement.classList.add(theme);
|
|
265
|
+
notify();
|
|
266
|
+
},
|
|
267
|
+
setLanguage: (lang: string) => {
|
|
268
|
+
if (env.language === lang) return;
|
|
269
|
+
env.language = lang;
|
|
270
|
+
notify();
|
|
271
|
+
},
|
|
272
|
+
getLanguages: () => {
|
|
273
|
+
return env.projectManifest?.languages ?? [];
|
|
274
|
+
},
|
|
275
|
+
getLanguage: () => {
|
|
276
|
+
return env.language;
|
|
277
|
+
},
|
|
278
|
+
getTheme: () => {
|
|
279
|
+
return env.theme;
|
|
280
|
+
},
|
|
281
|
+
getProjectManifest: () => {
|
|
282
|
+
if (env.projectManifest === undefined) {
|
|
283
|
+
throw new Error("Project manifest is not defined in Codenotch environment. Please set it in the Codenotch configuration.");
|
|
284
|
+
}
|
|
285
|
+
return env.projectManifest;
|
|
286
|
+
},
|
|
287
|
+
getAppManifest: () => {
|
|
288
|
+
return env.appManifest;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
return api;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
currentApi = createApi();
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Return the Codenotch client API bound to the current environment, from
|
|
298
|
+
* anywhere: event handlers, plain modules, class components, services…
|
|
299
|
+
*
|
|
300
|
+
* This is a plain function (not a hook). The returned object is never stale —
|
|
301
|
+
* its methods always read the live environment — but it does not trigger any
|
|
302
|
+
* re-render when the language or theme changes: inside React components prefer
|
|
303
|
+
* {@link useCodenotch} (function components) or {@link withCodenotch}
|
|
304
|
+
* (class components), which do.
|
|
305
|
+
*
|
|
306
|
+
* `init()` must have been called first, which the Codenotch runtime does
|
|
307
|
+
* automatically when serving the application.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* import { getCodenotch } from 'codenotch-react';
|
|
311
|
+
*
|
|
312
|
+
* export async function loadTodos(userId: string) {
|
|
313
|
+
* const result = await getCodenotch().startProcess('getTodos', 'start', { UserId: userId });
|
|
314
|
+
* return result.output.todos;
|
|
315
|
+
* }
|
|
316
|
+
*/
|
|
317
|
+
function getCodenotch(): ICodenotchApi {
|
|
318
|
+
return currentApi;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* React hook returning the Codenotch client API.
|
|
323
|
+
*
|
|
324
|
+
* The reference is stable across renders and only changes when the environment
|
|
325
|
+
* changes (`setLanguage`, `setTheme`, `init`), in which case every component
|
|
326
|
+
* using the hook re-renders output follows the current
|
|
327
|
+
* language automatically, and the object is safe to use in `useMemo` /
|
|
328
|
+
* `useEffect` dependency arrays.
|
|
329
|
+
*
|
|
330
|
+
* Regular hook rules apply (call it unconditionally at the top of a function
|
|
331
|
+
* component or a custom hook). Outside of components — handlers defined in
|
|
332
|
+
* plain modules, services, class components — use {@link getCodenotch} or
|
|
333
|
+
* {@link withCodenotch} instead.
|
|
334
|
+
*/
|
|
335
|
+
function useCodenotch(): ICodenotchApi {
|
|
336
|
+
return useSyncExternalStore(subscribe, getCodenotch, getCodenotch);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Register a listener called each time the Codenotch environment changes
|
|
341
|
+
* (`setLanguage`, `setTheme`, `init`). Escape hatch for code that cannot use
|
|
342
|
+
* {@link useCodenotch} or {@link withCodenotch}, e.g. a class component that
|
|
343
|
+
* wants to `forceUpdate()` itself, or a non-React module caching translations.
|
|
344
|
+
*
|
|
345
|
+
* @returns A disposable — call `dispose()` to stop listening.
|
|
346
|
+
* @example
|
|
347
|
+
* componentDidMount() {
|
|
348
|
+
* this.sub = onCodenotchChange(() => this.forceUpdate());
|
|
349
|
+
* }
|
|
350
|
+
* componentWillUnmount() {
|
|
351
|
+
* this.sub.dispose();
|
|
352
|
+
* }
|
|
353
|
+
*/
|
|
354
|
+
function onCodenotchChange(listener: () => void): IDisposable {
|
|
355
|
+
const unsubscribe = subscribe(listener);
|
|
356
|
+
return { dispose: unsubscribe };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** Props of a component wrapped by {@link withCodenotch}, without the injected `cn`. */
|
|
360
|
+
type WithoutCodenotchProps<P> = Omit<P, keyof WithCodenotchProps>;
|
|
361
|
+
|
|
362
|
+
/** Instance type of a component (class components only), used to type the forwarded `ref`. */
|
|
363
|
+
type ComponentInstance<C> = C extends new (...args: any[]) => infer I ? I : never;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Higher-order component injecting the Codenotch API as a `cn` prop.
|
|
367
|
+
*
|
|
368
|
+
* Meant for class components, which cannot call {@link useCodenotch}: the
|
|
369
|
+
* wrapped component receives `this.props.cn` and re-renders whenever the
|
|
370
|
+
* environment changes (language, theme…), exactly like the hook. A `ref`
|
|
371
|
+
* passed to the wrapper is forwarded to the wrapped component instance.
|
|
372
|
+
*
|
|
373
|
+
* @param Component A component whose props extend {@link WithCodenotchProps}.
|
|
374
|
+
* @returns A component with the same props minus `cn`.
|
|
375
|
+
*/
|
|
376
|
+
function withCodenotch<C extends ComponentType<any>>(
|
|
377
|
+
Component: C
|
|
378
|
+
): FunctionComponent<WithoutCodenotchProps<ComponentProps<C>> & RefAttributes<ComponentInstance<C>>> {
|
|
379
|
+
const Wrapped = (props: WithoutCodenotchProps<ComponentProps<C>> & RefAttributes<ComponentInstance<C>>) => {
|
|
380
|
+
const cn = useCodenotch();
|
|
381
|
+
// React 19: `ref` is a regular prop, so spreading `props` forwards it.
|
|
382
|
+
return createElement(Component, { ...props, cn });
|
|
383
|
+
};
|
|
384
|
+
Wrapped.displayName = `withCodenotch(${Component.displayName || Component.name || "Component"})`;
|
|
385
|
+
return Wrapped;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export {
|
|
389
|
+
env,
|
|
390
|
+
useCodenotch,
|
|
391
|
+
getCodenotch,
|
|
392
|
+
withCodenotch,
|
|
393
|
+
onCodenotchChange,
|
|
394
|
+
init,
|
|
395
|
+
CodeEditor
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
export * from "./models/Codenotch";
|
|
399
|
+
export * from "./models/Misc";
|
|
386
400
|
export * from "@codenotch/codenotch.core";
|