wyreframe 0.1.1 → 0.2.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 +9 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +34 -4
- package/src/parser/Detector/BoxTracer.mjs +122 -36
- package/src/parser/Detector/BoxTracer.res +172 -27
- package/src/parser/Detector/ShapeDetector.mjs +27 -9
- package/src/parser/Detector/ShapeDetector.res +15 -8
- package/src/parser/Errors/ErrorMessages.mjs +25 -0
- package/src/parser/Errors/ErrorMessages.res +17 -0
- package/src/parser/Errors/ErrorTypes.mjs +3 -0
- package/src/parser/Errors/ErrorTypes.res +8 -1
- package/src/parser/Parser.gen.tsx +3 -2
- package/src/parser/Parser.mjs +22 -10
- package/src/parser/Parser.res +27 -18
- package/src/renderer/Renderer.gen.tsx +7 -1
- package/src/renderer/Renderer.mjs +126 -25
- package/src/renderer/Renderer.res +116 -12
- package/src/test/Expect.mjs +0 -9
package/src/parser/Parser.res
CHANGED
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
// ============================================================================
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Parse result type - either a successful AST or an array of parse errors.
|
|
11
|
+
* Parse result type - either a successful (AST, warnings) tuple or an array of parse errors.
|
|
12
|
+
* Warnings are non-fatal issues like misaligned borders that don't prevent parsing.
|
|
12
13
|
* This Result type is compatible with TypeScript through GenType.
|
|
13
14
|
*/
|
|
14
|
-
type parseResult = result<Types.ast, array<ErrorTypes.t>>
|
|
15
|
+
type parseResult = result<(Types.ast, array<ErrorTypes.t>), array<ErrorTypes.t>>
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Interaction parse result type.
|
|
@@ -65,11 +66,11 @@ let scanGrid = (wireframe: string): result<Grid.t, array<ErrorTypes.t>> => {
|
|
|
65
66
|
* and builds parent-child hierarchy.
|
|
66
67
|
*
|
|
67
68
|
* @param grid The 2D grid from Stage 1
|
|
68
|
-
* @returns Result containing root boxes or errors
|
|
69
|
+
* @returns Result containing (root boxes, warnings) or errors
|
|
69
70
|
*
|
|
70
71
|
* Requirements: REQ-3, REQ-4, REQ-5, REQ-6, REQ-7 (Shape Detection)
|
|
71
72
|
*/
|
|
72
|
-
let detectShapes = (grid: Grid.t): result<array<box>, array<ErrorTypes.t>> => {
|
|
73
|
+
let detectShapes = (grid: Grid.t): result<(array<box>, array<ErrorTypes.t>), array<ErrorTypes.t>> => {
|
|
73
74
|
// Use ShapeDetector to detect all shapes in the grid
|
|
74
75
|
ShapeDetector.detect(grid)
|
|
75
76
|
}
|
|
@@ -215,7 +216,11 @@ let parseSingleScene = (
|
|
|
215
216
|
shapeErrors->Array.forEach(err => errors->Array.push(err)->ignore)
|
|
216
217
|
[]
|
|
217
218
|
}
|
|
218
|
-
| Ok(
|
|
219
|
+
| Ok((boxes, warnings)) => {
|
|
220
|
+
// Collect warnings (non-fatal issues like misaligned borders)
|
|
221
|
+
warnings->Array.forEach(w => errors->Array.push(w)->ignore)
|
|
222
|
+
boxes
|
|
223
|
+
}
|
|
219
224
|
}
|
|
220
225
|
|
|
221
226
|
// Stage 3: Parse box content into elements
|
|
@@ -273,8 +278,8 @@ let parseSingleScene = (
|
|
|
273
278
|
* @returns Result containing AST or array of parse errors
|
|
274
279
|
*/
|
|
275
280
|
let parseInternal = (wireframe: string, interactions: option<string>): parseResult => {
|
|
276
|
-
// Accumulator for all errors across stages
|
|
277
|
-
let
|
|
281
|
+
// Accumulator for all issues (errors and warnings) across stages
|
|
282
|
+
let allIssues = []
|
|
278
283
|
|
|
279
284
|
// Split wireframe into scene blocks
|
|
280
285
|
let sceneBlocks = SemanticParser.splitSceneBlocks(wireframe)
|
|
@@ -282,8 +287,8 @@ let parseInternal = (wireframe: string, interactions: option<string>): parseResu
|
|
|
282
287
|
// Check if wireframe is empty
|
|
283
288
|
let trimmed = wireframe->String.trim
|
|
284
289
|
if sceneBlocks->Array.length === 0 && trimmed === "" {
|
|
285
|
-
// Empty wireframe - return empty AST
|
|
286
|
-
Ok({scenes: []})
|
|
290
|
+
// Empty wireframe - return empty AST with no warnings
|
|
291
|
+
Ok(({scenes: []}: Types.ast, []))
|
|
287
292
|
} else {
|
|
288
293
|
// Parse each scene block
|
|
289
294
|
let scenes = []
|
|
@@ -297,7 +302,7 @@ let parseInternal = (wireframe: string, interactions: option<string>): parseResu
|
|
|
297
302
|
let sceneContent = contentLines->Array.join("\n")
|
|
298
303
|
|
|
299
304
|
// Parse this scene through 3-stage pipeline
|
|
300
|
-
switch parseSingleScene(sceneContent, metadata,
|
|
305
|
+
switch parseSingleScene(sceneContent, metadata, allIssues) {
|
|
301
306
|
| Some(scene) => scenes->Array.push(scene)->ignore
|
|
302
307
|
| None => () // Scene parsing failed, errors already collected
|
|
303
308
|
}
|
|
@@ -314,7 +319,7 @@ let parseInternal = (wireframe: string, interactions: option<string>): parseResu
|
|
|
314
319
|
|
|
315
320
|
switch interactionsResult {
|
|
316
321
|
| Error(errors) => {
|
|
317
|
-
errors->Array.forEach(err =>
|
|
322
|
+
errors->Array.forEach(err => allIssues->Array.push(err)->ignore)
|
|
318
323
|
baseAst // Return AST without interactions on error
|
|
319
324
|
}
|
|
320
325
|
| Ok(sceneInteractions) => {
|
|
@@ -325,19 +330,23 @@ let parseInternal = (wireframe: string, interactions: option<string>): parseResu
|
|
|
325
330
|
}
|
|
326
331
|
}
|
|
327
332
|
|
|
333
|
+
// Separate errors from warnings
|
|
334
|
+
let errors = allIssues->Array.filter(issue => ErrorTypes.isError(issue))
|
|
335
|
+
let warnings = allIssues->Array.filter(issue => ErrorTypes.isWarning(issue))
|
|
336
|
+
|
|
328
337
|
// Return final result
|
|
329
|
-
// Return Error if all boxes failed to parse and we have errors
|
|
330
|
-
// Return Ok if at least some elements were parsed successfully
|
|
338
|
+
// Return Error if all boxes failed to parse and we have actual errors
|
|
339
|
+
// Return Ok with warnings if at least some elements were parsed successfully
|
|
331
340
|
let totalElements = finalAst.scenes->Array.reduce(0, (acc, scene) => {
|
|
332
341
|
acc + Array.length(scene.elements)
|
|
333
342
|
})
|
|
334
343
|
|
|
335
|
-
if Array.length(
|
|
336
|
-
// No elements parsed and we have errors - return error
|
|
337
|
-
Error(
|
|
344
|
+
if Array.length(errors) > 0 && totalElements === 0 {
|
|
345
|
+
// No elements parsed and we have errors - return error (include warnings too)
|
|
346
|
+
Error(allIssues)
|
|
338
347
|
} else {
|
|
339
|
-
// Either no errors, or some elements were parsed - return Ok
|
|
340
|
-
Ok(finalAst)
|
|
348
|
+
// Either no errors, or some elements were parsed - return Ok with warnings
|
|
349
|
+
Ok((finalAst, warnings))
|
|
341
350
|
}
|
|
342
351
|
}
|
|
343
352
|
}
|
|
@@ -22,6 +22,11 @@ export type renderOptions = {
|
|
|
22
22
|
/** * Scene management interface returned by render function. */
|
|
23
23
|
export type sceneManager = {
|
|
24
24
|
readonly goto: (_1:string) => void;
|
|
25
|
+
readonly back: () => void;
|
|
26
|
+
readonly forward: () => void;
|
|
27
|
+
readonly refresh: () => void;
|
|
28
|
+
readonly canGoBack: () => boolean;
|
|
29
|
+
readonly canGoForward: () => boolean;
|
|
25
30
|
readonly getCurrentScene: () => (undefined | string);
|
|
26
31
|
readonly getSceneIds: () => string[]
|
|
27
32
|
};
|
|
@@ -34,7 +39,8 @@ export type renderResult = { readonly root: DomBindings_element; readonly sceneM
|
|
|
34
39
|
export type createUISuccessResult = {
|
|
35
40
|
readonly root: DomBindings_element;
|
|
36
41
|
readonly sceneManager: sceneManager;
|
|
37
|
-
readonly ast: Types_ast
|
|
42
|
+
readonly ast: Types_ast;
|
|
43
|
+
readonly warnings: ErrorTypes_t[]
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
export type createUIResult =
|
|
@@ -284,7 +284,13 @@ function createSceneManager(scenes) {
|
|
|
284
284
|
let currentScene = {
|
|
285
285
|
contents: undefined
|
|
286
286
|
};
|
|
287
|
-
let
|
|
287
|
+
let historyStack = {
|
|
288
|
+
contents: []
|
|
289
|
+
};
|
|
290
|
+
let forwardStack = {
|
|
291
|
+
contents: []
|
|
292
|
+
};
|
|
293
|
+
let switchToScene = id => {
|
|
288
294
|
let currentId = currentScene.contents;
|
|
289
295
|
if (currentId !== undefined) {
|
|
290
296
|
let el = scenes.get(currentId);
|
|
@@ -299,10 +305,74 @@ function createSceneManager(scenes) {
|
|
|
299
305
|
return;
|
|
300
306
|
}
|
|
301
307
|
};
|
|
308
|
+
let goto = id => {
|
|
309
|
+
let currentId = currentScene.contents;
|
|
310
|
+
if (currentId !== undefined && currentId !== id) {
|
|
311
|
+
historyStack.contents = historyStack.contents.concat([currentId]);
|
|
312
|
+
forwardStack.contents = [];
|
|
313
|
+
}
|
|
314
|
+
switchToScene(id);
|
|
315
|
+
};
|
|
316
|
+
let back = () => {
|
|
317
|
+
let history = historyStack.contents;
|
|
318
|
+
let len = history.length;
|
|
319
|
+
if (len <= 0) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
let prevId = history[len - 1 | 0];
|
|
323
|
+
if (prevId === undefined) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
let currentId = currentScene.contents;
|
|
327
|
+
if (currentId !== undefined) {
|
|
328
|
+
forwardStack.contents = forwardStack.contents.concat([currentId]);
|
|
329
|
+
}
|
|
330
|
+
historyStack.contents = history.slice(0, len - 1 | 0);
|
|
331
|
+
switchToScene(prevId);
|
|
332
|
+
};
|
|
333
|
+
let forward = () => {
|
|
334
|
+
let fwdStack = forwardStack.contents;
|
|
335
|
+
let len = fwdStack.length;
|
|
336
|
+
if (len <= 0) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
let nextId = fwdStack[len - 1 | 0];
|
|
340
|
+
if (nextId === undefined) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
let currentId = currentScene.contents;
|
|
344
|
+
if (currentId !== undefined) {
|
|
345
|
+
historyStack.contents = historyStack.contents.concat([currentId]);
|
|
346
|
+
}
|
|
347
|
+
forwardStack.contents = fwdStack.slice(0, len - 1 | 0);
|
|
348
|
+
switchToScene(nextId);
|
|
349
|
+
};
|
|
350
|
+
let refresh = () => {
|
|
351
|
+
let id = currentScene.contents;
|
|
352
|
+
if (id === undefined) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
let el = scenes.get(id);
|
|
356
|
+
if (el === undefined) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
let el$1 = Primitive_option.valFromOption(el);
|
|
360
|
+
el$1.classList.remove("active");
|
|
361
|
+
setTimeout(() => {
|
|
362
|
+
el$1.classList.add("active");
|
|
363
|
+
}, 0);
|
|
364
|
+
};
|
|
365
|
+
let canGoBack = () => historyStack.contents.length > 0;
|
|
366
|
+
let canGoForward = () => forwardStack.contents.length > 0;
|
|
302
367
|
let getCurrentScene = () => currentScene.contents;
|
|
303
368
|
let getSceneIds = () => Array.from(scenes.keys());
|
|
304
369
|
return {
|
|
305
370
|
goto: goto,
|
|
371
|
+
back: back,
|
|
372
|
+
forward: forward,
|
|
373
|
+
refresh: refresh,
|
|
374
|
+
canGoBack: canGoBack,
|
|
375
|
+
canGoForward: canGoForward,
|
|
306
376
|
getCurrentScene: getCurrentScene,
|
|
307
377
|
getSceneIds: getSceneIds
|
|
308
378
|
};
|
|
@@ -338,16 +408,41 @@ function render(ast, options) {
|
|
|
338
408
|
let gotoRef = {
|
|
339
409
|
contents: undefined
|
|
340
410
|
};
|
|
411
|
+
let backRef = {
|
|
412
|
+
contents: undefined
|
|
413
|
+
};
|
|
414
|
+
let forwardRef = {
|
|
415
|
+
contents: undefined
|
|
416
|
+
};
|
|
341
417
|
let handleAction = action => {
|
|
342
418
|
if (typeof action !== "object") {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
419
|
+
if (action === "Back") {
|
|
420
|
+
let back = backRef.contents;
|
|
421
|
+
if (back !== undefined) {
|
|
422
|
+
return back();
|
|
423
|
+
} else {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
let forward = forwardRef.contents;
|
|
428
|
+
if (forward !== undefined) {
|
|
429
|
+
return forward();
|
|
430
|
+
} else {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
switch (action.TAG) {
|
|
435
|
+
case "Goto" :
|
|
436
|
+
let goto = gotoRef.contents;
|
|
437
|
+
if (goto !== undefined) {
|
|
438
|
+
return goto(action.target);
|
|
439
|
+
} else {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
case "Validate" :
|
|
443
|
+
case "Call" :
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
351
446
|
}
|
|
352
447
|
};
|
|
353
448
|
let sceneMap = new Map();
|
|
@@ -358,6 +453,8 @@ function render(ast, options) {
|
|
|
358
453
|
});
|
|
359
454
|
let manager = createSceneManager(sceneMap);
|
|
360
455
|
gotoRef.contents = manager.goto;
|
|
456
|
+
backRef.contents = manager.back;
|
|
457
|
+
forwardRef.contents = manager.forward;
|
|
361
458
|
if (ast.scenes.length > 0) {
|
|
362
459
|
let firstScene$1 = ast.scenes[0];
|
|
363
460
|
if (firstScene$1 !== undefined) {
|
|
@@ -375,37 +472,41 @@ function toHTMLString(_ast, _options) {
|
|
|
375
472
|
}
|
|
376
473
|
|
|
377
474
|
function createUI(text, options) {
|
|
378
|
-
let
|
|
379
|
-
if (
|
|
475
|
+
let errors = Parser.parse(text);
|
|
476
|
+
if (errors.TAG !== "Ok") {
|
|
380
477
|
return {
|
|
381
478
|
TAG: "Error",
|
|
382
|
-
_0:
|
|
479
|
+
_0: errors._0
|
|
383
480
|
};
|
|
384
481
|
}
|
|
385
|
-
let
|
|
386
|
-
let
|
|
482
|
+
let match = errors._0;
|
|
483
|
+
let ast = match[0];
|
|
484
|
+
let match$1 = render(ast, options);
|
|
387
485
|
return {
|
|
388
486
|
TAG: "Ok",
|
|
389
487
|
_0: {
|
|
390
|
-
root: match.root,
|
|
391
|
-
sceneManager: match.sceneManager,
|
|
392
|
-
ast: ast
|
|
488
|
+
root: match$1.root,
|
|
489
|
+
sceneManager: match$1.sceneManager,
|
|
490
|
+
ast: ast,
|
|
491
|
+
warnings: match[1]
|
|
393
492
|
}
|
|
394
493
|
};
|
|
395
494
|
}
|
|
396
495
|
|
|
397
496
|
function createUIOrThrow(text, options) {
|
|
398
|
-
let
|
|
399
|
-
if (
|
|
400
|
-
let
|
|
401
|
-
let
|
|
497
|
+
let errors = Parser.parse(text);
|
|
498
|
+
if (errors.TAG === "Ok") {
|
|
499
|
+
let match = errors._0;
|
|
500
|
+
let ast = match[0];
|
|
501
|
+
let match$1 = render(ast, options);
|
|
402
502
|
return {
|
|
403
|
-
root: match.root,
|
|
404
|
-
sceneManager: match.sceneManager,
|
|
405
|
-
ast: ast
|
|
503
|
+
root: match$1.root,
|
|
504
|
+
sceneManager: match$1.sceneManager,
|
|
505
|
+
ast: ast,
|
|
506
|
+
warnings: match[1]
|
|
406
507
|
};
|
|
407
508
|
}
|
|
408
|
-
let messages =
|
|
509
|
+
let messages = errors._0.map(err => ErrorMessages.getTitle(err.code)).join("\n");
|
|
409
510
|
return Stdlib_JsError.throwWithMessage("Parse failed:\n" + messages);
|
|
410
511
|
}
|
|
411
512
|
|
|
@@ -77,6 +77,11 @@ let defaultOptions: renderOptions = {
|
|
|
77
77
|
*/
|
|
78
78
|
type sceneManager = {
|
|
79
79
|
goto: string => unit,
|
|
80
|
+
back: unit => unit,
|
|
81
|
+
forward: unit => unit,
|
|
82
|
+
refresh: unit => unit,
|
|
83
|
+
canGoBack: unit => bool,
|
|
84
|
+
canGoForward: unit => bool,
|
|
80
85
|
getCurrentScene: unit => option<string>,
|
|
81
86
|
getSceneIds: unit => array<string>,
|
|
82
87
|
}
|
|
@@ -415,8 +420,11 @@ let renderScene = (scene: scene, ~onAction: option<actionHandler>=?): DomBinding
|
|
|
415
420
|
|
|
416
421
|
let createSceneManager = (scenes: Map.t<string, DomBindings.element>): sceneManager => {
|
|
417
422
|
let currentScene = ref(None)
|
|
423
|
+
let historyStack: ref<array<string>> = ref([])
|
|
424
|
+
let forwardStack: ref<array<string>> = ref([])
|
|
418
425
|
|
|
419
|
-
|
|
426
|
+
// Internal function to switch scenes without affecting history
|
|
427
|
+
let switchToScene = (id: string): unit => {
|
|
420
428
|
switch currentScene.contents {
|
|
421
429
|
| Some(currentId) => {
|
|
422
430
|
switch scenes->Map.get(currentId) {
|
|
@@ -436,6 +444,88 @@ let createSceneManager = (scenes: Map.t<string, DomBindings.element>): sceneMana
|
|
|
436
444
|
}
|
|
437
445
|
}
|
|
438
446
|
|
|
447
|
+
let goto = (id: string): unit => {
|
|
448
|
+
// Add current scene to history before navigating
|
|
449
|
+
switch currentScene.contents {
|
|
450
|
+
| Some(currentId) if currentId != id => {
|
|
451
|
+
historyStack := historyStack.contents->Array.concat([currentId])
|
|
452
|
+
// Clear forward stack when navigating to new scene
|
|
453
|
+
forwardStack := []
|
|
454
|
+
}
|
|
455
|
+
| _ => ()
|
|
456
|
+
}
|
|
457
|
+
switchToScene(id)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
let back = (): unit => {
|
|
461
|
+
let history = historyStack.contents
|
|
462
|
+
let len = history->Array.length
|
|
463
|
+
if len > 0 {
|
|
464
|
+
switch history->Array.get(len - 1) {
|
|
465
|
+
| Some(prevId) => {
|
|
466
|
+
// Add current scene to forward stack
|
|
467
|
+
switch currentScene.contents {
|
|
468
|
+
| Some(currentId) => {
|
|
469
|
+
forwardStack := forwardStack.contents->Array.concat([currentId])
|
|
470
|
+
}
|
|
471
|
+
| None => ()
|
|
472
|
+
}
|
|
473
|
+
// Remove last item from history
|
|
474
|
+
historyStack := history->Array.slice(~start=0, ~end=len - 1)
|
|
475
|
+
// Navigate to previous scene
|
|
476
|
+
switchToScene(prevId)
|
|
477
|
+
}
|
|
478
|
+
| None => ()
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
let forward = (): unit => {
|
|
484
|
+
let fwdStack = forwardStack.contents
|
|
485
|
+
let len = fwdStack->Array.length
|
|
486
|
+
if len > 0 {
|
|
487
|
+
switch fwdStack->Array.get(len - 1) {
|
|
488
|
+
| Some(nextId) => {
|
|
489
|
+
// Add current scene to history
|
|
490
|
+
switch currentScene.contents {
|
|
491
|
+
| Some(currentId) => {
|
|
492
|
+
historyStack := historyStack.contents->Array.concat([currentId])
|
|
493
|
+
}
|
|
494
|
+
| None => ()
|
|
495
|
+
}
|
|
496
|
+
// Remove last item from forward stack
|
|
497
|
+
forwardStack := fwdStack->Array.slice(~start=0, ~end=len - 1)
|
|
498
|
+
// Navigate to next scene
|
|
499
|
+
switchToScene(nextId)
|
|
500
|
+
}
|
|
501
|
+
| None => ()
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
let refresh = (): unit => {
|
|
507
|
+
switch currentScene.contents {
|
|
508
|
+
| Some(id) => {
|
|
509
|
+
// Remove active class and re-add it to trigger any CSS animations
|
|
510
|
+
switch scenes->Map.get(id) {
|
|
511
|
+
| Some(el) => {
|
|
512
|
+
el->DomBindings.classList->DomBindings.remove("active")
|
|
513
|
+
// Use setTimeout to ensure the class removal is processed
|
|
514
|
+
let _ = Js.Global.setTimeout(() => {
|
|
515
|
+
el->DomBindings.classList->DomBindings.add("active")
|
|
516
|
+
}, 0)
|
|
517
|
+
}
|
|
518
|
+
| None => ()
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
| None => ()
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
let canGoBack = (): bool => historyStack.contents->Array.length > 0
|
|
526
|
+
|
|
527
|
+
let canGoForward = (): bool => forwardStack.contents->Array.length > 0
|
|
528
|
+
|
|
439
529
|
let getCurrentScene = (): option<string> => currentScene.contents
|
|
440
530
|
|
|
441
531
|
let getSceneIds = (): array<string> => {
|
|
@@ -444,6 +534,11 @@ let createSceneManager = (scenes: Map.t<string, DomBindings.element>): sceneMana
|
|
|
444
534
|
|
|
445
535
|
{
|
|
446
536
|
goto,
|
|
537
|
+
back,
|
|
538
|
+
forward,
|
|
539
|
+
refresh,
|
|
540
|
+
canGoBack,
|
|
541
|
+
canGoForward,
|
|
447
542
|
getCurrentScene,
|
|
448
543
|
getSceneIds,
|
|
449
544
|
}
|
|
@@ -494,10 +589,12 @@ let render = (ast: ast, options: option<renderOptions>): renderResult => {
|
|
|
494
589
|
| None => ()
|
|
495
590
|
}
|
|
496
591
|
|
|
497
|
-
// Create
|
|
592
|
+
// Create refs to hold navigation functions (set after sceneManager is created)
|
|
498
593
|
let gotoRef: ref<option<string => unit>> = ref(None)
|
|
594
|
+
let backRef: ref<option<unit => unit>> = ref(None)
|
|
595
|
+
let forwardRef: ref<option<unit => unit>> = ref(None)
|
|
499
596
|
|
|
500
|
-
// Create action handler that uses the
|
|
597
|
+
// Create action handler that uses the refs
|
|
501
598
|
let handleAction = (action: interactionAction): unit => {
|
|
502
599
|
switch action {
|
|
503
600
|
| Goto({target, _}) => {
|
|
@@ -507,12 +604,16 @@ let render = (ast: ast, options: option<renderOptions>): renderResult => {
|
|
|
507
604
|
}
|
|
508
605
|
}
|
|
509
606
|
| Back => {
|
|
510
|
-
|
|
511
|
-
()
|
|
607
|
+
switch backRef.contents {
|
|
608
|
+
| Some(back) => back()
|
|
609
|
+
| None => ()
|
|
610
|
+
}
|
|
512
611
|
}
|
|
513
612
|
| Forward => {
|
|
514
|
-
|
|
515
|
-
()
|
|
613
|
+
switch forwardRef.contents {
|
|
614
|
+
| Some(forward) => forward()
|
|
615
|
+
| None => ()
|
|
616
|
+
}
|
|
516
617
|
}
|
|
517
618
|
| Validate(_) => {
|
|
518
619
|
// TODO: Implement field validation
|
|
@@ -535,8 +636,10 @@ let render = (ast: ast, options: option<renderOptions>): renderResult => {
|
|
|
535
636
|
|
|
536
637
|
let manager = createSceneManager(sceneMap)
|
|
537
638
|
|
|
538
|
-
// Now that sceneManager is created, set the
|
|
639
|
+
// Now that sceneManager is created, set the refs
|
|
539
640
|
gotoRef := Some(manager.goto)
|
|
641
|
+
backRef := Some(manager.back)
|
|
642
|
+
forwardRef := Some(manager.forward)
|
|
540
643
|
|
|
541
644
|
if ast.scenes->Array.length > 0 {
|
|
542
645
|
switch ast.scenes->Array.get(0) {
|
|
@@ -568,6 +671,7 @@ type createUISuccessResult = {
|
|
|
568
671
|
root: DomBindings.element,
|
|
569
672
|
sceneManager: sceneManager,
|
|
570
673
|
ast: Types.ast,
|
|
674
|
+
warnings: array<ErrorTypes.t>,
|
|
571
675
|
}
|
|
572
676
|
|
|
573
677
|
type createUIResult = result<createUISuccessResult, array<ErrorTypes.t>>
|
|
@@ -595,9 +699,9 @@ type createUIResult = result<createUISuccessResult, array<ErrorTypes.t>>
|
|
|
595
699
|
@genType
|
|
596
700
|
let createUI = (text: string, options: option<renderOptions>): createUIResult => {
|
|
597
701
|
switch Parser.parse(text) {
|
|
598
|
-
| Ok(ast) => {
|
|
702
|
+
| Ok((ast, warnings)) => {
|
|
599
703
|
let {root, sceneManager} = render(ast, options)
|
|
600
|
-
Ok({root, sceneManager, ast})
|
|
704
|
+
Ok({root, sceneManager, ast, warnings})
|
|
601
705
|
}
|
|
602
706
|
| Error(errors) => Error(errors)
|
|
603
707
|
}
|
|
@@ -621,9 +725,9 @@ let createUI = (text: string, options: option<renderOptions>): createUIResult =>
|
|
|
621
725
|
@genType
|
|
622
726
|
let createUIOrThrow = (text: string, options: option<renderOptions>): createUISuccessResult => {
|
|
623
727
|
switch Parser.parse(text) {
|
|
624
|
-
| Ok(ast) => {
|
|
728
|
+
| Ok((ast, warnings)) => {
|
|
625
729
|
let {root, sceneManager} = render(ast, options)
|
|
626
|
-
{root, sceneManager, ast}
|
|
730
|
+
{root, sceneManager, ast, warnings}
|
|
627
731
|
}
|
|
628
732
|
| Error(errors) => {
|
|
629
733
|
let messages = errors
|