imxc 0.2.0 → 0.3.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/index.js CHANGED
@@ -1,12 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { parseArgs } from 'node:util';
3
- import * as fs from 'node:fs';
4
3
  import * as path from 'node:path';
5
- import { parseFile, extractImports } from './parser.js';
6
- import { validate } from './validator.js';
7
- import { lowerComponent } from './lowering.js';
8
- import { emitComponent, emitComponentHeader, emitRoot } from './emitter.js';
9
4
  import { initProject } from './init.js';
5
+ import { compile } from './compile.js';
6
+ import { startWatch } from './watch.js';
10
7
  // Handle `imxc init [dir]` subcommand
11
8
  if (process.argv[2] === 'init') {
12
9
  const dir = process.argv[3] ?? '.';
@@ -14,115 +11,38 @@ if (process.argv[2] === 'init') {
14
11
  initProject(absDir, path.basename(absDir));
15
12
  process.exit(0);
16
13
  }
17
- const { values, positionals } = parseArgs({
18
- allowPositionals: true,
19
- options: { output: { type: 'string', short: 'o' } },
20
- });
21
- if (positionals.length === 0) {
22
- console.error('Usage: imxc <input.tsx ...> -o <output-dir>');
23
- console.error(' imxc init [project-dir]');
24
- process.exit(1);
25
- }
26
- const outputDir = values.output ?? '.';
27
- fs.mkdirSync(outputDir, { recursive: true });
28
- let hasErrors = false;
29
- const compiled = [];
30
- // Phase 1: Parse, validate, and lower all components
31
- for (const file of positionals) {
32
- if (!fs.existsSync(file)) {
33
- console.error(`${file}:1:1 - error: file not found`);
34
- hasErrors = true;
35
- continue;
36
- }
37
- const source = fs.readFileSync(file, 'utf-8');
38
- const parsed = parseFile(file, source);
39
- if (parsed.errors.length > 0) {
40
- parsed.errors.forEach(e => console.error(`${e.file}:${e.line}:${e.col} - error: ${e.message}`));
41
- hasErrors = true;
42
- continue;
14
+ // Handle `imxc watch <dir> -o <output-dir>` subcommand
15
+ if (process.argv[2] === 'watch') {
16
+ const watchDir = process.argv[3];
17
+ if (!watchDir) {
18
+ console.error('Usage: imxc watch <dir> -o <output-dir>');
19
+ process.exit(1);
43
20
  }
44
- const validation = validate(parsed);
45
- if (validation.errors.length > 0) {
46
- validation.errors.forEach(e => console.error(`${e.file}:${e.line}:${e.col} - error: ${e.message}`));
47
- hasErrors = true;
48
- continue;
49
- }
50
- const ir = lowerComponent(parsed, validation);
51
- const imports = extractImports(parsed.sourceFile);
52
- compiled.push({
53
- name: ir.name,
54
- stateCount: ir.stateSlots.length,
55
- bufferCount: ir.bufferCount,
56
- ir,
57
- imports,
58
- hasProps: ir.params.length > 0,
21
+ const { values } = parseArgs({
22
+ args: process.argv.slice(4),
23
+ allowPositionals: false,
24
+ options: { output: { type: 'string', short: 'o' } },
59
25
  });
26
+ const outputDir = values.output ?? '.';
27
+ startWatch(path.resolve(watchDir), path.resolve(outputDir));
60
28
  }
61
- if (hasErrors)
62
- process.exit(1);
63
- // Phase 2: Build lookup of compiled components for cross-file resolution
64
- const componentMap = new Map();
65
- for (const comp of compiled) {
66
- componentMap.set(comp.name, comp);
67
- }
68
- // Phase 3: Resolve imported component stateCount/bufferCount in IR, then emit
69
- for (const comp of compiled) {
70
- // Resolve cross-file custom component info
71
- resolveCustomComponents(comp.ir.body, componentMap);
72
- // Build import info for #include directives
73
- const importInfos = [];
74
- for (const [importedName] of comp.imports) {
75
- const importedComp = componentMap.get(importedName);
76
- if (importedComp && importedComp.hasProps) {
77
- importInfos.push({
78
- name: importedName,
79
- headerFile: `${importedName}.gen.h`,
80
- });
81
- }
82
- }
83
- // Emit .gen.cpp
84
- const cppOutput = emitComponent(comp.ir, importInfos);
85
- const baseName = comp.name;
86
- const outPath = path.join(outputDir, `${baseName}.gen.cpp`);
87
- fs.writeFileSync(outPath, cppOutput);
88
- console.log(` ${baseName} -> ${outPath}`);
89
- // Emit .gen.h for components with props
90
- if (comp.hasProps) {
91
- const headerOutput = emitComponentHeader(comp.ir);
92
- const headerPath = path.join(outputDir, `${baseName}.gen.h`);
93
- fs.writeFileSync(headerPath, headerOutput);
94
- console.log(` ${baseName} -> ${headerPath} (header)`);
29
+ else {
30
+ // Default: build command
31
+ const { values, positionals } = parseArgs({
32
+ allowPositionals: true,
33
+ options: { output: { type: 'string', short: 'o' } },
34
+ });
35
+ if (positionals.length === 0) {
36
+ console.error('Usage: imxc <input.tsx ...> -o <output-dir>');
37
+ console.error(' imxc init [project-dir]');
38
+ console.error(' imxc watch <dir> -o <output-dir>');
39
+ process.exit(1);
95
40
  }
96
- }
97
- // Phase 4: Emit root entry point
98
- if (compiled.length > 0) {
99
- const root = compiled[0];
100
- const rootOutput = emitRoot(root.name, root.stateCount, root.bufferCount);
101
- const rootPath = path.join(outputDir, 'app_root.gen.cpp');
102
- fs.writeFileSync(rootPath, rootOutput);
103
- console.log(` -> ${rootPath} (root entry point)`);
104
- }
105
- console.log(`imxc: ${compiled.length} component(s) compiled successfully.`);
106
- /**
107
- * Walk IR nodes and update custom_component nodes with resolved stateCount/bufferCount
108
- * from the compiled component map.
109
- */
110
- function resolveCustomComponents(nodes, map) {
111
- for (const node of nodes) {
112
- if (node.kind === 'custom_component') {
113
- const target = map.get(node.name);
114
- if (target) {
115
- node.stateCount = target.stateCount;
116
- node.bufferCount = target.bufferCount;
117
- }
118
- }
119
- else if (node.kind === 'conditional') {
120
- resolveCustomComponents(node.body, map);
121
- if (node.elseBody)
122
- resolveCustomComponents(node.elseBody, map);
123
- }
124
- else if (node.kind === 'list_map') {
125
- resolveCustomComponents(node.body, map);
126
- }
41
+ const outputDir = values.output ?? '.';
42
+ const result = compile(positionals, outputDir);
43
+ if (!result.success) {
44
+ result.errors.forEach(e => console.error(e));
45
+ process.exit(1);
127
46
  }
47
+ console.log(`imxc: ${result.componentCount} component(s) compiled successfully.`);
128
48
  }
package/dist/init.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
- const MAIN_CPP = `#include <reimgui/runtime.h>
4
- #include <reimgui/renderer.h>
3
+ const MAIN_CPP = `#include <imx/runtime.h>
4
+ #include <imx/renderer.h>
5
5
 
6
6
  #include <imgui.h>
7
7
  #include <imgui_impl_glfw.h>
@@ -11,7 +11,7 @@ const MAIN_CPP = `#include <reimgui/runtime.h>
11
11
  struct App {
12
12
  GLFWwindow* window = nullptr;
13
13
  ImGuiIO* io = nullptr;
14
- reimgui::Runtime runtime;
14
+ imx::Runtime runtime;
15
15
  };
16
16
 
17
17
  static void render_frame(App& app) {
@@ -26,7 +26,7 @@ static void render_frame(App& app) {
26
26
  ImGui_ImplGlfw_NewFrame();
27
27
  ImGui::NewFrame();
28
28
 
29
- reimgui::render_root(app.runtime);
29
+ imx::render_root(app.runtime);
30
30
 
31
31
  ImGui::Render();
32
32
  glViewport(0, 0, fb_w, fb_h);
@@ -122,7 +122,7 @@ const APP_TSX = `export default function App() {
122
122
  );
123
123
  }
124
124
  `;
125
- const REIMGUI_DTS = `// reimgui.d.ts — Type definitions for ReImGui components
125
+ const IMX_DTS = `// imx.d.ts — Type definitions for IMX components
126
126
 
127
127
  interface Style {
128
128
  padding?: number;
@@ -140,7 +140,7 @@ interface Style {
140
140
 
141
141
  declare function useState<T>(initial: T): [T, (value: T) => void];
142
142
 
143
- interface WindowProps { title: string; style?: Style; children?: any; }
143
+ interface WindowProps { title: string; open?: boolean; onClose?: () => void; noTitleBar?: boolean; noResize?: boolean; noMove?: boolean; noCollapse?: boolean; noDocking?: boolean; noScrollbar?: boolean; style?: Style; children?: any; }
144
144
  interface ViewProps { style?: Style; children?: any; }
145
145
  interface RowProps { gap?: number; style?: Style; children?: any; }
146
146
  interface ColumnProps { gap?: number; style?: Style; children?: any; }
@@ -151,6 +151,21 @@ interface CheckboxProps { value: boolean; onChange: (v: boolean) => void; label?
151
151
  interface SeparatorProps {}
152
152
  interface PopupProps { id: string; style?: Style; children?: any; }
153
153
  interface DockSpaceProps { style?: Style; children?: any; }
154
+ interface DockLayoutProps { children?: any; }
155
+ interface DockSplitProps { direction: "horizontal" | "vertical"; size: number; children?: any; }
156
+ interface DockPanelProps { children?: any; }
157
+ interface ThemeProps {
158
+ preset: string;
159
+ accentColor?: [number, number, number, number];
160
+ backgroundColor?: [number, number, number, number];
161
+ textColor?: [number, number, number, number];
162
+ borderColor?: [number, number, number, number];
163
+ surfaceColor?: [number, number, number, number];
164
+ rounding?: number;
165
+ borderSize?: number;
166
+ spacing?: number;
167
+ children?: any;
168
+ }
154
169
  interface MenuBarProps { children?: any; }
155
170
  interface MenuProps { label: string; children?: any; }
156
171
  interface MenuItemProps { label: string; onPress?: () => void; shortcut?: string; }
@@ -171,6 +186,48 @@ interface ColorEditProps { label: string; value: number[]; onChange: (v: number[
171
186
  interface ListBoxProps { label: string; value: number; onChange: (v: number) => void; items: string[]; style?: Style; }
172
187
  interface ProgressBarProps { value: number; overlay?: string; style?: Style; }
173
188
  interface TooltipProps { text: string; }
189
+ interface BulletTextProps { style?: Style; children?: any; }
190
+ interface LabelTextProps { label: string; value: string; }
191
+ interface SelectableProps { label: string; selected?: boolean; onSelect?: () => void; style?: Style; }
192
+ interface RadioProps { label: string; value: number; index: number; onChange?: (v: number) => void; style?: Style; }
193
+ interface InputTextMultilineProps { label: string; value: string; style?: Style; }
194
+ interface ColorPickerProps { label: string; value: number[]; style?: Style; }
195
+ interface PlotLinesProps { label: string; values: number[]; overlay?: string; style?: Style; }
196
+ interface PlotHistogramProps { label: string; values: number[]; overlay?: string; style?: Style; }
197
+ interface ModalProps { title: string; open?: boolean; onClose?: () => void; style?: Style; children?: any; }
198
+ interface ImageProps { src: string; embed?: boolean; width?: number; height?: number; }
199
+ interface GroupProps { style?: Style; children?: any; }
200
+ interface IDProps { scope: string | number; children?: any; }
201
+ interface DragDropSourceProps { type: string; payload: number | string; children?: any; }
202
+ interface DragDropTargetProps { type: string; onDrop: (payload: any) => void; children?: any; }
203
+ interface CanvasProps { width: number; height: number; style?: Style; children?: any; }
204
+ interface DrawLineProps { p1: [number, number]; p2: [number, number]; color: [number, number, number, number]; thickness?: number; }
205
+ interface DrawRectProps { min: [number, number]; max: [number, number]; color: [number, number, number, number]; filled?: boolean; thickness?: number; rounding?: number; }
206
+ interface DrawCircleProps { center: [number, number]; radius: number; color: [number, number, number, number]; filled?: boolean; thickness?: number; }
207
+ interface DrawTextProps { pos: [number, number]; text: string; color: [number, number, number, number]; }
208
+ interface StyleColorProps {
209
+ text?: [number, number, number, number];
210
+ textDisabled?: [number, number, number, number];
211
+ windowBg?: [number, number, number, number];
212
+ frameBg?: [number, number, number, number];
213
+ frameBgHovered?: [number, number, number, number];
214
+ frameBgActive?: [number, number, number, number];
215
+ titleBg?: [number, number, number, number];
216
+ titleBgActive?: [number, number, number, number];
217
+ button?: [number, number, number, number];
218
+ buttonHovered?: [number, number, number, number];
219
+ buttonActive?: [number, number, number, number];
220
+ header?: [number, number, number, number];
221
+ headerHovered?: [number, number, number, number];
222
+ headerActive?: [number, number, number, number];
223
+ separator?: [number, number, number, number];
224
+ checkMark?: [number, number, number, number];
225
+ sliderGrab?: [number, number, number, number];
226
+ border?: [number, number, number, number];
227
+ popupBg?: [number, number, number, number];
228
+ tab?: [number, number, number, number];
229
+ children?: any;
230
+ }
174
231
 
175
232
  declare function Window(props: WindowProps): any;
176
233
  declare function View(props: ViewProps): any;
@@ -183,6 +240,10 @@ declare function Checkbox(props: CheckboxProps): any;
183
240
  declare function Separator(props: SeparatorProps): any;
184
241
  declare function Popup(props: PopupProps): any;
185
242
  declare function DockSpace(props: DockSpaceProps): any;
243
+ declare function DockLayout(props: DockLayoutProps): any;
244
+ declare function DockSplit(props: DockSplitProps): any;
245
+ declare function DockPanel(props: DockPanelProps): any;
246
+ declare function Theme(props: ThemeProps): any;
186
247
  declare function MenuBar(props: MenuBarProps): any;
187
248
  declare function Menu(props: MenuProps): any;
188
249
  declare function MenuItem(props: MenuItemProps): any;
@@ -203,8 +264,63 @@ declare function ColorEdit(props: ColorEditProps): any;
203
264
  declare function ListBox(props: ListBoxProps): any;
204
265
  declare function ProgressBar(props: ProgressBarProps): any;
205
266
  declare function Tooltip(props: TooltipProps): any;
267
+ declare function BulletText(props: BulletTextProps): any;
268
+ declare function LabelText(props: LabelTextProps): any;
269
+ declare function Selectable(props: SelectableProps): any;
270
+ declare function Radio(props: RadioProps): any;
271
+ declare function InputTextMultiline(props: InputTextMultilineProps): any;
272
+ declare function ColorPicker(props: ColorPickerProps): any;
273
+ declare function PlotLines(props: PlotLinesProps): any;
274
+ declare function PlotHistogram(props: PlotHistogramProps): any;
275
+ declare function Modal(props: ModalProps): any;
276
+ declare function Image(props: ImageProps): any;
277
+ declare function Group(props: GroupProps): any;
278
+ declare function ID(props: IDProps): any;
279
+ declare function StyleColor(props: StyleColorProps): any;
280
+ declare function Canvas(props: CanvasProps): any;
281
+ declare function DrawLine(props: DrawLineProps): any;
282
+ declare function DrawRect(props: DrawRectProps): any;
283
+ declare function DrawCircle(props: DrawCircleProps): any;
284
+ declare function DrawText(props: DrawTextProps): any;
285
+
286
+ interface StyleVarProps {
287
+ alpha?: number;
288
+ windowPadding?: [number, number];
289
+ windowRounding?: number;
290
+ framePadding?: [number, number];
291
+ frameRounding?: number;
292
+ frameBorderSize?: number;
293
+ itemSpacing?: [number, number];
294
+ itemInnerSpacing?: [number, number];
295
+ indentSpacing?: number;
296
+ cellPadding?: [number, number];
297
+ tabRounding?: number;
298
+ children?: any;
299
+ }
300
+ declare function StyleVar(props: StyleVarProps): any;
301
+ declare function DragDropSource(props: DragDropSourceProps): any;
302
+ declare function DragDropTarget(props: DragDropTargetProps): any;
303
+ interface DisabledProps { disabled?: boolean; children?: any; }
304
+ interface ChildProps { id: string; width?: number; height?: number; border?: boolean; style?: Style; children?: any; }
305
+ declare function Disabled(props: DisabledProps): any;
306
+ declare function Child(props: ChildProps): any;
307
+
308
+ declare function resetLayout(): void;
309
+
310
+ // --- Custom native widgets ---
311
+ // Declare your C++ registered widgets here for type checking:
312
+ //
313
+ // interface KnobProps {
314
+ // value: number;
315
+ // onChange: (value: number) => void;
316
+ // min: number;
317
+ // max: number;
318
+ // width?: number;
319
+ // height?: number;
320
+ // }
321
+ // declare function Knob(props: KnobProps): any;
206
322
 
207
- declare module "reimgui/jsx-runtime" {
323
+ declare module "imx/jsx-runtime" {
208
324
  export namespace JSX {
209
325
  type Element = any;
210
326
  interface IntrinsicElements { [tag: string]: any; }
@@ -222,12 +338,12 @@ const TSCONFIG = `{
222
338
  "module": "ES2022",
223
339
  "moduleResolution": "bundler",
224
340
  "jsx": "react-jsx",
225
- "jsxImportSource": "reimgui",
341
+ "jsxImportSource": "imx",
226
342
  "strict": false,
227
343
  "noEmit": true,
228
344
  "skipLibCheck": true
229
345
  },
230
- "include": ["src/**/*.tsx", "src/reimgui.d.ts"]
346
+ "include": ["src/**/*.tsx", "src/imx.d.ts"]
231
347
  }
232
348
  `;
233
349
  function cmakeTemplate(projectName, repoUrl) {
@@ -240,15 +356,15 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
240
356
  include(FetchContent)
241
357
 
242
358
  FetchContent_Declare(
243
- reimgui
359
+ imx
244
360
  GIT_REPOSITORY ${repoUrl}
245
361
  GIT_TAG main
246
362
  )
247
- FetchContent_MakeAvailable(reimgui)
363
+ FetchContent_MakeAvailable(imx)
248
364
 
249
- include(ReImGuiCompile)
365
+ include(ImxCompile)
250
366
 
251
- reimgui_compile_tsx(GENERATED
367
+ imx_compile_tsx(GENERATED
252
368
  SOURCES src/App.tsx
253
369
  OUTPUT_DIR \${CMAKE_BINARY_DIR}/generated
254
370
  )
@@ -257,8 +373,16 @@ add_executable(${projectName}
257
373
  src/main.cpp
258
374
  \${GENERATED}
259
375
  )
260
- target_link_libraries(${projectName} PRIVATE reimgui::renderer)
376
+ target_link_libraries(${projectName} PRIVATE imx::renderer)
261
377
  target_include_directories(${projectName} PRIVATE \${CMAKE_BINARY_DIR}/generated)
378
+
379
+ # Copy public/ assets to output directory
380
+ add_custom_command(TARGET ${projectName} POST_BUILD
381
+ COMMAND \${CMAKE_COMMAND} -E copy_directory
382
+ \${CMAKE_CURRENT_SOURCE_DIR}/public
383
+ $<TARGET_FILE_DIR:${projectName}>
384
+ COMMENT "Copying public/ assets"
385
+ )
262
386
  `;
263
387
  }
264
388
  export function initProject(projectDir, projectName) {
@@ -269,20 +393,23 @@ export function initProject(projectDir, projectName) {
269
393
  process.exit(1);
270
394
  }
271
395
  fs.mkdirSync(srcDir, { recursive: true });
396
+ const publicDir = path.join(projectDir, 'public');
397
+ fs.mkdirSync(publicDir, { recursive: true });
272
398
  // Write files
273
399
  fs.writeFileSync(path.join(srcDir, 'main.cpp'), MAIN_CPP.replace('APP_NAME', name));
274
400
  fs.writeFileSync(path.join(srcDir, 'App.tsx'), APP_TSX);
275
- fs.writeFileSync(path.join(srcDir, 'reimgui.d.ts'), REIMGUI_DTS);
401
+ fs.writeFileSync(path.join(srcDir, 'imx.d.ts'), IMX_DTS);
276
402
  fs.writeFileSync(path.join(projectDir, 'tsconfig.json'), TSCONFIG);
277
- fs.writeFileSync(path.join(projectDir, 'CMakeLists.txt'), cmakeTemplate(name, 'https://github.com/bgocumlu/reimgui.git'));
403
+ fs.writeFileSync(path.join(projectDir, 'CMakeLists.txt'), cmakeTemplate(name, 'https://github.com/bgocumlu/imx.git'));
278
404
  console.log(`imxc: initialized project "${name}"`);
279
405
  console.log('');
280
406
  console.log(' Created:');
281
407
  console.log(` src/main.cpp — app shell (GLFW + OpenGL + ImGui)`);
282
408
  console.log(` src/App.tsx — your root component`);
283
- console.log(` src/reimgui.d.ts — type definitions for IDE support`);
409
+ console.log(` src/imx.d.ts — type definitions for IDE support`);
284
410
  console.log(` tsconfig.json — TypeScript config`);
285
411
  console.log(` CMakeLists.txt — build config with FetchContent`);
412
+ console.log(` public/ — static assets (copied to exe directory)`);
286
413
  console.log('');
287
414
  console.log(' Next steps:');
288
415
  console.log(` cmake -B build`);