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/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "view-contracts",
3
- "version": "0.4.0",
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 — mirrors renderers/react/runtime/theme.css :root variables.
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');
@@ -1 +1,5 @@
1
- export { reactTemplatesDir, reactRuntimeDir } from '../../src/lib/rendererPaths.js';
1
+ export {
2
+ reactTemplatesDir,
3
+ reactRuntimeDir,
4
+ reactStructuralCssPath,
5
+ } from '../../src/lib/rendererPaths.js';
@@ -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 (typeof width === 'string' && width !== 'wrap' && width !== 'fill') style.width = width;
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
- if (typeof literals.maxWidth === 'number') style.maxWidth = len(literals.maxWidth);
46
- if (typeof literals.height === 'number') style.height = len(literals.height);
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
- if (typeof literals.maxHeight === 'number') style.maxHeight = len(literals.maxHeight);
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;