imxc 0.3.2 → 0.4.1

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/ir.d.ts CHANGED
@@ -12,6 +12,7 @@ export interface IRComponent {
12
12
  stateSlots: IRStateSlot[];
13
13
  bufferCount: number;
14
14
  params: IRPropParam[];
15
+ namedPropsType?: string;
15
16
  body: IRNode[];
16
17
  }
17
18
  export interface IRStateSlot {
@@ -47,6 +48,7 @@ export interface IRButton {
47
48
  kind: 'button';
48
49
  title: string;
49
50
  action: string[];
51
+ disabled?: boolean;
50
52
  style?: string;
51
53
  loc?: SourceLoc;
52
54
  }
@@ -64,6 +66,7 @@ export interface IRCheckbox {
64
66
  stateVar: string;
65
67
  valueExpr?: string;
66
68
  onChangeExpr?: string;
69
+ directBind?: boolean;
67
70
  style?: string;
68
71
  loc?: SourceLoc;
69
72
  }
@@ -125,6 +128,7 @@ export interface IRSliderFloat {
125
128
  stateVar: string;
126
129
  valueExpr?: string;
127
130
  onChangeExpr?: string;
131
+ directBind?: boolean;
128
132
  min: string;
129
133
  max: string;
130
134
  style?: string;
@@ -136,6 +140,7 @@ export interface IRSliderInt {
136
140
  stateVar: string;
137
141
  valueExpr?: string;
138
142
  onChangeExpr?: string;
143
+ directBind?: boolean;
139
144
  min: string;
140
145
  max: string;
141
146
  style?: string;
@@ -147,6 +152,7 @@ export interface IRDragFloat {
147
152
  stateVar: string;
148
153
  valueExpr?: string;
149
154
  onChangeExpr?: string;
155
+ directBind?: boolean;
150
156
  speed: string;
151
157
  style?: string;
152
158
  loc?: SourceLoc;
@@ -157,6 +163,7 @@ export interface IRDragInt {
157
163
  stateVar: string;
158
164
  valueExpr?: string;
159
165
  onChangeExpr?: string;
166
+ directBind?: boolean;
160
167
  speed: string;
161
168
  style?: string;
162
169
  loc?: SourceLoc;
@@ -167,6 +174,7 @@ export interface IRCombo {
167
174
  stateVar: string;
168
175
  valueExpr?: string;
169
176
  onChangeExpr?: string;
177
+ directBind?: boolean;
170
178
  items: string;
171
179
  style?: string;
172
180
  loc?: SourceLoc;
@@ -177,6 +185,7 @@ export interface IRInputInt {
177
185
  stateVar: string;
178
186
  valueExpr?: string;
179
187
  onChangeExpr?: string;
188
+ directBind?: boolean;
180
189
  style?: string;
181
190
  loc?: SourceLoc;
182
191
  }
@@ -186,6 +195,7 @@ export interface IRInputFloat {
186
195
  stateVar: string;
187
196
  valueExpr?: string;
188
197
  onChangeExpr?: string;
198
+ directBind?: boolean;
189
199
  style?: string;
190
200
  loc?: SourceLoc;
191
201
  }
@@ -193,6 +203,7 @@ export interface IRColorEdit {
193
203
  kind: 'color_edit';
194
204
  label: string;
195
205
  stateVar: string;
206
+ directBind?: boolean;
196
207
  style?: string;
197
208
  loc?: SourceLoc;
198
209
  }
@@ -202,6 +213,7 @@ export interface IRListBox {
202
213
  stateVar: string;
203
214
  valueExpr?: string;
204
215
  onChangeExpr?: string;
216
+ directBind?: boolean;
205
217
  items: string;
206
218
  style?: string;
207
219
  loc?: SourceLoc;
@@ -252,6 +264,7 @@ export interface IRRadio {
252
264
  stateVar: string;
253
265
  valueExpr?: string;
254
266
  onChangeExpr?: string;
267
+ directBind?: boolean;
255
268
  index: string;
256
269
  style?: string;
257
270
  loc?: SourceLoc;
@@ -268,6 +281,7 @@ export interface IRColorPicker {
268
281
  kind: 'color_picker';
269
282
  label: string;
270
283
  stateVar: string;
284
+ directBind?: boolean;
271
285
  style?: string;
272
286
  loc?: SourceLoc;
273
287
  }
@@ -1,16 +1,17 @@
1
1
  import ts from 'typescript';
2
2
  import type { ParsedFile } from './parser.js';
3
3
  import type { ValidationResult } from './validator.js';
4
- import type { IRComponent, IRStateSlot } from './ir.js';
4
+ import type { IRComponent, IRStateSlot, IRType } from './ir.js';
5
5
  interface LoweringContext {
6
6
  stateVars: Map<string, IRStateSlot>;
7
7
  setterMap: Map<string, string>;
8
8
  propsParam: string | null;
9
+ propsFieldTypes: Map<string, IRType | 'callback'>;
9
10
  bufferIndex: number;
10
11
  sourceFile: ts.SourceFile;
11
12
  customComponents: Map<string, string>;
12
13
  }
13
- export declare function lowerComponent(parsed: ParsedFile, validation: ValidationResult): IRComponent;
14
+ export declare function lowerComponent(parsed: ParsedFile, validation: ValidationResult, externalInterfaces?: Map<string, Map<string, IRType | 'callback'>>): IRComponent;
14
15
  /**
15
16
  * Convert a TypeScript expression to C++ code string.
16
17
  */
package/dist/lowering.js CHANGED
@@ -4,7 +4,7 @@ function getLoc(node, ctx) {
4
4
  const { line } = ctx.sourceFile.getLineAndCharacterOfPosition(node.getStart());
5
5
  return { file: ctx.sourceFile.fileName, line: line + 1 };
6
6
  }
7
- export function lowerComponent(parsed, validation) {
7
+ export function lowerComponent(parsed, validation, externalInterfaces) {
8
8
  const func = parsed.component;
9
9
  const name = func.name.text;
10
10
  // Build state slots
@@ -24,19 +24,37 @@ export function lowerComponent(parsed, validation) {
24
24
  }
25
25
  // Detect props parameter
26
26
  let propsParam = null;
27
+ let namedPropsType;
27
28
  const params = [];
29
+ const propsFieldTypes = new Map();
28
30
  if (func.parameters.length > 0) {
29
31
  const param = func.parameters[0];
30
32
  if (ts.isIdentifier(param.name)) {
31
33
  propsParam = param.name.text;
32
34
  }
33
- // Extract prop types from type annotation
34
- if (param.type && ts.isTypeLiteralNode(param.type)) {
35
- for (const member of param.type.members) {
36
- if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
37
- const propName = member.name.text;
38
- const propType = inferPropType(member);
39
- params.push({ name: propName, type: propType });
35
+ if (param.type) {
36
+ if (ts.isTypeLiteralNode(param.type)) {
37
+ // Inline type literal: extract field types
38
+ for (const member of param.type.members) {
39
+ if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
40
+ const propName = member.name.text;
41
+ const propType = inferPropType(member);
42
+ params.push({ name: propName, type: propType });
43
+ propsFieldTypes.set(propName, propType);
44
+ }
45
+ }
46
+ }
47
+ else if (ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)) {
48
+ // Named interface reference: extract the type name and scan source for the interface
49
+ namedPropsType = param.type.typeName.text;
50
+ // First try the source file itself, then fall back to external interfaces
51
+ extractInterfaceFields(namedPropsType, parsed.sourceFile, propsFieldTypes);
52
+ if (propsFieldTypes.size === 0 && externalInterfaces) {
53
+ const ext = externalInterfaces.get(namedPropsType);
54
+ if (ext) {
55
+ for (const [k, v] of ext)
56
+ propsFieldTypes.set(k, v);
57
+ }
40
58
  }
41
59
  }
42
60
  }
@@ -45,6 +63,7 @@ export function lowerComponent(parsed, validation) {
45
63
  stateVars,
46
64
  setterMap,
47
65
  propsParam,
66
+ propsFieldTypes,
48
67
  bufferIndex: 0,
49
68
  sourceFile: parsed.sourceFile,
50
69
  customComponents: validation.customComponents,
@@ -62,6 +81,7 @@ export function lowerComponent(parsed, validation) {
62
81
  stateSlots,
63
82
  bufferCount: ctx.bufferIndex,
64
83
  params,
84
+ namedPropsType,
65
85
  body,
66
86
  };
67
87
  }
@@ -79,6 +99,45 @@ function inferPropType(member) {
79
99
  return 'string';
80
100
  return 'string';
81
101
  }
102
+ /**
103
+ * Scan the source file for an interface declaration matching `interfaceName`
104
+ * and extract its field types into `fieldTypes`.
105
+ * Only handles direct fields — nested interface types are not resolved.
106
+ */
107
+ function extractInterfaceFields(interfaceName, sourceFile, fieldTypes) {
108
+ for (const stmt of sourceFile.statements) {
109
+ if (ts.isInterfaceDeclaration(stmt) && stmt.name.text === interfaceName) {
110
+ for (const member of stmt.members) {
111
+ if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
112
+ const fieldName = member.name.text;
113
+ if (!member.type) {
114
+ fieldTypes.set(fieldName, 'string');
115
+ continue;
116
+ }
117
+ if (ts.isFunctionTypeNode(member.type)) {
118
+ fieldTypes.set(fieldName, 'callback');
119
+ continue;
120
+ }
121
+ const typeText = member.type.getText();
122
+ if (typeText === 'number' || typeText === 'number | undefined') {
123
+ fieldTypes.set(fieldName, 'float');
124
+ }
125
+ else if (typeText === 'boolean') {
126
+ fieldTypes.set(fieldName, 'bool');
127
+ }
128
+ else if (typeText === 'string') {
129
+ fieldTypes.set(fieldName, 'string');
130
+ }
131
+ else {
132
+ // Array types, nested interfaces, etc. treated as opaque
133
+ fieldTypes.set(fieldName, 'string');
134
+ }
135
+ }
136
+ }
137
+ return;
138
+ }
139
+ }
140
+ }
82
141
  function inferTypeFromExpr(expr) {
83
142
  if (ts.isNumericLiteral(expr)) {
84
143
  // Use getText() to check original source text, since TS normalizes 5.0 to "5" in .text
@@ -151,6 +210,9 @@ export function exprToCpp(node, ctx) {
151
210
  if (ts.isPropertyAccessExpression(node)) {
152
211
  const obj = exprToCpp(node.expression, ctx);
153
212
  const prop = node.name.text;
213
+ // JS .length on arrays/vectors translates to C++ .size()
214
+ if (prop === 'length')
215
+ return `${obj}.size()`;
154
216
  return `${obj}.${prop}`;
155
217
  }
156
218
  // Parenthesized expression
@@ -167,6 +229,13 @@ export function exprToCpp(node, ctx) {
167
229
  op = '==';
168
230
  else if (op === '!==')
169
231
  op = '!=';
232
+ // String + non-string: JS string concatenation -> C++ std::string + std::to_string
233
+ if (op === '+' && (ts.isStringLiteral(node.left) || ts.isNoSubstitutionTemplateLiteral(node.left))) {
234
+ const rightType = inferExprType(node.right, ctx);
235
+ if (rightType === 'int' || rightType === 'float') {
236
+ return `(std::string(${left}) + std::to_string(${right}))`;
237
+ }
238
+ }
170
239
  return `${left} ${op} ${right}`;
171
240
  }
172
241
  // Prefix unary expression (e.g., !show)
@@ -258,8 +327,8 @@ function extractActionStatements(expr, ctx) {
258
327
  }
259
328
  // If not an arrow function, call it
260
329
  const code = exprToCpp(expr, ctx);
261
- // Bare identifier (not already a call) needs () to invoke
262
- if (ts.isIdentifier(expr)) {
330
+ // Bare identifier or property access (not already a call) needs () to invoke
331
+ if (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr)) {
263
332
  return [code + '();'];
264
333
  }
265
334
  return [code + ';'];
@@ -378,6 +447,12 @@ function lowerJsxElement(node, body, ctx) {
378
447
  body.push({ kind: 'end_container', tag: containerTag });
379
448
  return;
380
449
  }
450
+ // DockSpace: detect if any child is a MenuBar
451
+ if (containerTag === 'DockSpace') {
452
+ const hasMenuBar = node.children.some(c => ts.isJsxElement(c) && ts.isIdentifier(c.openingElement.tagName) && c.openingElement.tagName.text === 'MenuBar');
453
+ if (hasMenuBar)
454
+ attrs['hasMenuBar'] = 'true';
455
+ }
381
456
  body.push({ kind: 'begin_container', tag: containerTag, props: attrs, loc: getLoc(node, ctx) });
382
457
  for (const child of node.children) {
383
458
  lowerJsxChild(child, body, ctx);
@@ -556,8 +631,9 @@ function lowerButton(attrs, rawAttrs, body, ctx, loc) {
556
631
  if (onPressExpr) {
557
632
  action = extractActionStatements(onPressExpr, ctx);
558
633
  }
634
+ const disabled = attrs['disabled'] === 'true' ? true : undefined;
559
635
  const style = attrs['style'];
560
- body.push({ kind: 'button', title, action, style, loc });
636
+ body.push({ kind: 'button', title, action, disabled, style, loc });
561
637
  }
562
638
  function lowerTextInput(attrs, rawAttrs, body, ctx, loc) {
563
639
  const label = attrs['label'] ?? '""';
@@ -596,6 +672,7 @@ function lowerCheckbox(attrs, rawAttrs, body, ctx, loc) {
596
672
  valueExprStr = exprToCpp(valueExpr, ctx);
597
673
  }
598
674
  // If not state-bound, get onChange expression
675
+ let directBind;
599
676
  if (!stateVar) {
600
677
  const onChangeRaw = rawAttrs.get('onChange');
601
678
  if (onChangeRaw) {
@@ -605,9 +682,12 @@ function lowerCheckbox(attrs, rawAttrs, body, ctx, loc) {
605
682
  onChangeExprStr = `${onChangeExprStr}()`;
606
683
  }
607
684
  }
685
+ else if (valueExprStr && valueExprStr.startsWith('props.')) {
686
+ directBind = true;
687
+ }
608
688
  }
609
689
  const style = attrs['style'];
610
- body.push({ kind: 'checkbox', label, stateVar, valueExpr: valueExprStr, onChangeExpr: onChangeExprStr, style, loc });
690
+ body.push({ kind: 'checkbox', label, stateVar, valueExpr: valueExprStr, onChangeExpr: onChangeExprStr, directBind, style, loc });
611
691
  }
612
692
  function lowerMenuItem(attrs, rawAttrs, body, ctx, loc) {
613
693
  const label = attrs['label'] ?? '""';
@@ -647,8 +727,15 @@ function lowerTextElement(node, body, ctx, loc) {
647
727
  args.push(cppExpr);
648
728
  break;
649
729
  case 'float':
650
- format += '%.2f';
651
- args.push(cppExpr);
730
+ if (cppExpr.startsWith('props.')) {
731
+ // Props fields: number could be int or float in C++, cast to double for safe printf
732
+ format += '%g';
733
+ args.push(`(double)${cppExpr}`);
734
+ }
735
+ else {
736
+ format += '%.2f';
737
+ args.push(cppExpr);
738
+ }
652
739
  break;
653
740
  case 'bool':
654
741
  format += '%s';
@@ -699,8 +786,18 @@ function inferExprType(expr, ctx) {
699
786
  if (slot)
700
787
  return slot.type;
701
788
  }
702
- // Property access: props.name -> look for string by default
789
+ // Property access: props.name -> look up field type if available
703
790
  if (ts.isPropertyAccessExpression(expr)) {
791
+ const prop = expr.name.text;
792
+ // .length maps to .size() in C++ — always int
793
+ if (prop === 'length')
794
+ return 'int';
795
+ // If accessing a direct field of the props param, look up its type
796
+ if (ctx.propsParam && ts.isIdentifier(expr.expression) && expr.expression.text === ctx.propsParam) {
797
+ const ft = ctx.propsFieldTypes.get(prop);
798
+ if (ft && ft !== 'callback')
799
+ return ft;
800
+ }
704
801
  return 'string';
705
802
  }
706
803
  // Binary expression: infer from operands
@@ -933,8 +1030,8 @@ function lowerRadio(attrs, rawAttrs, body, ctx, loc) {
933
1030
  const label = attrs['label'] ?? '""';
934
1031
  const index = attrs['index'] ?? '0';
935
1032
  const style = attrs['style'];
936
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
937
- body.push({ kind: 'radio', label, stateVar, valueExpr, onChangeExpr, index, style, loc });
1033
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1034
+ body.push({ kind: 'radio', label, stateVar, valueExpr, onChangeExpr, directBind, index, style, loc });
938
1035
  }
939
1036
  function lowerInputTextMultiline(attrs, rawAttrs, body, ctx, loc) {
940
1037
  const label = attrs['label'] ?? '""';
@@ -1030,6 +1127,7 @@ function lowerValueOnChange(rawAttrs, ctx) {
1030
1127
  let stateVar = '';
1031
1128
  let valueExpr;
1032
1129
  let onChangeExpr;
1130
+ let directBind;
1033
1131
  const valueRaw = rawAttrs.get('value');
1034
1132
  if (valueRaw && ts.isIdentifier(valueRaw) && ctx.stateVars.has(valueRaw.text)) {
1035
1133
  stateVar = valueRaw.text;
@@ -1043,57 +1141,62 @@ function lowerValueOnChange(rawAttrs, ctx) {
1043
1141
  onChangeExpr = `${onChangeExpr}()`;
1044
1142
  }
1045
1143
  }
1144
+ else if (valueRaw && ts.isPropertyAccessExpression(valueRaw)) {
1145
+ // No onChange + property access = direct pointer binding
1146
+ // This covers both props.field and course.field (loop item fields)
1147
+ directBind = true;
1148
+ }
1046
1149
  }
1047
- return { stateVar, valueExpr, onChangeExpr };
1150
+ return { stateVar, valueExpr, onChangeExpr, directBind };
1048
1151
  }
1049
1152
  function lowerSliderFloat(attrs, rawAttrs, body, ctx, loc) {
1050
1153
  const label = attrs['label'] ?? '""';
1051
1154
  const min = attrs['min'] ?? '0.0f';
1052
1155
  const max = attrs['max'] ?? '1.0f';
1053
1156
  const style = attrs['style'];
1054
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1055
- body.push({ kind: 'slider_float', label, stateVar, valueExpr, onChangeExpr, min, max, style, loc });
1157
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1158
+ body.push({ kind: 'slider_float', label, stateVar, valueExpr, onChangeExpr, directBind, min, max, style, loc });
1056
1159
  }
1057
1160
  function lowerSliderInt(attrs, rawAttrs, body, ctx, loc) {
1058
1161
  const label = attrs['label'] ?? '""';
1059
1162
  const min = attrs['min'] ?? '0';
1060
1163
  const max = attrs['max'] ?? '100';
1061
1164
  const style = attrs['style'];
1062
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1063
- body.push({ kind: 'slider_int', label, stateVar, valueExpr, onChangeExpr, min, max, style, loc });
1165
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1166
+ body.push({ kind: 'slider_int', label, stateVar, valueExpr, onChangeExpr, directBind, min, max, style, loc });
1064
1167
  }
1065
1168
  function lowerDragFloat(attrs, rawAttrs, body, ctx, loc) {
1066
1169
  const label = attrs['label'] ?? '""';
1067
1170
  const speed = attrs['speed'] ?? '1.0f';
1068
1171
  const style = attrs['style'];
1069
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1070
- body.push({ kind: 'drag_float', label, stateVar, valueExpr, onChangeExpr, speed, style, loc });
1172
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1173
+ body.push({ kind: 'drag_float', label, stateVar, valueExpr, onChangeExpr, directBind, speed, style, loc });
1071
1174
  }
1072
1175
  function lowerDragInt(attrs, rawAttrs, body, ctx, loc) {
1073
1176
  const label = attrs['label'] ?? '""';
1074
1177
  const speed = attrs['speed'] ?? '1.0f';
1075
1178
  const style = attrs['style'];
1076
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1077
- body.push({ kind: 'drag_int', label, stateVar, valueExpr, onChangeExpr, speed, style, loc });
1179
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1180
+ body.push({ kind: 'drag_int', label, stateVar, valueExpr, onChangeExpr, directBind, speed, style, loc });
1078
1181
  }
1079
1182
  function lowerCombo(attrs, rawAttrs, body, ctx, loc) {
1080
1183
  const label = attrs['label'] ?? '""';
1081
1184
  const items = attrs['items'] ?? '';
1082
1185
  const style = attrs['style'];
1083
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1084
- body.push({ kind: 'combo', label, stateVar, valueExpr, onChangeExpr, items, style, loc });
1186
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1187
+ body.push({ kind: 'combo', label, stateVar, valueExpr, onChangeExpr, directBind, items, style, loc });
1085
1188
  }
1086
1189
  function lowerInputInt(attrs, rawAttrs, body, ctx, loc) {
1087
1190
  const label = attrs['label'] ?? '""';
1088
1191
  const style = attrs['style'];
1089
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1090
- body.push({ kind: 'input_int', label, stateVar, valueExpr, onChangeExpr, style, loc });
1192
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1193
+ body.push({ kind: 'input_int', label, stateVar, valueExpr, onChangeExpr, directBind, style, loc });
1091
1194
  }
1092
1195
  function lowerInputFloat(attrs, rawAttrs, body, ctx, loc) {
1093
1196
  const label = attrs['label'] ?? '""';
1094
1197
  const style = attrs['style'];
1095
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1096
- body.push({ kind: 'input_float', label, stateVar, valueExpr, onChangeExpr, style, loc });
1198
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1199
+ body.push({ kind: 'input_float', label, stateVar, valueExpr, onChangeExpr, directBind, style, loc });
1097
1200
  }
1098
1201
  function lowerColorEdit(attrs, rawAttrs, body, ctx, loc) {
1099
1202
  const label = attrs['label'] ?? '""';
@@ -1110,8 +1213,8 @@ function lowerListBox(attrs, rawAttrs, body, ctx, loc) {
1110
1213
  const label = attrs['label'] ?? '""';
1111
1214
  const items = attrs['items'] ?? '';
1112
1215
  const style = attrs['style'];
1113
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1114
- body.push({ kind: 'list_box', label, stateVar, valueExpr, onChangeExpr, items, style, loc });
1216
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1217
+ body.push({ kind: 'list_box', label, stateVar, valueExpr, onChangeExpr, directBind, items, style, loc });
1115
1218
  }
1116
1219
  function lowerProgressBar(attrs, rawAttrs, body, ctx, loc) {
1117
1220
  const value = attrs['value'] ?? '0.0f';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imxc",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "Compiler for IMX — compiles React-like .tsx to native Dear ImGui C++",
5
5
  "type": "module",
6
6
  "bin": {