flet-box 1.1.2 → 1.1.3

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 (154) hide show
  1. package/AGENTS_FOR_CODE_GEN.md +284 -0
  2. package/CONTRIBUTING.md +238 -0
  3. package/README.md +2 -2
  4. package/bin/cli.js +88 -1
  5. package/bin/commands/auditFix.js +181 -27
  6. package/bin/commands/checkApi.js +14 -3
  7. package/bin/commands/checkStructure.js +368 -0
  8. package/bin/commands/checkSyntax.js +293 -0
  9. package/bin/commands/checkUi.js +666 -14
  10. package/bin/commands/create.js +14 -0
  11. package/bin/commands/createBundle.js +2 -2
  12. package/bin/commands/metrics.js +448 -0
  13. package/bin/commands/release.js +18 -0
  14. package/bin/commands/report.js +849 -0
  15. package/bin/commands/tools.js +76 -0
  16. package/bin/commands/unpack.js +39 -11
  17. package/bin/commands/verify.js +879 -0
  18. package/bin/mcp.js +280 -0
  19. package/bin/utils/contract.js +27 -0
  20. package/bin/utils/dtsProps.js +5 -1
  21. package/bin/utils/migrationGuide.js +146 -0
  22. package/bin/utils/moduleExports.js +9 -5
  23. package/bin/utils/templates.js +225 -18
  24. package/bin/utils/toolkit.js +191 -0
  25. package/docs/README.md +5 -1
  26. package/docs/arquitectura.md +1 -1
  27. package/docs/cli/README.md +168 -2
  28. package/docs/guides/README.md +6 -0
  29. package/docs/guides/accessibility.md +2 -1
  30. package/docs/guides/ai-toolkit.md +166 -0
  31. package/docs/guides/compatibility.md +1 -1
  32. package/docs/guides/migration.md +92 -0
  33. package/docs/guides/minification-and-protection.md +2 -1
  34. package/docs/guides/mobile-and-platforms.md +1 -1
  35. package/docs/guides/online-updates.md +2 -0
  36. package/docs/server/deployment.md +1 -1
  37. package/docs/tools/styling.md +2 -2
  38. package/docs/tools/theme.md +139 -4
  39. package/docs/widget/Accordion.md +74 -8
  40. package/docs/widget/AdMob.md +68 -3
  41. package/docs/widget/AdSense.md +67 -2
  42. package/docs/widget/AlertDialog.md +1 -1
  43. package/docs/widget/AnimatedBox.md +80 -39
  44. package/docs/widget/AnimatedText.md +74 -18
  45. package/docs/widget/Audio.md +73 -14
  46. package/docs/widget/Avatar.md +69 -4
  47. package/docs/widget/Badge.md +68 -3
  48. package/docs/widget/BottomSheet.md +2 -2
  49. package/docs/widget/Button.md +69 -3
  50. package/docs/widget/Card.md +69 -4
  51. package/docs/widget/Carousel.md +67 -2
  52. package/docs/widget/Chart.md +67 -2
  53. package/docs/widget/Checkbox.md +74 -8
  54. package/docs/widget/Chip.md +68 -3
  55. package/docs/widget/CircularBar.md +74 -15
  56. package/docs/widget/CircularChart.md +71 -10
  57. package/docs/widget/CodeViewer.md +67 -2
  58. package/docs/widget/Column.md +68 -3
  59. package/docs/widget/Container.md +68 -3
  60. package/docs/widget/DataTable.md +67 -2
  61. package/docs/widget/Divider.md +67 -2
  62. package/docs/widget/DraggBox.md +67 -2
  63. package/docs/widget/Dropdown.md +76 -10
  64. package/docs/widget/DroppBox.md +67 -2
  65. package/docs/widget/FloatingActionButton.md +68 -3
  66. package/docs/widget/GridView.md +67 -2
  67. package/docs/widget/Icon.md +71 -5
  68. package/docs/widget/Image.md +68 -3
  69. package/docs/widget/Input.md +68 -3
  70. package/docs/widget/Inspector.md +2 -2
  71. package/docs/widget/InstallButton.md +73 -13
  72. package/docs/widget/ListTile.md +69 -3
  73. package/docs/widget/ListView.md +67 -2
  74. package/docs/widget/Maps.md +68 -3
  75. package/docs/widget/Markdown.md +74 -15
  76. package/docs/widget/MatrixRain.md +72 -11
  77. package/docs/widget/Menu.md +209 -0
  78. package/docs/widget/Modal.md +3 -3
  79. package/docs/widget/Pagination.md +68 -3
  80. package/docs/widget/ParallaxBox.md +72 -12
  81. package/docs/widget/ProgressBar.md +69 -4
  82. package/docs/widget/QRCode.md +67 -2
  83. package/docs/widget/README.md +54 -50
  84. package/docs/widget/Radio.md +76 -10
  85. package/docs/widget/Rating.md +70 -6
  86. package/docs/widget/Row.md +68 -3
  87. package/docs/widget/Skeleton.md +68 -3
  88. package/docs/widget/Slider.md +75 -9
  89. package/docs/widget/SnackBar.md +2 -2
  90. package/docs/widget/Stack.md +68 -3
  91. package/docs/widget/Stepper.md +68 -3
  92. package/docs/widget/Switch.md +74 -8
  93. package/docs/widget/Text.md +71 -4
  94. package/docs/widget/ThemePicker.md +197 -0
  95. package/docs/widget/Tooltip.md +72 -7
  96. package/docs/widget/TreeView.md +70 -10
  97. package/docs/widget/Video.md +71 -10
  98. package/package.json +10 -3
  99. package/skills/fletbox-design/SKILL.md +89 -5
  100. package/skills/fletbox-motion/SKILL.md +1 -1
  101. package/skills/fletbox-rules/SKILL.md +56 -4
  102. package/src/core/runApp.js +12 -0
  103. package/src/index.d.ts +374 -101
  104. package/src/index.js +9 -0
  105. package/src/navigations/AdaptiveScaffold.js +2 -2
  106. package/src/navigations/BottomNavigation.js +1 -1
  107. package/src/navigations/Scaffold.js +18 -6
  108. package/src/tools/useState.js +11 -4
  109. package/src/utils/colors.js +275 -0
  110. package/src/utils/errorBoundary.js +288 -0
  111. package/src/utils/index.js +10 -2
  112. package/src/utils/stackPosition.js +39 -24
  113. package/src/utils/syntaxHighlight.js +4 -1
  114. package/src/utils/themes.js +279 -3
  115. package/src/widget-factory/addChildren.js +17 -10
  116. package/src/widget-factory/assignProps.js +18 -6
  117. package/src/widget-factory/createWidget.js +7 -0
  118. package/src/widget-factory/deprecations.js +77 -0
  119. package/src/widget-factory/translateProps.js +11 -1
  120. package/src/widget-factory/warnings.js +1 -27
  121. package/src/widget-factory/widgetFactory.js +59 -17
  122. package/src/widgets/Accordion.js +21 -7
  123. package/src/widgets/Badge.js +11 -2
  124. package/src/widgets/Button.js +15 -3
  125. package/src/widgets/Carousel.js +12 -4
  126. package/src/widgets/Chart.js +7 -2
  127. package/src/widgets/Checkbox.js +1 -2
  128. package/src/widgets/Chip.js +24 -10
  129. package/src/widgets/CircularBar.js +9 -3
  130. package/src/widgets/CircularChart.js +5 -3
  131. package/src/widgets/Container.js +6 -0
  132. package/src/widgets/Divider.js +3 -1
  133. package/src/widgets/DraggBox.js +9 -0
  134. package/src/widgets/Dropdown.js +3 -3
  135. package/src/widgets/DroppBox.js +5 -0
  136. package/src/widgets/FloatingActionButton.js +1 -1
  137. package/src/widgets/Icon.js +5 -1
  138. package/src/widgets/InstallButton.js +4 -2
  139. package/src/widgets/Markdown.js +171 -103
  140. package/src/widgets/Menu.js +340 -0
  141. package/src/widgets/Modal.js +7 -1
  142. package/src/widgets/Pagination.js +10 -5
  143. package/src/widgets/ProgressBar.js +5 -4
  144. package/src/widgets/QRCode.js +4 -0
  145. package/src/widgets/Radio.js +1 -2
  146. package/src/widgets/Rating.js +9 -0
  147. package/src/widgets/Slider.js +4 -1
  148. package/src/widgets/SnackBar.js +2 -2
  149. package/src/widgets/Switch.js +1 -2
  150. package/src/widgets/Text.js +9 -5
  151. package/src/widgets/ThemePicker.js +234 -0
  152. package/src/widgets/TreeView.js +2 -1
  153. package/src/widgets/index.js +2 -0
  154. package/docs/CONTRIBUTING.md +0 -156
@@ -0,0 +1,288 @@
1
+ /**
2
+ * @file errorBoundary.js
3
+ * @description
4
+ * The wall that keeps one broken widget from taking the whole screen with it.
5
+ *
6
+ * A FletBox app is a tree of DOM nodes built by factories. A throw inside one
7
+ * of them — a prop that reads something undefined, a callback that assumes a
8
+ * shape the data no longer has — used to travel straight out of the factory,
9
+ * out of the component, out of `renderApp`, and leave the root empty: a white
10
+ * page, a red line in the console, and nothing on the screen to say where it
11
+ * went. The person saw a blank app; the developer saw a stack trace with no
12
+ * hint of which widget was on screen at the time.
13
+ *
14
+ * This module is that missing wall, in the three places a throw can escape:
15
+ *
16
+ * 1. **the factory** catches a throw while it builds or updates a widget, and
17
+ * the widget paints a placeholder in its own place — the rest of the
18
+ * screen keeps working;
19
+ * 2. **the app render** catches a throw that happened outside any widget, and
20
+ * the root paints the placeholder instead of staying empty;
21
+ * 3. **a handler** is wrapped where it is attached, so a throw inside an
22
+ * `onPress` is reported with the name of the widget that owns it.
23
+ *
24
+ * The placeholder is written with theme tokens and never with hard-coded
25
+ * colors, and it is announced (`role="alert"`): a broken widget is still
26
+ * information the person on the other side needs, and a silent empty box is
27
+ * how a blank screen becomes a mystery.
28
+ *
29
+ * Nothing here swallows an error. The global handler reports it and lets the
30
+ * browser report it too — the boundary adds context, it does not hide.
31
+ *
32
+ * @module errorBoundary
33
+ */
34
+
35
+ import { colors } from "./themes.js";
36
+ import { toREM } from "./units.js";
37
+
38
+ /**
39
+ * The attribute a failed widget carries, so a harness, an inspector or a test
40
+ * can find the failure without reading the console.
41
+ *
42
+ * @type {string}
43
+ */
44
+ export const FAILURE_ATTR = "data-fb-error";
45
+
46
+ /**
47
+ * The last failure the boundary saw. A harness reads it to assert *what* was
48
+ * reported, not only that something was painted.
49
+ *
50
+ * @type {{error: Error, message: string, widgetName: string|null, phase: string, event: string|null, source: string|null, when: string}|null}
51
+ */
52
+ let lastFailure = null;
53
+
54
+ /**
55
+ * The placeholder's own styles. Every length is a framework unit through
56
+ * `toREM`, so the note grows with the reader's font like the rest of the page,
57
+ * and every color is a theme token, so it follows the theme it is painted in.
58
+ *
59
+ * The ink is the page's own `text` and not the red: on the error tint the red
60
+ * measures 3.49:1 in the light theme, which is a rim and not a word. The rim
61
+ * carries the "this went wrong" and the text stays readable.
62
+ */
63
+ const PLACEHOLDER_CSS = `
64
+ display: flex;
65
+ flex-direction: column;
66
+ gap: ${toREM(4)};
67
+ padding: ${toREM(12)};
68
+ border-radius: ${toREM(8)};
69
+ border: 1px solid var(--color-error, ${colors.error});
70
+ background: var(--color-errorBg, ${colors.errorBg});
71
+ color: var(--color-text, ${colors.text});
72
+ font-size: ${toREM(14)};
73
+ line-height: 1.4;
74
+ overflow-wrap: anywhere;
75
+ `;
76
+
77
+ /**
78
+ * Reports a failure with the context that makes it actionable.
79
+ *
80
+ * The console line names the widget, the phase and the message; the original
81
+ * error is passed along so the stack stays clickable. The failure is also
82
+ * remembered, because a screen that shows a placeholder must be able to say
83
+ * *why* without the developer hunting through the console.
84
+ *
85
+ * @param {Error|unknown} error - What was thrown.
86
+ * @param {Object} [context={}] - Where it happened.
87
+ * @param {string} [context.widgetName] - The widget that owns the failure.
88
+ * @param {string} [context.phase] - `render`, `update`, `handler`, `app`…
89
+ * @param {string} [context.event] - The event name, when a handler threw.
90
+ * @param {string} [context.source] - `file:line`, when the browser reported it.
91
+ * @returns {Object} The recorded failure.
92
+ */
93
+ export function reportFailure(error, context = {}) {
94
+ const message =
95
+ error && typeof error.message === "string" && error.message
96
+ ? error.message
97
+ : String(error);
98
+
99
+ lastFailure = {
100
+ error,
101
+ message,
102
+ widgetName: context.widgetName || null,
103
+ phase: context.phase || "unknown",
104
+ event: context.event || null,
105
+ source: context.source || null,
106
+ when: new Date().toISOString(),
107
+ };
108
+
109
+ const whose = lastFailure.widgetName ? lastFailure.widgetName : "the app";
110
+ const where = lastFailure.event
111
+ ? `${whose} failed in its ${lastFailure.event} handler`
112
+ : `${whose} failed during ${lastFailure.phase}`;
113
+
114
+ console.error(`[FletBox] ${where}: ${message}`, error);
115
+ return lastFailure;
116
+ }
117
+
118
+ /**
119
+ * The last failure the boundary saw, or `null`.
120
+ *
121
+ * @returns {Object|null} The recorded failure.
122
+ */
123
+ export function getLastFailure() {
124
+ return lastFailure;
125
+ }
126
+
127
+ /**
128
+ * Paints the placeholder that stands in for a widget that could not be built.
129
+ *
130
+ * The placeholder goes *inside* the host, so the widget keeps its place in the
131
+ * layout: a broken card is a card that says so, not a hole where a card was.
132
+ * The host's own content is cleared first — a half-built widget is worse than
133
+ * an empty one, because it looks finished.
134
+ *
135
+ * The host is the element that carries the marker, so one failure is one
136
+ * marked element and the placeholder inside it is found by its role.
137
+ *
138
+ * @param {HTMLElement} host - The widget (or the app root) that failed.
139
+ * @param {Object} failure - The recorded failure.
140
+ * @param {Object} [context={}] - Where it happened.
141
+ * @returns {HTMLElement|null} The placeholder, or `null` outside a browser.
142
+ */
143
+ export function paintFailure(host, failure, context = {}) {
144
+ if (!host || typeof document === "undefined") return null;
145
+
146
+ const phase = context.phase || "render";
147
+
148
+ const box = document.createElement("div");
149
+ box.setAttribute("role", "alert");
150
+ box.style.cssText = PLACEHOLDER_CSS;
151
+
152
+ const title = document.createElement("strong");
153
+ title.textContent = context.widgetName
154
+ ? `${context.widgetName} could not be rendered`
155
+ : "This screen could not be rendered";
156
+
157
+ const detail = document.createElement("span");
158
+ detail.textContent = failure.message;
159
+
160
+ box.appendChild(title);
161
+ box.appendChild(detail);
162
+
163
+ while (host.firstChild) host.removeChild(host.firstChild);
164
+ host.appendChild(box);
165
+ host.setAttribute(FAILURE_ATTR, phase);
166
+ host._failureBox = box;
167
+ return box;
168
+ }
169
+
170
+ /**
171
+ * Reports a failure and paints the placeholder in its place.
172
+ *
173
+ * @param {HTMLElement} host - The widget (or the app root) that failed.
174
+ * @param {Error|unknown} error - What was thrown.
175
+ * @param {Object} [context={}] - Where it happened.
176
+ * @returns {HTMLElement|null} The placeholder, or `null` outside a browser.
177
+ */
178
+ export function renderFailure(host, error, context = {}) {
179
+ if (!host || typeof document === "undefined") return null;
180
+ const failure = reportFailure(error, context);
181
+ return paintFailure(host, failure, context);
182
+ }
183
+
184
+ /**
185
+ * Removes the placeholder a previous failure painted, so a later successful
186
+ * update can put the real content back.
187
+ *
188
+ * @param {HTMLElement} host - The widget that may carry a placeholder.
189
+ * @returns {void}
190
+ */
191
+ export function clearFailure(host) {
192
+ if (!host) return;
193
+ if (host._failureBox && host._failureBox.parentNode === host) {
194
+ host._failureBox.remove();
195
+ }
196
+ host._failureBox = null;
197
+ host.removeAttribute(FAILURE_ATTR);
198
+ }
199
+
200
+ /**
201
+ * Wraps an event handler so a throw inside it is caught with the name of the
202
+ * widget that owns it.
203
+ *
204
+ * Without this, an `onPress` that throws reaches `window.onerror` as an
205
+ * anonymous stack: the developer learns that *something* threw and has to
206
+ * guess which control. Here the console names the widget.
207
+ *
208
+ * @param {Function} handler - The handler to protect.
209
+ * @param {Object} [context={}] - Where it happened.
210
+ * @returns {Function} The protected handler.
211
+ */
212
+ export function guardHandler(handler, context = {}) {
213
+ if (typeof handler !== "function") return handler;
214
+ return (event) => {
215
+ try {
216
+ return handler(event);
217
+ } catch (error) {
218
+ reportFailure(error, {
219
+ ...context,
220
+ phase: "handler",
221
+ event: (event && event.type) || context.event || null,
222
+ });
223
+ return undefined;
224
+ }
225
+ };
226
+ }
227
+
228
+ /**
229
+ * Installs the global handler: the last wall, for the errors no widget owns.
230
+ *
231
+ * A rejected promise and an error thrown from a timer never reach a widget,
232
+ * so nothing above can catch them. This reports them with whatever context the
233
+ * browser carries (the file and the line) and leaves the browser's own report
234
+ * in place — `preventDefault()` is not called, because an error that nobody
235
+ * can see is worse than a noisy console.
236
+ *
237
+ * Calling it twice installs one handler: the second call returns the same
238
+ * disposer.
239
+ *
240
+ * @param {Object} [options={}] - Optional `onError(error, context)` hook.
241
+ * @returns {Function|null} A disposer, or `null` outside a browser.
242
+ */
243
+ export function installErrorBoundary(options = {}) {
244
+ if (typeof window === "undefined" || !window.addEventListener) return null;
245
+ if (installErrorBoundary._release) return installErrorBoundary._release;
246
+
247
+ const onError = (event) => {
248
+ // A resource that failed to load (an image, a font) also arrives here, and
249
+ // it is not a thrown error: it has no stack and it is not a bug in the app.
250
+ if (!event.error && !event.filename) return;
251
+
252
+ const error = event.error || new Error(event.message || "Unknown error");
253
+ const context = {
254
+ phase: "global",
255
+ source: event.filename ? `${event.filename}:${event.lineno}` : null,
256
+ };
257
+ const failure = reportFailure(error, context);
258
+ if (typeof options.onError === "function") options.onError(error, failure);
259
+ };
260
+
261
+ const onRejection = (event) => {
262
+ const reason = event.reason;
263
+ const error = reason instanceof Error ? reason : new Error(String(reason));
264
+ const failure = reportFailure(error, { phase: "rejection" });
265
+ if (typeof options.onError === "function") options.onError(error, failure);
266
+ };
267
+
268
+ window.addEventListener("error", onError);
269
+ window.addEventListener("unhandledrejection", onRejection);
270
+
271
+ installErrorBoundary._release = () => {
272
+ window.removeEventListener("error", onError);
273
+ window.removeEventListener("unhandledrejection", onRejection);
274
+ installErrorBoundary._release = null;
275
+ };
276
+ return installErrorBoundary._release;
277
+ }
278
+
279
+ export default {
280
+ FAILURE_ATTR,
281
+ reportFailure,
282
+ getLastFailure,
283
+ paintFailure,
284
+ renderFailure,
285
+ clearFailure,
286
+ guardHandler,
287
+ installErrorBoundary,
288
+ };
@@ -10,12 +10,13 @@
10
10
  * ```
11
11
  *
12
12
  * Included utilities:
13
- * - **themes** — color palette, theme switching, CSS variable sync
13
+ * - **themes** — color palette, theme switching, CSS variable sync,
14
+ * accent presets (a hue family painted over the palette)
14
15
  * - **getWidgetProps** — read widget `_props` safely
15
16
  * - **syntaxHighlight** — JavaScript/FletBox tokenizer and HTML highlighter
16
17
  * - **markdownParser** — markdown → FletBox widget tree converter
17
18
  * - **visualEffects** — CSS animations (stripes, shimmer, glow, pulse)
18
- * - **stackPosition** — dataset-based absolute positioning for Stack children
19
+ * - **stackPosition** — absolute positioning for a widget layered over its parent
19
20
  * - **units** — px ↔ rem conversion helpers
20
21
  * - **navigation** — tree-navigation mixin for widget objects
21
22
  * - **stopWebRefresh** — disables browser refresh gestures
@@ -26,6 +27,13 @@ export {
26
27
  colors,
27
28
  setTheme,
28
29
  registerPalette,
30
+ registerAccent,
31
+ setAccent,
32
+ applyAccentPreference,
33
+ getAccent,
34
+ getAccentSurfaces,
35
+ listAccents,
36
+ listAccentRamps,
29
37
  getTheme,
30
38
  toggleTheme,
31
39
  subscribeTheme,
@@ -1,31 +1,46 @@
1
1
  // utils/stackPosition.js
2
-
3
2
  /**
4
- * Reads `top`, `right`, `bottom`, and `left` from `props` and stores them as
5
- * `dataset` attributes on the widget element so that a parent `Stack` widget
6
- * can position this child absolutely at render time.
3
+ * @file stackPosition.js
4
+ * @description Anchors a widget over its positioned ancestor — the public, call
5
+ * it-yourself form of what `position: "stack"` does inside the factory.
6
+ *
7
+ * The helper used to write `top`, `right`, `bottom` and `left` into the element's
8
+ * `dataset` "so that a parent Stack can read it later". No parent ever read it:
9
+ * `Stack` layers its children with one stylesheet rule (`stackLayout.js`), and
10
+ * nothing in the framework looks at `data-top`. So the call the docs, the snippet
11
+ * library and the design skill all recommend —
12
+ * `stackPosition(badge, { top: 8, right: 8 })` — returned the widget with its
13
+ * edges recorded and its position untouched, which is worse than an error: the
14
+ * overlay simply stayed where the flow had put it.
15
+ *
16
+ * It now delegates to the engine's own implementation, so there is one set of
17
+ * rules for edges, centring and the rem contract rather than two that drift. The
18
+ * engine gates on `position: "stack"` because it sees every prop of every widget
19
+ * and must not treat an unrelated `top` as a placement; an explicit call to this
20
+ * helper *is* the intent to place, so it passes that gate itself.
7
21
  *
8
- * @param {HTMLElement} widget - The widget element to annotate.
9
- * @param {Object} props - Props object that may contain `top`, `right`, `bottom`, and/or `left`.
10
- * @param {number|string} [props.top] - Distance from the top edge (px or CSS string).
11
- * @param {number|string} [props.right] - Distance from the right edge (px or CSS string).
12
- * @param {number|string} [props.bottom] - Distance from the bottom edge (px or CSS string).
13
- * @param {number|string} [props.left] - Distance from the left edge (px or CSS string).
14
- * @returns {HTMLElement} The same `widget` reference, with dataset attributes set.
22
+ * @module stackPosition
15
23
  */
16
- export const stackPosition = (widget, props) => {
17
- const top = props.top;
18
- const right = props.right;
19
- const bottom = props.bottom;
20
- const left = props.left;
24
+ import { stackPosition as placeInStack } from "../widget-factory/stackPosition.js";
21
25
 
22
- // Store position in dataset so the parent Stack can read it later
23
- if (top !== undefined) widget.dataset.top = top;
24
- if (right !== undefined) widget.dataset.right = right;
25
- if (bottom !== undefined) widget.dataset.bottom = bottom;
26
- if (left !== undefined) widget.dataset.left = left;
27
-
28
- return widget;
29
- };
26
+ /**
27
+ * Absolutely position a widget relative to its nearest positioned ancestor.
28
+ *
29
+ * Numbers follow the framework-wide unit contract: they are pixels on the base
30
+ * font size and are emitted as `rem`, so an overlay moves with the reader's text
31
+ * size instead of freezing at the pixel it was written in.
32
+ *
33
+ * @param {HTMLElement} widget - The widget to anchor. Returned unchanged.
34
+ * @param {Object} [props] - Any of `top`, `right`, `bottom`, `left`, `start`,
35
+ * `end`, `centerX`, `centerY`, `center`, `fullWidth`, `fullHeight`, `full`.
36
+ * A value of `false` means "do not apply".
37
+ * @returns {HTMLElement} The same `widget`, now positioned.
38
+ *
39
+ * @example
40
+ * const badge = Icon({ name: "notifications" });
41
+ * stackPosition(badge, { top: 8, right: 8 }); // → position:absolute; top:.5rem; right:.5rem
42
+ */
43
+ export const stackPosition = (widget, props = {}) =>
44
+ placeInStack(widget, { ...props, position: "stack" });
30
45
 
31
46
  export default stackPosition;
@@ -156,7 +156,10 @@ const LIGHT_HIGHLIGHT_COLORS = {
156
156
  [TokenType.NUMBER]: "#0550ae", // blue
157
157
  [TokenType.COMMENT]: "#57606a", // gray
158
158
  [TokenType.PROPERTY]: "#953800", // brown
159
- [TokenType.FUNCTION]: "#8250df", // purple
159
+ // `#8250df` clears AA on the framework's own #fafafa pane by 0.33 and fails
160
+ // one on a slightly darker app plate (#eef2f7 → 4.49). `#6f42c1` is the same
161
+ // GitHub purple a step deeper: 5.79 there, and 6.51 on white.
162
+ [TokenType.FUNCTION]: "#6f42c1", // purple
160
163
  [TokenType.BRACKET]: "#57606a", // gray
161
164
  [TokenType.PUNCTUATION]: "#57606a", // gray
162
165
  [TokenType.FLETBOX_COLOR]: "#0e7490", // teal