xbintsc 0.3.25 → 0.3.46

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.
Files changed (187) hide show
  1. package/README.md +39 -3
  2. package/dist/src/cli/main.js +124 -30
  3. package/dist/src/cli/main.js.map +1 -1
  4. package/dist/src/codegen/generator/calls/access.js +8 -1
  5. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  6. package/dist/src/codegen/generator/module.js +10 -0
  7. package/dist/src/codegen/generator/module.js.map +1 -1
  8. package/dist/src/codegen/generator/state.js +24 -0
  9. package/dist/src/codegen/generator/state.js.map +1 -1
  10. package/dist/src/codegen/generator/tables.d.ts +6 -0
  11. package/dist/src/codegen/generator/tables.js +38 -0
  12. package/dist/src/codegen/generator/tables.js.map +1 -1
  13. package/dist/src/driver/bundler/commonjs.d.ts +52 -0
  14. package/dist/src/driver/bundler/commonjs.js +493 -0
  15. package/dist/src/driver/bundler/commonjs.js.map +1 -0
  16. package/dist/src/driver/bundler/graph.js +14 -0
  17. package/dist/src/driver/bundler/graph.js.map +1 -1
  18. package/dist/src/driver/bundler/merge.js +14 -0
  19. package/dist/src/driver/bundler/merge.js.map +1 -1
  20. package/dist/src/driver/bundler/types.d.ts +8 -0
  21. package/dist/src/driver/compiler.d.ts +8 -1
  22. package/dist/src/driver/compiler.js +73 -5
  23. package/dist/src/driver/compiler.js.map +1 -1
  24. package/dist/src/driver/config.d.ts +53 -0
  25. package/dist/src/driver/config.js +141 -0
  26. package/dist/src/driver/config.js.map +1 -0
  27. package/dist/src/driver/icon.d.ts +62 -0
  28. package/dist/src/driver/icon.js +151 -0
  29. package/dist/src/driver/icon.js.map +1 -0
  30. package/dist/src/driver/index.d.ts +4 -0
  31. package/dist/src/driver/index.js +4 -0
  32. package/dist/src/driver/index.js.map +1 -1
  33. package/dist/src/driver/mac-bundle.d.ts +41 -0
  34. package/dist/src/driver/mac-bundle.js +123 -0
  35. package/dist/src/driver/mac-bundle.js.map +1 -0
  36. package/dist/src/driver/win-icon.d.ts +27 -0
  37. package/dist/src/driver/win-icon.js +80 -0
  38. package/dist/src/driver/win-icon.js.map +1 -0
  39. package/dist/src/parser/statements.js +17 -4
  40. package/dist/src/parser/statements.js.map +1 -1
  41. package/dist/tests/cli/main.test.js +46 -1
  42. package/dist/tests/cli/main.test.js.map +1 -1
  43. package/dist/tests/driver/config.test.d.ts +1 -0
  44. package/dist/tests/driver/config.test.js +124 -0
  45. package/dist/tests/driver/config.test.js.map +1 -0
  46. package/dist/tests/driver/icon.test.d.ts +1 -0
  47. package/dist/tests/driver/icon.test.js +421 -0
  48. package/dist/tests/driver/icon.test.js.map +1 -0
  49. package/dist/tests/driver/modules.test.js +109 -0
  50. package/dist/tests/driver/modules.test.js.map +1 -1
  51. package/dist/tests/e2e/fetch.test.d.ts +12 -0
  52. package/dist/tests/e2e/fetch.test.js +272 -0
  53. package/dist/tests/e2e/fetch.test.js.map +1 -0
  54. package/dist/tests/e2e/gc.test.d.ts +1 -0
  55. package/dist/tests/e2e/gc.test.js +168 -0
  56. package/dist/tests/e2e/gc.test.js.map +1 -0
  57. package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
  58. package/dist/tests/e2e/gui-dom.test.js +102 -0
  59. package/dist/tests/e2e/gui-dom.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-events.test.d.ts +5 -0
  61. package/dist/tests/e2e/gui-events.test.js +60 -0
  62. package/dist/tests/e2e/gui-events.test.js.map +1 -0
  63. package/dist/tests/e2e/gui-helpers.d.ts +31 -0
  64. package/dist/tests/e2e/gui-helpers.js +74 -0
  65. package/dist/tests/e2e/gui-helpers.js.map +1 -0
  66. package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
  67. package/dist/tests/e2e/gui-layout.test.js +88 -0
  68. package/dist/tests/e2e/gui-layout.test.js.map +1 -0
  69. package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
  70. package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
  71. package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
  72. package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
  73. package/dist/tests/e2e/gui-paint.test.js +111 -0
  74. package/dist/tests/e2e/gui-paint.test.js.map +1 -0
  75. package/dist/tests/e2e/gui-script.test.d.ts +5 -0
  76. package/dist/tests/e2e/gui-script.test.js +78 -0
  77. package/dist/tests/e2e/gui-script.test.js.map +1 -0
  78. package/dist/tests/e2e/gui-style.test.d.ts +5 -0
  79. package/dist/tests/e2e/gui-style.test.js +175 -0
  80. package/dist/tests/e2e/gui-style.test.js.map +1 -0
  81. package/dist/tests/e2e/harness.d.ts +2 -0
  82. package/dist/tests/e2e/harness.js +1 -0
  83. package/dist/tests/e2e/harness.js.map +1 -1
  84. package/dist/tests/e2e/icon.test.d.ts +7 -0
  85. package/dist/tests/e2e/icon.test.js +80 -0
  86. package/dist/tests/e2e/icon.test.js.map +1 -0
  87. package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
  88. package/dist/tests/e2e/npm-modules.test.js +47 -1
  89. package/dist/tests/e2e/npm-modules.test.js.map +1 -1
  90. package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
  91. package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
  92. package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
  93. package/dist/tests/parser/statements.test.js +11 -0
  94. package/dist/tests/parser/statements.test.js.map +1 -1
  95. package/package.json +3 -2
  96. package/runtime/ext_gui/css.cpp +4 -517
  97. package/runtime/ext_gui/css_internal.h +27 -0
  98. package/runtime/ext_gui/css_match.cpp +195 -0
  99. package/runtime/ext_gui/css_selector.cpp +207 -0
  100. package/runtime/ext_gui/css_sheet.cpp +129 -0
  101. package/runtime/ext_gui/dom.cpp +7 -403
  102. package/runtime/ext_gui/dom_api.cpp +63 -1146
  103. package/runtime/ext_gui/dom_api_document.cpp +96 -0
  104. package/runtime/ext_gui/dom_api_events.cpp +123 -0
  105. package/runtime/ext_gui/dom_api_internal.h +135 -0
  106. package/runtime/ext_gui/dom_api_node.cpp +329 -0
  107. package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
  108. package/runtime/ext_gui/dom_api_proto.cpp +135 -0
  109. package/runtime/ext_gui/dom_api_style.cpp +192 -0
  110. package/runtime/ext_gui/dom_internal.h +39 -0
  111. package/runtime/ext_gui/dom_parse.cpp +348 -0
  112. package/runtime/ext_gui/dom_serialize.cpp +54 -0
  113. package/runtime/ext_gui/gui.cpp +56 -286
  114. package/runtime/ext_gui/gui_events.cpp +294 -0
  115. package/runtime/ext_gui/gui_internal.h +29 -0
  116. package/runtime/ext_gui/layout.cpp +6 -597
  117. package/runtime/ext_gui/layout_flex.cpp +200 -0
  118. package/runtime/ext_gui/layout_flow.cpp +191 -0
  119. package/runtime/ext_gui/layout_flow_internal.h +149 -0
  120. package/runtime/ext_gui/layout_inline.cpp +181 -0
  121. package/runtime/ext_gui/layout_internal.h +20 -0
  122. package/runtime/ext_gui/renderer.cpp +15 -679
  123. package/runtime/ext_gui/renderer_image.cpp +233 -0
  124. package/runtime/ext_gui/renderer_internal.h +112 -0
  125. package/runtime/ext_gui/renderer_shaders.h +108 -0
  126. package/runtime/ext_gui/renderer_text.cpp +342 -0
  127. package/runtime/ext_gui/style.cpp +12 -660
  128. package/runtime/ext_gui/style_internal.h +33 -0
  129. package/runtime/ext_gui/style_print.cpp +113 -0
  130. package/runtime/ext_gui/style_ua.cpp +98 -0
  131. package/runtime/ext_gui/style_values.cpp +468 -0
  132. package/runtime/ext_gui/window.cpp +1 -0
  133. package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
  134. package/runtime/ext_node/dgram/dgram.c +1 -0
  135. package/runtime/ext_node/events/events.c +1 -0
  136. package/runtime/ext_node/fs/fs_ops.c +10 -25
  137. package/runtime/ext_node/fs/glob.c +13 -30
  138. package/runtime/ext_node/fs/promises.c +1 -0
  139. package/runtime/ext_node/http/parts/prototypes.inc +5 -0
  140. package/runtime/ext_node/net/parts/prototypes.inc +2 -0
  141. package/runtime/ext_node/process/process.c +9 -7
  142. package/runtime/ext_node/stream/stream.c +1 -0
  143. package/runtime/ext_node/util/util.c +6 -6
  144. package/runtime/rt.h +16 -0
  145. package/runtime/rt_internal.h +67 -2
  146. package/runtime/xt_alloc.c +442 -5
  147. package/runtime/xt_builtins.c +1 -0
  148. package/runtime/xt_containers/access.inc +32 -1
  149. package/runtime/xt_fetch/buffer.inc +64 -0
  150. package/runtime/xt_fetch/headers.inc +312 -0
  151. package/runtime/xt_fetch/request.inc +386 -0
  152. package/runtime/xt_fetch/response.inc +107 -0
  153. package/runtime/xt_fetch/socket.inc +72 -0
  154. package/runtime/xt_fetch/url.inc +108 -0
  155. package/runtime/xt_fetch.c +39 -0
  156. package/runtime/xt_generator.c +95 -1
  157. package/runtime/xt_loop.c +35 -1
  158. package/runtime/xt_promise.c +46 -0
  159. package/runtime/xt_stdlib2/error.inc +1 -0
  160. package/runtime/xt_stdlib2/regexp-match.inc +8 -7
  161. package/runtime/xt_stdlib2/set.inc +1 -1
  162. package/runtime/xt_symbol.c +2 -0
  163. package/runtime/xt_typed_array/construction.inc +142 -0
  164. package/runtime/xt_typed_array/elements.inc +92 -0
  165. package/runtime/xt_typed_array/methods.inc +329 -0
  166. package/runtime/xt_typed_array.c +72 -0
  167. package/runtime/xt_values/number-format.inc +26 -0
  168. package/scripts/check-file-length.ts +85 -0
  169. package/src/cli/main.ts +137 -29
  170. package/src/codegen/generator/calls/access.ts +9 -1
  171. package/src/codegen/generator/module.ts +10 -0
  172. package/src/codegen/generator/state.ts +24 -0
  173. package/src/codegen/generator/tables.ts +40 -0
  174. package/src/driver/bundler/commonjs.ts +553 -0
  175. package/src/driver/bundler/graph.ts +14 -0
  176. package/src/driver/bundler/merge.ts +15 -0
  177. package/src/driver/bundler/types.ts +8 -0
  178. package/src/driver/compiler.ts +83 -5
  179. package/src/driver/config.ts +173 -0
  180. package/src/driver/icon.ts +184 -0
  181. package/src/driver/index.ts +30 -0
  182. package/src/driver/mac-bundle.ts +156 -0
  183. package/src/driver/win-icon.ts +96 -0
  184. package/src/parser/statements.ts +17 -4
  185. package/dist/tests/e2e/gui.test.d.ts +0 -12
  186. package/dist/tests/e2e/gui.test.js +0 -702
  187. package/dist/tests/e2e/gui.test.js.map +0 -1
@@ -0,0 +1,468 @@
1
+ /*
2
+ * xbintsc GUI engine — CSS declaration application (value parsing and how each
3
+ * property maps onto `XtStyle`).
4
+ */
5
+
6
+ #include "style_internal.h"
7
+
8
+ #include <cstdlib>
9
+ #include <vector>
10
+
11
+ namespace xtgui {
12
+
13
+ /* -- small helpers -------------------------------------------------------- */
14
+
15
+ static void setEdge(Edges *edges, const std::string &side, Length value) {
16
+ if (side == "top") edges->top = value;
17
+ else if (side == "right") edges->right = value;
18
+ else if (side == "bottom") edges->bottom = value;
19
+ else if (side == "left") edges->left = value;
20
+ }
21
+
22
+ static bool parseEdgeLengths(const std::string &value, Edges *edges) {
23
+ std::vector<std::string> parts = xt_css_split_whitespace(value);
24
+ if (parts.empty() || parts.size() > 4) return false;
25
+ Length values[4];
26
+ for (size_t i = 0; i < parts.size(); i++) {
27
+ Length length = xt_css_parse_length(parts[i]);
28
+ if (length.unit == Unit::Invalid) {
29
+ std::string keyword = xt_css_lower(parts[i]);
30
+ if (keyword == "thin") length = {1.0f, Unit::Px};
31
+ else if (keyword == "medium") length = {3.0f, Unit::Px};
32
+ else if (keyword == "thick") length = {5.0f, Unit::Px};
33
+ else return false;
34
+ }
35
+ values[i] = length;
36
+ }
37
+ switch (parts.size()) {
38
+ case 1:
39
+ edges->set(values[0]);
40
+ break;
41
+ case 2:
42
+ edges->top = edges->bottom = values[0];
43
+ edges->right = edges->left = values[1];
44
+ break;
45
+ case 3:
46
+ edges->top = values[0];
47
+ edges->right = edges->left = values[1];
48
+ edges->bottom = values[2];
49
+ break;
50
+ default:
51
+ edges->top = values[0];
52
+ edges->right = values[1];
53
+ edges->bottom = values[2];
54
+ edges->left = values[3];
55
+ break;
56
+ }
57
+ return true;
58
+ }
59
+
60
+ static bool assignLength(Length *target, const std::string &value) {
61
+ Length length = xt_css_parse_length(value);
62
+ if (length.unit == Unit::Invalid) return false;
63
+ *target = length;
64
+ return true;
65
+ }
66
+
67
+ static bool assignColor(Color *target, bool *has, const std::string &value) {
68
+ Color color = xt_css_parse_color(value);
69
+ if (!color.valid) return false;
70
+ *target = color;
71
+ *has = true;
72
+ return true;
73
+ }
74
+
75
+ float xt_style_resolve_absolute(const Length &length, float base, float em, float root, float vw,
76
+ float vh) {
77
+ switch (length.unit) {
78
+ case Unit::Px:
79
+ case Unit::Number:
80
+ return length.value;
81
+ case Unit::Percent:
82
+ return base * length.value / 100.0f;
83
+ case Unit::Em:
84
+ return em * length.value;
85
+ case Unit::Rem:
86
+ return root * length.value;
87
+ case Unit::Vw:
88
+ return vw * length.value / 100.0f;
89
+ case Unit::Vh:
90
+ return vh * length.value / 100.0f;
91
+ default:
92
+ return 0.0f;
93
+ }
94
+ }
95
+
96
+ static bool parseEnum(const std::string &value, const char *const *names, int count, int *out) {
97
+ std::string text = xt_css_lower(xt_css_trim(value));
98
+ for (int i = 0; i < count; i++) {
99
+ if (text == names[i]) {
100
+ *out = i;
101
+ return true;
102
+ }
103
+ }
104
+ return false;
105
+ }
106
+
107
+ /* -- transitions ---------------------------------------------------------- */
108
+
109
+ static bool parseTimeSeconds(const std::string &text, float *out) {
110
+ std::string token = xt_css_trim(text);
111
+ if (token.empty()) return false;
112
+ const char *begin = token.c_str();
113
+ char *end = nullptr;
114
+ double value = std::strtod(begin, &end);
115
+ if (end == begin) return false;
116
+ std::string unit = xt_css_lower(xt_css_trim(std::string(end)));
117
+ if (unit == "ms") {
118
+ *out = (float)(value / 1000.0);
119
+ } else if (unit.empty() || unit == "s") {
120
+ *out = (float)value;
121
+ } else {
122
+ return false;
123
+ }
124
+ return true;
125
+ }
126
+
127
+ static bool parseTimingFunction(const std::string &text, TimingFunction *out) {
128
+ std::string token = xt_css_lower(xt_css_trim(text));
129
+ if (token == "linear") {
130
+ *out = TimingFunction::Linear;
131
+ } else if (token == "ease") {
132
+ *out = TimingFunction::Ease;
133
+ } else if (token == "ease-in") {
134
+ *out = TimingFunction::EaseIn;
135
+ } else if (token == "ease-out") {
136
+ *out = TimingFunction::EaseOut;
137
+ } else if (token == "ease-in-out") {
138
+ *out = TimingFunction::EaseInOut;
139
+ } else {
140
+ return false;
141
+ }
142
+ return true;
143
+ }
144
+
145
+ static std::vector<std::string> splitCommas(const std::string &text) {
146
+ std::vector<std::string> parts;
147
+ size_t start = 0;
148
+ for (size_t i = 0; i <= text.size(); i++) {
149
+ if (i == text.size() || text[i] == ',') {
150
+ std::string part = xt_css_trim(text.substr(start, i - start));
151
+ if (!part.empty()) parts.push_back(part);
152
+ start = i + 1;
153
+ }
154
+ }
155
+ return parts;
156
+ }
157
+
158
+ static void parseTransitionShorthand(const std::string &value, std::vector<TransitionSpec> *out) {
159
+ out->clear();
160
+ for (const std::string &group : splitCommas(value)) {
161
+ std::vector<std::string> tokens = xt_css_split_whitespace(group);
162
+ if (tokens.empty()) continue;
163
+ TransitionSpec spec;
164
+ int time_seen = 0;
165
+ for (const std::string &token : tokens) {
166
+ float seconds = 0;
167
+ TimingFunction timing = TimingFunction::Ease;
168
+ if (parseTimeSeconds(token, &seconds)) {
169
+ if (time_seen == 0) {
170
+ spec.duration = seconds;
171
+ } else {
172
+ spec.delay = seconds;
173
+ }
174
+ time_seen++;
175
+ } else if (parseTimingFunction(token, &timing)) {
176
+ spec.timing = timing;
177
+ } else {
178
+ spec.property = xt_css_lower(token);
179
+ }
180
+ }
181
+ out->push_back(spec);
182
+ }
183
+ }
184
+
185
+ /* -- declaration application ---------------------------------------------- */
186
+
187
+ void xt_style_apply_declaration(XtStyle *style, const std::string &property,
188
+ const std::string &rawValue, const ApplyContext &context) {
189
+ std::string value = xt_css_trim(rawValue);
190
+
191
+ if (property == "display") {
192
+ static const char *const names[] = {"none", "block", "inline", "inline-block", "flex", "list-item"};
193
+ int index = 0;
194
+ if (parseEnum(value, names, 6, &index)) style->display = (Display)index;
195
+ return;
196
+ }
197
+ if (property == "position") {
198
+ static const char *const names[] = {"static", "relative", "absolute", "fixed"};
199
+ int index = 0;
200
+ if (parseEnum(value, names, 4, &index)) style->position = (Position)index;
201
+ return;
202
+ }
203
+ if (property == "width") { assignLength(&style->width, value); return; }
204
+ if (property == "height") { assignLength(&style->height, value); return; }
205
+ if (property == "min-width") { assignLength(&style->min_width, value); return; }
206
+ if (property == "min-height") { assignLength(&style->min_height, value); return; }
207
+ if (property == "max-width") { assignLength(&style->max_width, value); return; }
208
+ if (property == "max-height") { assignLength(&style->max_height, value); return; }
209
+ if (property == "top") { assignLength(&style->top, value); return; }
210
+ if (property == "right") { assignLength(&style->right, value); return; }
211
+ if (property == "bottom") { assignLength(&style->bottom, value); return; }
212
+ if (property == "left") { assignLength(&style->left, value); return; }
213
+
214
+ if (property == "margin") { parseEdgeLengths(value, &style->margin); return; }
215
+ if (property == "padding") { parseEdgeLengths(value, &style->padding); return; }
216
+ if (property == "margin-top") { setEdge(&style->margin, "top", xt_css_parse_length(value)); return; }
217
+ if (property == "margin-right") { setEdge(&style->margin, "right", xt_css_parse_length(value)); return; }
218
+ if (property == "margin-bottom") { setEdge(&style->margin, "bottom", xt_css_parse_length(value)); return; }
219
+ if (property == "margin-left") { setEdge(&style->margin, "left", xt_css_parse_length(value)); return; }
220
+ if (property == "padding-top") { setEdge(&style->padding, "top", xt_css_parse_length(value)); return; }
221
+ if (property == "padding-right") { setEdge(&style->padding, "right", xt_css_parse_length(value)); return; }
222
+ if (property == "padding-bottom") { setEdge(&style->padding, "bottom", xt_css_parse_length(value)); return; }
223
+ if (property == "padding-left") { setEdge(&style->padding, "left", xt_css_parse_length(value)); return; }
224
+
225
+ if (property == "border-width") { parseEdgeLengths(value, &style->border_width); return; }
226
+ if (property == "border-top-width") { setEdge(&style->border_width, "top", xt_css_parse_length(value)); return; }
227
+ if (property == "border-right-width") { setEdge(&style->border_width, "right", xt_css_parse_length(value)); return; }
228
+ if (property == "border-bottom-width") { setEdge(&style->border_width, "bottom", xt_css_parse_length(value)); return; }
229
+ if (property == "border-left-width") { setEdge(&style->border_width, "left", xt_css_parse_length(value)); return; }
230
+ if (property == "border-radius") {
231
+ Length length = xt_css_parse_length(value);
232
+ if (length.unit == Unit::Px || length.unit == Unit::Number) style->border_radius = length.value;
233
+ return;
234
+ }
235
+ if (property == "border-style" || property == "border-top-style" || property == "border-right-style" ||
236
+ property == "border-bottom-style" || property == "border-left-style") {
237
+ static const char *const names[] = {"none", "solid", "dashed", "dotted"};
238
+ int index = 0;
239
+ if (parseEnum(value, names, 4, &index)) style->border_style = (BorderStyle)index;
240
+ return;
241
+ }
242
+ if (property == "border-color" || property == "border-top-color" || property == "border-right-color" ||
243
+ property == "border-bottom-color" || property == "border-left-color") {
244
+ assignColor(&style->border_color, &style->has_border_color, value);
245
+ return;
246
+ }
247
+ if (property == "border" || property == "border-top" || property == "border-right" ||
248
+ property == "border-bottom" || property == "border-left") {
249
+ for (const std::string &token : xt_css_split_whitespace(value)) {
250
+ Length length = xt_css_parse_length(token);
251
+ if (length.unit == Unit::Px || length.unit == Unit::Number) {
252
+ if (property == "border") style->border_width.set(length);
253
+ else setEdge(&style->border_width, property.substr(7), length);
254
+ continue;
255
+ }
256
+ static const char *const names[] = {"none", "solid", "dashed", "dotted"};
257
+ int index = 0;
258
+ if (parseEnum(token, names, 4, &index)) {
259
+ style->border_style = (BorderStyle)index;
260
+ continue;
261
+ }
262
+ Color color = xt_css_parse_color(token);
263
+ if (color.valid) {
264
+ style->border_color = color;
265
+ style->has_border_color = true;
266
+ }
267
+ }
268
+ return;
269
+ }
270
+
271
+ if (property == "color") {
272
+ Color color = xt_css_parse_color(value);
273
+ if (color.valid) style->color = color;
274
+ return;
275
+ }
276
+ if (property == "background-color") { assignColor(&style->background, &style->has_background, value); return; }
277
+ if (property == "background") {
278
+ Color color = xt_css_parse_color(value);
279
+ if (color.valid) {
280
+ style->background = color;
281
+ style->has_background = true;
282
+ }
283
+ return;
284
+ }
285
+
286
+ if (property == "font-size") {
287
+ Length length = xt_css_parse_length(value);
288
+ if (length.unit == Unit::Invalid) return;
289
+ /* `em`/`%` resolve against the *parent* font size, never the element's own
290
+ * (possibly UA-set) size. */
291
+ style->font_size = xt_style_resolve_absolute(length, context.parentFontSize, context.parentFontSize,
292
+ context.rootFontSize, context.viewportWidth, context.viewportHeight);
293
+ if (style->font_size <= 0.0f) style->font_size = 16.0f;
294
+ return;
295
+ }
296
+ if (property == "font-weight") {
297
+ std::string text = xt_css_lower(value);
298
+ if (text == "normal") style->font_weight = 400;
299
+ else if (text == "bold") style->font_weight = 700;
300
+ else if (text == "lighter") style->font_weight = 100;
301
+ else if (text == "bolder") style->font_weight = 700;
302
+ else {
303
+ float number = 0;
304
+ if (xt_css_parse_number(text, &number)) style->font_weight = (int)number;
305
+ }
306
+ return;
307
+ }
308
+ if (property == "font-family") {
309
+ style->font_family = value;
310
+ return;
311
+ }
312
+ if (property == "font-style") {
313
+ style->font_style = (xt_css_lower(value) == "italic") ? FontStyle::Italic : FontStyle::Normal;
314
+ return;
315
+ }
316
+ if (property == "line-height") {
317
+ std::string text = xt_css_lower(value);
318
+ if (text == "normal") {
319
+ style->line_height = {};
320
+ return;
321
+ }
322
+ Length length = xt_css_parse_length(text);
323
+ if (length.unit == Unit::Invalid) return;
324
+ /* Relative units are resolved in `finalizeStyle`, once the element's own
325
+ * font size is settled. */
326
+ style->line_height = length;
327
+ return;
328
+ }
329
+ if (property == "text-align") {
330
+ std::string text = xt_css_lower(value);
331
+ if (text == "left" || text == "start") style->text_align = TextAlign::Start;
332
+ else if (text == "right" || text == "end") style->text_align = TextAlign::End;
333
+ else if (text == "center") style->text_align = TextAlign::Center;
334
+ else if (text == "justify") style->text_align = TextAlign::Justify;
335
+ return;
336
+ }
337
+ if (property == "white-space") {
338
+ static const char *const names[] = {"normal", "nowrap", "pre", "pre-wrap", "pre-line"};
339
+ int index = 0;
340
+ if (parseEnum(value, names, 5, &index)) style->white_space = (WhiteSpace)index;
341
+ return;
342
+ }
343
+
344
+ if (property == "flex-direction") {
345
+ static const char *const names[] = {"row", "row-reverse", "column", "column-reverse"};
346
+ int index = 0;
347
+ if (parseEnum(value, names, 4, &index)) style->flex_direction = (FlexDirection)index;
348
+ return;
349
+ }
350
+ if (property == "justify-content") {
351
+ static const char *const names[] = {"flex-start", "flex-end", "center", "space-between",
352
+ "space-around", "space-evenly"};
353
+ int index = 0;
354
+ if (parseEnum(value, names, 6, &index)) style->justify_content = (JustifyContent)index;
355
+ return;
356
+ }
357
+ if (property == "align-items") {
358
+ static const char *const names[] = {"stretch", "flex-start", "flex-end", "center", "baseline"};
359
+ int index = 0;
360
+ if (parseEnum(value, names, 5, &index)) style->align_items = (AlignItems)index;
361
+ return;
362
+ }
363
+ if (property == "gap" || property == "row-gap" || property == "column-gap") {
364
+ assignLength(&style->gap, value);
365
+ return;
366
+ }
367
+ if (property == "flex-grow") {
368
+ float number = 0;
369
+ if (xt_css_parse_number(value, &number)) style->flex_grow = number;
370
+ return;
371
+ }
372
+ if (property == "flex-shrink") {
373
+ float number = 0;
374
+ if (xt_css_parse_number(value, &number)) style->flex_shrink = number;
375
+ return;
376
+ }
377
+ if (property == "flex-basis") { assignLength(&style->flex_basis, value); return; }
378
+ if (property == "flex") {
379
+ std::string text = xt_css_lower(value);
380
+ if (text == "none") {
381
+ style->flex_grow = 0;
382
+ style->flex_shrink = 0;
383
+ style->flex_basis = {0.0f, Unit::Auto};
384
+ return;
385
+ }
386
+ if (text == "auto") {
387
+ style->flex_grow = 1;
388
+ style->flex_shrink = 1;
389
+ style->flex_basis = {0.0f, Unit::Auto};
390
+ return;
391
+ }
392
+ if (text == "initial") {
393
+ style->flex_grow = 0;
394
+ style->flex_shrink = 1;
395
+ style->flex_basis = {0.0f, Unit::Auto};
396
+ return;
397
+ }
398
+ std::vector<std::string> parts = xt_css_split_whitespace(text);
399
+ style->flex_grow = 0;
400
+ style->flex_shrink = 1;
401
+ style->flex_basis = {0.0f, Unit::Auto};
402
+ size_t index = 0;
403
+ float number = 0;
404
+ if (index < parts.size() && xt_css_parse_number(parts[index], &number)) {
405
+ style->flex_grow = number;
406
+ index++;
407
+ if (index < parts.size() && xt_css_parse_number(parts[index], &number)) {
408
+ style->flex_shrink = number;
409
+ index++;
410
+ }
411
+ if (index >= parts.size()) style->flex_basis = {0.0f, Unit::Px};
412
+ }
413
+ if (index < parts.size()) assignLength(&style->flex_basis, parts[index]);
414
+ return;
415
+ }
416
+
417
+ if (property == "opacity") {
418
+ float number = 0;
419
+ if (xt_css_parse_number(value, &number)) style->opacity = number < 0 ? 0 : (number > 1 ? 1 : number);
420
+ return;
421
+ }
422
+ if (property == "overflow" || property == "overflow-x" || property == "overflow-y") {
423
+ static const char *const names[] = {"visible", "hidden", "scroll", "auto"};
424
+ int index = 0;
425
+ if (parseEnum(value, names, 4, &index)) style->overflow = (Overflow)index;
426
+ return;
427
+ }
428
+ if (property == "z-index") {
429
+ float number = 0;
430
+ if (xt_css_parse_number(value, &number)) {
431
+ style->z_index = (int)number;
432
+ style->has_z_index = true;
433
+ }
434
+ return;
435
+ }
436
+ if (property == "transition") {
437
+ parseTransitionShorthand(value, &style->transitions);
438
+ return;
439
+ }
440
+ if (property == "transition-property") {
441
+ std::vector<std::string> parts = splitCommas(value);
442
+ style->transitions.resize(parts.size());
443
+ for (size_t i = 0; i < parts.size(); i++) style->transitions[i].property = xt_css_lower(parts[i]);
444
+ return;
445
+ }
446
+ if (property == "transition-duration" || property == "transition-delay") {
447
+ std::vector<std::string> parts = splitCommas(value);
448
+ style->transitions.resize(parts.size());
449
+ for (size_t i = 0; i < parts.size(); i++) {
450
+ float seconds = 0;
451
+ if (!parseTimeSeconds(parts[i], &seconds)) continue;
452
+ if (property == "transition-duration") style->transitions[i].duration = seconds;
453
+ else style->transitions[i].delay = seconds;
454
+ }
455
+ return;
456
+ }
457
+ if (property == "transition-timing-function") {
458
+ std::vector<std::string> parts = splitCommas(value);
459
+ style->transitions.resize(parts.size());
460
+ for (size_t i = 0; i < parts.size(); i++) {
461
+ TimingFunction timing = TimingFunction::Ease;
462
+ if (parseTimingFunction(parts[i], &timing)) style->transitions[i].timing = timing;
463
+ }
464
+ return;
465
+ }
466
+ }
467
+
468
+ } // namespace xtgui
@@ -434,6 +434,7 @@ static void define_getter(xt_value proto, const char *name, void *fn) {
434
434
  }
435
435
 
436
436
  xt_value xt_gui_window_proto(void) {
437
+ xt_gc_add_root(&g_window_proto);
437
438
  if (XT_IS_OBJECT(g_window_proto)) return g_window_proto;
438
439
  xt_value proto = xt_object_new();
439
440
  define_method(proto, "setTitle", (void *)win_set_title);
@@ -2,6 +2,7 @@
2
2
 
3
3
  static xt_value xt_buffer_proto(void) {
4
4
  static xt_value proto = 0;
5
+ xt_gc_add_root(&proto);
5
6
  if (proto) return proto;
6
7
  proto = xt_object_new();
7
8
  xt_node_define_method(proto, "toString", (void *)xt_buffer_method_to_string);
@@ -316,6 +316,7 @@ xt_value xt_dgram_static(xt_value name, int32_t argc, xt_value *argv) {
316
316
 
317
317
  static xt_value xt_dgram_socket_proto(void) {
318
318
  static xt_value proto = 0;
319
+ xt_gc_add_root(&proto);
319
320
  if (proto) return proto;
320
321
  proto = xt_object_new();
321
322
  xt_node_install_emitter(proto);
@@ -267,6 +267,7 @@ xt_value xt_events_static(xt_value name, int32_t argc, xt_value *argv) {
267
267
 
268
268
  static xt_value xt_events_proto(void) {
269
269
  static xt_value proto = 0;
270
+ xt_gc_add_root(&proto);
270
271
  if (proto) return proto;
271
272
  proto = xt_object_new();
272
273
  xt_node_install_emitter(proto);
@@ -59,27 +59,18 @@ xt_value xt_node_dirent_result(const char *dir, const char *name) {
59
59
  return object;
60
60
  }
61
61
 
62
- typedef struct {
63
- xt_value *items;
64
- size_t count;
65
- size_t capacity;
66
- } xt_fs_list;
67
-
68
- static void xt_fs_list_push(xt_fs_list *list, xt_value value) {
69
- if (list->count == list->capacity) {
70
- list->capacity = list->capacity ? list->capacity * 2 : 16;
71
- xt_value *grown = (xt_value *)realloc(list->items, sizeof(xt_value) * list->capacity);
72
- if (!grown) return;
73
- list->items = grown;
74
- }
75
- list->items[list->count++] = value;
76
- }
62
+ /* Entries accumulate in an `xt_array`, not a raw `xt_value` buffer: the
63
+ * collector cannot see a plain malloc'd buffer, so a `Dirent` created earlier
64
+ * in the walk would be swept as soon as a later entry allocates (which is
65
+ * exactly the GC-stress failure "object has no callable method
66
+ * 'isDirectory'"). The array lives on the caller's stack, so the conservative
67
+ * stack scan keeps it, and every entry it holds, reachable. */
77
68
 
78
69
  /* Depth-first walk used by `readdirSync(path, { recursive: true })`. `prefix`
79
70
  is the directory path relative to `base` (empty for the root, otherwise
80
71
  ending in `/`). */
81
72
  static void xt_fs_readdir_into(const char *base, const char *prefix, int withFileTypes, int recursive,
82
- xt_fs_list *list) {
73
+ xt_value list) {
83
74
  size_t baseLength = strlen(base);
84
75
  size_t prefixLength = strlen(prefix);
85
76
  size_t dirSize = baseLength + prefixLength + 2;
@@ -104,7 +95,7 @@ static void xt_fs_readdir_into(const char *base, const char *prefix, int withFil
104
95
  char *relative = (char *)malloc(relSize);
105
96
  if (!relative) continue;
106
97
  snprintf(relative, relSize, "%s%s", prefix, name);
107
- xt_fs_list_push(list, withFileTypes ? xt_node_dirent_result(dirPath, name) : xt_string_from_cstr(relative));
98
+ xt_array_push(list, withFileTypes ? xt_node_dirent_result(dirPath, name) : xt_string_from_cstr(relative));
108
99
 
109
100
  if (recursive) {
110
101
  size_t fullSize = dirLength + nameLength + 2;
@@ -141,14 +132,8 @@ xt_value xt_node_read_dir(int32_t argc, xt_value *argv) {
141
132
  int withFileTypes = XT_IS_OBJECT(options) && xt_truthy(xt_object_get_cstr(options, "withFileTypes"));
142
133
  int recursive = XT_IS_OBJECT(options) && xt_truthy(xt_object_get_cstr(options, "recursive"));
143
134
 
144
- xt_fs_list list;
145
- list.items = NULL;
146
- list.count = 0;
147
- list.capacity = 0;
148
- xt_fs_readdir_into(path, "", withFileTypes, recursive, &list);
149
-
150
- xt_value result = xt_array_new((int32_t)list.count, list.items);
151
- free(list.items);
135
+ xt_value result = xt_array_new(0, NULL);
136
+ xt_fs_readdir_into(path, "", withFileTypes, recursive, result);
152
137
  return result;
153
138
  }
154
139
 
@@ -10,21 +10,9 @@
10
10
  #include "../node_common.h"
11
11
  #include "fs_common.h"
12
12
 
13
- typedef struct {
14
- xt_value *items;
15
- size_t count;
16
- size_t capacity;
17
- } xt_glob_list;
18
-
19
- static void xt_glob_push(xt_glob_list *list, xt_value value) {
20
- if (list->count == list->capacity) {
21
- list->capacity = list->capacity ? list->capacity * 2 : 16;
22
- xt_value *grown = (xt_value *)realloc(list->items, sizeof(xt_value) * list->capacity);
23
- if (!grown) return;
24
- list->items = grown;
25
- }
26
- list->items[list->count++] = value;
27
- }
13
+ /* Matches accumulate in an `xt_array` rather than a raw buffer so the garbage
14
+ * collector can see the `Dirent`/string entries created earlier in the walk;
15
+ * see the same note in `fs_ops.c`. */
28
16
 
29
17
  static char *xt_glob_join(const char *dir, const char *name) {
30
18
  size_t dirLength = strlen(dir);
@@ -96,11 +84,11 @@ static int xt_glob_is_dir(const char *path, int follow) {
96
84
  }
97
85
 
98
86
  static void xt_glob_walk(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
99
- xt_glob_list *list);
87
+ xt_value list);
100
88
 
101
89
  /* Recurse one directory level for the `**` segment. */
102
90
  static void xt_glob_descend(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
103
- xt_glob_list *list) {
91
+ xt_value list) {
104
92
  xt_fs_dir dir;
105
93
  if (!xt_fs_dir_open(&dir, dirPath)) return;
106
94
  while (xt_fs_dir_next(&dir)) {
@@ -123,7 +111,7 @@ static void xt_glob_descend(const char *dirPath, const char *pattern, const char
123
111
  }
124
112
 
125
113
  static void xt_glob_walk(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
126
- xt_glob_list *list) {
114
+ xt_value list) {
127
115
  if (pattern[0] == '\0') {
128
116
  if (resultPrefix[0] != '\0') {
129
117
  size_t length = strlen(resultPrefix);
@@ -131,7 +119,7 @@ static void xt_glob_walk(const char *dirPath, const char *pattern, const char *r
131
119
  if (!trimmed) return;
132
120
  memcpy(trimmed, resultPrefix, length + 1);
133
121
  while (length > 1 && trimmed[length - 1] == '/') trimmed[--length] = '\0';
134
- xt_glob_push(list, xt_string_from_cstr(trimmed));
122
+ xt_array_push(list, xt_string_from_cstr(trimmed));
135
123
  free(trimmed);
136
124
  }
137
125
  return;
@@ -165,13 +153,13 @@ static void xt_glob_walk(const char *dirPath, const char *pattern, const char *r
165
153
  char *child = xt_glob_join(dirPath, name);
166
154
  if (!child) continue;
167
155
  if (rest[0] == '\0') {
168
- if (withFileTypes) xt_glob_push(list, xt_node_dirent_result(dirPath, name));
156
+ if (withFileTypes) xt_array_push(list, xt_node_dirent_result(dirPath, name));
169
157
  else {
170
158
  size_t size = strlen(resultPrefix) + strlen(name) + 1;
171
159
  char *result = (char *)malloc(size);
172
160
  if (result) {
173
161
  snprintf(result, size, "%s%s", resultPrefix, name);
174
- xt_glob_push(list, xt_string_from_cstr(result));
162
+ xt_array_push(list, xt_string_from_cstr(result));
175
163
  free(result);
176
164
  }
177
165
  }
@@ -189,7 +177,7 @@ static void xt_glob_walk(const char *dirPath, const char *pattern, const char *r
189
177
  xt_fs_dir_close(&dir);
190
178
  }
191
179
 
192
- static void xt_glob_run(const char *pattern, const char *cwd, int withFileTypes, xt_glob_list *list) {
180
+ static void xt_glob_run(const char *pattern, const char *cwd, int withFileTypes, xt_value list) {
193
181
  if (!pattern) return;
194
182
  const char *base = cwd && cwd[0] ? cwd : ".";
195
183
  const char *prefix = "";
@@ -209,24 +197,19 @@ xt_value xt_node_glob(int32_t argc, xt_value *argv) {
209
197
  xt_value cwdValue = XT_IS_OBJECT(options) ? xt_object_get_cstr(options, "cwd") : XT_UNDEFINED;
210
198
  if (XT_IS_STRING(cwdValue)) cwd = xt_string_data(cwdValue);
211
199
 
212
- xt_glob_list list;
213
- list.items = NULL;
214
- list.count = 0;
215
- list.capacity = 0;
200
+ xt_value result = xt_array_new(0, NULL);
216
201
 
217
202
  xt_value patternValue = argv[0];
218
203
  if (XT_IS_ARRAY(patternValue)) {
219
204
  int32_t count = (int32_t)xt_to_number(xt_array_length(patternValue));
220
205
  for (int32_t i = 0; i < count; i++) {
221
206
  xt_value item = xt_array_get(patternValue, xt_number((double)i));
222
- if (XT_IS_STRING(item)) xt_glob_run(xt_string_data(item), cwd, withFileTypes, &list);
207
+ if (XT_IS_STRING(item)) xt_glob_run(xt_string_data(item), cwd, withFileTypes, result);
223
208
  }
224
209
  } else {
225
210
  const char *pattern = xt_node_cstr(patternValue);
226
- xt_glob_run(pattern, cwd, withFileTypes, &list);
211
+ xt_glob_run(pattern, cwd, withFileTypes, result);
227
212
  }
228
213
 
229
- xt_value result = xt_array_new((int32_t)list.count, list.items);
230
- free(list.items);
231
214
  return result;
232
215
  }
@@ -242,6 +242,7 @@ XT_FS_PROMISES_FACADE(xt_node_p_open, _)
242
242
 
243
243
  xt_value xt_fs_promises(void) {
244
244
  static xt_value cached = 0;
245
+ xt_gc_add_root(&cached);
245
246
  if (XT_IS_OBJECT(cached)) return cached;
246
247
  xt_value object = xt_object_new();
247
248
  #define XT_FS_PROMISE_METHOD(jsName, name) \