@mgz-dev/inspect 0.1.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.
Files changed (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/Inspect.cjs +1 -0
  4. package/dist/Inspect.d.ts +68 -0
  5. package/dist/Inspect.mjs +1 -0
  6. package/dist/InspectRegistry.cjs +1 -0
  7. package/dist/InspectRegistry.d.ts +52 -0
  8. package/dist/InspectRegistry.mjs +1 -0
  9. package/dist/assets/default-atlas.cjs +1 -0
  10. package/dist/assets/default-atlas.d.ts +6 -0
  11. package/dist/assets/default-atlas.mjs +1 -0
  12. package/dist/core/atlas.cjs +1 -0
  13. package/dist/core/atlas.d.ts +29 -0
  14. package/dist/core/atlas.mjs +1 -0
  15. package/dist/core/canvas2d-renderer.cjs +1 -0
  16. package/dist/core/canvas2d-renderer.d.ts +94 -0
  17. package/dist/core/canvas2d-renderer.mjs +1 -0
  18. package/dist/core/font-generator.cjs +1 -0
  19. package/dist/core/font-generator.d.ts +20 -0
  20. package/dist/core/font-generator.mjs +1 -0
  21. package/dist/core/glyph-rasterizer.cjs +1 -0
  22. package/dist/core/glyph-rasterizer.d.ts +10 -0
  23. package/dist/core/glyph-rasterizer.mjs +1 -0
  24. package/dist/core/input.cjs +1 -0
  25. package/dist/core/input.d.ts +89 -0
  26. package/dist/core/input.mjs +1 -0
  27. package/dist/core/layout.d.ts +10 -0
  28. package/dist/core/layouts/stack-layout.cjs +1 -0
  29. package/dist/core/layouts/stack-layout.d.ts +69 -0
  30. package/dist/core/layouts/stack-layout.mjs +1 -0
  31. package/dist/core/renderer.d.ts +65 -0
  32. package/dist/core/surface.cjs +1 -0
  33. package/dist/core/surface.d.ts +81 -0
  34. package/dist/core/surface.mjs +1 -0
  35. package/dist/core/theme.d.ts +33 -0
  36. package/dist/core/ttf-parser.cjs +1 -0
  37. package/dist/core/ttf-parser.d.ts +29 -0
  38. package/dist/core/ttf-parser.mjs +1 -0
  39. package/dist/core/widget.cjs +1 -0
  40. package/dist/core/widget.d.ts +213 -0
  41. package/dist/core/widget.mjs +1 -0
  42. package/dist/defaults.cjs +1 -0
  43. package/dist/defaults.d.ts +113 -0
  44. package/dist/defaults.mjs +1 -0
  45. package/dist/gui/gui-button-row.cjs +1 -0
  46. package/dist/gui/gui-button-row.d.ts +21 -0
  47. package/dist/gui/gui-button-row.mjs +1 -0
  48. package/dist/gui/gui-button.cjs +1 -0
  49. package/dist/gui/gui-button.d.ts +9 -0
  50. package/dist/gui/gui-button.mjs +1 -0
  51. package/dist/gui/gui-color.cjs +1 -0
  52. package/dist/gui/gui-color.d.ts +105 -0
  53. package/dist/gui/gui-color.mjs +1 -0
  54. package/dist/gui/gui-folder.cjs +1 -0
  55. package/dist/gui/gui-folder.d.ts +49 -0
  56. package/dist/gui/gui-folder.mjs +1 -0
  57. package/dist/gui/gui-monitor.cjs +1 -0
  58. package/dist/gui/gui-monitor.d.ts +53 -0
  59. package/dist/gui/gui-monitor.mjs +1 -0
  60. package/dist/gui/gui-point2d.d.ts +43 -0
  61. package/dist/gui/gui-point3d.d.ts +48 -0
  62. package/dist/gui/gui-reorder-list.cjs +1 -0
  63. package/dist/gui/gui-reorder-list.d.ts +42 -0
  64. package/dist/gui/gui-reorder-list.mjs +1 -0
  65. package/dist/gui/gui-select.cjs +1 -0
  66. package/dist/gui/gui-select.d.ts +40 -0
  67. package/dist/gui/gui-select.mjs +1 -0
  68. package/dist/gui/gui-separator.cjs +1 -0
  69. package/dist/gui/gui-separator.d.ts +10 -0
  70. package/dist/gui/gui-separator.mjs +1 -0
  71. package/dist/gui/gui-slider.cjs +1 -0
  72. package/dist/gui/gui-slider.d.ts +34 -0
  73. package/dist/gui/gui-slider.mjs +1 -0
  74. package/dist/gui/gui-tab-container.d.ts +26 -0
  75. package/dist/gui/gui-text-log.cjs +1 -0
  76. package/dist/gui/gui-text-log.d.ts +24 -0
  77. package/dist/gui/gui-text-log.mjs +1 -0
  78. package/dist/gui/gui-text.cjs +1 -0
  79. package/dist/gui/gui-text.d.ts +22 -0
  80. package/dist/gui/gui-text.mjs +1 -0
  81. package/dist/gui/gui-toggle.cjs +1 -0
  82. package/dist/gui/gui-toggle.d.ts +19 -0
  83. package/dist/gui/gui-toggle.mjs +1 -0
  84. package/dist/gui/labeled-control.cjs +1 -0
  85. package/dist/gui/labeled-control.d.ts +21 -0
  86. package/dist/gui/labeled-control.mjs +1 -0
  87. package/dist/index.cjs +1 -0
  88. package/dist/index.d.ts +19 -0
  89. package/dist/index.mjs +1 -0
  90. package/dist/panel/inspect-debugger.cjs +1 -0
  91. package/dist/panel/inspect-debugger.d.ts +30 -0
  92. package/dist/panel/inspect-debugger.mjs +1 -0
  93. package/dist/panel/inspect-panel.cjs +1 -0
  94. package/dist/panel/inspect-panel.d.ts +66 -0
  95. package/dist/panel/inspect-panel.mjs +1 -0
  96. package/dist/panel/state-manager.cjs +1 -0
  97. package/dist/panel/state-manager.d.ts +8 -0
  98. package/dist/panel/state-manager.mjs +1 -0
  99. package/dist/panel/widget-builder.cjs +1 -0
  100. package/dist/panel/widget-builder.d.ts +80 -0
  101. package/dist/panel/widget-builder.mjs +1 -0
  102. package/dist/themes/dark.cjs +1 -0
  103. package/dist/themes/dark.d.ts +4 -0
  104. package/dist/themes/dark.mjs +1 -0
  105. package/dist/themes/inspect-theme.d.ts +20 -0
  106. package/dist/types.cjs +1 -0
  107. package/dist/types.d.ts +354 -0
  108. package/dist/types.mjs +1 -0
  109. package/dist/utils/activity.cjs +1 -0
  110. package/dist/utils/activity.d.ts +4 -0
  111. package/dist/utils/activity.mjs +1 -0
  112. package/dist/utils/color-convert.cjs +1 -0
  113. package/dist/utils/color-convert.d.ts +31 -0
  114. package/dist/utils/color-convert.mjs +1 -0
  115. package/dist/utils/index.d.ts +3 -0
  116. package/dist/utils/persistence.cjs +1 -0
  117. package/dist/utils/persistence.d.ts +16 -0
  118. package/dist/utils/persistence.mjs +1 -0
  119. package/dist/widgets/box.cjs +1 -0
  120. package/dist/widgets/box.d.ts +24 -0
  121. package/dist/widgets/box.mjs +1 -0
  122. package/dist/widgets/button.cjs +1 -0
  123. package/dist/widgets/button.d.ts +50 -0
  124. package/dist/widgets/button.mjs +1 -0
  125. package/dist/widgets/canvas-widget.cjs +1 -0
  126. package/dist/widgets/canvas-widget.d.ts +12 -0
  127. package/dist/widgets/canvas-widget.mjs +1 -0
  128. package/dist/widgets/label.cjs +1 -0
  129. package/dist/widgets/label.d.ts +41 -0
  130. package/dist/widgets/label.mjs +1 -0
  131. package/dist/widgets/number-input.cjs +1 -0
  132. package/dist/widgets/number-input.d.ts +65 -0
  133. package/dist/widgets/number-input.mjs +1 -0
  134. package/dist/widgets/overlay.d.ts +16 -0
  135. package/dist/widgets/scroll-area.cjs +1 -0
  136. package/dist/widgets/scroll-area.d.ts +80 -0
  137. package/dist/widgets/scroll-area.mjs +1 -0
  138. package/dist/widgets/separator.cjs +1 -0
  139. package/dist/widgets/separator.d.ts +16 -0
  140. package/dist/widgets/separator.mjs +1 -0
  141. package/dist/widgets/slider.cjs +1 -0
  142. package/dist/widgets/slider.d.ts +36 -0
  143. package/dist/widgets/slider.mjs +1 -0
  144. package/dist/widgets/tab-bar.cjs +1 -0
  145. package/dist/widgets/tab-bar.d.ts +72 -0
  146. package/dist/widgets/tab-bar.mjs +1 -0
  147. package/dist/widgets/text-input.cjs +1 -0
  148. package/dist/widgets/text-input.d.ts +54 -0
  149. package/dist/widgets/text-input.mjs +1 -0
  150. package/dist/widgets/toggle-button.d.ts +18 -0
  151. package/dist/widgets/toggle-icon.cjs +1 -0
  152. package/dist/widgets/toggle-icon.d.ts +38 -0
  153. package/dist/widgets/toggle-icon.mjs +1 -0
  154. package/package.json +95 -0
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./labeled-control.cjs"),i=require("../core/layouts/stack-layout.cjs"),e=require("../defaults.cjs"),r=require("../widgets/box.cjs"),a=require("../widgets/canvas-widget.cjs"),s=require("../widgets/label.cjs");class n extends t.LabeledControl{_config;_valueLabel;_valueBg;_graph;_ringBuffer;_ringHead=0;_ringCount=0;_ringMin=1/0;_ringMax=-1/0;_lastRefreshTime=0;_lastRawValue="";_lastFormattedNum=NaN;_lastFormattedStr="";_lastGraphValue=NaN;_graphStableCount=0;constructor(t){if(super(),this._config=t,this.labelText=t.label,this._ringBuffer=new Float64Array(e.DEFAULTS.monitorRingSize),t.graph)this._valueLabel=null,this._valueBg=null,this.sizingY="fixed",this.preferredHeight=e.DEFAULTS.monitorGraphHeight,this._graph=new a.CanvasWidget,this._graph.sizingX="grow",this._graph.sizingY="grow",this._graph.flexGrow=1,this._graph.onDraw=(t,i,e,r)=>{this.drawGraph(t,i,e,r)},this.controlContainer.addChild(this._graph);else{this._graph=null,this._valueBg=new r.Box;const t=this._valueBg;t.sizingX="grow",t.sizingY="grow",t.flexGrow=1,t.borderRadius=2,t.layout=new i.StackLayout("horizontal",0,"end","stretch"),t.setPadding(0,4,0,4),this._valueLabel=new s.Label,this._valueLabel.sizingX="grow",this._valueLabel.sizingY="grow",this._valueLabel.flexGrow=1,this._valueLabel.hAlign="right",this._valueLabel.vAlign="middle",this._valueLabel.overflow="ellipsis",t.addChild(this._valueLabel),this.controlContainer.addChild(t)}}get config(){return this._config}applyTheme(t){super.applyTheme(t);const i=t;this._graph&&(this.preferredHeight=e.DEFAULTS.monitorGraphHeight),this._valueLabel&&(this._valueLabel.color=i.textValue),this._valueBg&&(this._valueBg.bgColor=t.bgPanelInset)}refresh(){const t=this._config.interval??0;if(t>0){const i=Date.now();if(i-this._lastRefreshTime<t)return;this._lastRefreshTime=i}const i=this._config.value();if(this._graph){const t="number"==typeof i?i:parseFloat(i)||0;t!==this._lastGraphValue?(this._lastGraphValue=t,this._graphStableCount=0,this.pushRingValue(t),this._graph.markDirty()):this._graphStableCount<e.DEFAULTS.monitorRingSize&&(this._graphStableCount++,this.pushRingValue(t),this._graph.markDirty())}else if(this._valueLabel){if(i===this._lastRawValue)return;this._lastRawValue=i;const t=this.formatValue(i);if(this._valueLabel.text===t)return;this._valueLabel.text=t,this._valueLabel.markDirty()}}pushRingValue(t){const i=this._ringCount>=e.DEFAULTS.monitorRingSize?this._ringBuffer[this._ringHead]:void 0;if(this._ringBuffer[this._ringHead]=t,this._ringHead=(this._ringHead+1)%e.DEFAULTS.monitorRingSize,this._ringCount<e.DEFAULTS.monitorRingSize&&this._ringCount++,t<this._ringMin&&(this._ringMin=t),t>this._ringMax&&(this._ringMax=t),void 0!==i&&(i===this._ringMin||i===this._ringMax)){this._ringMin=1/0,this._ringMax=-1/0;for(let t=0;t<this._ringCount;t++){const i=this._ringBuffer[t];i<this._ringMin&&(this._ringMin=i),i>this._ringMax&&(this._ringMax=i)}}}drawGraph(t,i,r,a){const s=i;if(t.fillRoundedRect(0,0,r,a,e.DEFAULTS.monitorGraphBorderRadius,i.bgPanelInset),t.strokeRoundedRect(0,0,r,a,e.DEFAULTS.monitorGraphBorderRadius,i.borderInset,e.DEFAULTS.monitorGraphStrokeWidth),this._ringCount<2)return;const n=this._config.min??this._ringMin,h=(this._config.max??this._ringMax)-n,o=h>0?h:1,l=s.bgAccent||i.textHighlight,g=r/(e.DEFAULTS.monitorRingSize-1),_=this._ringCount<e.DEFAULTS.monitorRingSize?0:this._ringHead;t.beginPath();for(let i=0;i<this._ringCount;i++){const r=(_+i)%e.DEFAULTS.monitorRingSize,s=i*g|0,h=a-1-(this._ringBuffer[r]-n)/o*(a-2)|0;0===i?t.moveTo(s,h):t.lineTo(s,h)}t.strokePath(l,e.DEFAULTS.monitorGraphStrokeWidth)}formatValue(t){return"string"==typeof t?t:(t===this._lastFormattedNum||(this._lastFormattedNum=t,this._config.format?this._lastFormattedStr=this._config.format(t):Number.isInteger(t)?this._lastFormattedStr=String(t):this._lastFormattedStr=t.toFixed(2)),this._lastFormattedStr)}}exports.GuiMonitor=n;
@@ -0,0 +1,53 @@
1
+ import { LabeledControl } from './labeled-control';
2
+ import { Theme } from '../core/theme';
3
+ /** Configuration for a read-only monitor display (text or graph). */
4
+ export interface GuiMonitorConfig {
5
+ key: string;
6
+ label: string;
7
+ value: () => string | number;
8
+ format?: (value: number) => string;
9
+ graph?: boolean;
10
+ interval?: number;
11
+ min?: number;
12
+ max?: number;
13
+ }
14
+ /**
15
+ * Read-only control displaying a live value as text or a scrolling line graph.
16
+ * Uses a ring buffer to store recent samples for graph mode.
17
+ */
18
+ export declare class GuiMonitor extends LabeledControl {
19
+ private readonly _config;
20
+ private readonly _valueLabel;
21
+ private readonly _valueBg;
22
+ private readonly _graph;
23
+ private readonly _ringBuffer;
24
+ private _ringHead;
25
+ private _ringCount;
26
+ private _ringMin;
27
+ private _ringMax;
28
+ private _lastRefreshTime;
29
+ private _lastRawValue;
30
+ private _lastFormattedNum;
31
+ private _lastFormattedStr;
32
+ private _lastGraphValue;
33
+ private _graphStableCount;
34
+ /** builds either a graph canvas or a right-aligned value label, depending on config.graph. */
35
+ constructor(config: GuiMonitorConfig);
36
+ /** the config this monitor was created with. */
37
+ get config(): GuiMonitorConfig;
38
+ /** applies theme colors to the value label and background. */
39
+ applyTheme(theme: Theme): void;
40
+ /** samples the current value and updates the text or graph, honoring the refresh interval. */
41
+ refresh(): void;
42
+ /**
43
+ * Appends a sample to the ring buffer, advancing the head and tracking the
44
+ * running min and max. The min and max update incrementally, but when the
45
+ * evicted sample was the current min or max the extent is rescanned since its
46
+ * true value can no longer be known.
47
+ */
48
+ private pushRingValue;
49
+ /** draws the inset background and a polyline of the buffered samples, oldest to newest, scaled to the value range. */
50
+ private drawGraph;
51
+ /** formats a number (config formatter, integer, or two decimals), caching the last; strings pass through. */
52
+ private formatValue;
53
+ }
@@ -0,0 +1 @@
1
+ import{LabeledControl as t}from"./labeled-control.mjs";import{StackLayout as i}from"../core/layouts/stack-layout.mjs";import{DEFAULTS as r}from"../defaults.mjs";import{Box as e}from"../widgets/box.mjs";import{CanvasWidget as s}from"../widgets/canvas-widget.mjs";import{Label as a}from"../widgets/label.mjs";class n extends t{_config;_valueLabel;_valueBg;_graph;_ringBuffer;_ringHead=0;_ringCount=0;_ringMin=1/0;_ringMax=-1/0;_lastRefreshTime=0;_lastRawValue="";_lastFormattedNum=NaN;_lastFormattedStr="";_lastGraphValue=NaN;_graphStableCount=0;constructor(t){if(super(),this._config=t,this.labelText=t.label,this._ringBuffer=new Float64Array(r.monitorRingSize),t.graph)this._valueLabel=null,this._valueBg=null,this.sizingY="fixed",this.preferredHeight=r.monitorGraphHeight,this._graph=new s,this._graph.sizingX="grow",this._graph.sizingY="grow",this._graph.flexGrow=1,this._graph.onDraw=(t,i,r,e)=>{this.drawGraph(t,i,r,e)},this.controlContainer.addChild(this._graph);else{this._graph=null,this._valueBg=new e;const t=this._valueBg;t.sizingX="grow",t.sizingY="grow",t.flexGrow=1,t.borderRadius=2,t.layout=new i("horizontal",0,"end","stretch"),t.setPadding(0,4,0,4),this._valueLabel=new a,this._valueLabel.sizingX="grow",this._valueLabel.sizingY="grow",this._valueLabel.flexGrow=1,this._valueLabel.hAlign="right",this._valueLabel.vAlign="middle",this._valueLabel.overflow="ellipsis",t.addChild(this._valueLabel),this.controlContainer.addChild(t)}}get config(){return this._config}applyTheme(t){super.applyTheme(t);const i=t;this._graph&&(this.preferredHeight=r.monitorGraphHeight),this._valueLabel&&(this._valueLabel.color=i.textValue),this._valueBg&&(this._valueBg.bgColor=t.bgPanelInset)}refresh(){const t=this._config.interval??0;if(t>0){const i=Date.now();if(i-this._lastRefreshTime<t)return;this._lastRefreshTime=i}const i=this._config.value();if(this._graph){const t="number"==typeof i?i:parseFloat(i)||0;t!==this._lastGraphValue?(this._lastGraphValue=t,this._graphStableCount=0,this.pushRingValue(t),this._graph.markDirty()):this._graphStableCount<r.monitorRingSize&&(this._graphStableCount++,this.pushRingValue(t),this._graph.markDirty())}else if(this._valueLabel){if(i===this._lastRawValue)return;this._lastRawValue=i;const t=this.formatValue(i);if(this._valueLabel.text===t)return;this._valueLabel.text=t,this._valueLabel.markDirty()}}pushRingValue(t){const i=this._ringCount>=r.monitorRingSize?this._ringBuffer[this._ringHead]:void 0;if(this._ringBuffer[this._ringHead]=t,this._ringHead=(this._ringHead+1)%r.monitorRingSize,this._ringCount<r.monitorRingSize&&this._ringCount++,t<this._ringMin&&(this._ringMin=t),t>this._ringMax&&(this._ringMax=t),void 0!==i&&(i===this._ringMin||i===this._ringMax)){this._ringMin=1/0,this._ringMax=-1/0;for(let t=0;t<this._ringCount;t++){const i=this._ringBuffer[t];i<this._ringMin&&(this._ringMin=i),i>this._ringMax&&(this._ringMax=i)}}}drawGraph(t,i,e,s){const a=i;if(t.fillRoundedRect(0,0,e,s,r.monitorGraphBorderRadius,i.bgPanelInset),t.strokeRoundedRect(0,0,e,s,r.monitorGraphBorderRadius,i.borderInset,r.monitorGraphStrokeWidth),this._ringCount<2)return;const n=this._config.min??this._ringMin,h=(this._config.max??this._ringMax)-n,o=h>0?h:1,l=a.bgAccent||i.textHighlight,g=e/(r.monitorRingSize-1),_=this._ringCount<r.monitorRingSize?0:this._ringHead;t.beginPath();for(let i=0;i<this._ringCount;i++){const e=(_+i)%r.monitorRingSize,a=i*g|0,h=s-1-(this._ringBuffer[e]-n)/o*(s-2)|0;0===i?t.moveTo(a,h):t.lineTo(a,h)}t.strokePath(l,r.monitorGraphStrokeWidth)}formatValue(t){return"string"==typeof t?t:(t===this._lastFormattedNum||(this._lastFormattedNum=t,this._config.format?this._lastFormattedStr=this._config.format(t):Number.isInteger(t)?this._lastFormattedStr=String(t):this._lastFormattedStr=t.toFixed(2)),this._lastFormattedStr)}}export{n as GuiMonitor};
@@ -0,0 +1,43 @@
1
+ import { LabeledControl } from './labeled-control';
2
+ import { Theme } from '../core/theme';
3
+ /** Per-axis numeric constraints for a point control. */
4
+ export interface AxisConfig {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ }
9
+ /** Configuration for a 2D point editor control. */
10
+ export interface GuiPoint2dConfig {
11
+ key: string;
12
+ label: string;
13
+ value: () => {
14
+ x: number;
15
+ y: number;
16
+ };
17
+ onChange: (value: {
18
+ x: number;
19
+ y: number;
20
+ }) => void;
21
+ x?: AxisConfig;
22
+ y?: AxisConfig;
23
+ }
24
+ /** Two-axis numeric input for editing a 2D point. */
25
+ export declare class GuiPoint2d extends LabeledControl {
26
+ private readonly _config;
27
+ private readonly _inputX;
28
+ private readonly _inputY;
29
+ /** builds an X and a Y labeled input side by side, each wired to update its axis. */
30
+ constructor(config: GuiPoint2dConfig);
31
+ /** parses the edited axis, clamps to its constraints, and emits the combined point; ignores non-numeric input. */
32
+ private handleChange;
33
+ /** clamps a value to the axis's optional min and max, returning it unchanged when no constraints are set. */
34
+ private clampAxis;
35
+ private _lastX;
36
+ private _lastY;
37
+ /** syncs both axis inputs from the current value when it changed. */
38
+ refresh(): void;
39
+ /** applies base theme then styles the two axis inputs. */
40
+ applyTheme(theme: Theme): void;
41
+ /** the configuration this control was built from. */
42
+ get config(): GuiPoint2dConfig;
43
+ }
@@ -0,0 +1,48 @@
1
+ import { LabeledControl } from './labeled-control';
2
+ import { Theme } from '../core/theme';
3
+ /** Per-axis numeric constraints for a point control. */
4
+ export interface AxisConfig {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ }
9
+ /** Configuration for a 3D point editor control. */
10
+ export interface GuiPoint3dConfig {
11
+ key: string;
12
+ label: string;
13
+ value: () => {
14
+ x: number;
15
+ y: number;
16
+ z: number;
17
+ };
18
+ onChange: (value: {
19
+ x: number;
20
+ y: number;
21
+ z: number;
22
+ }) => void;
23
+ x?: AxisConfig;
24
+ y?: AxisConfig;
25
+ z?: AxisConfig;
26
+ }
27
+ /** Three-axis numeric input for editing a 3D point. */
28
+ export declare class GuiPoint3d extends LabeledControl {
29
+ private readonly _config;
30
+ private readonly _inputX;
31
+ private readonly _inputY;
32
+ private readonly _inputZ;
33
+ /** builds X, Y, and Z labeled inputs side by side, each wired to update its axis. */
34
+ constructor(config: GuiPoint3dConfig);
35
+ /** parses the edited axis, clamps to its constraints, and emits the combined point; ignores non-numeric input. */
36
+ private handleChange;
37
+ /** clamps a value to the axis's optional min and max, returning it unchanged when no constraints are set. */
38
+ private clampAxis;
39
+ private _lastX;
40
+ private _lastY;
41
+ private _lastZ;
42
+ /** syncs the three axis inputs from the current value if any axis changed. */
43
+ refresh(): void;
44
+ /** applies theme colors and border radius to the axis inputs. */
45
+ applyTheme(theme: Theme): void;
46
+ /** returns the control's configuration. */
47
+ get config(): GuiPoint3dConfig;
48
+ }
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../core/layouts/stack-layout.cjs"),t=require("../core/widget.cjs"),i=require("../defaults.cjs"),r=require("../widgets/box.cjs"),s=require("../widgets/label.cjs");class o extends t.Widget{_config;_rows=[];_itemOrder;_dragging=!1;_dragIndex=-1;_dragPointerId=-1;_insertIndex=-1;constructor(t){super(),this._config=t,this._itemOrder=t.items.map(e=>e.id),this.sizingX="grow",this.sizingY="fit",this.layout=new e.StackLayout("vertical",2,"start","stretch"),this.buildRows()}buildRows(){this.removeAllChildren(),this._rows=[];for(let t=0;t<this._config.items.length;t++){const o=this.findItemById(this._itemOrder[t]);if(!o)continue;const n=new r.Box;n.sizingX="grow",n.sizingY="fixed",n.preferredHeight=i.DEFAULTS.reorderRowHeight,n.bgColor=i.DEFAULTS.reorderRowBgColor,n.borderRadius=i.DEFAULTS.reorderRowBorderRadius,n.layout=new e.StackLayout("horizontal",6,"start","stretch"),n.setPadding(0,8,0,8);const d=new s.Label;d.text="::",d.color=i.DEFAULTS.reorderHandleColor,d.sizingX="fixed",d.sizingY="grow",d.preferredWidth=i.DEFAULTS.reorderHandleWidth,d.hAlign="center",d.vAlign="middle";const h=new s.Label;h.text=o.label,h.sizingX="grow",h.sizingY="grow",h.flexGrow=1,h.vAlign="middle",h.color=i.DEFAULTS.reorderItemLabelColor,n.addChild(d),n.addChild(h),this._rows.push(n),this.addChild(n)}}findItemById(e){const t=this._config.items;for(let i=0;i<t.length;i++)if(t[i].id===e)return t[i]}onPointerDown(e,r,s,o){if(0!==o)return!1;if(r>i.DEFAULTS.reorderHandleWidth+16)return!1;for(let i=0;i<this._rows.length;i++){const r=this._rows[i].geometry[t.SLOT_Y],o=this._rows[i].geometry[t.SLOT_HEIGHT];if(s>=r&&s<r+o)return this._dragging=!0,this._dragIndex=i,this._dragPointerId=e,this._insertIndex=i,this.markDirty(),!0}return!1}onPointerMove(e,i,r){if(!this._dragging||e!==this._dragPointerId)return;let s=0;for(let e=0;e<this._rows.length;e++)r>this._rows[e].geometry[t.SLOT_Y]+this._rows[e].geometry[t.SLOT_HEIGHT]/2&&(s=e+1);s=Math.max(0,Math.min(this._rows.length,s)),s!==this._insertIndex&&(this._insertIndex=s,this.markDirty())}onPointerUp(e,t,i,r){if(this._dragging&&e===this._dragPointerId){if(this._dragging=!1,this._dragPointerId=-1,this._insertIndex!==this._dragIndex&&this._insertIndex!==this._dragIndex+1){const e=this._itemOrder[this._dragIndex],t=[...this._itemOrder];t.splice(this._dragIndex,1);const i=this._insertIndex>this._dragIndex?this._insertIndex-1:this._insertIndex;t.splice(i,0,e),this._itemOrder=t,this._config.onChange(t),this.buildRows()}this._insertIndex=-1,this.markDirty()}}drawSelf(e,r){if(!this._dragging||this._insertIndex<0)return;let s;if(this._insertIndex>=this._rows.length){const e=this._rows[this._rows.length-1];s=e.geometry[t.SLOT_Y]+e.geometry[t.SLOT_HEIGHT]}else s=this._rows[this._insertIndex].geometry[t.SLOT_Y];e.fillRect(0,s-1,this.width,i.DEFAULTS.reorderIndicatorHeight,i.DEFAULTS.reorderIndicatorColor)}applyTheme(e){for(const e of this._rows)e.bgColor=0}get config(){return this._config}}exports.GuiReorderList=o;
@@ -0,0 +1,42 @@
1
+ import { Widget } from '../core/widget';
2
+ import { Renderer } from '../core/renderer';
3
+ import { Theme } from '../core/theme';
4
+ /** A single item within a reorderable list. */
5
+ export interface ReorderItem {
6
+ id: string;
7
+ label: string;
8
+ }
9
+ /** Configuration for a drag-to-reorder list control. */
10
+ export interface GuiReorderListConfig {
11
+ key: string;
12
+ items: ReorderItem[];
13
+ onChange: (newOrder: string[]) => void;
14
+ }
15
+ /** Drag-to-reorder list widget with handle-based pointer interaction. */
16
+ export declare class GuiReorderList extends Widget {
17
+ private readonly _config;
18
+ private _rows;
19
+ private _itemOrder;
20
+ private _dragging;
21
+ private _dragIndex;
22
+ private _dragPointerId;
23
+ private _insertIndex;
24
+ /** seeds the working order from the config items and builds the initial rows. */
25
+ constructor(config: GuiReorderListConfig);
26
+ /** rebuilds the row widgets from the current order, each with a drag handle and item label. */
27
+ private buildRows;
28
+ /** looks up an item by id, or undefined when it is not in the config. */
29
+ private findItemById;
30
+ /** begins a drag when the press lands on a row handle with the primary button. */
31
+ onPointerDown(pointerId: number, x: number, y: number, button: number): boolean;
32
+ /** tracks the pointer to update the insertion indicator while dragging. */
33
+ onPointerMove(pointerId: number, _x: number, y: number): void;
34
+ /** commits the new order on release and notifies the change callback. */
35
+ onPointerUp(pointerId: number, _x: number, _y: number, _button: number): void;
36
+ /** while dragging, draws the horizontal insertion indicator at the target drop position. */
37
+ protected drawSelf(renderer: Renderer, _theme: Theme): void;
38
+ /** resets row backgrounds when the theme changes. */
39
+ applyTheme(_theme: Theme): void;
40
+ /** returns the list configuration. */
41
+ get config(): GuiReorderListConfig;
42
+ }
@@ -0,0 +1 @@
1
+ import{StackLayout as t}from"../core/layouts/stack-layout.mjs";import{Widget as i,SLOT_Y as r,SLOT_HEIGHT as e}from"../core/widget.mjs";import{DEFAULTS as s}from"../defaults.mjs";import{Box as o}from"../widgets/box.mjs";import{Label as n}from"../widgets/label.mjs";class d extends i{_config;_rows=[];_itemOrder;_dragging=!1;_dragIndex=-1;_dragPointerId=-1;_insertIndex=-1;constructor(i){super(),this._config=i,this._itemOrder=i.items.map(t=>t.id),this.sizingX="grow",this.sizingY="fit",this.layout=new t("vertical",2,"start","stretch"),this.buildRows()}buildRows(){this.removeAllChildren(),this._rows=[];for(let i=0;i<this._config.items.length;i++){const r=this.findItemById(this._itemOrder[i]);if(!r)continue;const e=new o;e.sizingX="grow",e.sizingY="fixed",e.preferredHeight=s.reorderRowHeight,e.bgColor=s.reorderRowBgColor,e.borderRadius=s.reorderRowBorderRadius,e.layout=new t("horizontal",6,"start","stretch"),e.setPadding(0,8,0,8);const d=new n;d.text="::",d.color=s.reorderHandleColor,d.sizingX="fixed",d.sizingY="grow",d.preferredWidth=s.reorderHandleWidth,d.hAlign="center",d.vAlign="middle";const h=new n;h.text=r.label,h.sizingX="grow",h.sizingY="grow",h.flexGrow=1,h.vAlign="middle",h.color=s.reorderItemLabelColor,e.addChild(d),e.addChild(h),this._rows.push(e),this.addChild(e)}}findItemById(t){const i=this._config.items;for(let r=0;r<i.length;r++)if(i[r].id===t)return i[r]}onPointerDown(t,i,o,n){if(0!==n)return!1;if(i>s.reorderHandleWidth+16)return!1;for(let i=0;i<this._rows.length;i++){const s=this._rows[i].geometry[r],n=this._rows[i].geometry[e];if(o>=s&&o<s+n)return this._dragging=!0,this._dragIndex=i,this._dragPointerId=t,this._insertIndex=i,this.markDirty(),!0}return!1}onPointerMove(t,i,s){if(!this._dragging||t!==this._dragPointerId)return;let o=0;for(let t=0;t<this._rows.length;t++)s>this._rows[t].geometry[r]+this._rows[t].geometry[e]/2&&(o=t+1);o=Math.max(0,Math.min(this._rows.length,o)),o!==this._insertIndex&&(this._insertIndex=o,this.markDirty())}onPointerUp(t,i,r,e){if(this._dragging&&t===this._dragPointerId){if(this._dragging=!1,this._dragPointerId=-1,this._insertIndex!==this._dragIndex&&this._insertIndex!==this._dragIndex+1){const t=this._itemOrder[this._dragIndex],i=[...this._itemOrder];i.splice(this._dragIndex,1);const r=this._insertIndex>this._dragIndex?this._insertIndex-1:this._insertIndex;i.splice(r,0,t),this._itemOrder=i,this._config.onChange(i),this.buildRows()}this._insertIndex=-1,this.markDirty()}}drawSelf(t,i){if(!this._dragging||this._insertIndex<0)return;let o;if(this._insertIndex>=this._rows.length){const t=this._rows[this._rows.length-1];o=t.geometry[r]+t.geometry[e]}else o=this._rows[this._insertIndex].geometry[r];t.fillRect(0,o-1,this.width,s.reorderIndicatorHeight,s.reorderIndicatorColor)}applyTheme(t){for(const t of this._rows)t.bgColor=0}get config(){return this._config}}export{d as GuiReorderList};
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./labeled-control.cjs"),e=require("../core/layouts/stack-layout.cjs"),o=require("../defaults.cjs"),r=require("../widgets/box.cjs"),i=require("../widgets/button.cjs");class n extends t.LabeledControl{_config;_button;_cachedKeys;_cachedValues;_dropdown=null;_backdrop=null;_open=!1;constructor(t){super(),this._config=t,this.labelText=t.label;const e=Object.keys(t.options);this._cachedKeys=e,this._cachedValues=e.map(e=>t.options[e]),this._button=new i.Button,this._button.sizingX="grow",this._button.sizingY="grow",this._button.flexGrow=1,this._button.text=this.findCurrentLabel(),this._button.onClick=()=>this.toggleDropdown(),this.controlContainer.addChild(this._button)}findCurrentLabel(){const t=this._config.value();for(let e=0;e<this._cachedValues.length;e++)if(this._cachedValues[e]===t)return this._cachedKeys[e];return""}toggleDropdown(){this._open?this.closeDropdown():this.openDropdown()}getSurfaceRoot(){let t=this.parent;for(;t&&t.parent;)t=t.parent;return t}ensureDropdownWidgets(){if(this._dropdown)return;const t=this._cachedKeys,n=this._cachedValues,s=new r.Box;s.absolute=!0,s.anchorTop=0,s.anchorLeft=0,s.anchorRight=0,s.anchorBottom=0,s.bgColor=o.DEFAULTS.backdropColor,s.onPointerDown=()=>(this.closeDropdown(),!0);const h=new r.Box;h.absolute=!0,h.sizingX="fixed",h.sizingY="fixed",h.borderWidth=o.DEFAULTS.selectDropdownBorderWidth,h.layout=new e.StackLayout("vertical",0,"start","stretch");for(let e=0;e<t.length;e++){const o=t[e],r=n[e],s=new i.Button;s.sizingX="grow",s.sizingY="fixed",s.text=o,s.onClick=()=>{this._config.onChange(r),this._button.text=o,this._button.markDirty(),this.closeDropdown()},h.addChild(s)}this._backdrop=s,this._dropdown=h}openDropdown(){if(this._open)return;this._open=!0;const t=this.getSurfaceRoot();if(!t)return;this.ensureDropdownWidgets();const e=this._dropdown,o=this._backdrop,r=this.surface?.getTheme(),i=r?.controlHeight??24;e.anchorLeft=this._button.absoluteX,e.anchorTop=this._button.absoluteY+this._button.height,e.preferredWidth=this._button.width,e.preferredHeight=this._cachedKeys.length*i,e.bgColor=r?.bgPanelRaised??858993663,e.borderColor=r?.borderPanel??1431655935;for(let t=0;t<e.children.length;t++)e.children[t].preferredHeight=i;t.addChild(o),t.addChild(e),t.needsLayout=!0}closeDropdown(){if(!this._open)return;this._open=!1;const t=this.getSurfaceRoot();t&&(this._backdrop&&t.removeChild(this._backdrop),this._dropdown&&t.removeChild(this._dropdown))}refresh(){this._button.text=this.findCurrentLabel(),this._button.markDirty()}applyTheme(t){super.applyTheme(t),this._button.borderRadius=t.borderRadius,this._button.borderColor=t.borderInset,this._button.borderWidth=1}get config(){return this._config}}exports.GuiSelect=n;
@@ -0,0 +1,40 @@
1
+ import { LabeledControl } from './labeled-control';
2
+ import { Theme } from '../core/theme';
3
+ /** Configuration for a dropdown select control. */
4
+ export interface GuiSelectConfig<T> {
5
+ key: string;
6
+ label: string;
7
+ options: Record<string, T>;
8
+ value: () => T;
9
+ onChange: (value: T) => void;
10
+ }
11
+ /** Dropdown select control that shows options in an overlay list. */
12
+ export declare class GuiSelect<T> extends LabeledControl {
13
+ private readonly _config;
14
+ private readonly _button;
15
+ private readonly _cachedKeys;
16
+ private readonly _cachedValues;
17
+ private _dropdown;
18
+ private _backdrop;
19
+ private _open;
20
+ /** caches the option keys and values and builds the button that opens the dropdown. */
21
+ constructor(config: GuiSelectConfig<T>);
22
+ /** option key whose value equals the current bound value, or an empty string when none matches. */
23
+ private findCurrentLabel;
24
+ /** opens the dropdown when closed, closes it when open. */
25
+ private toggleDropdown;
26
+ /** walks up to the topmost ancestor, used as the parent for the dropdown and backdrop so they escape any clipping. */
27
+ private getSurfaceRoot;
28
+ /** lazily builds the backdrop and the dropdown list of option buttons on first open. */
29
+ private ensureDropdownWidgets;
30
+ /** anchors the dropdown below the button, themes it, and attaches it and the backdrop to the root. */
31
+ private openDropdown;
32
+ /** removes the dropdown and backdrop from the root. */
33
+ private closeDropdown;
34
+ /** updates the button label to reflect the current bound value. */
35
+ refresh(): void;
36
+ /** applies theme values to the select button. */
37
+ applyTheme(theme: Theme): void;
38
+ /** the configuration this control was built from. */
39
+ get config(): GuiSelectConfig<T>;
40
+ }
@@ -0,0 +1 @@
1
+ import{LabeledControl as t}from"./labeled-control.mjs";import{StackLayout as o}from"../core/layouts/stack-layout.mjs";import{DEFAULTS as e}from"../defaults.mjs";import{Box as r}from"../widgets/box.mjs";import{Button as i}from"../widgets/button.mjs";class n extends t{_config;_button;_cachedKeys;_cachedValues;_dropdown=null;_backdrop=null;_open=!1;constructor(t){super(),this._config=t,this.labelText=t.label;const o=Object.keys(t.options);this._cachedKeys=o,this._cachedValues=o.map(o=>t.options[o]),this._button=new i,this._button.sizingX="grow",this._button.sizingY="grow",this._button.flexGrow=1,this._button.text=this.findCurrentLabel(),this._button.onClick=()=>this.toggleDropdown(),this.controlContainer.addChild(this._button)}findCurrentLabel(){const t=this._config.value();for(let o=0;o<this._cachedValues.length;o++)if(this._cachedValues[o]===t)return this._cachedKeys[o];return""}toggleDropdown(){this._open?this.closeDropdown():this.openDropdown()}getSurfaceRoot(){let t=this.parent;for(;t&&t.parent;)t=t.parent;return t}ensureDropdownWidgets(){if(this._dropdown)return;const t=this._cachedKeys,n=this._cachedValues,s=new r;s.absolute=!0,s.anchorTop=0,s.anchorLeft=0,s.anchorRight=0,s.anchorBottom=0,s.bgColor=e.backdropColor,s.onPointerDown=()=>(this.closeDropdown(),!0);const h=new r;h.absolute=!0,h.sizingX="fixed",h.sizingY="fixed",h.borderWidth=e.selectDropdownBorderWidth,h.layout=new o("vertical",0,"start","stretch");for(let o=0;o<t.length;o++){const e=t[o],r=n[o],s=new i;s.sizingX="grow",s.sizingY="fixed",s.text=e,s.onClick=()=>{this._config.onChange(r),this._button.text=e,this._button.markDirty(),this.closeDropdown()},h.addChild(s)}this._backdrop=s,this._dropdown=h}openDropdown(){if(this._open)return;this._open=!0;const t=this.getSurfaceRoot();if(!t)return;this.ensureDropdownWidgets();const o=this._dropdown,e=this._backdrop,r=this.surface?.getTheme(),i=r?.controlHeight??24;o.anchorLeft=this._button.absoluteX,o.anchorTop=this._button.absoluteY+this._button.height,o.preferredWidth=this._button.width,o.preferredHeight=this._cachedKeys.length*i,o.bgColor=r?.bgPanelRaised??858993663,o.borderColor=r?.borderPanel??1431655935;for(let t=0;t<o.children.length;t++)o.children[t].preferredHeight=i;t.addChild(e),t.addChild(o),t.needsLayout=!0}closeDropdown(){if(!this._open)return;this._open=!1;const t=this.getSurfaceRoot();t&&(this._backdrop&&t.removeChild(this._backdrop),this._dropdown&&t.removeChild(this._dropdown))}refresh(){this._button.text=this.findCurrentLabel(),this._button.markDirty()}applyTheme(t){super.applyTheme(t),this._button.borderRadius=t.borderRadius,this._button.borderColor=t.borderInset,this._button.borderWidth=1}get config(){return this._config}}export{n as GuiSelect};
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("../core/widget.cjs"),e=require("../defaults.cjs"),s=require("../widgets/separator.cjs");class t extends r.Widget{_separator;constructor(){super(),this.sizingX="grow",this.sizingY="fixed",this.preferredHeight=e.DEFAULTS.separatorHeight,this._separator=new s.Separator,this._separator.sizingX="grow",this._separator.sizingY="grow",this._separator.direction="horizontal",this._separator.color=r.THEME_DEFAULT,this.addChild(this._separator)}applyTheme(r){this._separator.color=r.borderInset,this._separator.thickness=r.separatorThickness}}exports.GuiSeparator=t;
@@ -0,0 +1,10 @@
1
+ import { Widget } from '../core/widget';
2
+ import { Theme } from '../core/theme';
3
+ /** Themed horizontal divider line between controls. */
4
+ export declare class GuiSeparator extends Widget {
5
+ private readonly _separator;
6
+ /** builds a fixed-height row holding a horizontal divider that fills its width. */
7
+ constructor();
8
+ /** applies theme color and thickness to the divider. */
9
+ applyTheme(theme: Theme): void;
10
+ }
@@ -0,0 +1 @@
1
+ import{Widget as r,THEME_DEFAULT as s}from"../core/widget.mjs";import{DEFAULTS as t}from"../defaults.mjs";import{Separator as e}from"../widgets/separator.mjs";class i extends r{_separator;constructor(){super(),this.sizingX="grow",this.sizingY="fixed",this.preferredHeight=t.separatorHeight,this._separator=new e,this._separator.sizingX="grow",this._separator.sizingY="grow",this._separator.direction="horizontal",this._separator.color=s,this.addChild(this._separator)}applyTheme(r){this._separator.color=r.borderInset,this._separator.thickness=r.separatorThickness}}export{i as GuiSeparator};
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./labeled-control.cjs"),t=require("../core/layouts/stack-layout.cjs"),i=require("../defaults.cjs"),s=require("../widgets/number-input.cjs"),n=require("../widgets/slider.cjs");class l extends e.LabeledControl{_slider;_valueInput;_config;_pendingValue;constructor(e){super(),this._config=e;const l=e.step>=1?0:Math.max(0,Math.ceil(-Math.log10(e.step)));this.labelText=e.label,this._pendingValue=e.value(),this.controlContainer.layout=new t.StackLayout("horizontal",i.DEFAULTS.controlInnerGap,"start","stretch"),this._slider=new n.Slider,this._slider.sizingX="grow",this._slider.sizingY="grow",this._slider.flexGrow=2,this._slider.min=e.min,this._slider.max=e.max,this._slider.value=e.value(),this._slider.onChange=t=>{const i=this.snap(t);this._slider.value=i,this._pendingValue=i,this._valueInput.numValue=i,this._valueInput.markDirty(),e.applyOnRelease||e.onChange(i)},e.applyOnRelease&&(this._slider.onRelease=()=>{e.onChange(this._pendingValue)}),this.controlContainer.addChild(this._slider),this._valueInput=new s.NumberInput,this._valueInput.sizingX="grow",this._valueInput.sizingY="grow",this._valueInput.flexGrow=1,this._valueInput.numValue=e.value(),this._valueInput.min=e.min,this._valueInput.max=e.max,this._valueInput.step=e.step,this._valueInput.decimals=l,this._valueInput.updatePointerScale(),this._valueInput.onChange=t=>{this._slider.value=t,this._slider.markDirty(),this._pendingValue=t,e.applyOnRelease||e.onChange(t)},this._valueInput.onCommit=t=>{e.applyOnRelease&&e.onChange(t)},this.controlContainer.addChild(this._valueInput)}get config(){return this._config}applyTheme(e){super.applyTheme(e);const t=e;this._valueInput.textColor=e.textValue,this._slider.fillColor=t.bgAccent}refresh(){const e=this._config.value(),t=this.snap(e);t!==this._slider.value&&(this._slider.value=t,this._pendingValue=t,this._valueInput.numValue=t,this.markDirty())}snap(e){const t=this._config.step;return t<=0?e:Math.round(e/t)*t}}exports.GuiSlider=l;
@@ -0,0 +1,34 @@
1
+ import { LabeledControl } from './labeled-control';
2
+ import { Theme } from '../core/theme';
3
+ /** Configuration for a numeric slider control. */
4
+ export interface GuiSliderConfig {
5
+ key: string;
6
+ label: string;
7
+ min: number;
8
+ max: number;
9
+ step: number;
10
+ value: () => number;
11
+ onChange: (value: number) => void;
12
+ applyOnRelease?: boolean;
13
+ }
14
+ /** Slider with adjacent numeric input, supporting step-snapping and optional deferred commit. */
15
+ export declare class GuiSlider extends LabeledControl {
16
+ private readonly _slider;
17
+ private readonly _valueInput;
18
+ private readonly _config;
19
+ private _pendingValue;
20
+ /**
21
+ * Builds a slider paired with a numeric input over the same value. Both push
22
+ * changes to each other and either commit immediately or defer to release when
23
+ * applyOnRelease is set. Display precision is derived from the step size.
24
+ */
25
+ constructor(config: GuiSliderConfig);
26
+ /** the configuration this control was built from. */
27
+ get config(): GuiSliderConfig;
28
+ /** applies theme values to the slider and numeric input. */
29
+ applyTheme(theme: Theme): void;
30
+ /** syncs the slider and input to the current bound value if it changed. */
31
+ refresh(): void;
32
+ /** rounds a value to the nearest multiple of the step, or returns it unchanged when step is not positive. */
33
+ private snap;
34
+ }
@@ -0,0 +1 @@
1
+ import{LabeledControl as e}from"./labeled-control.mjs";import{StackLayout as t}from"../core/layouts/stack-layout.mjs";import{DEFAULTS as i}from"../defaults.mjs";import{NumberInput as s}from"../widgets/number-input.mjs";import{Slider as n}from"../widgets/slider.mjs";class l extends e{_slider;_valueInput;_config;_pendingValue;constructor(e){super(),this._config=e;const l=e.step>=1?0:Math.max(0,Math.ceil(-Math.log10(e.step)));this.labelText=e.label,this._pendingValue=e.value(),this.controlContainer.layout=new t("horizontal",i.controlInnerGap,"start","stretch"),this._slider=new n,this._slider.sizingX="grow",this._slider.sizingY="grow",this._slider.flexGrow=2,this._slider.min=e.min,this._slider.max=e.max,this._slider.value=e.value(),this._slider.onChange=t=>{const i=this.snap(t);this._slider.value=i,this._pendingValue=i,this._valueInput.numValue=i,this._valueInput.markDirty(),e.applyOnRelease||e.onChange(i)},e.applyOnRelease&&(this._slider.onRelease=()=>{e.onChange(this._pendingValue)}),this.controlContainer.addChild(this._slider),this._valueInput=new s,this._valueInput.sizingX="grow",this._valueInput.sizingY="grow",this._valueInput.flexGrow=1,this._valueInput.numValue=e.value(),this._valueInput.min=e.min,this._valueInput.max=e.max,this._valueInput.step=e.step,this._valueInput.decimals=l,this._valueInput.updatePointerScale(),this._valueInput.onChange=t=>{this._slider.value=t,this._slider.markDirty(),this._pendingValue=t,e.applyOnRelease||e.onChange(t)},this._valueInput.onCommit=t=>{e.applyOnRelease&&e.onChange(t)},this.controlContainer.addChild(this._valueInput)}get config(){return this._config}applyTheme(e){super.applyTheme(e);const t=e;this._valueInput.textColor=e.textValue,this._slider.fillColor=t.bgAccent}refresh(){const e=this._config.value(),t=this.snap(e);t!==this._slider.value&&(this._slider.value=t,this._pendingValue=t,this._valueInput.numValue=t,this.markDirty())}snap(e){const t=this._config.step;return t<=0?e:Math.round(e/t)*t}}export{l as GuiSlider};
@@ -0,0 +1,26 @@
1
+ import { Widget } from '../core/widget';
2
+ import { Box } from '../widgets/box';
3
+ import { TabBar } from '../widgets/tab-bar';
4
+ import { Theme } from '../core/theme';
5
+ /** Container with a tab bar and switchable page widgets. */
6
+ export declare class GuiTabContainer extends Widget {
7
+ private readonly _tabBar;
8
+ private readonly _pages;
9
+ private _activeIndex;
10
+ /** builds the tab bar and wires tab selection to switch the active page. */
11
+ constructor();
12
+ /** adds a new page with the given tab title and returns its content box. */
13
+ addPage(title: string): Box;
14
+ /** returns the number of pages. */
15
+ getPageCount(): number;
16
+ /** switches the active page to the given index, ignoring out-of-range values. */
17
+ setActivePage(index: number): void;
18
+ /** returns the index of the active page. */
19
+ getActivePage(): number;
20
+ /** returns the content box for the page at the given index, or null if out of range. */
21
+ getPage(index: number): Box | null;
22
+ /** the underlying tab bar widget. */
23
+ get tabBar(): TabBar;
24
+ /** applies theme values to the tab bar. */
25
+ applyTheme(theme: Theme): void;
26
+ }
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../core/layouts/stack-layout.cjs"),t=require("../core/widget.cjs"),i=require("../defaults.cjs"),s=require("../widgets/label.cjs");class l extends t.Widget{_config;_label;constructor(t){super(),this._config=t;const l=t.rows??i.DEFAULTS.textLogDefaultRows;this.sizingX="grow",this.sizingY="fixed",this.preferredHeight=l*i.DEFAULTS.textLogDefaultLineHeight,this.layout=new e.StackLayout("vertical",0,"start","stretch"),this._label=new s.Label,this._label.sizingX="grow",this._label.sizingY="grow",this._label.flexGrow=1,this._label.vAlign="top",this._label.color=i.DEFAULTS.textLogLabelColor,this._label.text=t.getValue(),this.addChild(this._label)}drawSelf(e,t){e.fillRoundedRect(0,0,this.width,this.height,i.DEFAULTS.textLogBorderRadius,t.bgPanelInset)}refresh(){const e=this._config.getValue();this._label.text!==e&&(this._label.text=e,this.markDirty())}applyTheme(e){const t=this._config.rows??i.DEFAULTS.textLogDefaultRows;this.preferredHeight=t*i.DEFAULTS.textLogThemeLineHeight+i.DEFAULTS.textLogThemePadding}get config(){return this._config}}exports.GuiTextLog=l;
@@ -0,0 +1,24 @@
1
+ import { Widget } from '../core/widget';
2
+ import { Renderer } from '../core/renderer';
3
+ import { Theme } from '../core/theme';
4
+ /** Configuration for a multi-line read-only text log widget. */
5
+ export interface GuiTextLogConfig {
6
+ key: string;
7
+ rows?: number;
8
+ getValue: () => string;
9
+ }
10
+ /** Multi-line read-only text display, refreshed each frame. */
11
+ export declare class GuiTextLog extends Widget {
12
+ private readonly _config;
13
+ private readonly _label;
14
+ /** sizes the log to the requested row count and adds a top-aligned label holding the initial value. */
15
+ constructor(config: GuiTextLogConfig);
16
+ /** paints the inset rounded background behind the log text. */
17
+ protected drawSelf(renderer: Renderer, theme: Theme): void;
18
+ /** pulls the latest value and redraws when the text changed. */
19
+ refresh(): void;
20
+ /** recomputes preferred height from row count and theme metrics. */
21
+ applyTheme(_theme: Theme): void;
22
+ /** the configuration this log was built from. */
23
+ get config(): GuiTextLogConfig;
24
+ }
@@ -0,0 +1 @@
1
+ import{StackLayout as t}from"../core/layouts/stack-layout.mjs";import{Widget as e}from"../core/widget.mjs";import{DEFAULTS as i}from"../defaults.mjs";import{Label as s}from"../widgets/label.mjs";class o extends e{_config;_label;constructor(e){super(),this._config=e;const o=e.rows??i.textLogDefaultRows;this.sizingX="grow",this.sizingY="fixed",this.preferredHeight=o*i.textLogDefaultLineHeight,this.layout=new t("vertical",0,"start","stretch"),this._label=new s,this._label.sizingX="grow",this._label.sizingY="grow",this._label.flexGrow=1,this._label.vAlign="top",this._label.color=i.textLogLabelColor,this._label.text=e.getValue(),this.addChild(this._label)}drawSelf(t,e){t.fillRoundedRect(0,0,this.width,this.height,i.textLogBorderRadius,e.bgPanelInset)}refresh(){const t=this._config.getValue();this._label.text!==t&&(this._label.text=t,this.markDirty())}applyTheme(t){const e=this._config.rows??i.textLogDefaultRows;this.preferredHeight=e*i.textLogThemeLineHeight+i.textLogThemePadding}get config(){return this._config}}export{o as GuiTextLog};
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./labeled-control.cjs"),i=require("../defaults.cjs"),e=require("../widgets/text-input.cjs");class n extends t.LabeledControl{_input;_config;constructor(t){super(),this._config=t,this.labelText=t.label,this._input=new e.TextInput,this._input.sizingX="grow",this._input.sizingY="grow",this._input.flexGrow=1,this._input.text=t.value(),this._input.onChange=i=>{t.onChange(i)},this.controlContainer.addChild(this._input)}get config(){return this._config}applyTheme(t){super.applyTheme(t),this._input.bgColor=t.bgInput,this._input.textColor=t.textValue,this._input.borderRadius=i.DEFAULTS.pointInputBorderRadius}refresh(){if(this._input.focused)return;const t=this._config.value();this._input.text!==t&&(this._input.text=t,this._input.cursorPos=t.length,this.markDirty())}}exports.GuiText=n;
@@ -0,0 +1,22 @@
1
+ import { LabeledControl } from './labeled-control';
2
+ import { Theme } from '../core/theme';
3
+ /** Configuration for a single-line text input control. */
4
+ export interface GuiTextConfig {
5
+ key: string;
6
+ label: string;
7
+ value: () => string;
8
+ onChange: (value: string) => void;
9
+ }
10
+ /** Single-line text input control with label. */
11
+ export declare class GuiText extends LabeledControl {
12
+ private readonly _input;
13
+ private readonly _config;
14
+ /** builds the labeled text input and wires its change callback to the config. */
15
+ constructor(config: GuiTextConfig);
16
+ /** the configuration this control was built from. */
17
+ get config(): GuiTextConfig;
18
+ /** applies base theme spacing then the input's background, text color, and corner radius. */
19
+ applyTheme(theme: Theme): void;
20
+ /** syncs the input to the bound value, skipping the update while focused so typing is not clobbered. */
21
+ refresh(): void;
22
+ }
@@ -0,0 +1 @@
1
+ import{LabeledControl as t}from"./labeled-control.mjs";import{DEFAULTS as i}from"../defaults.mjs";import{TextInput as n}from"../widgets/text-input.mjs";class s extends t{_input;_config;constructor(t){super(),this._config=t,this.labelText=t.label,this._input=new n,this._input.sizingX="grow",this._input.sizingY="grow",this._input.flexGrow=1,this._input.text=t.value(),this._input.onChange=i=>{t.onChange(i)},this.controlContainer.addChild(this._input)}get config(){return this._config}applyTheme(t){super.applyTheme(t),this._input.bgColor=t.bgInput,this._input.textColor=t.textValue,this._input.borderRadius=i.pointInputBorderRadius}refresh(){if(this._input.focused)return;const t=this._config.value();this._input.text!==t&&(this._input.text=t,this._input.cursorPos=t.length,this.markDirty())}}export{s as GuiText};
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./labeled-control.cjs"),h=require("../core/widget.cjs"),t=require("../defaults.cjs");class c extends h.Widget{checked=!1;boxColor=h.THEME_DEFAULT;checkColor=h.THEME_DEFAULT;onChange=null;constructor(){super(),this.sizingX="fixed",this.sizingY="fixed",this.preferredWidth=t.DEFAULTS.checkboxSize,this.preferredHeight=t.DEFAULTS.checkboxSize,this.focusable=!0}drawSelf(e,c){const i=this.width,o=this.height,s=Math.min(t.DEFAULTS.checkboxBorderRadius,i/4),r=this.boxColor===h.THEME_DEFAULT?t.DEFAULTS.checkboxDefaultBorderColor:this.boxColor;if(e.fillRoundedRect(0,0,i,o,s,c.bgInput),e.strokeRoundedRect(0,0,i,o,s,r,1),this.checked){const s=this.checkColor===h.THEME_DEFAULT?c.textPrimary:this.checkColor;e.pushAlpha(.9),e.drawLine(.2*i|0,.5*o|0,.42*i|0,.72*o|0,s,t.DEFAULTS.checkboxCheckLineWidth),e.drawLine(.42*i|0,.72*o|0,.8*i|0,.28*o|0,s,t.DEFAULTS.checkboxCheckLineWidth),e.popAlpha()}}onPointerDown(e,h,t,c){return this.checked=!this.checked,this.onChange&&this.onChange(this.checked),this.markDirty(),!0}onKeyDown(e,h,t,c,i){return"Space"===h||"Enter"===h?(this.checked=!this.checked,this.onChange&&this.onChange(this.checked),this.markDirty(),!0):super.onKeyDown(e,h,t,c,i)}}class i extends e.LabeledControl{_checkbox;_config;constructor(e){super(),this._config=e,this.labelText=e.label,this._checkbox=new c,this._checkbox.checked=e.value(),this._checkbox.alignSelf="center",this._checkbox.onChange=h=>{e.onChange(h)},this.controlContainer.addChild(this._checkbox)}get config(){return this._config}refresh(){const e=this._config.value();this._checkbox.checked!==e&&(this._checkbox.checked=e,this.markDirty())}}exports.GuiToggle=i;
@@ -0,0 +1,19 @@
1
+ import { LabeledControl } from './labeled-control';
2
+ /** Configuration for a boolean toggle control. */
3
+ export interface GuiToggleConfig {
4
+ key: string;
5
+ label: string;
6
+ value: () => boolean;
7
+ onChange: (value: boolean) => void;
8
+ }
9
+ /** Boolean toggle control displayed as a labeled checkbox. */
10
+ export declare class GuiToggle extends LabeledControl {
11
+ private readonly _checkbox;
12
+ private readonly _config;
13
+ /** builds the labeled checkbox and wires its change callback to the config. */
14
+ constructor(config: GuiToggleConfig);
15
+ /** the configuration this control was built from. */
16
+ get config(): GuiToggleConfig;
17
+ /** re-reads the bound value and updates the checkbox state. */
18
+ refresh(): void;
19
+ }
@@ -0,0 +1 @@
1
+ import{LabeledControl as e}from"./labeled-control.mjs";import{Widget as h,THEME_DEFAULT as c}from"../core/widget.mjs";import{DEFAULTS as t}from"../defaults.mjs";class o extends h{checked=!1;boxColor=c;checkColor=c;onChange=null;constructor(){super(),this.sizingX="fixed",this.sizingY="fixed",this.preferredWidth=t.checkboxSize,this.preferredHeight=t.checkboxSize,this.focusable=!0}drawSelf(e,h){const o=this.width,i=this.height,s=Math.min(t.checkboxBorderRadius,o/4),r=this.boxColor===c?t.checkboxDefaultBorderColor:this.boxColor;if(e.fillRoundedRect(0,0,o,i,s,h.bgInput),e.strokeRoundedRect(0,0,o,i,s,r,1),this.checked){const s=this.checkColor===c?h.textPrimary:this.checkColor;e.pushAlpha(.9),e.drawLine(.2*o|0,.5*i|0,.42*o|0,.72*i|0,s,t.checkboxCheckLineWidth),e.drawLine(.42*o|0,.72*i|0,.8*o|0,.28*i|0,s,t.checkboxCheckLineWidth),e.popAlpha()}}onPointerDown(e,h,c,t){return this.checked=!this.checked,this.onChange&&this.onChange(this.checked),this.markDirty(),!0}onKeyDown(e,h,c,t,o){return"Space"===h||"Enter"===h?(this.checked=!this.checked,this.onChange&&this.onChange(this.checked),this.markDirty(),!0):super.onKeyDown(e,h,c,t,o)}}class i extends e{_checkbox;_config;constructor(e){super(),this._config=e,this.labelText=e.label,this._checkbox=new o,this._checkbox.checked=e.value(),this._checkbox.alignSelf="center",this._checkbox.onChange=h=>{e.onChange(h)},this.controlContainer.addChild(this._checkbox)}get config(){return this._config}refresh(){const e=this._config.value();this._checkbox.checked!==e&&(this._checkbox.checked=e,this.markDirty())}}export{i as GuiToggle};
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("../core/layouts/stack-layout.cjs"),e=require("../core/widget.cjs"),i=require("../defaults.cjs"),l=require("../widgets/box.cjs"),o=require("../widgets/label.cjs"),r=i.DEFAULTS.valueColumnWidth;class s extends e.Widget{_label;_controlContainer;constructor(){super(),this.sizingX="grow",this.sizingY="fixed",this.layout=new t.StackLayout("horizontal",i.DEFAULTS.labeledControlGap,"start","stretch"),this._label=new o.Label,this._label.sizingX="grow",this._label.sizingY="grow",this._label.flexGrow=1,this._label.overflow="ellipsis",this._label.vAlign="middle",this._label.color=e.THEME_DEFAULT,this.addChild(this._label),this._controlContainer=new l.Box,this._controlContainer.sizingX="fixed",this._controlContainer.preferredWidth=r,this._controlContainer.sizingY="grow",this._controlContainer.flexShrink=0,this._controlContainer.layout=new t.StackLayout("horizontal",i.DEFAULTS.controlInnerGap,"start","stretch"),this.addChild(this._controlContainer)}get controlContainer(){return this._controlContainer}set labelText(t){this._label.text=t,this.markDirty()}get labelText(){return this._label.text}applyTheme(t){const e=t;this.preferredHeight=e.controlHeight,this._label.textOffsetY=e.textOffsetY??0,e.textLabel&&(this._label.color=e.textLabel)}}exports.LabeledControl=s;
@@ -0,0 +1,21 @@
1
+ import { Widget } from '../core/widget';
2
+ import { Box } from '../widgets/box';
3
+ import { Theme } from '../core/theme';
4
+ /**
5
+ * Base class for controls that display a label on the left and a value/input
6
+ * region on the right, using a fixed-width value column.
7
+ */
8
+ export declare class LabeledControl extends Widget {
9
+ private readonly _label;
10
+ private readonly _controlContainer;
11
+ /** builds the ellipsized label and a fixed-width value column that subclasses fill with their control. */
12
+ constructor();
13
+ /** the fixed-width right-hand column that subclasses add their control widgets to. */
14
+ get controlContainer(): Box;
15
+ /** sets the label text and marks the control for repaint. */
16
+ set labelText(value: string);
17
+ /** the current label text. */
18
+ get labelText(): string;
19
+ /** applies theme height, label offset, and label color. */
20
+ applyTheme(theme: Theme): void;
21
+ }
@@ -0,0 +1 @@
1
+ import{StackLayout as t}from"../core/layouts/stack-layout.mjs";import{Widget as e,THEME_DEFAULT as o}from"../core/widget.mjs";import{DEFAULTS as i}from"../defaults.mjs";import{Box as l}from"../widgets/box.mjs";import{Label as r}from"../widgets/label.mjs";const s=i.valueColumnWidth;class n extends e{_label;_controlContainer;constructor(){super(),this.sizingX="grow",this.sizingY="fixed",this.layout=new t("horizontal",i.labeledControlGap,"start","stretch"),this._label=new r,this._label.sizingX="grow",this._label.sizingY="grow",this._label.flexGrow=1,this._label.overflow="ellipsis",this._label.vAlign="middle",this._label.color=o,this.addChild(this._label),this._controlContainer=new l,this._controlContainer.sizingX="fixed",this._controlContainer.preferredWidth=s,this._controlContainer.sizingY="grow",this._controlContainer.flexShrink=0,this._controlContainer.layout=new t("horizontal",i.controlInnerGap,"start","stretch"),this.addChild(this._controlContainer)}get controlContainer(){return this._controlContainer}set labelText(t){this._label.text=t,this.markDirty()}get labelText(){return this._label.text}applyTheme(t){const e=t;this.preferredHeight=e.controlHeight,this._label.textOffsetY=e.textOffsetY??0,e.textLabel&&(this._label.color=e.textLabel)}}export{n as LabeledControl};
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./types.cjs"),t=require("./utils/activity.cjs"),r=require("./utils/persistence.cjs"),o=require("./Inspect.cjs"),s=require("./InspectRegistry.cjs"),i=require("./core/widget.cjs"),p=require("./core/layouts/stack-layout.cjs"),c=require("./widgets/canvas-widget.cjs"),n=require("./widgets/box.cjs"),x=require("./widgets/label.cjs"),a=require("./widgets/button.cjs"),u=require("./widgets/slider.cjs");exports.button=e.button,exports.buttonRow=e.buttonRow,Object.defineProperty(exports,"color",{enumerable:!0,get:()=>e.color}),exports.customControl=e.customControl,exports.matrixGrid=e.matrixGrid,exports.monitor=e.monitor,Object.defineProperty(exports,"point2d",{enumerable:!0,get:()=>e.point2d}),Object.defineProperty(exports,"point3d",{enumerable:!0,get:()=>e.point3d}),exports.reorderList=e.reorderList,Object.defineProperty(exports,"select",{enumerable:!0,get:()=>e.select}),exports.separator=e.separator,Object.defineProperty(exports,"slider",{enumerable:!0,get:()=>e.slider}),exports.subfolder=e.subfolder,Object.defineProperty(exports,"text",{enumerable:!0,get:()=>e.text}),exports.textLog=e.textLog,Object.defineProperty(exports,"toggle",{enumerable:!0,get:()=>e.toggle}),exports.getGuiActive=t.getGuiActive,exports.setGuiActive=t.setGuiActive,exports.clearConfig=r.clearConfig,exports.copyConfigToClipboard=r.copyConfigToClipboard,exports.exportStateAsJSON=r.exportStateAsJSON,exports.importStateFromJSON=r.importStateFromJSON,exports.loadConfig=r.loadConfig,exports.saveConfig=r.saveConfig,exports.Inspect=o.Inspect,exports.InspectRegistry=s.InspectRegistry,exports.THEME_DEFAULT=i.THEME_DEFAULT,exports.Widget=i.Widget,exports.StackLayout=p.StackLayout,exports.CanvasWidget=c.CanvasWidget,exports.Box=n.Box,exports.Label=x.Label,exports.Button=a.Button,exports.Slider=u.Slider;
@@ -0,0 +1,19 @@
1
+ export type { SliderConfig, ToggleConfig, SelectConfig, ButtonConfig, ButtonRowConfig, SubfolderConfig, ReorderListConfig, TextLogConfig, MonitorConfig, ColorConfig, ColorDisplayFormat, TextConfig, Point2DConfig, Point3DConfig, SeparatorConfig, MatrixGridConfig, ControlConfig, CustomControlBuilder, FolderConfig, TabPageConfig, BaseMountOptions, MountOptions, KeysOfType } from './types';
2
+ export { slider, toggle, select, button, buttonRow, monitor, color, text, point2d, point3d, separator, matrixGrid, subfolder, reorderList, textLog, customControl } from './types';
3
+ export { setGuiActive, getGuiActive } from './utils/activity';
4
+ export { exportStateAsJSON, importStateFromJSON, saveConfig, loadConfig, clearConfig, copyConfigToClipboard } from './utils/persistence';
5
+ export { Inspect } from './Inspect';
6
+ export { InspectRegistry } from './InspectRegistry';
7
+ export type { InspectTheme } from './themes/inspect-theme';
8
+ export { Widget, THEME_DEFAULT } from './core/widget';
9
+ export type { HAlign, VAlign, SizingType, CrossAlign } from './core/widget';
10
+ export type { Renderer } from './core/renderer';
11
+ export type { Theme } from './core/theme';
12
+ export type { FontAtlas } from './core/atlas';
13
+ export { StackLayout } from './core/layouts/stack-layout';
14
+ export { CanvasWidget } from './widgets/canvas-widget';
15
+ export type { CustomDrawFn } from './widgets/canvas-widget';
16
+ export { Box } from './widgets/box';
17
+ export { Label } from './widgets/label';
18
+ export { Button } from './widgets/button';
19
+ export { Slider } from './widgets/slider';
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{button as m,buttonRow as t,color as o,customControl as r,matrixGrid as s,monitor as i,point2d as e,point3d as p,reorderList as f,select as j,separator as c,slider as d,subfolder as g,text as a,textLog as l,toggle as w}from"./types.mjs";import{getGuiActive as n,setGuiActive as u}from"./utils/activity.mjs";import{clearConfig as y,copyConfigToClipboard as b,exportStateAsJSON as v,importStateFromJSON as x,loadConfig as I,saveConfig as k}from"./utils/persistence.mjs";import{Inspect as R}from"./Inspect.mjs";import{InspectRegistry as h}from"./InspectRegistry.mjs";import{THEME_DEFAULT as q,Widget as z}from"./core/widget.mjs";import{StackLayout as A}from"./core/layouts/stack-layout.mjs";import{CanvasWidget as B}from"./widgets/canvas-widget.mjs";import{Box as C}from"./widgets/box.mjs";import{Label as D}from"./widgets/label.mjs";import{Button as E}from"./widgets/button.mjs";import{Slider as F}from"./widgets/slider.mjs";export{C as Box,E as Button,B as CanvasWidget,R as Inspect,h as InspectRegistry,D as Label,F as Slider,A as StackLayout,q as THEME_DEFAULT,z as Widget,m as button,t as buttonRow,y as clearConfig,o as color,b as copyConfigToClipboard,r as customControl,v as exportStateAsJSON,n as getGuiActive,x as importStateFromJSON,I as loadConfig,s as matrixGrid,i as monitor,e as point2d,p as point3d,f as reorderList,k as saveConfig,j as select,c as separator,u as setGuiActive,d as slider,g as subfolder,a as text,l as textLog,w as toggle};
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=2e3;exports.InspectDebugger=class{surface;overlay;overlayCtx;borderClearTimer=0;labelClearTimer=0;_boundClearBorder;_boundClearLabel;logEntries=new Array(e).fill("");_logHead=0;_logCount=0;_firstPaint=!0;constructor(e,t){this.surface=e,this.overlay=document.createElement("canvas"),this.overlay.style.position="fixed",this.overlay.style.top=t.style.top,this.overlay.style.width=t.style.width,this.overlay.style.height=t.style.height,this.overlay.style.pointerEvents="none",this.overlay.style.zIndex="10000000",t.style.right?this.overlay.style.right=t.style.right:this.overlay.style.left=t.style.left,t.style.borderRadius&&(this.overlay.style.borderRadius=t.style.borderRadius),t.parentElement?.appendChild(this.overlay);const i=this.overlay.getContext("2d");if(!i)throw new Error("Failed to create debug overlay context");this.overlayCtx=i,this._boundClearBorder=()=>{this.borderClearTimer=0,this.overlayCtx.clearRect(0,0,this.overlay.width,this.overlay.height-18)},this._boundClearLabel=()=>{this.labelClearTimer=0;const e=this.overlayCtx,t=this.overlay.height-18;e.clearRect(0,t,this.overlay.width,18)},e.onAfterFlush=e=>this.onFlush(e)}formatEntry(e){const t=performance.now()/1e3|0,i=t%60,r=(t/60|0)%60,l=`${(t/3600|0)%24}:${r<10?"0":""}${r}:${i<10?"0":""}${i}`;if(e.isFullRepaint)return this._firstPaint?(this._firstPaint=!1,`[${l}] FIRST PAINT (FULL) ${e.flushTimeMs.toFixed(2)}ms 100%`):`[${l}] FULL ${e.flushTimeMs.toFixed(2)}ms 100%`;let s=0;for(let t=0;t<e.dirtyRectCount;t++){const i=e.dirtyRects[t];s+=i.width*i.height}const o=(s/(e.canvasWidth*e.canvasHeight)*100).toFixed(1);return`[${l}] ${e.dirtyRectCount}r ${e.flushTimeMs.toFixed(2)}ms ${o}%`}onFlush(t){this.overlay.width===t.canvasWidth&&this.overlay.height===t.canvasHeight||(this.overlay.width=t.canvasWidth,this.overlay.height=t.canvasHeight);const i=this._firstPaint,r=this.formatEntry(t);this.logEntries[this._logHead]=r,this._logHead=(this._logHead+1)%e,this._logCount<e&&this._logCount++;const l=this.overlayCtx;if(l.clearRect(0,0,t.canvasWidth,t.canvasHeight),!t.isFullRepaint){l.strokeStyle="rgba(255, 0, 0, 0.7)",l.lineWidth=2;for(let e=0;e<t.dirtyRectCount;e++){const i=t.dirtyRects[e];l.strokeRect(1+(0|i.x),1+(0|i.y),(0|i.width)-2,(0|i.height)-2)}}const s=t.canvasHeight-18;l.fillStyle="rgba(0, 0, 0, 0.7)",l.fillRect(0,s,t.canvasWidth,18),l.fillStyle=i?"#ffffff":t.isFullRepaint?"#ff4444":"#44ff44",l.font="11px monospace",l.fillText(r,8,s+13),this.borderClearTimer&&clearTimeout(this.borderClearTimer),this.borderClearTimer=window.setTimeout(this._boundClearBorder,60),this.labelClearTimer&&clearTimeout(this.labelClearTimer),this.labelClearTimer=window.setTimeout(this._boundClearLabel,5e3)}dispose(){this.surface.onAfterFlush=null,this.borderClearTimer&&clearTimeout(this.borderClearTimer),this.labelClearTimer&&clearTimeout(this.labelClearTimer),this.overlay.parentNode&&this.overlay.parentNode.removeChild(this.overlay)}};
@@ -0,0 +1,30 @@
1
+ import { Surface } from '../core/surface';
2
+ /**
3
+ * Debug overlay that visualizes dirty rects and flush timing on top of the panel canvas.
4
+ * Renders red stroke outlines around partial repaints and a status bar showing timing.
5
+ */
6
+ export declare class InspectDebugger {
7
+ private surface;
8
+ private overlay;
9
+ private overlayCtx;
10
+ private borderClearTimer;
11
+ private labelClearTimer;
12
+ private readonly _boundClearBorder;
13
+ private readonly _boundClearLabel;
14
+ private logEntries;
15
+ private _logHead;
16
+ private _logCount;
17
+ private _firstPaint;
18
+ /** creates an overlay canvas mirroring the panel's position and size and hooks the surface's after-flush callback. */
19
+ constructor(surface: Surface, panelCanvas: HTMLCanvasElement);
20
+ /** builds the one-line flush status: wall-clock timestamp, repaint kind, flush time, and repainted area percent. */
21
+ private formatEntry;
22
+ /**
23
+ * Runs after each surface flush: logs the entry into the ring buffer, strokes
24
+ * red outlines around partial dirty rects, and draws the status bar. Timers
25
+ * then fade the outlines and the label after their respective delays.
26
+ */
27
+ private onFlush;
28
+ /** clears pending timers and removes the overlay canvas. */
29
+ dispose(): void;
30
+ }
@@ -0,0 +1 @@
1
+ const e=2e3;class t{surface;overlay;overlayCtx;borderClearTimer=0;labelClearTimer=0;_boundClearBorder;_boundClearLabel;logEntries=new Array(e).fill("");_logHead=0;_logCount=0;_firstPaint=!0;constructor(e,t){this.surface=e,this.overlay=document.createElement("canvas"),this.overlay.style.position="fixed",this.overlay.style.top=t.style.top,this.overlay.style.width=t.style.width,this.overlay.style.height=t.style.height,this.overlay.style.pointerEvents="none",this.overlay.style.zIndex="10000000",t.style.right?this.overlay.style.right=t.style.right:this.overlay.style.left=t.style.left,t.style.borderRadius&&(this.overlay.style.borderRadius=t.style.borderRadius),t.parentElement?.appendChild(this.overlay);const i=this.overlay.getContext("2d");if(!i)throw new Error("Failed to create debug overlay context");this.overlayCtx=i,this._boundClearBorder=()=>{this.borderClearTimer=0,this.overlayCtx.clearRect(0,0,this.overlay.width,this.overlay.height-18)},this._boundClearLabel=()=>{this.labelClearTimer=0;const e=this.overlayCtx,t=this.overlay.height-18;e.clearRect(0,t,this.overlay.width,18)},e.onAfterFlush=e=>this.onFlush(e)}formatEntry(e){const t=performance.now()/1e3|0,i=t%60,r=(t/60|0)%60,l=`${(t/3600|0)%24}:${r<10?"0":""}${r}:${i<10?"0":""}${i}`;if(e.isFullRepaint)return this._firstPaint?(this._firstPaint=!1,`[${l}] FIRST PAINT (FULL) ${e.flushTimeMs.toFixed(2)}ms 100%`):`[${l}] FULL ${e.flushTimeMs.toFixed(2)}ms 100%`;let s=0;for(let t=0;t<e.dirtyRectCount;t++){const i=e.dirtyRects[t];s+=i.width*i.height}const a=(s/(e.canvasWidth*e.canvasHeight)*100).toFixed(1);return`[${l}] ${e.dirtyRectCount}r ${e.flushTimeMs.toFixed(2)}ms ${a}%`}onFlush(t){this.overlay.width===t.canvasWidth&&this.overlay.height===t.canvasHeight||(this.overlay.width=t.canvasWidth,this.overlay.height=t.canvasHeight);const i=this._firstPaint,r=this.formatEntry(t);this.logEntries[this._logHead]=r,this._logHead=(this._logHead+1)%e,this._logCount<e&&this._logCount++;const l=this.overlayCtx;if(l.clearRect(0,0,t.canvasWidth,t.canvasHeight),!t.isFullRepaint){l.strokeStyle="rgba(255, 0, 0, 0.7)",l.lineWidth=2;for(let e=0;e<t.dirtyRectCount;e++){const i=t.dirtyRects[e];l.strokeRect(1+(0|i.x),1+(0|i.y),(0|i.width)-2,(0|i.height)-2)}}const s=t.canvasHeight-18;l.fillStyle="rgba(0, 0, 0, 0.7)",l.fillRect(0,s,t.canvasWidth,18),l.fillStyle=i?"#ffffff":t.isFullRepaint?"#ff4444":"#44ff44",l.font="11px monospace",l.fillText(r,8,s+13),this.borderClearTimer&&clearTimeout(this.borderClearTimer),this.borderClearTimer=window.setTimeout(this._boundClearBorder,60),this.labelClearTimer&&clearTimeout(this.labelClearTimer),this.labelClearTimer=window.setTimeout(this._boundClearLabel,5e3)}dispose(){this.surface.onAfterFlush=null,this.borderClearTimer&&clearTimeout(this.borderClearTimer),this.labelClearTimer&&clearTimeout(this.labelClearTimer),this.overlay.parentNode&&this.overlay.parentNode.removeChild(this.overlay)}}export{t as InspectDebugger};