imxc 0.3.2 → 0.4.0

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'] ?? '""';
@@ -699,8 +779,18 @@ function inferExprType(expr, ctx) {
699
779
  if (slot)
700
780
  return slot.type;
701
781
  }
702
- // Property access: props.name -> look for string by default
782
+ // Property access: props.name -> look up field type if available
703
783
  if (ts.isPropertyAccessExpression(expr)) {
784
+ const prop = expr.name.text;
785
+ // .length maps to .size() in C++ — always int
786
+ if (prop === 'length')
787
+ return 'int';
788
+ // If accessing a direct field of the props param, look up its type
789
+ if (ctx.propsParam && ts.isIdentifier(expr.expression) && expr.expression.text === ctx.propsParam) {
790
+ const ft = ctx.propsFieldTypes.get(prop);
791
+ if (ft && ft !== 'callback')
792
+ return ft;
793
+ }
704
794
  return 'string';
705
795
  }
706
796
  // Binary expression: infer from operands
@@ -933,8 +1023,8 @@ function lowerRadio(attrs, rawAttrs, body, ctx, loc) {
933
1023
  const label = attrs['label'] ?? '""';
934
1024
  const index = attrs['index'] ?? '0';
935
1025
  const style = attrs['style'];
936
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
937
- body.push({ kind: 'radio', label, stateVar, valueExpr, onChangeExpr, index, style, loc });
1026
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1027
+ body.push({ kind: 'radio', label, stateVar, valueExpr, onChangeExpr, directBind, index, style, loc });
938
1028
  }
939
1029
  function lowerInputTextMultiline(attrs, rawAttrs, body, ctx, loc) {
940
1030
  const label = attrs['label'] ?? '""';
@@ -1030,6 +1120,7 @@ function lowerValueOnChange(rawAttrs, ctx) {
1030
1120
  let stateVar = '';
1031
1121
  let valueExpr;
1032
1122
  let onChangeExpr;
1123
+ let directBind;
1033
1124
  const valueRaw = rawAttrs.get('value');
1034
1125
  if (valueRaw && ts.isIdentifier(valueRaw) && ctx.stateVars.has(valueRaw.text)) {
1035
1126
  stateVar = valueRaw.text;
@@ -1043,57 +1134,62 @@ function lowerValueOnChange(rawAttrs, ctx) {
1043
1134
  onChangeExpr = `${onChangeExpr}()`;
1044
1135
  }
1045
1136
  }
1137
+ else if (valueRaw && ts.isPropertyAccessExpression(valueRaw)) {
1138
+ // No onChange + property access = direct pointer binding
1139
+ // This covers both props.field and course.field (loop item fields)
1140
+ directBind = true;
1141
+ }
1046
1142
  }
1047
- return { stateVar, valueExpr, onChangeExpr };
1143
+ return { stateVar, valueExpr, onChangeExpr, directBind };
1048
1144
  }
1049
1145
  function lowerSliderFloat(attrs, rawAttrs, body, ctx, loc) {
1050
1146
  const label = attrs['label'] ?? '""';
1051
1147
  const min = attrs['min'] ?? '0.0f';
1052
1148
  const max = attrs['max'] ?? '1.0f';
1053
1149
  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 });
1150
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1151
+ body.push({ kind: 'slider_float', label, stateVar, valueExpr, onChangeExpr, directBind, min, max, style, loc });
1056
1152
  }
1057
1153
  function lowerSliderInt(attrs, rawAttrs, body, ctx, loc) {
1058
1154
  const label = attrs['label'] ?? '""';
1059
1155
  const min = attrs['min'] ?? '0';
1060
1156
  const max = attrs['max'] ?? '100';
1061
1157
  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 });
1158
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1159
+ body.push({ kind: 'slider_int', label, stateVar, valueExpr, onChangeExpr, directBind, min, max, style, loc });
1064
1160
  }
1065
1161
  function lowerDragFloat(attrs, rawAttrs, body, ctx, loc) {
1066
1162
  const label = attrs['label'] ?? '""';
1067
1163
  const speed = attrs['speed'] ?? '1.0f';
1068
1164
  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 });
1165
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1166
+ body.push({ kind: 'drag_float', label, stateVar, valueExpr, onChangeExpr, directBind, speed, style, loc });
1071
1167
  }
1072
1168
  function lowerDragInt(attrs, rawAttrs, body, ctx, loc) {
1073
1169
  const label = attrs['label'] ?? '""';
1074
1170
  const speed = attrs['speed'] ?? '1.0f';
1075
1171
  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 });
1172
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1173
+ body.push({ kind: 'drag_int', label, stateVar, valueExpr, onChangeExpr, directBind, speed, style, loc });
1078
1174
  }
1079
1175
  function lowerCombo(attrs, rawAttrs, body, ctx, loc) {
1080
1176
  const label = attrs['label'] ?? '""';
1081
1177
  const items = attrs['items'] ?? '';
1082
1178
  const style = attrs['style'];
1083
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1084
- body.push({ kind: 'combo', label, stateVar, valueExpr, onChangeExpr, items, style, loc });
1179
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1180
+ body.push({ kind: 'combo', label, stateVar, valueExpr, onChangeExpr, directBind, items, style, loc });
1085
1181
  }
1086
1182
  function lowerInputInt(attrs, rawAttrs, body, ctx, loc) {
1087
1183
  const label = attrs['label'] ?? '""';
1088
1184
  const style = attrs['style'];
1089
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1090
- body.push({ kind: 'input_int', label, stateVar, valueExpr, onChangeExpr, style, loc });
1185
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1186
+ body.push({ kind: 'input_int', label, stateVar, valueExpr, onChangeExpr, directBind, style, loc });
1091
1187
  }
1092
1188
  function lowerInputFloat(attrs, rawAttrs, body, ctx, loc) {
1093
1189
  const label = attrs['label'] ?? '""';
1094
1190
  const style = attrs['style'];
1095
- const { stateVar, valueExpr, onChangeExpr } = lowerValueOnChange(rawAttrs, ctx);
1096
- body.push({ kind: 'input_float', label, stateVar, valueExpr, onChangeExpr, style, loc });
1191
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1192
+ body.push({ kind: 'input_float', label, stateVar, valueExpr, onChangeExpr, directBind, style, loc });
1097
1193
  }
1098
1194
  function lowerColorEdit(attrs, rawAttrs, body, ctx, loc) {
1099
1195
  const label = attrs['label'] ?? '""';
@@ -1110,8 +1206,8 @@ function lowerListBox(attrs, rawAttrs, body, ctx, loc) {
1110
1206
  const label = attrs['label'] ?? '""';
1111
1207
  const items = attrs['items'] ?? '';
1112
1208
  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 });
1209
+ const { stateVar, valueExpr, onChangeExpr, directBind } = lowerValueOnChange(rawAttrs, ctx);
1210
+ body.push({ kind: 'list_box', label, stateVar, valueExpr, onChangeExpr, directBind, items, style, loc });
1115
1211
  }
1116
1212
  function lowerProgressBar(attrs, rawAttrs, body, ctx, loc) {
1117
1213
  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.0",
4
4
  "description": "Compiler for IMX — compiles React-like .tsx to native Dear ImGui C++",
5
5
  "type": "module",
6
6
  "bin": {