view-contracts 0.4.0 → 0.4.2
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/dsl.js +2 -0
- package/dist/dsl.js.map +7 -0
- package/dist/preview.mjs +61 -62
- package/dist/preview.mjs.map +4 -4
- package/dist/view-contracts.bundle.mjs +232 -176
- package/dist/view-contracts.bundle.mjs.map +4 -4
- package/package.json +5 -4
- package/renderers/react/defaults/theme.yaml +29 -0
- package/renderers/react/defaults/tokens.yaml +61 -1
- package/renderers/react/lowering/lowerToJsx.ts +6 -6
- package/renderers/react/paths.ts +5 -1
- package/renderers/react/runtime/structural.css +38 -0
- package/renderers/react/style/foldLiterals.ts +14 -4
- package/renderers/swiftui/lowering/lowerToSwiftUI.ts +250 -31
- package/renderers/swiftui/lowering/modifiers.ts +90 -11
- package/renderers/swiftui/renderRuntime.ts +48 -0
- package/renderers/swiftui/renderTheme.ts +69 -3
- package/src/dsl/index.ts +16 -0
- package/src/dsl/screen.tsx +54 -0
- package/src/dsl/types.ts +68 -0
- package/renderers/react/runtime/theme.css +0 -57
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "view-contracts",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Contract-first UI design toolchain — restricted TSX to View IR to platform renderers",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"workspaces": [
|
|
7
|
-
"packages/*"
|
|
8
|
-
],
|
|
9
6
|
"bin": {
|
|
10
7
|
"view-contracts": "dist/view-contracts.bundle.mjs"
|
|
11
8
|
},
|
|
12
9
|
"exports": {
|
|
13
10
|
".": "./dist/view-contracts.bundle.mjs",
|
|
11
|
+
"./dsl": "./dist/dsl.js",
|
|
14
12
|
"./preview": "./dist/preview.mjs",
|
|
15
13
|
"./package.json": "./package.json"
|
|
16
14
|
},
|
|
17
15
|
"files": [
|
|
18
16
|
"dist/view-contracts.bundle.mjs",
|
|
19
17
|
"dist/view-contracts.bundle.mjs.map",
|
|
18
|
+
"dist/dsl.js",
|
|
19
|
+
"dist/dsl.js.map",
|
|
20
20
|
"dist/preview.mjs",
|
|
21
21
|
"dist/preview.mjs.map",
|
|
22
22
|
"dist/preview-style-helpers.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"renderers/react/runtime",
|
|
25
25
|
"renderers/react/defaults",
|
|
26
26
|
"src/generated/schema",
|
|
27
|
+
"src/dsl",
|
|
27
28
|
"spec",
|
|
28
29
|
"renderers",
|
|
29
30
|
"cli-contract.yaml",
|
|
@@ -29,6 +29,21 @@ defaults:
|
|
|
29
29
|
border: "{ui.layout.border-1}"
|
|
30
30
|
borderColor: "{ui.border}"
|
|
31
31
|
minHeight: "{ui.space.input-min-height}"
|
|
32
|
+
TableHeader:
|
|
33
|
+
background: "{ui.surface-muted}"
|
|
34
|
+
alignItems: "{ui.layout.align-center}"
|
|
35
|
+
TableColumn:
|
|
36
|
+
padding: "{ui.space.table-column-padding}"
|
|
37
|
+
fontSize: "{ui.space.table-column-font-size}"
|
|
38
|
+
fontWeight: "{ui.layout.font-semibold}"
|
|
39
|
+
foreground: "{ui.text-muted}"
|
|
40
|
+
TableCell:
|
|
41
|
+
padding: "{ui.space.table-cell-padding}"
|
|
42
|
+
fontSize: "{ui.space.table-cell-font-size}"
|
|
43
|
+
TableRow:
|
|
44
|
+
alignItems: "{ui.layout.align-center}"
|
|
45
|
+
Box:
|
|
46
|
+
width: "{ui.layout.width-full}"
|
|
32
47
|
variants:
|
|
33
48
|
Button:
|
|
34
49
|
primary:
|
|
@@ -46,6 +61,16 @@ variants:
|
|
|
46
61
|
Text:
|
|
47
62
|
muted:
|
|
48
63
|
foreground: "{ui.text-muted}"
|
|
64
|
+
field-label:
|
|
65
|
+
fontSize: "{ui.space.font-field-label}"
|
|
66
|
+
fontWeight: "{ui.layout.font-bold}"
|
|
67
|
+
foreground: "{ui.text}"
|
|
68
|
+
field-help:
|
|
69
|
+
fontSize: "{ui.space.font-field-help}"
|
|
70
|
+
foreground: "{ui.text-muted}"
|
|
71
|
+
field-error:
|
|
72
|
+
fontSize: "{ui.space.font-field-help}"
|
|
73
|
+
foreground: "{ui.danger}"
|
|
49
74
|
primary:
|
|
50
75
|
foreground: "{ui.primary}"
|
|
51
76
|
success:
|
|
@@ -54,3 +79,7 @@ variants:
|
|
|
54
79
|
foreground: "{ui.warning}"
|
|
55
80
|
danger:
|
|
56
81
|
foreground: "{ui.danger}"
|
|
82
|
+
Box:
|
|
83
|
+
card:
|
|
84
|
+
background: "{ui.surface}"
|
|
85
|
+
width: "{ui.layout.width-full}"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Default design tokens —
|
|
1
|
+
# Default design tokens — merged into generated/react/theme.css via emitTokenCss().
|
|
2
2
|
ui:
|
|
3
3
|
bg:
|
|
4
4
|
$type: color
|
|
@@ -61,9 +61,18 @@ ui:
|
|
|
61
61
|
font-bold:
|
|
62
62
|
$type: layout
|
|
63
63
|
$value: "700"
|
|
64
|
+
font-semibold:
|
|
65
|
+
$type: layout
|
|
66
|
+
$value: "600"
|
|
64
67
|
border-1:
|
|
65
68
|
$type: layout
|
|
66
69
|
$value: "1px solid"
|
|
70
|
+
align-center:
|
|
71
|
+
$type: layout
|
|
72
|
+
$value: "center"
|
|
73
|
+
width-full:
|
|
74
|
+
$type: layout
|
|
75
|
+
$value: "100%"
|
|
67
76
|
space:
|
|
68
77
|
radius-md:
|
|
69
78
|
$type: dimension
|
|
@@ -83,3 +92,54 @@ ui:
|
|
|
83
92
|
textarea-padding:
|
|
84
93
|
$type: dimension
|
|
85
94
|
$value: "10px 12px"
|
|
95
|
+
field-gap:
|
|
96
|
+
$type: dimension
|
|
97
|
+
$value: "6px"
|
|
98
|
+
field-inner-gap:
|
|
99
|
+
$type: dimension
|
|
100
|
+
$value: "4px"
|
|
101
|
+
checkbox-gap:
|
|
102
|
+
$type: dimension
|
|
103
|
+
$value: "8px"
|
|
104
|
+
list-indent:
|
|
105
|
+
$type: dimension
|
|
106
|
+
$value: "20px"
|
|
107
|
+
list-item-padding:
|
|
108
|
+
$type: dimension
|
|
109
|
+
$value: "2px 0"
|
|
110
|
+
line-height-body:
|
|
111
|
+
$type: dimension
|
|
112
|
+
$value: "1.6"
|
|
113
|
+
line-height-heading:
|
|
114
|
+
$type: dimension
|
|
115
|
+
$value: "1.2"
|
|
116
|
+
font-field-label:
|
|
117
|
+
$type: dimension
|
|
118
|
+
$value: "13px"
|
|
119
|
+
font-field-help:
|
|
120
|
+
$type: dimension
|
|
121
|
+
$value: "12px"
|
|
122
|
+
table-column-padding:
|
|
123
|
+
$type: dimension
|
|
124
|
+
$value: "12px 14px"
|
|
125
|
+
table-column-font-size:
|
|
126
|
+
$type: dimension
|
|
127
|
+
$value: "12px"
|
|
128
|
+
table-cell-padding:
|
|
129
|
+
$type: dimension
|
|
130
|
+
$value: "14px"
|
|
131
|
+
table-cell-font-size:
|
|
132
|
+
$type: dimension
|
|
133
|
+
$value: "14px"
|
|
134
|
+
input-min-height-sm:
|
|
135
|
+
$type: dimension
|
|
136
|
+
$value: "34px"
|
|
137
|
+
input-min-height-lg:
|
|
138
|
+
$type: dimension
|
|
139
|
+
$value: "50px"
|
|
140
|
+
input-padding-sm:
|
|
141
|
+
$type: dimension
|
|
142
|
+
$value: "6px 10px"
|
|
143
|
+
input-padding-lg:
|
|
144
|
+
$type: dimension
|
|
145
|
+
$value: "12px 14px"
|
|
@@ -160,20 +160,20 @@ function emitFieldWrap(inner: string, props: Record<string, IrValue>, level: num
|
|
|
160
160
|
const error = propExpr(props, 'error');
|
|
161
161
|
const lines = [`${indent(level)}<label className="vc-field">`];
|
|
162
162
|
if (typeof labelLit === 'string') {
|
|
163
|
-
lines.push(`${indent(level + 1)}<span className="vc-field-label">{${JSON.stringify(labelLit)}}</span>`);
|
|
163
|
+
lines.push(`${indent(level + 1)}<span className="vc-text vc-text--field-label">{${JSON.stringify(labelLit)}}</span>`);
|
|
164
164
|
} else if (label) {
|
|
165
|
-
lines.push(`${indent(level + 1)}{${label} ? <span className="vc-field-label">{${label}}</span> : null}`);
|
|
165
|
+
lines.push(`${indent(level + 1)}{${label} ? <span className="vc-text vc-text--field-label">{${label}}</span> : null}`);
|
|
166
166
|
}
|
|
167
167
|
lines.push(`${indent(level + 1)}${inner}`);
|
|
168
168
|
if (typeof helpLit === 'string') {
|
|
169
|
-
lines.push(`${indent(level + 1)}<span className="vc-field-help">{${JSON.stringify(helpLit)}}</span>`);
|
|
169
|
+
lines.push(`${indent(level + 1)}<span className="vc-text vc-text--field-help">{${JSON.stringify(helpLit)}}</span>`);
|
|
170
170
|
} else if (help) {
|
|
171
|
-
lines.push(`${indent(level + 1)}{${help} ? <span className="vc-field-help">{${help}}</span> : null}`);
|
|
171
|
+
lines.push(`${indent(level + 1)}{${help} ? <span className="vc-text vc-text--field-help">{${help}}</span> : null}`);
|
|
172
172
|
}
|
|
173
173
|
if (typeof errorLit === 'string') {
|
|
174
|
-
lines.push(`${indent(level + 1)}<span className="vc-field-error">{${JSON.stringify(errorLit)}}</span>`);
|
|
174
|
+
lines.push(`${indent(level + 1)}<span className="vc-text vc-text--field-error">{${JSON.stringify(errorLit)}}</span>`);
|
|
175
175
|
} else if (error) {
|
|
176
|
-
lines.push(`${indent(level + 1)}{${error} ? <span className="vc-field-error">{${error}}</span> : null}`);
|
|
176
|
+
lines.push(`${indent(level + 1)}{${error} ? <span className="vc-text vc-text--field-error">{${error}}</span> : null}`);
|
|
177
177
|
}
|
|
178
178
|
lines.push(`${indent(level)}</label>`);
|
|
179
179
|
return lines.join('\n');
|
package/renderers/react/paths.ts
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* DSL structural layer — layout/control mechanics only; no visual token values. */
|
|
2
|
+
* { box-sizing: border-box; }
|
|
3
|
+
body { margin: 0; background: var(--ui-bg); color: var(--ui-text); font-family: var(--ui-font); font-size: var(--ui-space-font-body); }
|
|
4
|
+
button, input, textarea, select { font: inherit; }
|
|
5
|
+
|
|
6
|
+
.vc-page { min-height: 100vh; min-width: 0; }
|
|
7
|
+
.vc-box { display: block; min-width: 0; }
|
|
8
|
+
.vc-column { display: flex; flex-direction: column; min-width: 0; }
|
|
9
|
+
.vc-stack { display: flex; flex-direction: column; min-width: 0; }
|
|
10
|
+
.vc-row { display: flex; flex-direction: row; min-width: 0; }
|
|
11
|
+
.vc-wrap { flex-wrap: wrap; }
|
|
12
|
+
.vc-spacer { min-width: 0; min-height: 0; }
|
|
13
|
+
|
|
14
|
+
.vc-heading { margin: 0; line-height: 1.2; letter-spacing: -0.02em; }
|
|
15
|
+
.vc-text { margin: 0; line-height: 1.6; }
|
|
16
|
+
.vc-link { color: var(--ui-primary); text-decoration: none; }
|
|
17
|
+
.vc-link:hover { text-decoration: underline; }
|
|
18
|
+
.vc-divider { border: 0; border-top: 1px solid var(--ui-border); width: 100%; }
|
|
19
|
+
|
|
20
|
+
.vc-button { border: 0; cursor: pointer; }
|
|
21
|
+
|
|
22
|
+
.vc-form { display: flex; flex-direction: column; min-width: 0; }
|
|
23
|
+
.vc-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
24
|
+
|
|
25
|
+
.vc-text-input, .vc-text-area, .vc-select { outline: none; }
|
|
26
|
+
.vc-text-input:focus, .vc-text-area:focus, .vc-select:focus { border-color: var(--ui-primary); box-shadow: 0 0 0 3px var(--ui-primary-weak); }
|
|
27
|
+
.vc-select { appearance: none; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6l4 4 4-4' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; background-size: 16px; }
|
|
28
|
+
.vc-text-area { resize: vertical; }
|
|
29
|
+
|
|
30
|
+
.vc-checkbox { display: inline-flex; align-items: center; gap: 8px; }
|
|
31
|
+
.vc-list { margin: 0; padding-left: 20px; }
|
|
32
|
+
.vc-list-item { margin: 0; padding: 2px 0; }
|
|
33
|
+
|
|
34
|
+
.vc-table { width: 100%; display: flex; flex-direction: column; min-width: 0; }
|
|
35
|
+
.vc-table-header { display: flex; align-items: center; min-width: 0; }
|
|
36
|
+
.vc-table-column { display: flex; align-items: center; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
|
|
37
|
+
.vc-table-row { display: flex; align-items: center; border-top: 1px solid var(--ui-border); min-width: 0; }
|
|
38
|
+
.vc-table-cell { display: flex; align-items: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
|
|
@@ -39,13 +39,23 @@ export function foldLiteralsToCss(literals: Record<string, unknown>): FoldedCss
|
|
|
39
39
|
const style: FoldedCss = {};
|
|
40
40
|
const width = literals.width;
|
|
41
41
|
if (typeof width === 'number') style.width = len(width);
|
|
42
|
-
else if (
|
|
42
|
+
else if (width === 'fill') style.width = '100%';
|
|
43
|
+
else if (typeof width === 'string' && width !== 'wrap') style.width = width;
|
|
43
44
|
|
|
44
45
|
if (typeof literals.minWidth === 'number') style.minWidth = len(literals.minWidth);
|
|
45
|
-
|
|
46
|
-
if (typeof
|
|
46
|
+
const maxWidth = literals.maxWidth;
|
|
47
|
+
if (typeof maxWidth === 'number') style.maxWidth = len(maxWidth);
|
|
48
|
+
else if (maxWidth === 'fill') style.maxWidth = '100%';
|
|
49
|
+
|
|
50
|
+
const height = literals.height;
|
|
51
|
+
if (typeof height === 'number') style.height = len(height);
|
|
52
|
+
else if (height === 'fill') style.height = '100%';
|
|
53
|
+
else if (typeof height === 'string' && height !== 'wrap') style.height = height;
|
|
54
|
+
|
|
47
55
|
if (typeof literals.minHeight === 'number') style.minHeight = len(literals.minHeight);
|
|
48
|
-
|
|
56
|
+
const maxHeight = literals.maxHeight;
|
|
57
|
+
if (typeof maxHeight === 'number') style.maxHeight = len(maxHeight);
|
|
58
|
+
else if (maxHeight === 'fill') style.maxHeight = '100%';
|
|
49
59
|
if (literals.aspectRatio !== undefined) style.aspectRatio = String(literals.aspectRatio);
|
|
50
60
|
|
|
51
61
|
const weight = literals.weight;
|