wyreframe 0.1.0 → 0.1.5
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/LICENSE +692 -0
- package/README.md +65 -5
- package/package.json +8 -7
- package/src/index.ts +425 -0
- package/src/renderer/Renderer.gen.tsx +49 -0
- package/src/renderer/Renderer.mjs +41 -1
- package/src/renderer/Renderer.res +78 -0
- package/src/parser/Core/__tests__/Bounds_test.mjs +0 -326
- package/src/parser/Core/__tests__/Bounds_test.res +0 -412
- package/src/parser/Core/__tests__/Grid_test.mjs +0 -322
- package/src/parser/Core/__tests__/Grid_test.res +0 -319
- package/src/parser/Core/__tests__/Types_test.mjs +0 -614
- package/src/parser/Core/__tests__/Types_test.res +0 -650
- package/src/parser/Detector/__tests__/BoxTracer_test.mjs +0 -70
- package/src/parser/Detector/__tests__/BoxTracer_test.res +0 -92
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +0 -489
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +0 -849
- package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +0 -377
- package/src/parser/Detector/__tests__/ShapeDetector_test.res +0 -563
- package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +0 -576
- package/src/parser/Interactions/__tests__/InteractionMerger_test.res +0 -646
- package/src/parser/Scanner/__tests__/Grid_manual.mjs +0 -214
- package/src/parser/Scanner/__tests__/Grid_manual.res +0 -141
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +0 -189
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +0 -257
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +0 -202
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +0 -250
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +0 -293
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +0 -134
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +0 -253
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +0 -304
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +0 -289
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +0 -402
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +0 -149
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +0 -167
- package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +0 -187
- package/src/parser/Semantic/__tests__/ASTBuilder_test.res +0 -192
- package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +0 -154
- package/src/parser/Semantic/__tests__/ParserRegistry_test.res +0 -191
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +0 -768
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +0 -1069
- package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +0 -1329
- package/src/parser/Semantic/__tests__/SemanticParser_manual.res +0 -544
- package/src/parser/__tests__/GridScanner_integration.test.mjs +0 -632
- package/src/parser/__tests__/GridScanner_integration.test.res +0 -816
- package/src/parser/__tests__/Performance.test.mjs +0 -244
- package/src/parser/__tests__/Performance.test.res +0 -371
- package/src/parser/__tests__/PerformanceFixtures.mjs +0 -200
- package/src/parser/__tests__/PerformanceFixtures.res +0 -284
- package/src/parser/__tests__/WyreframeParser_integration.test.mjs +0 -770
- package/src/parser/__tests__/WyreframeParser_integration.test.res +0 -1008
- package/src/parser/__tests__/fixtures/alignment-test.txt +0 -9
- package/src/parser/__tests__/fixtures/all-elements.txt +0 -16
- package/src/parser/__tests__/fixtures/login-scene.txt +0 -17
- package/src/parser/__tests__/fixtures/multi-scene.txt +0 -25
- package/src/parser/__tests__/fixtures/nested-boxes.txt +0 -15
- package/src/parser/__tests__/fixtures/simple-box.txt +0 -5
- package/src/parser/__tests__/fixtures/with-dividers.txt +0 -14
package/README.md
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
> A library that converts ASCII wireframes into working HTML/UI
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/wyreframe)
|
|
6
|
-
[](https://www.npmjs.com/package/wyreframe)
|
|
7
|
+
[](https://codecov.io/gh/wickedev/wyreframe)
|
|
8
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
9
|
+
[](https://rescript-lang.org/)
|
|
10
|
+
[](https://nodejs.org/)
|
|
7
11
|
|
|
8
12
|
```
|
|
9
13
|
+---------------------------+
|
|
@@ -52,6 +56,36 @@ if (result.success) {
|
|
|
52
56
|
}
|
|
53
57
|
```
|
|
54
58
|
|
|
59
|
+
### ReScript
|
|
60
|
+
|
|
61
|
+
```rescript
|
|
62
|
+
let ui = `
|
|
63
|
+
@scene: login
|
|
64
|
+
|
|
65
|
+
+---------------------------+
|
|
66
|
+
| 'WYREFRAME' |
|
|
67
|
+
| +---------------------+ |
|
|
68
|
+
| | #email | |
|
|
69
|
+
| +---------------------+ |
|
|
70
|
+
| [ Login ] |
|
|
71
|
+
+---------------------------+
|
|
72
|
+
|
|
73
|
+
#email:
|
|
74
|
+
placeholder: "Enter your email"
|
|
75
|
+
|
|
76
|
+
[Login]:
|
|
77
|
+
@click -> goto(dashboard, slide-left)
|
|
78
|
+
`
|
|
79
|
+
|
|
80
|
+
switch Renderer.createUI(ui, None) {
|
|
81
|
+
| Ok({root, sceneManager, _}) => {
|
|
82
|
+
// Append root to DOM
|
|
83
|
+
sceneManager.goto("login")
|
|
84
|
+
}
|
|
85
|
+
| Error(errors) => Console.error(errors)
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
55
89
|
## Syntax Summary
|
|
56
90
|
|
|
57
91
|
| Syntax | Description | Example |
|
|
@@ -66,6 +100,8 @@ if (result.success) {
|
|
|
66
100
|
|
|
67
101
|
## API
|
|
68
102
|
|
|
103
|
+
### JavaScript/TypeScript
|
|
104
|
+
|
|
69
105
|
```javascript
|
|
70
106
|
import { parse, render, createUI, createUIOrThrow } from 'wyreframe';
|
|
71
107
|
|
|
@@ -82,6 +118,22 @@ const result = createUI(text);
|
|
|
82
118
|
const { root, sceneManager } = createUIOrThrow(text);
|
|
83
119
|
```
|
|
84
120
|
|
|
121
|
+
### ReScript
|
|
122
|
+
|
|
123
|
+
```rescript
|
|
124
|
+
// Parse only
|
|
125
|
+
let result = Parser.parse(text)
|
|
126
|
+
|
|
127
|
+
// Render only
|
|
128
|
+
let {root, sceneManager} = Renderer.render(ast, None)
|
|
129
|
+
|
|
130
|
+
// Parse + Render (recommended)
|
|
131
|
+
let result = Renderer.createUI(text, None)
|
|
132
|
+
|
|
133
|
+
// Throw on error
|
|
134
|
+
let {root, sceneManager, ast} = Renderer.createUIOrThrow(text, None)
|
|
135
|
+
```
|
|
136
|
+
|
|
85
137
|
### SceneManager
|
|
86
138
|
|
|
87
139
|
```javascript
|
|
@@ -90,6 +142,12 @@ sceneManager.getCurrentScene(); // Get current scene
|
|
|
90
142
|
sceneManager.getSceneIds(); // Get all scene IDs
|
|
91
143
|
```
|
|
92
144
|
|
|
145
|
+
```rescript
|
|
146
|
+
sceneManager.goto("dashboard") // Navigate to scene
|
|
147
|
+
sceneManager.getCurrentScene() // Get current scene (option<string>)
|
|
148
|
+
sceneManager.getSceneIds() // Get all scene IDs (array<string>)
|
|
149
|
+
```
|
|
150
|
+
|
|
93
151
|
## Interactions
|
|
94
152
|
|
|
95
153
|
```yaml
|
|
@@ -105,9 +163,11 @@ sceneManager.getSceneIds(); // Get all scene IDs
|
|
|
105
163
|
|
|
106
164
|
## Documentation
|
|
107
165
|
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
110
|
-
- [Examples](examples
|
|
166
|
+
- [API Reference](docs/api.md)
|
|
167
|
+
- [Developer Guide](docs/developer-guide.md)
|
|
168
|
+
- [Examples](docs/examples.md)
|
|
169
|
+
- [Testing Guide](docs/testing.md)
|
|
170
|
+
- [Live Demo](examples/index.html)
|
|
111
171
|
|
|
112
172
|
## Development
|
|
113
173
|
|
|
@@ -120,4 +180,4 @@ npm test # Run tests
|
|
|
120
180
|
|
|
121
181
|
## License
|
|
122
182
|
|
|
123
|
-
|
|
183
|
+
GPL-3.0 License - see [LICENSE](LICENSE) for details.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wyreframe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "ASCII wireframe + interaction DSL to HTML converter with scene transitions",
|
|
5
5
|
"author": "wickedev",
|
|
6
6
|
"repository": {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"types": "dist/index.d.ts",
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
|
-
"src
|
|
17
|
-
"src
|
|
16
|
+
"src",
|
|
17
|
+
"!src/**/__tests__",
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"exports": {
|
|
@@ -50,14 +50,15 @@
|
|
|
50
50
|
"html",
|
|
51
51
|
"prototype"
|
|
52
52
|
],
|
|
53
|
-
"license": "
|
|
53
|
+
"license": "GPL-3.0",
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@rescript/core": "^1.6.1",
|
|
56
|
-
"rescript": "12"
|
|
57
|
-
"vitest": "^3.2.4"
|
|
56
|
+
"rescript": "12"
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
59
|
+
"@vitest/coverage-v8": "^3.1.4",
|
|
60
60
|
"rescript-vitest": "^2.1.1",
|
|
61
|
-
"typescript": "^5.9.3"
|
|
61
|
+
"typescript": "^5.9.3",
|
|
62
|
+
"vitest": "^3.1.4"
|
|
62
63
|
}
|
|
63
64
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wyreframe - ASCII Wireframe to HTML/UI Converter
|
|
3
|
+
*
|
|
4
|
+
* This module provides a TypeScript-friendly API wrapper
|
|
5
|
+
* around the ReScript parser implementation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Note: These imports use relative paths that work when compiled to dist/
|
|
9
|
+
// The ReScript .mjs files remain in src/parser/, so dist/index.js imports ../src/parser/
|
|
10
|
+
// @ts-ignore - ReScript generated module
|
|
11
|
+
import * as Parser from '../src/parser/Parser.mjs';
|
|
12
|
+
// @ts-ignore - ReScript generated module
|
|
13
|
+
import * as Renderer from '../src/renderer/Renderer.mjs';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Type Definitions
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/** Device type for responsive design */
|
|
20
|
+
export type DeviceType =
|
|
21
|
+
| 'desktop'
|
|
22
|
+
| 'laptop'
|
|
23
|
+
| 'tablet'
|
|
24
|
+
| 'tablet-landscape'
|
|
25
|
+
| 'mobile'
|
|
26
|
+
| 'mobile-landscape';
|
|
27
|
+
|
|
28
|
+
/** Transition effect for scene changes */
|
|
29
|
+
export type TransitionType = 'fade' | 'slide-left' | 'slide-right' | 'zoom';
|
|
30
|
+
|
|
31
|
+
/** Text alignment */
|
|
32
|
+
export type Alignment = 'left' | 'center' | 'right';
|
|
33
|
+
|
|
34
|
+
/** Button variant styles */
|
|
35
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'ghost';
|
|
36
|
+
|
|
37
|
+
/** Action types for interactions */
|
|
38
|
+
export interface GotoAction {
|
|
39
|
+
type: 'goto';
|
|
40
|
+
target: string;
|
|
41
|
+
transition?: TransitionType;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type Action = GotoAction;
|
|
45
|
+
|
|
46
|
+
/** UI Element types */
|
|
47
|
+
export interface BoxElement {
|
|
48
|
+
TAG: 'Box';
|
|
49
|
+
name?: string;
|
|
50
|
+
children: Element[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ButtonElement {
|
|
54
|
+
TAG: 'Button';
|
|
55
|
+
id: string;
|
|
56
|
+
text: string;
|
|
57
|
+
align: Alignment;
|
|
58
|
+
variant?: ButtonVariant;
|
|
59
|
+
actions: Action[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface InputElement {
|
|
63
|
+
TAG: 'Input';
|
|
64
|
+
id: string;
|
|
65
|
+
placeholder?: string;
|
|
66
|
+
variant?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface LinkElement {
|
|
70
|
+
TAG: 'Link';
|
|
71
|
+
id: string;
|
|
72
|
+
text: string;
|
|
73
|
+
align: Alignment;
|
|
74
|
+
actions: Action[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface TextElement {
|
|
78
|
+
TAG: 'Text';
|
|
79
|
+
content: string;
|
|
80
|
+
emphasis: boolean;
|
|
81
|
+
align: Alignment;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface CheckboxElement {
|
|
85
|
+
TAG: 'Checkbox';
|
|
86
|
+
checked: boolean;
|
|
87
|
+
label: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface DividerElement {
|
|
91
|
+
TAG: 'Divider';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface RowElement {
|
|
95
|
+
TAG: 'Row';
|
|
96
|
+
children: Element[];
|
|
97
|
+
align: Alignment;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface SectionElement {
|
|
101
|
+
TAG: 'Section';
|
|
102
|
+
name: string;
|
|
103
|
+
children: Element[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type Element =
|
|
107
|
+
| BoxElement
|
|
108
|
+
| ButtonElement
|
|
109
|
+
| InputElement
|
|
110
|
+
| LinkElement
|
|
111
|
+
| TextElement
|
|
112
|
+
| CheckboxElement
|
|
113
|
+
| DividerElement
|
|
114
|
+
| RowElement
|
|
115
|
+
| SectionElement;
|
|
116
|
+
|
|
117
|
+
/** Scene definition */
|
|
118
|
+
export interface Scene {
|
|
119
|
+
id: string;
|
|
120
|
+
title: string;
|
|
121
|
+
device: DeviceType;
|
|
122
|
+
transition: TransitionType;
|
|
123
|
+
elements: Element[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Abstract Syntax Tree */
|
|
127
|
+
export interface AST {
|
|
128
|
+
scenes: Scene[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Parse error information */
|
|
132
|
+
export interface ParseError {
|
|
133
|
+
message: string;
|
|
134
|
+
line?: number;
|
|
135
|
+
column?: number;
|
|
136
|
+
source?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Scene manager for programmatic navigation */
|
|
140
|
+
export interface SceneManager {
|
|
141
|
+
/** Navigate to a scene by ID */
|
|
142
|
+
goto: (sceneId: string) => void;
|
|
143
|
+
/** Get the current scene ID */
|
|
144
|
+
getCurrentScene: () => string | undefined;
|
|
145
|
+
/** Get all available scene IDs */
|
|
146
|
+
getSceneIds: () => string[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Render options */
|
|
150
|
+
export interface RenderOptions {
|
|
151
|
+
/** Theme name */
|
|
152
|
+
theme?: string;
|
|
153
|
+
/** Enable interactions (default: true) */
|
|
154
|
+
interactive?: boolean;
|
|
155
|
+
/** Inject default styles (default: true) */
|
|
156
|
+
injectStyles?: boolean;
|
|
157
|
+
/** Additional CSS class for container */
|
|
158
|
+
containerClass?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Render result */
|
|
162
|
+
export interface RenderResult {
|
|
163
|
+
/** Rendered root element */
|
|
164
|
+
root: HTMLElement;
|
|
165
|
+
/** Scene manager for navigation */
|
|
166
|
+
sceneManager: SceneManager;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// Result Types (TypeScript-friendly)
|
|
171
|
+
// ============================================================================
|
|
172
|
+
|
|
173
|
+
export type ParseSuccessResult = {
|
|
174
|
+
success: true;
|
|
175
|
+
ast: AST;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export type ParseErrorResult = {
|
|
179
|
+
success: false;
|
|
180
|
+
errors: ParseError[];
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export type ParseResult = ParseSuccessResult | ParseErrorResult;
|
|
184
|
+
|
|
185
|
+
export type InteractionSuccessResult = {
|
|
186
|
+
success: true;
|
|
187
|
+
interactions: unknown[];
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export type InteractionErrorResult = {
|
|
191
|
+
success: false;
|
|
192
|
+
errors: ParseError[];
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export type InteractionResult = InteractionSuccessResult | InteractionErrorResult;
|
|
196
|
+
|
|
197
|
+
export type CreateUISuccessResult = {
|
|
198
|
+
success: true;
|
|
199
|
+
root: HTMLElement;
|
|
200
|
+
sceneManager: SceneManager;
|
|
201
|
+
ast: AST;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export type CreateUIErrorResult = {
|
|
205
|
+
success: false;
|
|
206
|
+
errors: ParseError[];
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export type CreateUIResult = CreateUISuccessResult | CreateUIErrorResult;
|
|
210
|
+
|
|
211
|
+
// ============================================================================
|
|
212
|
+
// Internal ReScript Result Type
|
|
213
|
+
// ============================================================================
|
|
214
|
+
|
|
215
|
+
interface ReScriptOk<T> {
|
|
216
|
+
TAG: 'Ok';
|
|
217
|
+
_0: T;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
interface ReScriptError<E> {
|
|
221
|
+
TAG: 'Error';
|
|
222
|
+
_0: E;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
type ReScriptResult<T, E> = ReScriptOk<T> | ReScriptError<E>;
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// API Functions
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Parse mixed text containing wireframe and interactions.
|
|
233
|
+
*
|
|
234
|
+
* @param text - Text containing ASCII wireframe and/or interaction DSL
|
|
235
|
+
* @returns Parse result with success flag
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```typescript
|
|
239
|
+
* const result = parse(text);
|
|
240
|
+
* if (result.success) {
|
|
241
|
+
* const { root, sceneManager } = render(result.ast);
|
|
242
|
+
* document.body.appendChild(root);
|
|
243
|
+
* } else {
|
|
244
|
+
* console.error(result.errors);
|
|
245
|
+
* }
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
export function parse(text: string): ParseResult {
|
|
249
|
+
const result = Parser.parse(text) as ReScriptResult<AST, ParseError[]>;
|
|
250
|
+
|
|
251
|
+
if (result.TAG === 'Ok') {
|
|
252
|
+
return { success: true, ast: result._0 };
|
|
253
|
+
} else {
|
|
254
|
+
return { success: false, errors: result._0 };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Parse text and throw on error.
|
|
260
|
+
* Use this for simpler code when you expect parsing to succeed.
|
|
261
|
+
*
|
|
262
|
+
* @param text - Text containing ASCII wireframe and/or interaction DSL
|
|
263
|
+
* @returns Parsed AST
|
|
264
|
+
* @throws Error if parsing fails
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* try {
|
|
269
|
+
* const ast = parseOrThrow(text);
|
|
270
|
+
* const { root } = render(ast);
|
|
271
|
+
* document.body.appendChild(root);
|
|
272
|
+
* } catch (error) {
|
|
273
|
+
* console.error('Parse failed:', error.message);
|
|
274
|
+
* }
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
export function parseOrThrow(text: string): AST {
|
|
278
|
+
const result = parse(text);
|
|
279
|
+
|
|
280
|
+
if (result.success) {
|
|
281
|
+
return result.ast;
|
|
282
|
+
} else {
|
|
283
|
+
const errorMessages = result.errors
|
|
284
|
+
.map((e) => e.message || JSON.stringify(e))
|
|
285
|
+
.join('\n');
|
|
286
|
+
throw new Error(`Parse failed:\n${errorMessages}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Parse only the wireframe structure (no interactions).
|
|
292
|
+
*
|
|
293
|
+
* @param wireframe - ASCII wireframe string
|
|
294
|
+
* @returns Parse result with success flag
|
|
295
|
+
*/
|
|
296
|
+
export function parseWireframe(wireframe: string): ParseResult {
|
|
297
|
+
const result = Parser.parseWireframe(wireframe) as ReScriptResult<AST, ParseError[]>;
|
|
298
|
+
|
|
299
|
+
if (result.TAG === 'Ok') {
|
|
300
|
+
return { success: true, ast: result._0 };
|
|
301
|
+
} else {
|
|
302
|
+
return { success: false, errors: result._0 };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Parse only the interaction DSL.
|
|
308
|
+
*
|
|
309
|
+
* @param dsl - Interaction DSL string
|
|
310
|
+
* @returns Interaction result with success flag
|
|
311
|
+
*/
|
|
312
|
+
export function parseInteractions(dsl: string): InteractionResult {
|
|
313
|
+
const result = Parser.parseInteractions(dsl) as ReScriptResult<unknown[], ParseError[]>;
|
|
314
|
+
|
|
315
|
+
if (result.TAG === 'Ok') {
|
|
316
|
+
return { success: true, interactions: result._0 };
|
|
317
|
+
} else {
|
|
318
|
+
return { success: false, errors: result._0 };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Render AST to DOM elements.
|
|
324
|
+
*
|
|
325
|
+
* @param ast - Parsed AST from parse()
|
|
326
|
+
* @param options - Render options
|
|
327
|
+
* @returns Render result with root element and scene manager
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```typescript
|
|
331
|
+
* const { root, sceneManager } = render(ast);
|
|
332
|
+
* document.getElementById('app')!.appendChild(root);
|
|
333
|
+
*
|
|
334
|
+
* // Navigate between scenes
|
|
335
|
+
* sceneManager.goto('dashboard');
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
export function render(ast: AST, options?: RenderOptions): RenderResult {
|
|
339
|
+
// Pass undefined if no options, so ReScript uses its defaults
|
|
340
|
+
const result = Renderer.render(ast, options);
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
root: result.root,
|
|
344
|
+
sceneManager: {
|
|
345
|
+
goto: result.sceneManager.goto,
|
|
346
|
+
getCurrentScene: result.sceneManager.getCurrentScene,
|
|
347
|
+
getSceneIds: result.sceneManager.getSceneIds,
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Parse and render in one step.
|
|
354
|
+
* Convenience function that combines parse() and render().
|
|
355
|
+
*
|
|
356
|
+
* @param text - Text containing ASCII wireframe and/or interaction DSL
|
|
357
|
+
* @param options - Render options
|
|
358
|
+
* @returns Create UI result with success flag
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```typescript
|
|
362
|
+
* const result = createUI(text);
|
|
363
|
+
* if (result.success) {
|
|
364
|
+
* document.getElementById('app')!.appendChild(result.root);
|
|
365
|
+
* result.sceneManager.goto('login');
|
|
366
|
+
* }
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
export function createUI(text: string, options?: RenderOptions): CreateUIResult {
|
|
370
|
+
const parseResult = parse(text);
|
|
371
|
+
|
|
372
|
+
if (!parseResult.success) {
|
|
373
|
+
return parseResult;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const { root, sceneManager } = render(parseResult.ast, options);
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
success: true,
|
|
380
|
+
root,
|
|
381
|
+
sceneManager,
|
|
382
|
+
ast: parseResult.ast,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Parse and render, throwing on error.
|
|
388
|
+
*
|
|
389
|
+
* @param text - Text containing ASCII wireframe and/or interaction DSL
|
|
390
|
+
* @param options - Render options
|
|
391
|
+
* @returns Render result with root element, scene manager, and AST
|
|
392
|
+
* @throws Error if parsing fails
|
|
393
|
+
*
|
|
394
|
+
* @example
|
|
395
|
+
* ```typescript
|
|
396
|
+
* const { root, sceneManager } = createUIOrThrow(text);
|
|
397
|
+
* document.getElementById('app')!.appendChild(root);
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
export function createUIOrThrow(
|
|
401
|
+
text: string,
|
|
402
|
+
options?: RenderOptions
|
|
403
|
+
): RenderResult & { ast: AST } {
|
|
404
|
+
const ast = parseOrThrow(text);
|
|
405
|
+
const { root, sceneManager } = render(ast, options);
|
|
406
|
+
|
|
407
|
+
return { root, sceneManager, ast };
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Version info
|
|
411
|
+
export const version: string = Parser.version;
|
|
412
|
+
export const implementation: string = Parser.implementation;
|
|
413
|
+
|
|
414
|
+
// Default export for convenience
|
|
415
|
+
export default {
|
|
416
|
+
parse,
|
|
417
|
+
parseOrThrow,
|
|
418
|
+
parseWireframe,
|
|
419
|
+
parseInteractions,
|
|
420
|
+
render,
|
|
421
|
+
createUI,
|
|
422
|
+
createUIOrThrow,
|
|
423
|
+
version,
|
|
424
|
+
implementation,
|
|
425
|
+
};
|
|
@@ -7,6 +7,8 @@ import * as RendererJS from './Renderer.mjs';
|
|
|
7
7
|
|
|
8
8
|
import type {ast as Types_ast} from '../../src/parser/Core/Types.gen';
|
|
9
9
|
|
|
10
|
+
import type {t as ErrorTypes_t} from '../../src/parser/Errors/ErrorTypes.gen';
|
|
11
|
+
|
|
10
12
|
export abstract class DomBindings_element { protected opaque!: any }; /* simulate opaque types */
|
|
11
13
|
|
|
12
14
|
/** * Configuration for the rendering process. */
|
|
@@ -27,6 +29,53 @@ export type sceneManager = {
|
|
|
27
29
|
/** * Render result containing the root element and scene manager. */
|
|
28
30
|
export type renderResult = { readonly root: DomBindings_element; readonly sceneManager: sceneManager };
|
|
29
31
|
|
|
32
|
+
/** * Result type for createUI function.
|
|
33
|
+
* Contains either success with rendered elements or error with parse errors. */
|
|
34
|
+
export type createUISuccessResult = {
|
|
35
|
+
readonly root: DomBindings_element;
|
|
36
|
+
readonly sceneManager: sceneManager;
|
|
37
|
+
readonly ast: Types_ast
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type createUIResult =
|
|
41
|
+
{ TAG: "Ok"; _0: createUISuccessResult }
|
|
42
|
+
| { TAG: "Error"; _0: ErrorTypes_t[] };
|
|
43
|
+
|
|
30
44
|
export const render: (ast:Types_ast, options:(undefined | renderOptions)) => renderResult = RendererJS.render as any;
|
|
31
45
|
|
|
32
46
|
export const toHTMLString: (_ast:Types_ast, _options:(undefined | renderOptions)) => string = RendererJS.toHTMLString as any;
|
|
47
|
+
|
|
48
|
+
/** * Parse and render wireframe in one step.
|
|
49
|
+
* Combines Parser.parse() and Renderer.render() for convenience.
|
|
50
|
+
*
|
|
51
|
+
* @param text Text containing ASCII wireframe and/or interaction DSL
|
|
52
|
+
* @param options Optional render options
|
|
53
|
+
* @returns Result containing root element, scene manager, and AST, or errors
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```rescript
|
|
57
|
+
* let result = Renderer.createUI(wireframeText, None)
|
|
58
|
+
* switch result {
|
|
59
|
+
* | Ok({root, sceneManager, ast}) => {
|
|
60
|
+
* // Append root to DOM
|
|
61
|
+
* sceneManager.goto("login")
|
|
62
|
+
* }
|
|
63
|
+
* | Error(errors) => Console.error(errors)
|
|
64
|
+
* }
|
|
65
|
+
* ``` */
|
|
66
|
+
export const createUI: (text:string, options:(undefined | renderOptions)) => createUIResult = RendererJS.createUI as any;
|
|
67
|
+
|
|
68
|
+
/** * Parse and render wireframe, throwing on error.
|
|
69
|
+
* Use this for simpler code when parsing is expected to succeed.
|
|
70
|
+
*
|
|
71
|
+
* @param text Text containing ASCII wireframe and/or interaction DSL
|
|
72
|
+
* @param options Optional render options
|
|
73
|
+
* @returns Rendered root element, scene manager, and AST
|
|
74
|
+
* @raises Js.Exn.Error if parsing fails
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```rescript
|
|
78
|
+
* let {root, sceneManager, ast} = Renderer.createUIOrThrow(wireframeText, None)
|
|
79
|
+
* // Use root directly - errors will throw exception
|
|
80
|
+
* ``` */
|
|
81
|
+
export const createUIOrThrow: (text:string, options:(undefined | renderOptions)) => createUISuccessResult = RendererJS.createUIOrThrow as any;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Parser from "../parser/Parser.mjs";
|
|
3
4
|
import * as Core__Option from "@rescript/core/src/Core__Option.mjs";
|
|
5
|
+
import * as ErrorMessages from "../parser/Errors/ErrorMessages.mjs";
|
|
6
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
4
7
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
5
8
|
|
|
6
9
|
let DomBindings = {};
|
|
@@ -371,6 +374,41 @@ function toHTMLString(_ast, _options) {
|
|
|
371
374
|
return "<!-- Static HTML generation not yet implemented -->";
|
|
372
375
|
}
|
|
373
376
|
|
|
377
|
+
function createUI(text, options) {
|
|
378
|
+
let ast = Parser.parse(text);
|
|
379
|
+
if (ast.TAG !== "Ok") {
|
|
380
|
+
return {
|
|
381
|
+
TAG: "Error",
|
|
382
|
+
_0: ast._0
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
let ast$1 = ast._0;
|
|
386
|
+
let match = render(ast$1, options);
|
|
387
|
+
return {
|
|
388
|
+
TAG: "Ok",
|
|
389
|
+
_0: {
|
|
390
|
+
root: match.root,
|
|
391
|
+
sceneManager: match.sceneManager,
|
|
392
|
+
ast: ast$1
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function createUIOrThrow(text, options) {
|
|
398
|
+
let ast = Parser.parse(text);
|
|
399
|
+
if (ast.TAG === "Ok") {
|
|
400
|
+
let ast$1 = ast._0;
|
|
401
|
+
let match = render(ast$1, options);
|
|
402
|
+
return {
|
|
403
|
+
root: match.root,
|
|
404
|
+
sceneManager: match.sceneManager,
|
|
405
|
+
ast: ast$1
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
let messages = ast._0.map(err => ErrorMessages.getTitle(err.code)).join("\n");
|
|
409
|
+
return Stdlib_JsError.throwWithMessage("Parse failed:\n" + messages);
|
|
410
|
+
}
|
|
411
|
+
|
|
374
412
|
export {
|
|
375
413
|
DomBindings,
|
|
376
414
|
defaultOptions,
|
|
@@ -387,5 +425,7 @@ export {
|
|
|
387
425
|
injectStyles,
|
|
388
426
|
render,
|
|
389
427
|
toHTMLString,
|
|
428
|
+
createUI,
|
|
429
|
+
createUIOrThrow,
|
|
390
430
|
}
|
|
391
|
-
/*
|
|
431
|
+
/* Parser Not a pure module */
|