Skip to content

Commit 147ebbe

Browse files
committed
Improve editor styling and describe it in README.md
1 parent 4f3cf1d commit 147ebbe

12 files changed

Lines changed: 554 additions & 125 deletions

README.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@ Try it in the [interactive playground](https://snatalenko.github.io/morphos/play
2323

2424
- [Overview](#overview)
2525
- [Reasoning](#reasoning)
26-
- [Optional Packages](#optional-packages)
26+
- [Visual Mapping Editor](#visual-mapping-editor)
27+
- [AI Mapping Generation](#ai-mapping-generation)
2728
- [Quick Start Example](#quick-start-example)
2829
- [Compatibility](#compatibility)
2930
- [Security](#security)
3031
- [Mapping Instructions](#mapping-instructions)
3132
- [Runtime Variables Quick Reference](#runtime-variables-quick-reference)
3233
- [Objects](#objects)
3334
- [Arrays](#arrays)
34-
- [String\[\] from Object\[\]](#string-from-object)
35-
- [String\[\] from String\[\]](#string-from-string)
35+
- [String\[\] from Object\[\]](#string-from-object)
36+
- [String\[\] from String\[\]](#string-from-string)
3637
- [Tuple Arrays](#tuple-arrays)
3738
- [Context Switching](#context-switching)
3839
- [Conditional Fields](#conditional-fields)
@@ -54,12 +55,44 @@ That is where Morphos came in:
5455
- **Typed** - written in TypeScript
5556
- **Lightweight** - no dependencies
5657

57-
### Optional Packages
58+
### Visual Mapping Editor
5859

59-
Morphos also ships optional subpath entries that are loaded only when imported:
60+
Need users to build or maintain mappings in a web app? Use the React mapping editor and save the result as the same JSON mapping spec the runtime executes.
6061

61-
- [`morphos/react`](src/react/README.md) - a React visual mapping editor with schema-driven field suggestions, mapping type selection, and built-in default/Bootstrap themes.
62-
- [`morphos/openai`](src/openai/README.md) - an OpenAI-powered helper that generates a `RootMapping` from source and destination schemas, optionally guided by natural-language instructions.
62+
<table>
63+
<tr>
64+
<td width="50%" style="border: none">
65+
<img src="docs/images/mapping-editor-browser.png" alt="Mapping editor in browser" width="100%" />
66+
</td>
67+
<td width="50%" style="border: none">
68+
<img src="docs/images/mapping-editor-code.png" alt="Mapping JSON in code editor" width="100%" />
69+
</td>
70+
</tr>
71+
</table>
72+
73+
The editor can suggest source and destination fields from JSON Schemas, lets users choose mapping instructions such as fields, objects, arrays, conditionals, and concatenation, and outputs plain JSON. A typical flow is: users build mappings in a web app, the app saves those JSON specs, and the server executes them later in the secure runtime.
74+
75+
The UI is available as an optional subpath import and is loaded only when used:
76+
77+
```ts
78+
import { MappingEditor } from 'morphos/react';
79+
```
80+
81+
See [`morphos/react`](src/react/README.md) for the editor API, schema-driven suggestions, change handling, and built-in default/Bootstrap themes.
82+
83+
### AI Mapping Generation
84+
85+
When both incoming and outgoing formats are known, OpenAI can generate a first-pass mapping from two JSON Schemas.
86+
87+
This is useful for document-to-document transformations, API payload conversions, imports, exports, and other structured JSON workflows: as long as the source and destination formats are known, the model can infer likely field matches, calculations, object mappings, list mappings, and conditional rules. The generated output is still just a JSON mapping spec, so it can be reviewed in the editor, adjusted, stored, and executed by the same runtime.
88+
89+
The OpenAI helper is also an optional subpath import:
90+
91+
```ts
92+
import { generateMapping } from 'morphos/openai';
93+
```
94+
95+
See [`morphos/openai`](src/openai/README.md) for schema-based mapping generation and natural-language instructions.
6396

6497
### Quick Start Example
6598

730 KB
Loading
663 KB
Loading

docs/images/mapping-editor.png

866 KB
Loading

index.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,9 @@
247247
<img src="docs/images/morphos_logo.svg" alt="Morphos" class="h-8 w-16" />
248248
<div class="flex-1">
249249
<h1 class="text-lg font-semibold text-slate-900 dark:text-slate-100">Morphos</h1>
250-
<p class="text-xs text-slate-500 dark:text-slate-400 -mt-0.5">JSON-to-JSON mapper with user-defined JSON mapping specs, plain JS transformation expressions, and secure VM execution</p>
250+
<p class="text-xs text-slate-500 dark:text-slate-400 -mt-0.5 hidden sm:block">JSON-to-JSON mapper with user-defined JSON mapping specs, plain JS transformation expressions, and secure VM execution</p>
251251
</div>
252-
<button id="theme-toggle" type="button" aria-label="Toggle dark mode" class="text-sm text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white p-1.5 border border-slate-200 dark:border-slate-700 rounded-md hover:bg-slate-50 dark:hover:bg-slate-800">
252+
<button id="theme-toggle" type="button" aria-label="Toggle dark mode" class="ml-auto text-sm text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white p-1.5 border border-slate-200 dark:border-slate-700 rounded-md hover:bg-slate-50 dark:hover:bg-slate-800">
253253
<svg class="w-4 h-4 hidden dark:block" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
254254
<circle cx="12" cy="12" r="4"></circle>
255255
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"></path>
@@ -277,7 +277,8 @@ <h1 class="text-lg font-semibold text-slate-900 dark:text-slate-100">Morphos</h1
277277
<a href="#overview" class="toc-link block px-2 py-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800">Overview</a>
278278
<ul class="toc-sub space-y-0.5">
279279
<li><a href="#reasoning" class="toc-link block px-2 py-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800">Reasoning</a></li>
280-
<li><a href="#optional-packages" class="toc-link block px-2 py-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800">Optional Packages</a></li>
280+
<li><a href="#visual-mapping-editor" class="toc-link block px-2 py-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800">Visual Mapping Editor</a></li>
281+
<li><a href="#ai-mapping-generation" class="toc-link block px-2 py-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800">AI Mapping Generation</a></li>
281282
<li><a href="#quick-start-example" class="toc-link block px-2 py-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800">Quick Start</a></li>
282283
</ul>
283284
</li>

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"object-mapper",
1919
"object-mapping",
2020
"transformation",
21-
"typescript"
21+
"typescript",
22+
"react"
2223
],
2324
"type": "module",
2425
"engines": {
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
.dm-mapping-editor .form-group > .col-sm-3 .form-control,
2+
.dm-mapping-editor .form-group > .col-sm-5:first-child .form-control {
3+
background-color: var(--dm-key-bg);
4+
border-color: var(--dm-key-border);
5+
color: #17324d;
6+
}
7+
8+
.dm-mapping-editor .form-group > .col-sm-3 select.form-control,
9+
.dm-mapping-editor .form-group > .col-sm-5:first-child select.form-control {
10+
background-color: var(--dm-key-bg);
11+
border-color: var(--dm-key-border);
12+
border-style: solid;
13+
color: #17324d;
14+
font-weight: 600;
15+
}
16+
17+
.dm-mapping-editor .form-group > .col-sm-3 input.form-control,
18+
.dm-mapping-editor .form-group > .col-sm-5:first-child input.form-control {
19+
background-color: var(--dm-custom-key-bg);
20+
border-color: var(--dm-custom-key-border);
21+
border-style: dashed;
22+
color: #1f3b57;
23+
font-weight: 500;
24+
}
25+
26+
.dm-mapping-editor .form-group > .col-sm-3 .form-control::placeholder,
27+
.dm-mapping-editor .form-group > .col-sm-5:first-child .form-control::placeholder {
28+
color: #7894b5;
29+
}
30+
31+
.dm-mapping-editor .form-group > .col-sm-2 .form-control {
32+
background-color: var(--dm-type-bg);
33+
border-color: var(--dm-type-border);
34+
color: #46307a;
35+
font-weight: 600;
36+
}
37+
38+
.dm-mapping-editor .form-group > .col-sm-5:not(:first-child) .form-control {
39+
background-color: var(--dm-value-bg);
40+
border-color: var(--dm-value-border);
41+
color: #65380f;
42+
}
43+
44+
.dm-mapping-editor .form-group > .col-sm-5:not(:first-child) select.form-control {
45+
background-color: var(--dm-source-bg);
46+
border-color: var(--dm-source-border);
47+
color: #14532d;
48+
font-family: system-ui, -apple-system, sans-serif !important;
49+
font-weight: 600;
50+
}
51+
52+
.dm-mapping-editor .form-group > .col-sm-5:not(:first-child) input.form-control {
53+
background-color: var(--dm-value-bg);
54+
border-color: var(--dm-value-border);
55+
border-left: 3px solid #d5902f;
56+
color: #65380f;
57+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace !important;
58+
}
59+
60+
.dm-mapping-editor .form-group > .col-sm-5:not(:first-child) .form-control::placeholder {
61+
color: #a9814a;
62+
}
63+
64+
.dm-mapping-editor .form-control:focus {
65+
border-color: #6aa0f6;
66+
box-shadow: 0 0 0 3px var(--dm-control-focus);
67+
outline: 0;
68+
}
69+
70+
.dm-mapping-editor .input-group {
71+
border-radius: 5px;
72+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
73+
}
74+
75+
.dm-mapping-editor .panel {
76+
border-color: #d8e3f0;
77+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
78+
}
79+
80+
.dm-mapping-editor .panel-heading {
81+
background: #eef6ff;
82+
border-color: #d8e3f0;
83+
}
84+
85+
.dm-mapping-editor code {
86+
color: #174ea6;
87+
background: #eaf3ff !important;
88+
border: 1px solid #c9ddfb;
89+
}
90+
91+
.dm-mapping-editor .form-control,
92+
.dm-mapping-editor .control-label,
93+
.dm-mapping-editor .btn {
94+
font-size: 12px;
95+
line-height: 1.25;
96+
padding-top: 3px;
97+
padding-bottom: 3px;
98+
height: auto;
99+
}
100+
101+
.dm-mapping-editor .btn {
102+
padding-left: 5px;
103+
padding-right: 5px;
104+
}
105+
106+
.dm-mapping-editor .form-group {
107+
margin-bottom: 5px;
108+
}
109+
110+
.dm-mapping-editor .panel-body {
111+
padding: 7px;
112+
}
113+
114+
.dm-mapping-editor .panel-body .form-group:last-child {
115+
margin-bottom: 0;
116+
}
117+
118+
.dm-mapping-editor .panel-body > .panel {
119+
margin-bottom: 5px;
120+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
.dm-mapping-editor .row > .col-3 .form-control,
2+
.dm-mapping-editor .row > .col-3 .form-select,
3+
.dm-mapping-editor .row > .col-5:first-child .form-control {
4+
background-color: var(--dm-key-bg);
5+
border-color: var(--dm-key-border);
6+
color: #17324d;
7+
}
8+
9+
.dm-mapping-editor .row > .col-3 select.form-control,
10+
.dm-mapping-editor .row > .col-3 select.form-select,
11+
.dm-mapping-editor .row > .col-5:first-child select.form-control,
12+
.dm-mapping-editor .row > .col-5:first-child select.form-select {
13+
background-color: var(--dm-key-bg);
14+
border-color: var(--dm-key-border);
15+
border-style: solid;
16+
color: #17324d;
17+
font-weight: 600;
18+
}
19+
20+
.dm-mapping-editor .row > .col-3 input.form-control,
21+
.dm-mapping-editor .row > .col-5:first-child input.form-control {
22+
background-color: var(--dm-custom-key-bg);
23+
border-color: var(--dm-custom-key-border);
24+
border-style: dashed;
25+
color: #1f3b57;
26+
font-weight: 500;
27+
}
28+
29+
.dm-mapping-editor .row > .col-3 .form-control::placeholder,
30+
.dm-mapping-editor .row > .col-5:first-child .form-control::placeholder {
31+
color: #7894b5;
32+
}
33+
34+
.dm-mapping-editor .row > .col-2 .form-select {
35+
background-color: var(--dm-type-bg);
36+
border-color: var(--dm-type-border);
37+
color: #46307a;
38+
font-weight: 600;
39+
}
40+
41+
.dm-mapping-editor .row > .col-5:not(:first-child) .form-control,
42+
.dm-mapping-editor .row > .col-5:not(:first-child) .form-select {
43+
background-color: var(--dm-value-bg);
44+
border-color: var(--dm-value-border);
45+
color: #65380f;
46+
}
47+
48+
.dm-mapping-editor .row > .col-5:not(:first-child) select.form-control,
49+
.dm-mapping-editor .row > .col-5:not(:first-child) select.form-select {
50+
background-color: var(--dm-source-bg);
51+
border-color: var(--dm-source-border);
52+
color: #14532d;
53+
font-family: system-ui, -apple-system, sans-serif !important;
54+
font-weight: 600;
55+
}
56+
57+
.dm-mapping-editor .row > .col-5:not(:first-child) input.form-control {
58+
background-color: var(--dm-value-bg);
59+
border-color: var(--dm-value-border);
60+
border-left: 3px solid #d5902f;
61+
color: #65380f;
62+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace !important;
63+
}
64+
65+
.dm-mapping-editor .row > .col-5:not(:first-child) .form-control::placeholder {
66+
color: #a9814a;
67+
}
68+
69+
.dm-mapping-editor .form-control:focus,
70+
.dm-mapping-editor .form-select:focus {
71+
border-color: #6aa0f6;
72+
box-shadow: 0 0 0 3px var(--dm-control-focus);
73+
outline: 0;
74+
}
75+
76+
.dm-mapping-editor .input-group {
77+
border-radius: 5px;
78+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
79+
}
80+
81+
.dm-mapping-editor .card {
82+
border-color: #d8e3f0;
83+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
84+
}
85+
86+
.dm-mapping-editor .card-header {
87+
background: #eef6ff;
88+
border-color: #d8e3f0;
89+
}
90+
91+
.dm-mapping-editor code {
92+
color: #174ea6;
93+
background: #eaf3ff !important;
94+
border: 1px solid #c9ddfb;
95+
}
96+
97+
.dm-mapping-editor .form-control,
98+
.dm-mapping-editor .form-select,
99+
.dm-mapping-editor .col-form-label,
100+
.dm-mapping-editor .btn {
101+
font-size: 12px;
102+
line-height: 1.25;
103+
padding-top: 3px;
104+
padding-bottom: 3px;
105+
height: auto;
106+
}
107+
108+
.dm-mapping-editor .btn {
109+
padding-left: 5px;
110+
padding-right: 5px;
111+
}
112+
113+
.dm-mapping-editor .card-body {
114+
padding: 7px;
115+
}

playground/dist/index.js

Lines changed: 14 additions & 46 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

playground/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Morphos · Playground</title>
77
<link rel="icon" type="image/svg+xml" href="../docs/images/morphos_logo.svg" />
8+
<link rel="stylesheet" href="./style.css" />
9+
<link rel="stylesheet" href="./bootstrap34-customizations.css" />
10+
<link rel="stylesheet" href="./bootstrap53-customizations.css" />
811
</head>
912
<body>
1013
<div id="root"></div>

0 commit comments

Comments
 (0)