@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.
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/Inspect.cjs +1 -0
- package/dist/Inspect.d.ts +68 -0
- package/dist/Inspect.mjs +1 -0
- package/dist/InspectRegistry.cjs +1 -0
- package/dist/InspectRegistry.d.ts +52 -0
- package/dist/InspectRegistry.mjs +1 -0
- package/dist/assets/default-atlas.cjs +1 -0
- package/dist/assets/default-atlas.d.ts +6 -0
- package/dist/assets/default-atlas.mjs +1 -0
- package/dist/core/atlas.cjs +1 -0
- package/dist/core/atlas.d.ts +29 -0
- package/dist/core/atlas.mjs +1 -0
- package/dist/core/canvas2d-renderer.cjs +1 -0
- package/dist/core/canvas2d-renderer.d.ts +94 -0
- package/dist/core/canvas2d-renderer.mjs +1 -0
- package/dist/core/font-generator.cjs +1 -0
- package/dist/core/font-generator.d.ts +20 -0
- package/dist/core/font-generator.mjs +1 -0
- package/dist/core/glyph-rasterizer.cjs +1 -0
- package/dist/core/glyph-rasterizer.d.ts +10 -0
- package/dist/core/glyph-rasterizer.mjs +1 -0
- package/dist/core/input.cjs +1 -0
- package/dist/core/input.d.ts +89 -0
- package/dist/core/input.mjs +1 -0
- package/dist/core/layout.d.ts +10 -0
- package/dist/core/layouts/stack-layout.cjs +1 -0
- package/dist/core/layouts/stack-layout.d.ts +69 -0
- package/dist/core/layouts/stack-layout.mjs +1 -0
- package/dist/core/renderer.d.ts +65 -0
- package/dist/core/surface.cjs +1 -0
- package/dist/core/surface.d.ts +81 -0
- package/dist/core/surface.mjs +1 -0
- package/dist/core/theme.d.ts +33 -0
- package/dist/core/ttf-parser.cjs +1 -0
- package/dist/core/ttf-parser.d.ts +29 -0
- package/dist/core/ttf-parser.mjs +1 -0
- package/dist/core/widget.cjs +1 -0
- package/dist/core/widget.d.ts +213 -0
- package/dist/core/widget.mjs +1 -0
- package/dist/defaults.cjs +1 -0
- package/dist/defaults.d.ts +113 -0
- package/dist/defaults.mjs +1 -0
- package/dist/gui/gui-button-row.cjs +1 -0
- package/dist/gui/gui-button-row.d.ts +21 -0
- package/dist/gui/gui-button-row.mjs +1 -0
- package/dist/gui/gui-button.cjs +1 -0
- package/dist/gui/gui-button.d.ts +9 -0
- package/dist/gui/gui-button.mjs +1 -0
- package/dist/gui/gui-color.cjs +1 -0
- package/dist/gui/gui-color.d.ts +105 -0
- package/dist/gui/gui-color.mjs +1 -0
- package/dist/gui/gui-folder.cjs +1 -0
- package/dist/gui/gui-folder.d.ts +49 -0
- package/dist/gui/gui-folder.mjs +1 -0
- package/dist/gui/gui-monitor.cjs +1 -0
- package/dist/gui/gui-monitor.d.ts +53 -0
- package/dist/gui/gui-monitor.mjs +1 -0
- package/dist/gui/gui-point2d.d.ts +43 -0
- package/dist/gui/gui-point3d.d.ts +48 -0
- package/dist/gui/gui-reorder-list.cjs +1 -0
- package/dist/gui/gui-reorder-list.d.ts +42 -0
- package/dist/gui/gui-reorder-list.mjs +1 -0
- package/dist/gui/gui-select.cjs +1 -0
- package/dist/gui/gui-select.d.ts +40 -0
- package/dist/gui/gui-select.mjs +1 -0
- package/dist/gui/gui-separator.cjs +1 -0
- package/dist/gui/gui-separator.d.ts +10 -0
- package/dist/gui/gui-separator.mjs +1 -0
- package/dist/gui/gui-slider.cjs +1 -0
- package/dist/gui/gui-slider.d.ts +34 -0
- package/dist/gui/gui-slider.mjs +1 -0
- package/dist/gui/gui-tab-container.d.ts +26 -0
- package/dist/gui/gui-text-log.cjs +1 -0
- package/dist/gui/gui-text-log.d.ts +24 -0
- package/dist/gui/gui-text-log.mjs +1 -0
- package/dist/gui/gui-text.cjs +1 -0
- package/dist/gui/gui-text.d.ts +22 -0
- package/dist/gui/gui-text.mjs +1 -0
- package/dist/gui/gui-toggle.cjs +1 -0
- package/dist/gui/gui-toggle.d.ts +19 -0
- package/dist/gui/gui-toggle.mjs +1 -0
- package/dist/gui/labeled-control.cjs +1 -0
- package/dist/gui/labeled-control.d.ts +21 -0
- package/dist/gui/labeled-control.mjs +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.mjs +1 -0
- package/dist/panel/inspect-debugger.cjs +1 -0
- package/dist/panel/inspect-debugger.d.ts +30 -0
- package/dist/panel/inspect-debugger.mjs +1 -0
- package/dist/panel/inspect-panel.cjs +1 -0
- package/dist/panel/inspect-panel.d.ts +66 -0
- package/dist/panel/inspect-panel.mjs +1 -0
- package/dist/panel/state-manager.cjs +1 -0
- package/dist/panel/state-manager.d.ts +8 -0
- package/dist/panel/state-manager.mjs +1 -0
- package/dist/panel/widget-builder.cjs +1 -0
- package/dist/panel/widget-builder.d.ts +80 -0
- package/dist/panel/widget-builder.mjs +1 -0
- package/dist/themes/dark.cjs +1 -0
- package/dist/themes/dark.d.ts +4 -0
- package/dist/themes/dark.mjs +1 -0
- package/dist/themes/inspect-theme.d.ts +20 -0
- package/dist/types.cjs +1 -0
- package/dist/types.d.ts +354 -0
- package/dist/types.mjs +1 -0
- package/dist/utils/activity.cjs +1 -0
- package/dist/utils/activity.d.ts +4 -0
- package/dist/utils/activity.mjs +1 -0
- package/dist/utils/color-convert.cjs +1 -0
- package/dist/utils/color-convert.d.ts +31 -0
- package/dist/utils/color-convert.mjs +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/persistence.cjs +1 -0
- package/dist/utils/persistence.d.ts +16 -0
- package/dist/utils/persistence.mjs +1 -0
- package/dist/widgets/box.cjs +1 -0
- package/dist/widgets/box.d.ts +24 -0
- package/dist/widgets/box.mjs +1 -0
- package/dist/widgets/button.cjs +1 -0
- package/dist/widgets/button.d.ts +50 -0
- package/dist/widgets/button.mjs +1 -0
- package/dist/widgets/canvas-widget.cjs +1 -0
- package/dist/widgets/canvas-widget.d.ts +12 -0
- package/dist/widgets/canvas-widget.mjs +1 -0
- package/dist/widgets/label.cjs +1 -0
- package/dist/widgets/label.d.ts +41 -0
- package/dist/widgets/label.mjs +1 -0
- package/dist/widgets/number-input.cjs +1 -0
- package/dist/widgets/number-input.d.ts +65 -0
- package/dist/widgets/number-input.mjs +1 -0
- package/dist/widgets/overlay.d.ts +16 -0
- package/dist/widgets/scroll-area.cjs +1 -0
- package/dist/widgets/scroll-area.d.ts +80 -0
- package/dist/widgets/scroll-area.mjs +1 -0
- package/dist/widgets/separator.cjs +1 -0
- package/dist/widgets/separator.d.ts +16 -0
- package/dist/widgets/separator.mjs +1 -0
- package/dist/widgets/slider.cjs +1 -0
- package/dist/widgets/slider.d.ts +36 -0
- package/dist/widgets/slider.mjs +1 -0
- package/dist/widgets/tab-bar.cjs +1 -0
- package/dist/widgets/tab-bar.d.ts +72 -0
- package/dist/widgets/tab-bar.mjs +1 -0
- package/dist/widgets/text-input.cjs +1 -0
- package/dist/widgets/text-input.d.ts +54 -0
- package/dist/widgets/text-input.mjs +1 -0
- package/dist/widgets/toggle-button.d.ts +18 -0
- package/dist/widgets/toggle-icon.cjs +1 -0
- package/dist/widgets/toggle-icon.d.ts +38 -0
- package/dist/widgets/toggle-icon.mjs +1 -0
- package/package.json +95 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marco Gomez ( @TheCodeTherapy )
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @mgz-dev/inspect
|
|
2
|
+
|
|
3
|
+
A GUI panel for games and dev tools (sliders, toggles, colors, selects, monitors, and more), drawn
|
|
4
|
+
entirely to a single `<canvas>`.
|
|
5
|
+
|
|
6
|
+
The panel brings its own dirty-rect compositor, layout engine, font atlas and input dispatcher, so
|
|
7
|
+
it builds no DOM widget tree and injects no stylesheets. That keeps it out of the browser's style,
|
|
8
|
+
layout and paint pipeline, which is the part that competes with a render loop for main-thread time.
|
|
9
|
+
It has no runtime dependencies.
|
|
10
|
+
|
|
11
|
+
The panel is one fixed-position canvas element. Two small exceptions are worth knowing about: a
|
|
12
|
+
transient DOM `<input>` is created while a numeric or text value is being edited, and the optional
|
|
13
|
+
toggle button is a second, small canvas.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pnpm add @mgz-dev/inspect
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { Inspect, slider, toggle, color } from "@mgz-dev/inspect";
|
|
25
|
+
|
|
26
|
+
const state = { speed: 0.17, enabled: true, tint: { r: 1, g: 1, b: 1 } };
|
|
27
|
+
const render = () => {
|
|
28
|
+
/* redraw your scene with the new state */
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
Inspect.registerTabs([
|
|
32
|
+
{
|
|
33
|
+
title: "SETTINGS",
|
|
34
|
+
folders: [
|
|
35
|
+
{
|
|
36
|
+
id: "demo",
|
|
37
|
+
title: "Demo",
|
|
38
|
+
expanded: true,
|
|
39
|
+
controls: [
|
|
40
|
+
slider.of(state, "speed", { min: 0, max: 1, step: 0.01 }, () => render()),
|
|
41
|
+
toggle.of(state, "enabled", {}, () => render()),
|
|
42
|
+
color.of(state, "tint", { colorType: "float" }, () => render())
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
Inspect.mount(document.body, { position: "right", toggleKey: "p" });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Call `Inspect.update()` once per frame from your render loop to refresh monitors and live readouts.
|
|
53
|
+
Guarding it with `Inspect.isVisible()` skips the work while the panel is hidden:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
function frame() {
|
|
57
|
+
requestAnimationFrame(frame);
|
|
58
|
+
drawScene();
|
|
59
|
+
if (Inspect.isVisible()) {
|
|
60
|
+
Inspect.update();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Widget toolkit
|
|
66
|
+
|
|
67
|
+
The same entry point also exports the primitives the panel is built from, for custom controls:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { Widget, Box, Label, Button, Slider, StackLayout, CanvasWidget, THEME_DEFAULT } from "@mgz-dev/inspect";
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Notes
|
|
74
|
+
|
|
75
|
+
Because the panel is painted rather than composed from elements, it is not exposed to assistive
|
|
76
|
+
technology: screen readers cannot read it, its values cannot be selected or copied, and the host
|
|
77
|
+
page cannot restyle it with CSS. For a debug and tuning overlay that is usually the right trade,
|
|
78
|
+
but it is worth knowing before reaching for it as general application UI.
|
package/dist/Inspect.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./core/layouts/stack-layout.cjs"),e=require("./defaults.cjs"),s=require("./gui/gui-button.cjs"),i=require("./gui/gui-separator.cjs"),o=require("./panel/inspect-panel.cjs"),r=require("./panel/widget-builder.cjs"),l=require("./utils/activity.cjs"),n=require("./utils/persistence.cjs"),d=require("./widgets/box.cjs"),a=require("./widgets/scroll-area.cjs"),h=require("./widgets/tab-bar.cjs"),u=require("./widgets/toggle-icon.cjs"),g=new class{panel=null;builder=null;folderConfigs=new Map;tabConfigs=null;options={};customBuilders={};toggleIcon=null;keyHandler=null;mounted=!1;mount(t,s){if(this.mounted)return;this.options={...this.options,...s},this.mounted=!0,this.customBuilders=this.options.customControlBuilders??{},this.panel=new o.InspectPanel(t,this.options);const i=this.options.toggleKey??e.DEFAULTS.panelDefaultToggleKey;if(this.keyHandler=t=>{const e=t.target?.tagName;"INPUT"!==e&&"TEXTAREA"!==e&&"SELECT"!==e&&t.key.toLowerCase()===i.toLowerCase()&&this.toggle()},window.addEventListener("keydown",this.keyHandler),this.options.showToggleButton){const s=this.options.toggleButtonSize??21,i=this.options.margin??0,o=new u.ToggleIcon(s,this.options.position??"right",i,e.DEFAULTS.tabBarHeight);o.setState(this.panel.visible?"x":"hamburger"),o.onClick=()=>this.toggle(),this.toggleIcon=o,t.appendChild(o.element)}this.panel.ready.then(()=>{this.rebuild()})}isMounted(){return this.mounted}registerFolder(t){this.folderConfigs.set(t.id,t),this.mounted&&this.scheduleRebuild()}registerFolders(t){for(const e of t)this.folderConfigs.set(e.id,e);this.mounted&&this.scheduleRebuild()}unregisterFolder(t){this.folderConfigs.delete(t),this.mounted&&this.scheduleRebuild()}unregisterFolders(t){for(const e of t)this.folderConfigs.delete(e);this.mounted&&this.scheduleRebuild()}registerTabs(t){this.tabConfigs=t,this.folderConfigs.clear();for(const e of t)for(const t of e.folders)this.folderConfigs.set(t.id,t);this.mounted&&this.scheduleRebuild()}update(){this.panel&&this.panel.visible&&this.builder&&(this.builder.refreshMonitors(),this.builder.refreshTextLogs())}toggle(){this.panel?.toggle(),this.toggleIcon?.setState(this.panel?.visible?"x":"hamburger")}show(){this.panel?.show(),this.toggleIcon?.setState("x")}hide(){this.panel?.hide(),this.toggleIcon?.setState("hamburger")}isVisible(){return this.panel?.isVisible()??!1}isActive(){return!!this.panel&&(this.panel.input.getActivePointerCount()>0||!!this.panel.isHovered()||l.getGuiActive())}getFolder(t){return this.builder?.folders.get(t)}getCustomControl(t){return this.builder?.customControls.get(t)}getReorderList(t){return this.builder?.reorderLists.get(t)??null}getPane(){return null}dispose(){this.builder&&(this.builder.clear(),this.builder=null),this.panel&&(this.panel.dispose(),this.panel=null),this.folderConfigs.clear(),this.tabConfigs=null,this.customBuilders={},this.mounted=!1}scheduleRebuild(){this.panel&&this.panel.theme.fontAtlas&&this.rebuild()}rebuild(){if(!this.panel)return;const t=this.panel.theme;t.fontAtlas&&(this.builder&&this.builder.clear(),this.builder=new r.WidgetBuilder(t),this.tabConfigs?this.buildTabs(t):this.buildFolders(t))}buildTabs(s){if(!this.panel||!this.builder||!this.tabConfigs)return;const i=new d.Box;i.sizingX="grow",i.sizingY="grow",i.layout=new t.StackLayout("vertical",0,"start","stretch");const o=new h.TabBar;o.sizingX="grow",o.sizingY="fixed",o.preferredHeight=e.DEFAULTS.tabBarHeight,o.tabSizing="content",o.tabs=this.tabConfigs.map((t,e)=>({label:t.title.toUpperCase(),value:String(e)})),o.value="0",this.options.showToggleButton?o.reservedEdge=(this.options.toggleButtonSize??17)+12:this.options.tabBarReservedEdge&&(o.reservedEdge=this.options.tabBarReservedEdge),i.addChild(o);const r=[];for(let e=0;e<this.tabConfigs.length;e++){const o=this.tabConfigs[e],l=new a.ScrollArea;l.sizingX="grow",l.sizingY="grow",l.flexGrow=1,l.visible=0===e,l.bgColor=4294967066,l.scrollbarWidth=s.scrollbarWidth,l.layout=new t.StackLayout("vertical",s.controlGap,"start","stretch"),l.setPaddingAll(s.panelPadding),l.reserveScrollbarSpace();for(const t of o.folders){const e=this.builder.buildFolder(t);this.builder.buildControls(e,t.controls,this.customBuilders),l.addChild(e)}o.showImportExport&&this.addImportExportButtons(l,s),r.push(l),i.addChild(l)}o.onChange=t=>{const e=parseInt(t,10);for(let t=0;t<r.length;t++)r[t].visible=t===e,t===e&&(r[t].needsLayout=!0);this.panel&&(this.panel.root.needsLayout=!0,this.panel.surface.markDirty())},this.panel.setContent(i)}buildFolders(e){if(!this.panel||!this.builder)return;const s=new a.ScrollArea;s.sizingX="grow",s.sizingY="grow",s.flexGrow=1,s.scrollbarWidth=e.scrollbarWidth,s.layout=new t.StackLayout("vertical",e.controlGap,"start","stretch"),s.setPaddingAll(e.panelPadding),s.reserveScrollbarSpace();const i=this.getSortedFolders(),o=new Map;for(const t of i){const e=this.builder.buildFolder(t);if(this.builder.buildControls(e,t.controls,this.customBuilders),o.set(t.id,e),t.parent){const i=o.get(t.parent);i?i.addControl(e):s.addChild(e)}else s.addChild(e)}this.options.showImportExport&&this.addImportExportButtons(s,e),this.panel.setContent(s)}addImportExportButtons(t,e){const o=new i.GuiSeparator;o.applyTheme(e),t.addChild(o);const r=new s.GuiButton("Export Settings",()=>{if(this.builder){const t={};for(const[e,s]of this.builder.monitors)t[e]=s.config.value();n.exportStateAsJSON(t)}});r.applyTheme(e),t.addChild(r);const l=new s.GuiButton("Import Settings",()=>{n.importStateFromJSON(t=>{})});l.applyTheme(e),t.addChild(l)}getSortedFolders(){const t=Array.from(this.folderConfigs.values()),e=[],s=new Set,i=t=>{if(!s.has(t.id)){if(t.parent){const e=this.folderConfigs.get(t.parent);e&&!s.has(e.id)&&i(e)}e.push(t),s.add(t.id)}};for(const e of t)i(e);return e}};exports.Inspect=g;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { GuiFolder } from './gui/gui-folder';
|
|
2
|
+
import { MountOptions, FolderConfig, TabPageConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Singleton API for the canvas-based inspect panel.
|
|
5
|
+
* Manages mounting, folder/tab registration, keyboard toggle, and per-frame refresh.
|
|
6
|
+
*/
|
|
7
|
+
declare class InspectSingleton {
|
|
8
|
+
private panel;
|
|
9
|
+
private builder;
|
|
10
|
+
private folderConfigs;
|
|
11
|
+
private tabConfigs;
|
|
12
|
+
private options;
|
|
13
|
+
private customBuilders;
|
|
14
|
+
private toggleIcon;
|
|
15
|
+
private keyHandler;
|
|
16
|
+
private mounted;
|
|
17
|
+
/** mounts the panel into the container, wiring the toggle key and optional toggle button. */
|
|
18
|
+
mount(container: HTMLElement, options?: Partial<MountOptions>): void;
|
|
19
|
+
/** returns true once mount has run. */
|
|
20
|
+
isMounted(): boolean;
|
|
21
|
+
/** registers a single folder, rebuilding if already mounted. */
|
|
22
|
+
registerFolder(config: FolderConfig): void;
|
|
23
|
+
/** registers multiple folders, rebuilding once if already mounted. */
|
|
24
|
+
registerFolders(configs: FolderConfig[]): void;
|
|
25
|
+
/** removes a folder by id, rebuilding if already mounted. */
|
|
26
|
+
unregisterFolder(id: string): void;
|
|
27
|
+
/** removes multiple folders by id, rebuilding once if already mounted. */
|
|
28
|
+
unregisterFolders(ids: string[]): void;
|
|
29
|
+
/** replaces all folders with the given tab pages, rebuilding if already mounted. */
|
|
30
|
+
registerTabs(tabs: TabPageConfig[]): void;
|
|
31
|
+
/** per-frame refresh of monitors and text logs; call from the render loop. */
|
|
32
|
+
update(): void;
|
|
33
|
+
/** toggles panel visibility and syncs the toggle button icon. */
|
|
34
|
+
toggle(): void;
|
|
35
|
+
/** shows the panel and syncs the toggle button icon. */
|
|
36
|
+
show(): void;
|
|
37
|
+
/** hides the panel and syncs the toggle button icon. */
|
|
38
|
+
hide(): void;
|
|
39
|
+
/** returns true if the panel is currently visible. */
|
|
40
|
+
isVisible(): boolean;
|
|
41
|
+
/** returns true if the panel has pointer activity, hover, or an active gui interaction. */
|
|
42
|
+
isActive(): boolean;
|
|
43
|
+
/** returns the built folder for an id, or undefined if not built. */
|
|
44
|
+
getFolder(id: string): GuiFolder | undefined;
|
|
45
|
+
/** returns a registered custom control by key, or undefined. */
|
|
46
|
+
getCustomControl<T = unknown>(key: string): T | undefined;
|
|
47
|
+
/** returns a registered reorder list by key, or null. */
|
|
48
|
+
getReorderList(key: string): unknown;
|
|
49
|
+
/** compatibility shim; the canvas panel has no DOM pane, so this returns null. */
|
|
50
|
+
getPane(): null;
|
|
51
|
+
/** tears down the builder, panel, listeners, and registered config. */
|
|
52
|
+
dispose(): void;
|
|
53
|
+
/** rebuild now if the font atlas has loaded; otherwise defer until the panel is ready. */
|
|
54
|
+
private scheduleRebuild;
|
|
55
|
+
/** discard the current builder and rebuild the panel content in tab or flat-folder layout. */
|
|
56
|
+
private rebuild;
|
|
57
|
+
/** build the tabbed layout: a tab bar switching between one scroll-area page per tab config. */
|
|
58
|
+
private buildTabs;
|
|
59
|
+
/** build the flat layout: every registered folder in one scroll area, nesting children under their parent. */
|
|
60
|
+
private buildFolders;
|
|
61
|
+
/** append a separator and the export/import settings buttons to a container. */
|
|
62
|
+
private addImportExportButtons;
|
|
63
|
+
/** order folders so every parent precedes its children (depth-first over the parent links). */
|
|
64
|
+
private getSortedFolders;
|
|
65
|
+
}
|
|
66
|
+
/** Global singleton instance of the canvas-based inspect panel. */
|
|
67
|
+
export declare const Inspect: InspectSingleton;
|
|
68
|
+
export {};
|
package/dist/Inspect.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{StackLayout as t}from"./core/layouts/stack-layout.mjs";import{DEFAULTS as e}from"./defaults.mjs";import{GuiButton as s}from"./gui/gui-button.mjs";import{GuiSeparator as i}from"./gui/gui-separator.mjs";import{InspectPanel as o}from"./panel/inspect-panel.mjs";import{WidgetBuilder as r}from"./panel/widget-builder.mjs";import{getGuiActive as l}from"./utils/activity.mjs";import{exportStateAsJSON as n,importStateFromJSON as d}from"./utils/persistence.mjs";import{Box as h}from"./widgets/box.mjs";import{ScrollArea as a}from"./widgets/scroll-area.mjs";import{TabBar as u}from"./widgets/tab-bar.mjs";import{ToggleIcon as g}from"./widgets/toggle-icon.mjs";const p=new class{panel=null;builder=null;folderConfigs=/* @__PURE__ */new Map;tabConfigs=null;options={};customBuilders={};toggleIcon=null;keyHandler=null;mounted=!1;mount(t,s){if(this.mounted)return;this.options={...this.options,...s},this.mounted=!0,this.customBuilders=this.options.customControlBuilders??{},this.panel=new o(t,this.options);const i=this.options.toggleKey??e.panelDefaultToggleKey;if(this.keyHandler=t=>{const e=t.target?.tagName;"INPUT"!==e&&"TEXTAREA"!==e&&"SELECT"!==e&&t.key.toLowerCase()===i.toLowerCase()&&this.toggle()},window.addEventListener("keydown",this.keyHandler),this.options.showToggleButton){const s=this.options.toggleButtonSize??21,i=this.options.margin??0,o=new g(s,this.options.position??"right",i,e.tabBarHeight);o.setState(this.panel.visible?"x":"hamburger"),o.onClick=()=>this.toggle(),this.toggleIcon=o,t.appendChild(o.element)}this.panel.ready.then(()=>{this.rebuild()})}isMounted(){return this.mounted}registerFolder(t){this.folderConfigs.set(t.id,t),this.mounted&&this.scheduleRebuild()}registerFolders(t){for(const e of t)this.folderConfigs.set(e.id,e);this.mounted&&this.scheduleRebuild()}unregisterFolder(t){this.folderConfigs.delete(t),this.mounted&&this.scheduleRebuild()}unregisterFolders(t){for(const e of t)this.folderConfigs.delete(e);this.mounted&&this.scheduleRebuild()}registerTabs(t){this.tabConfigs=t,this.folderConfigs.clear();for(const e of t)for(const t of e.folders)this.folderConfigs.set(t.id,t);this.mounted&&this.scheduleRebuild()}update(){this.panel&&this.panel.visible&&this.builder&&(this.builder.refreshMonitors(),this.builder.refreshTextLogs())}toggle(){this.panel?.toggle(),this.toggleIcon?.setState(this.panel?.visible?"x":"hamburger")}show(){this.panel?.show(),this.toggleIcon?.setState("x")}hide(){this.panel?.hide(),this.toggleIcon?.setState("hamburger")}isVisible(){return this.panel?.isVisible()??!1}isActive(){return!!this.panel&&(this.panel.input.getActivePointerCount()>0||!!this.panel.isHovered()||l())}getFolder(t){return this.builder?.folders.get(t)}getCustomControl(t){return this.builder?.customControls.get(t)}getReorderList(t){return this.builder?.reorderLists.get(t)??null}getPane(){return null}dispose(){this.builder&&(this.builder.clear(),this.builder=null),this.panel&&(this.panel.dispose(),this.panel=null),this.folderConfigs.clear(),this.tabConfigs=null,this.customBuilders={},this.mounted=!1}scheduleRebuild(){this.panel&&this.panel.theme.fontAtlas&&this.rebuild()}rebuild(){if(!this.panel)return;const t=this.panel.theme;t.fontAtlas&&(this.builder&&this.builder.clear(),this.builder=new r(t),this.tabConfigs?this.buildTabs(t):this.buildFolders(t))}buildTabs(s){if(!this.panel||!this.builder||!this.tabConfigs)return;const i=new h;i.sizingX="grow",i.sizingY="grow",i.layout=new t("vertical",0,"start","stretch");const o=new u;o.sizingX="grow",o.sizingY="fixed",o.preferredHeight=e.tabBarHeight,o.tabSizing="content",o.tabs=this.tabConfigs.map((t,e)=>({label:t.title.toUpperCase(),value:String(e)})),o.value="0",this.options.showToggleButton?o.reservedEdge=(this.options.toggleButtonSize??17)+12:this.options.tabBarReservedEdge&&(o.reservedEdge=this.options.tabBarReservedEdge),i.addChild(o);const r=[];for(let e=0;e<this.tabConfigs.length;e++){const o=this.tabConfigs[e],l=new a;l.sizingX="grow",l.sizingY="grow",l.flexGrow=1,l.visible=0===e,l.bgColor=4294967066,l.scrollbarWidth=s.scrollbarWidth,l.layout=new t("vertical",s.controlGap,"start","stretch"),l.setPaddingAll(s.panelPadding),l.reserveScrollbarSpace();for(const t of o.folders){const e=this.builder.buildFolder(t);this.builder.buildControls(e,t.controls,this.customBuilders),l.addChild(e)}o.showImportExport&&this.addImportExportButtons(l,s),r.push(l),i.addChild(l)}o.onChange=t=>{const e=parseInt(t,10);for(let t=0;t<r.length;t++)r[t].visible=t===e,t===e&&(r[t].needsLayout=!0);this.panel&&(this.panel.root.needsLayout=!0,this.panel.surface.markDirty())},this.panel.setContent(i)}buildFolders(e){if(!this.panel||!this.builder)return;const s=new a;s.sizingX="grow",s.sizingY="grow",s.flexGrow=1,s.scrollbarWidth=e.scrollbarWidth,s.layout=new t("vertical",e.controlGap,"start","stretch"),s.setPaddingAll(e.panelPadding),s.reserveScrollbarSpace();const i=this.getSortedFolders(),o=/* @__PURE__ */new Map;for(const t of i){const e=this.builder.buildFolder(t);if(this.builder.buildControls(e,t.controls,this.customBuilders),o.set(t.id,e),t.parent){const i=o.get(t.parent);i?i.addControl(e):s.addChild(e)}else s.addChild(e)}this.options.showImportExport&&this.addImportExportButtons(s,e),this.panel.setContent(s)}addImportExportButtons(t,e){const o=new i;o.applyTheme(e),t.addChild(o);const r=new s("Export Settings",()=>{if(this.builder){const t={};for(const[e,s]of this.builder.monitors)t[e]=s.config.value();n(t)}});r.applyTheme(e),t.addChild(r);const l=new s("Import Settings",()=>{d(t=>{})});l.applyTheme(e),t.addChild(l)}getSortedFolders(){const t=Array.from(this.folderConfigs.values()),e=[],s=/* @__PURE__ */new Set,i=t=>{if(!s.has(t.id)){if(t.parent){const e=this.folderConfigs.get(t.parent);e&&!s.has(e.id)&&i(e)}e.push(t),s.add(t.id)}};for(const e of t)i(e);return e}};export{p as Inspect};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core/layouts/stack-layout.cjs"),t=require("./panel/inspect-panel.cjs"),i=require("./panel/state-manager.cjs"),s=require("./panel/widget-builder.cjs"),r=require("./utils/activity.cjs"),l=require("./widgets/scroll-area.cjs");exports.InspectRegistry=class{panel=null;builder=null;folderConfigs=new Map;options={};customBuilders={};stateManager=new i.StateManager;registerFolder(e){return this.folderConfigs.set(e.id,e),this}registerFolders(e){for(const t of e)this.folderConfigs.set(t.id,t);return this}unregisterFolder(e){return this.folderConfigs.delete(e),this}clearFolders(){return this.folderConfigs.clear(),this}mount(e,i){this.panel||(this.options={...this.options,...i},this.customBuilders=this.options.customControlBuilders??{},this.panel=new t.InspectPanel(e,this.options),this.panel.ready.then(()=>{this.rebuild()}))}update(){this.panel&&this.panel.visible&&this.builder&&(this.builder.refreshMonitors(),this.builder.refreshTextLogs())}toggle(){this.panel?.toggle()}show(){this.panel?.show()}hide(){this.panel?.hide()}isVisible(){return this.panel?.isVisible()??!1}isActive(){return!!this.panel&&(this.panel.input.getActivePointerCount()>0||!!this.panel.isHovered()||r.getGuiActive())}getFolder(e){return this.builder?.folders.get(e)}getCustomControl(e){return this.builder?.customControls.get(e)}exportState(){return this.builder?this.stateManager.exportState(this.builder):null}importState(e){this.builder&&(this.stateManager.importState(this.builder,e),this.panel?.surface.markDirty())}dispose(){this.builder&&(this.builder.clear(),this.builder=null),this.panel&&(this.panel.dispose(),this.panel=null),this.folderConfigs.clear(),this.customBuilders={}}rebuild(){if(!this.panel)return;const t=this.panel.theme;if(!t.fontAtlas)return;this.builder&&this.builder.clear(),this.builder=new s.WidgetBuilder(t);const i=new l.ScrollArea;i.sizingX="grow",i.sizingY="grow",i.flexGrow=1,i.layout=new e.StackLayout("vertical",t.controlGap,"start","stretch"),i.setPaddingAll(t.panelPadding);const r=this.getSortedFolders(),n=new Map;for(const e of r){const t=this.builder.buildFolder(e);if(this.builder.buildControls(t,e.controls,this.customBuilders),n.set(e.id,t),e.parent){const s=n.get(e.parent);s?s.addControl(t):i.addChild(t)}else i.addChild(t)}this.updateScrollContentHeight(i,t),this.panel.setContent(i)}getSortedFolders(){const e=Array.from(this.folderConfigs.values()),t=[],i=new Set,s=e=>{if(!i.has(e.id)){if(e.parent){const t=this.folderConfigs.get(e.parent);t&&!i.has(t.id)&&s(t)}t.push(e),i.add(e.id)}};for(const t of e)s(t);return t}updateScrollContentHeight(e,t){let i=0;for(const t of e.children)i+=t.measureIntrinsicHeight();e.contentHeight=i+e.children.length*t.controlGap}};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { GuiFolder } from './gui/gui-folder';
|
|
2
|
+
import { MountOptions, FolderConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Non-singleton inspect panel registry.
|
|
5
|
+
* Allows multiple independent panel instances with separate folder sets.
|
|
6
|
+
*/
|
|
7
|
+
export declare class InspectRegistry {
|
|
8
|
+
private panel;
|
|
9
|
+
private builder;
|
|
10
|
+
private folderConfigs;
|
|
11
|
+
private options;
|
|
12
|
+
private customBuilders;
|
|
13
|
+
private stateManager;
|
|
14
|
+
/** registers a folder config, replacing any existing folder with the same id. */
|
|
15
|
+
registerFolder(config: FolderConfig): this;
|
|
16
|
+
/** registers multiple folder configs at once. */
|
|
17
|
+
registerFolders(configs: FolderConfig[]): this;
|
|
18
|
+
/** removes the folder config with the given id. */
|
|
19
|
+
unregisterFolder(id: string): this;
|
|
20
|
+
/** removes all registered folder configs. */
|
|
21
|
+
clearFolders(): this;
|
|
22
|
+
/** mounts the panel into the container and builds the registered folders. */
|
|
23
|
+
mount(container: HTMLElement, options?: Partial<MountOptions>): void;
|
|
24
|
+
/** refreshes monitors and text logs while the panel is visible. */
|
|
25
|
+
update(): void;
|
|
26
|
+
/** toggles panel visibility. */
|
|
27
|
+
toggle(): void;
|
|
28
|
+
/** shows the panel. */
|
|
29
|
+
show(): void;
|
|
30
|
+
/** hides the panel. */
|
|
31
|
+
hide(): void;
|
|
32
|
+
/** returns whether the panel is currently visible. */
|
|
33
|
+
isVisible(): boolean;
|
|
34
|
+
/** returns whether the panel is being interacted with (pointer down, hover, or active gui). */
|
|
35
|
+
isActive(): boolean;
|
|
36
|
+
/** returns the built folder widget for the given id, if any. */
|
|
37
|
+
getFolder(id: string): GuiFolder | undefined;
|
|
38
|
+
/** returns the custom control instance registered under the given key, if any. */
|
|
39
|
+
getCustomControl<T = unknown>(key: string): T | undefined;
|
|
40
|
+
/** serializes the current control values to a plain object, or null if not built. */
|
|
41
|
+
exportState(): Record<string, unknown> | null;
|
|
42
|
+
/** applies previously exported control values and redraws. */
|
|
43
|
+
importState(state: Record<string, unknown>): void;
|
|
44
|
+
/** tears down the panel and clears all registered folders and builders. */
|
|
45
|
+
dispose(): void;
|
|
46
|
+
/** discard the current builder and rebuild the folder tree into a fresh scroll area. */
|
|
47
|
+
private rebuild;
|
|
48
|
+
/** order folders so every parent precedes its children (depth-first over the parent links). */
|
|
49
|
+
private getSortedFolders;
|
|
50
|
+
/** set the scroll content height to the summed child intrinsic heights plus one gap per child. */
|
|
51
|
+
private updateScrollContentHeight;
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{StackLayout as t}from"./core/layouts/stack-layout.mjs";import{InspectPanel as e}from"./panel/inspect-panel.mjs";import{StateManager as i}from"./panel/state-manager.mjs";import{WidgetBuilder as s}from"./panel/widget-builder.mjs";import{getGuiActive as r}from"./utils/activity.mjs";import{ScrollArea as o}from"./widgets/scroll-area.mjs";class l{panel=null;builder=null;folderConfigs=/* @__PURE__ */new Map;options={};customBuilders={};stateManager=new i;registerFolder(t){return this.folderConfigs.set(t.id,t),this}registerFolders(t){for(const e of t)this.folderConfigs.set(e.id,e);return this}unregisterFolder(t){return this.folderConfigs.delete(t),this}clearFolders(){return this.folderConfigs.clear(),this}mount(t,i){this.panel||(this.options={...this.options,...i},this.customBuilders=this.options.customControlBuilders??{},this.panel=new e(t,this.options),this.panel.ready.then(()=>{this.rebuild()}))}update(){this.panel&&this.panel.visible&&this.builder&&(this.builder.refreshMonitors(),this.builder.refreshTextLogs())}toggle(){this.panel?.toggle()}show(){this.panel?.show()}hide(){this.panel?.hide()}isVisible(){return this.panel?.isVisible()??!1}isActive(){return!!this.panel&&(this.panel.input.getActivePointerCount()>0||!!this.panel.isHovered()||r())}getFolder(t){return this.builder?.folders.get(t)}getCustomControl(t){return this.builder?.customControls.get(t)}exportState(){return this.builder?this.stateManager.exportState(this.builder):null}importState(t){this.builder&&(this.stateManager.importState(this.builder,t),this.panel?.surface.markDirty())}dispose(){this.builder&&(this.builder.clear(),this.builder=null),this.panel&&(this.panel.dispose(),this.panel=null),this.folderConfigs.clear(),this.customBuilders={}}rebuild(){if(!this.panel)return;const e=this.panel.theme;if(!e.fontAtlas)return;this.builder&&this.builder.clear(),this.builder=new s(e);const i=new o;i.sizingX="grow",i.sizingY="grow",i.flexGrow=1,i.layout=new t("vertical",e.controlGap,"start","stretch"),i.setPaddingAll(e.panelPadding);const r=this.getSortedFolders(),l=/* @__PURE__ */new Map;for(const t of r){const e=this.builder.buildFolder(t);if(this.builder.buildControls(e,t.controls,this.customBuilders),l.set(t.id,e),t.parent){const s=l.get(t.parent);s?s.addControl(e):i.addChild(e)}else i.addChild(e)}this.updateScrollContentHeight(i,e),this.panel.setContent(i)}getSortedFolders(){const t=Array.from(this.folderConfigs.values()),e=[],i=/* @__PURE__ */new Set,s=t=>{if(!i.has(t.id)){if(t.parent){const e=this.folderConfigs.get(t.parent);e&&!i.has(e.id)&&s(e)}e.push(t),i.add(t.id)}};for(const e of t)s(e);return e}updateScrollContentHeight(t,e){let i=0;for(const e of t.children)i+=e.measureIntrinsicHeight();t.contentHeight=i+t.children.length*e.controlGap}}export{l as InspectRegistry};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("../core/atlas.cjs");exports.loadDefaultAtlas=function(){return new Promise((n,e)=>{const f=new Image;f.onload=()=>{const e=P.buildCharTable(8,14,32,126);n({image:f,charWidth:8,lineHeight:14,firstChar:32,lastChar:126,charTable:e,getRegion:()=>null,getCharRegion:P=>null})},f.onerror=()=>e(new Error("Failed to load default font atlas")),f.src="data:image/png;base64,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"})};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{buildCharTable as P}from"../core/atlas.mjs";function n(){return new Promise((n,f)=>{const w=new Image;w.onload=()=>{const f=P(8,14,32,126);n({image:w,charWidth:8,lineHeight:14,firstChar:32,lastChar:126,charTable:f,getRegion:()=>null,getCharRegion:P=>null})},w.onerror=()=>f(new Error("Failed to load default font atlas")),w.src="data:image/png;base64,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"})}export{n as loadDefaultAtlas};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),exports.buildCharTable=function(t,e,r,o){const n=o-r+1,l=new Int32Array(4*n);for(let r=0;r<n;r++){const o=4*r;l[o]=r*t,l[o+1]=0,l[o+2]=t,l[o+3]=e}return l};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Rectangular sub-region within a texture atlas image. */
|
|
2
|
+
export interface AtlasRegion {
|
|
3
|
+
readonly x: number;
|
|
4
|
+
readonly y: number;
|
|
5
|
+
readonly width: number;
|
|
6
|
+
readonly height: number;
|
|
7
|
+
}
|
|
8
|
+
/** A texture atlas backed by a single image, addressable by named region. */
|
|
9
|
+
export interface TextureAtlas {
|
|
10
|
+
readonly image: HTMLImageElement;
|
|
11
|
+
/** looks up a named region, or null when the name is unknown. */
|
|
12
|
+
getRegion(name: string): AtlasRegion | null;
|
|
13
|
+
}
|
|
14
|
+
/** Monospace font atlas with fixed-width character cells and lookup table. */
|
|
15
|
+
export interface FontAtlas extends TextureAtlas {
|
|
16
|
+
readonly charWidth: number;
|
|
17
|
+
readonly lineHeight: number;
|
|
18
|
+
readonly firstChar: number;
|
|
19
|
+
readonly lastChar: number;
|
|
20
|
+
/** Flat array of [x, y, w, h] tuples per contiguous character slot. */
|
|
21
|
+
readonly charTable: Int32Array;
|
|
22
|
+
/** region for a character code, covering both the contiguous range and any extra glyphs, or null. */
|
|
23
|
+
getCharRegion(code: number): AtlasRegion | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Builds a flat Int32Array lookup table for contiguous character codes.
|
|
27
|
+
* Each character occupies 4 slots: [x, y, width, height].
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildCharTable(charWidth: number, lineHeight: number, firstChar: number, lastChar: number): Int32Array;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function n(n,t,r,o){const e=o-r+1,c=new Int32Array(4*e);for(let r=0;r<e;r++){const o=4*r;c[o]=r*n,c[o+1]=0,c[o+2]=n,c[o+3]=t}return c}export{n as buildCharTable};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=2*Math.PI,i=Array.from({length:256},(t,i)=>i.toString(16).padStart(2,"0"));exports.Canvas2DRenderer=class{ctx;alphaStack=new Float64Array(32);alphaStackIndex=0;currentAlpha=1;colorCache=new Map;partialFrame=!1;_lastCSSColor=-1;_lastCSSResult="";_lastTintColor=-1;_lastTintResult=null;constructor(t){const i=t.getContext("2d",{alpha:!0});if(!i)throw new Error("Failed to get 2D context");this.ctx=i}begin(t,i){this.ctx.clearRect(0,0,t,i),this.ctx.imageSmoothingEnabled=!1,this.alphaStackIndex=0,this.currentAlpha=1,this.ctx.globalAlpha=1,this.partialFrame=!1}beginPartial(t,i,e,s){const h=this.ctx;h.clearRect(0|t,0|i,0|e,0|s),h.save(),h.beginPath(),h.rect(0|t,0|i,0|e,0|s),h.clip(),h.imageSmoothingEnabled=!1,this.alphaStackIndex=0,this.currentAlpha=1,h.globalAlpha=1,this.partialFrame=!0}end(){this.partialFrame&&(this.ctx.restore(),this.partialFrame=!1)}fillRect(t,i,e,s,h){this.ctx.fillStyle=this.colorToCSS(h),this.ctx.fillRect(0|t,0|i,0|e,0|s)}strokeRect(t,i,e,s,h,l){this.ctx.strokeStyle=this.colorToCSS(h),this.ctx.lineWidth=l,this.ctx.strokeRect(.5+(0|t),.5+(0|i),(0|e)-1,(0|s)-1)}fillRoundedRect(t,i,e,s,h,l){const a=this.ctx,c=0|t,r=0|i,o=0|e,n=0|s;if(o<=0||n<=0)return;const x=Math.max(0,Math.min(h,o/2,n/2));a.fillStyle=this.colorToCSS(l),a.beginPath(),a.roundRect(c,r,o,n,x),a.fill()}strokeRoundedRect(t,i,e,s,h,l,a){const c=this.ctx,r=.5+(0|t),o=.5+(0|i),n=(0|e)-1,x=(0|s)-1;if(n<=0||x<=0)return;const S=Math.max(0,Math.min(h,n/2,x/2));c.strokeStyle=this.colorToCSS(l),c.lineWidth=a,c.beginPath(),c.roundRect(r,o,n,x,S),c.stroke()}drawImage(t,i,e,s,h,l,a,c,r){this.ctx.drawImage(t.image,i,e,s,h,0|l,0|a,0|c,0|r)}drawText(t,i,e,s,h,l,a){const c=t.charWidth,r=t.lineHeight,o=t.charTable,n=t.firstChar,x=t.lastChar,S=this.ctx,p=this.getTintedAtlas(t,h),C=l??1,g=c*C|0,d=r*C|0,u=g+(a??0);for(let h=0;h<i.length;h++){const l=i.charCodeAt(h);if(l>=n&&l<=x){const t=4*(l-n);S.drawImage(p,o[t],o[t+1],o[t+2],o[t+3],e+h*u|0,0|s,g,d)}else{const i=t.getCharRegion(l);i&&S.drawImage(p,i.x,i.y,i.width,i.height,e+h*u|0,0|s,g,d)}}}tintedAtlasCache=new Map;getTintedAtlas(t,i){const e=i>>>0;if(e===this._lastTintColor&&this._lastTintResult)return this._lastTintResult;let s=this.tintedAtlasCache.get(e);if(s)return this._lastTintColor=e,this._lastTintResult=s,s;const h=t.image;s=document.createElement("canvas"),s.width=h.width,s.height=h.height;const l=s.getContext("2d");return l?(l.imageSmoothingEnabled=!1,l.drawImage(h,0,0),l.globalCompositeOperation="source-in",l.fillStyle=this.colorToCSS(i),l.fillRect(0,0,s.width,s.height),this.tintedAtlasCache.size>16&&this.tintedAtlasCache.clear(),this.tintedAtlasCache.set(e,s),this._lastTintColor=e,this._lastTintResult=s,s):h}drawLine(t,i,e,s,h,l){this.ctx.strokeStyle=this.colorToCSS(h),this.ctx.lineWidth=l,this.ctx.beginPath(),this.ctx.moveTo(t+.5,i+.5),this.ctx.lineTo(e+.5,s+.5),this.ctx.stroke()}fillCircle(i,e,s,h){this.ctx.fillStyle=this.colorToCSS(h),this.ctx.beginPath(),this.ctx.arc(i,e,s,0,t),this.ctx.fill()}strokeCircle(i,e,s,h,l){this.ctx.strokeStyle=this.colorToCSS(h),this.ctx.lineWidth=l,this.ctx.beginPath(),this.ctx.arc(i,e,s,0,t),this.ctx.stroke()}strokeArc(t,i,e,s,h,l,a,c="butt"){const r=this.ctx;r.strokeStyle=this.colorToCSS(l),r.lineWidth=a,r.lineCap=c,r.beginPath(),r.arc(t,i,e,s,h),r.stroke(),r.lineCap="butt"}beginPath(){this.ctx.beginPath()}moveTo(t,i){this.ctx.moveTo(t,i)}lineTo(t,i){this.ctx.lineTo(t,i)}strokePath(t,i){this.ctx.strokeStyle=this.colorToCSS(t),this.ctx.lineWidth=i,this.ctx.stroke()}fillPath(t){this.ctx.fillStyle=this.colorToCSS(t),this.ctx.fill()}pushClip(t,i,e,s){this.ctx.save(),this.ctx.beginPath(),this.ctx.rect(0|t,0|i,0|e,0|s),this.ctx.clip()}popClip(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}pushTranslateClip(t,i,e,s,h,l){this.ctx.save(),this.ctx.translate(0|t,0|i),this.ctx.beginPath(),this.ctx.rect(0|e,0|s,0|h,0|l),this.ctx.clip()}popTranslateClip(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}pushAlpha(t){this.alphaStack[this.alphaStackIndex++]=this.currentAlpha,this.currentAlpha*=t,this.ctx.globalAlpha=this.currentAlpha}popAlpha(){this.currentAlpha=this.alphaStack[--this.alphaStackIndex],this.ctx.globalAlpha=this.currentAlpha}pushTranslate(t,i){this.ctx.save(),this.ctx.translate(0|t,0|i)}popTranslate(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}pushScale(t,i){this.ctx.save(),this.ctx.scale(t,i)}popScale(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}dispose(){this.colorCache.clear(),this.tintedAtlasCache.clear(),this._lastCSSColor=-1,this._lastCSSResult="",this._lastTintColor=-1,this._lastTintResult=null}colorToCSS(t){if((t>>>=0)===this._lastCSSColor)return this._lastCSSResult;let e=this.colorCache.get(t);if(void 0!==e)return this._lastCSSColor=t,this._lastCSSResult=e,e;const s=t>>>24&255,h=t>>>16&255,l=t>>>8&255,a=255&t;return e=255===a?"#"+i[s]+i[h]+i[l]:"#"+i[s]+i[h]+i[l]+i[a],this.colorCache.size>256&&this.colorCache.clear(),this.colorCache.set(t,e),this._lastCSSColor=t,this._lastCSSResult=e,e}};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { FontAtlas, TextureAtlas } from './atlas';
|
|
2
|
+
import { Renderer } from './renderer';
|
|
3
|
+
/** Canvas 2D implementation of the Renderer interface. */
|
|
4
|
+
export declare class Canvas2DRenderer implements Renderer {
|
|
5
|
+
private ctx;
|
|
6
|
+
private alphaStack;
|
|
7
|
+
private alphaStackIndex;
|
|
8
|
+
private currentAlpha;
|
|
9
|
+
private colorCache;
|
|
10
|
+
private partialFrame;
|
|
11
|
+
private _lastCSSColor;
|
|
12
|
+
private _lastCSSResult;
|
|
13
|
+
private _lastTintColor;
|
|
14
|
+
private _lastTintResult;
|
|
15
|
+
/** acquires the canvas 2D context, throwing when the browser cannot provide one. */
|
|
16
|
+
constructor(canvas: HTMLCanvasElement);
|
|
17
|
+
/** clears the whole canvas and resets alpha state for a full-frame pass. */
|
|
18
|
+
begin(width: number, height: number): void;
|
|
19
|
+
/** clears and clips to the given rect for a partial pass, saving ctx state to restore in end(). */
|
|
20
|
+
beginPartial(x: number, y: number, width: number, height: number): void;
|
|
21
|
+
/** restores the clip and state saved by beginPartial; a no-op after a full begin(). */
|
|
22
|
+
end(): void;
|
|
23
|
+
/** fills a pixel-snapped rectangle. */
|
|
24
|
+
fillRect(x: number, y: number, width: number, height: number, color: number): void;
|
|
25
|
+
/** strokes a rectangle outline, offset half a pixel so a 1px line stays crisp. */
|
|
26
|
+
strokeRect(x: number, y: number, width: number, height: number, color: number, lineWidth: number): void;
|
|
27
|
+
/** fills a rounded rectangle, clamping the radius to at most half the smaller side. */
|
|
28
|
+
fillRoundedRect(x: number, y: number, width: number, height: number, radius: number, color: number): void;
|
|
29
|
+
/** strokes a rounded-rectangle outline, offset half a pixel for a crisp 1px line. */
|
|
30
|
+
strokeRoundedRect(x: number, y: number, width: number, height: number, radius: number, color: number, lineWidth: number): void;
|
|
31
|
+
/** blits a source sub-region of the atlas image into the destination rect. */
|
|
32
|
+
drawImage(atlas: TextureAtlas, srcX: number, srcY: number, srcWidth: number, srcHeight: number, dstX: number, dstY: number, dstWidth: number, dstHeight: number): void;
|
|
33
|
+
/**
|
|
34
|
+
* Draws text one glyph at a time from the atlas, recolored to the given
|
|
35
|
+
* color via a cached tinted copy of the atlas. Glyphs inside the contiguous
|
|
36
|
+
* range use the flat char table; others fall back to getCharRegion.
|
|
37
|
+
*/
|
|
38
|
+
drawText(atlas: FontAtlas, text: string, x: number, y: number, color: number, scale?: number, letterSpacing?: number): void;
|
|
39
|
+
private tintedAtlasCache;
|
|
40
|
+
/**
|
|
41
|
+
* Returns a copy of the atlas recolored to the given color, building it once
|
|
42
|
+
* per color via a source-in composite and caching the result. A one-entry
|
|
43
|
+
* fast path and a 16-entry cache keep repeated text draws off the composite
|
|
44
|
+
* path; the cache is flushed once it grows past that bound.
|
|
45
|
+
*/
|
|
46
|
+
private getTintedAtlas;
|
|
47
|
+
/** strokes a line, offset half a pixel so a 1px stroke stays crisp. */
|
|
48
|
+
drawLine(x1: number, y1: number, x2: number, y2: number, color: number, lineWidth: number): void;
|
|
49
|
+
/** fills a full circle. */
|
|
50
|
+
fillCircle(cx: number, cy: number, radius: number, color: number): void;
|
|
51
|
+
/** strokes a full circle outline. */
|
|
52
|
+
strokeCircle(cx: number, cy: number, radius: number, color: number, lineWidth: number): void;
|
|
53
|
+
/** strokes an arc, restoring the default butt cap afterward so it does not leak into later strokes. */
|
|
54
|
+
strokeArc(cx: number, cy: number, radius: number, startAngle: number, endAngle: number, color: number, lineWidth: number, lineCap?: "butt" | "round" | "square"): void;
|
|
55
|
+
/** begins a new canvas path for the moveTo/lineTo/strokePath/fillPath primitives. */
|
|
56
|
+
beginPath(): void;
|
|
57
|
+
/** starts a new subpath at the given point. */
|
|
58
|
+
moveTo(x: number, y: number): void;
|
|
59
|
+
/** adds a line segment to the current path. */
|
|
60
|
+
lineTo(x: number, y: number): void;
|
|
61
|
+
/** strokes the current path. */
|
|
62
|
+
strokePath(color: number, lineWidth: number): void;
|
|
63
|
+
/** fills the current path. */
|
|
64
|
+
fillPath(color: number): void;
|
|
65
|
+
/** saves ctx state and clips to a pixel-snapped rect. */
|
|
66
|
+
pushClip(x: number, y: number, width: number, height: number): void;
|
|
67
|
+
/** restores the clip and reapplies the tracked alpha, since ctx.restore also reverts globalAlpha. */
|
|
68
|
+
popClip(): void;
|
|
69
|
+
/** saves ctx state, translates, then clips to a rect expressed in the translated space. */
|
|
70
|
+
pushTranslateClip(tx: number, ty: number, cx: number, cy: number, cw: number, ch: number): void;
|
|
71
|
+
/** restores translate and clip, reapplying the tracked alpha that ctx.restore reverted. */
|
|
72
|
+
popTranslateClip(): void;
|
|
73
|
+
/** pushes the current alpha and multiplies it by the given factor (a separate stack from ctx save/restore). */
|
|
74
|
+
pushAlpha(alpha: number): void;
|
|
75
|
+
/** pops back to the alpha saved by the matching pushAlpha. */
|
|
76
|
+
popAlpha(): void;
|
|
77
|
+
/** saves ctx state and applies a pixel-snapped translation. */
|
|
78
|
+
pushTranslate(x: number, y: number): void;
|
|
79
|
+
/** restores the translation, reapplying the tracked alpha that ctx.restore reverted. */
|
|
80
|
+
popTranslate(): void;
|
|
81
|
+
/** saves ctx state and applies a scale. */
|
|
82
|
+
pushScale(scaleX: number, scaleY: number): void;
|
|
83
|
+
/** restores the scale, reapplying the tracked alpha that ctx.restore reverted. */
|
|
84
|
+
popScale(): void;
|
|
85
|
+
/** clears the color and tinted-atlas caches and their fast-path slots. */
|
|
86
|
+
dispose(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Converts a packed RGBA color (0xRRGGBBAA) to a CSS hex string, dropping the
|
|
89
|
+
* alpha byte when it is fully opaque. A single-slot fast path plus a 256-entry
|
|
90
|
+
* cache keep the common repeated colors off the per-call string building; the
|
|
91
|
+
* cache is flushed once it grows past that bound.
|
|
92
|
+
*/
|
|
93
|
+
private colorToCSS;
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=2*Math.PI,i=Array.from({length:256},(t,i)=>i.toString(16).padStart(2,"0"));class s{ctx;alphaStack=new Float64Array(32);alphaStackIndex=0;currentAlpha=1;colorCache=/* @__PURE__ */new Map;partialFrame=!1;_lastCSSColor=-1;_lastCSSResult="";_lastTintColor=-1;_lastTintResult=null;constructor(t){const i=t.getContext("2d",{alpha:!0});if(!i)throw new Error("Failed to get 2D context");this.ctx=i}begin(t,i){this.ctx.clearRect(0,0,t,i),this.ctx.imageSmoothingEnabled=!1,this.alphaStackIndex=0,this.currentAlpha=1,this.ctx.globalAlpha=1,this.partialFrame=!1}beginPartial(t,i,s,e){const h=this.ctx;h.clearRect(0|t,0|i,0|s,0|e),h.save(),h.beginPath(),h.rect(0|t,0|i,0|s,0|e),h.clip(),h.imageSmoothingEnabled=!1,this.alphaStackIndex=0,this.currentAlpha=1,h.globalAlpha=1,this.partialFrame=!0}end(){this.partialFrame&&(this.ctx.restore(),this.partialFrame=!1)}fillRect(t,i,s,e,h){this.ctx.fillStyle=this.colorToCSS(h),this.ctx.fillRect(0|t,0|i,0|s,0|e)}strokeRect(t,i,s,e,h,l){this.ctx.strokeStyle=this.colorToCSS(h),this.ctx.lineWidth=l,this.ctx.strokeRect(.5+(0|t),.5+(0|i),(0|s)-1,(0|e)-1)}fillRoundedRect(t,i,s,e,h,l){const a=this.ctx,c=0|t,o=0|i,r=0|s,n=0|e;if(r<=0||n<=0)return;const x=Math.max(0,Math.min(h,r/2,n/2));a.fillStyle=this.colorToCSS(l),a.beginPath(),a.roundRect(c,o,r,n,x),a.fill()}strokeRoundedRect(t,i,s,e,h,l,a){const c=this.ctx,o=.5+(0|t),r=.5+(0|i),n=(0|s)-1,x=(0|e)-1;if(n<=0||x<=0)return;const S=Math.max(0,Math.min(h,n/2,x/2));c.strokeStyle=this.colorToCSS(l),c.lineWidth=a,c.beginPath(),c.roundRect(o,r,n,x,S),c.stroke()}drawImage(t,i,s,e,h,l,a,c,o){this.ctx.drawImage(t.image,i,s,e,h,0|l,0|a,0|c,0|o)}drawText(t,i,s,e,h,l,a){const c=t.charWidth,o=t.lineHeight,r=t.charTable,n=t.firstChar,x=t.lastChar,S=this.ctx,p=this.getTintedAtlas(t,h),C=l??1,g=c*C|0,d=o*C|0,u=g+(a??0);for(let h=0;h<i.length;h++){const l=i.charCodeAt(h);if(l>=n&&l<=x){const t=4*(l-n);S.drawImage(p,r[t],r[t+1],r[t+2],r[t+3],s+h*u|0,0|e,g,d)}else{const i=t.getCharRegion(l);i&&S.drawImage(p,i.x,i.y,i.width,i.height,s+h*u|0,0|e,g,d)}}}tintedAtlasCache=/* @__PURE__ */new Map;getTintedAtlas(t,i){const s=i>>>0;if(s===this._lastTintColor&&this._lastTintResult)return this._lastTintResult;let e=this.tintedAtlasCache.get(s);if(e)return this._lastTintColor=s,this._lastTintResult=e,e;const h=t.image;e=document.createElement("canvas"),e.width=h.width,e.height=h.height;const l=e.getContext("2d");return l?(l.imageSmoothingEnabled=!1,l.drawImage(h,0,0),l.globalCompositeOperation="source-in",l.fillStyle=this.colorToCSS(i),l.fillRect(0,0,e.width,e.height),this.tintedAtlasCache.size>16&&this.tintedAtlasCache.clear(),this.tintedAtlasCache.set(s,e),this._lastTintColor=s,this._lastTintResult=e,e):h}drawLine(t,i,s,e,h,l){this.ctx.strokeStyle=this.colorToCSS(h),this.ctx.lineWidth=l,this.ctx.beginPath(),this.ctx.moveTo(t+.5,i+.5),this.ctx.lineTo(s+.5,e+.5),this.ctx.stroke()}fillCircle(i,s,e,h){this.ctx.fillStyle=this.colorToCSS(h),this.ctx.beginPath(),this.ctx.arc(i,s,e,0,t),this.ctx.fill()}strokeCircle(i,s,e,h,l){this.ctx.strokeStyle=this.colorToCSS(h),this.ctx.lineWidth=l,this.ctx.beginPath(),this.ctx.arc(i,s,e,0,t),this.ctx.stroke()}strokeArc(t,i,s,e,h,l,a,c="butt"){const o=this.ctx;o.strokeStyle=this.colorToCSS(l),o.lineWidth=a,o.lineCap=c,o.beginPath(),o.arc(t,i,s,e,h),o.stroke(),o.lineCap="butt"}beginPath(){this.ctx.beginPath()}moveTo(t,i){this.ctx.moveTo(t,i)}lineTo(t,i){this.ctx.lineTo(t,i)}strokePath(t,i){this.ctx.strokeStyle=this.colorToCSS(t),this.ctx.lineWidth=i,this.ctx.stroke()}fillPath(t){this.ctx.fillStyle=this.colorToCSS(t),this.ctx.fill()}pushClip(t,i,s,e){this.ctx.save(),this.ctx.beginPath(),this.ctx.rect(0|t,0|i,0|s,0|e),this.ctx.clip()}popClip(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}pushTranslateClip(t,i,s,e,h,l){this.ctx.save(),this.ctx.translate(0|t,0|i),this.ctx.beginPath(),this.ctx.rect(0|s,0|e,0|h,0|l),this.ctx.clip()}popTranslateClip(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}pushAlpha(t){this.alphaStack[this.alphaStackIndex++]=this.currentAlpha,this.currentAlpha*=t,this.ctx.globalAlpha=this.currentAlpha}popAlpha(){this.currentAlpha=this.alphaStack[--this.alphaStackIndex],this.ctx.globalAlpha=this.currentAlpha}pushTranslate(t,i){this.ctx.save(),this.ctx.translate(0|t,0|i)}popTranslate(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}pushScale(t,i){this.ctx.save(),this.ctx.scale(t,i)}popScale(){this.ctx.restore(),this.ctx.globalAlpha=this.currentAlpha}dispose(){this.colorCache.clear(),this.tintedAtlasCache.clear(),this._lastCSSColor=-1,this._lastCSSResult="",this._lastTintColor=-1,this._lastTintResult=null}colorToCSS(t){if((t>>>=0)===this._lastCSSColor)return this._lastCSSResult;let s=this.colorCache.get(t);if(void 0!==s)return this._lastCSSColor=t,this._lastCSSResult=s,s;const e=t>>>24&255,h=t>>>16&255,l=t>>>8&255,a=255&t;return s=255===a?"#"+i[e]+i[h]+i[l]:"#"+i[e]+i[h]+i[l]+i[a],this.colorCache.size>256&&this.colorCache.clear(),this.colorCache.set(t,s),this._lastCSSColor=t,this._lastCSSResult=s,s}}export{s as Canvas2DRenderer};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./atlas.cjs"),e=require("./glyph-rasterizer.cjs"),n=require("./ttf-parser.cjs");function r(t,e,n){const r=[],a=new Set;for(let o=0;o<t.length;o++){const i=t.codePointAt(o);void 0!==i&&(i>65535&&o++,i>=e&&i<=n||a.has(i)||(a.add(i),r.push(i)))}return r}function a(t,e,n,r){const a=new Map;for(let o=0;o<t.length;o++)a.set(t[o],{x:(e+o)*n,y:0,width:n,height:r});return a}function o(t,e,n,r,a,o){const i=t.createImageData(a,o),l=i.data;for(let t=0;t<e.length;t++){const n=e[t],r=4*t;l[r]=n,l[r+1]=n,l[r+2]=n,l[r+3]=n}t.putImageData(i,n,r)}function i(t,e,n,r,a,o,i){return{image:t,charWidth:e,lineHeight:n,firstChar:r,lastChar:a,charTable:o,getRegion:t=>null,getCharRegion:(()=>{const t={x:0,y:0,width:e,height:n};return n=>n>=r&&n<=a?(t.x=(n-r)*e,t):i.get(n)??null})()}}function l(t){return new Promise((e,n)=>{const r=new Image;r.onload=()=>e(r),r.onerror=()=>n(new Error("Failed to convert atlas canvas to image")),r.src=t.toDataURL()})}exports.createFontAtlas=async function(s){const{family:c,size:h,fontUrl:f,fontData:d,firstChar:g=32,lastChar:u=126,extraChars:m="",bold:w=!1,italic:p=!1,pixelPerfect:x=!1}=s;return!x||void 0===f&&void 0===d?async function(e,n,o,s,c,h,f){await document.fonts.ready;const d=`${f?"italic":"normal"} ${h?"bold":"normal"} ${n}px "${e}", monospace`,g=document.createElement("canvas");g.width=1,g.height=1;const u=g.getContext("2d");u.font=d,u.textBaseline="top";const m=Math.ceil(u.measureText("M").width),w=n,p=s-o+1,x=r(c,o,s),y=(p+x.length)*m,C=w,v=document.createElement("canvas");v.width=y,v.height=C;const b=v.getContext("2d");b.imageSmoothingEnabled=!1,b.font=d,b.textBaseline="top",b.fillStyle="#ffffff";for(let t=0;t<p;t++)b.fillText(String.fromCharCode(o+t),t*m,0);for(let t=0;t<x.length;t++)b.fillText(String.fromCodePoint(x[t]),(p+t)*m,0);const E=t.buildCharTable(m,w,o,s),T=a(x,p,m,w);return i(await l(v),m,w,o,s,E,T)}(c,h,g,u,m,w,p):async function(s,c,h,f,d,g){if(!s&&!c)throw new Error("Pixel perfect atlas requires fontUrl or fontData");const u=c??s;if(!u)throw new Error("Invalid font source");const m=await n.parseTTF(u),w=h/m.unitsPerEm,p=d-f+1,x=r(g,f,d),y=p+x.length,C=Math.round(m.advanceWidth*w),v=h,b=y*C,E=v,T=document.createElement("canvas");T.width=b,T.height=E;const P=T.getContext("2d");if(!P)throw new Error("Failed to create atlas canvas context");const S=new Uint8Array(C*v);for(let t=0;t<p;t++){const n=f+t,r=m.glyphs.get(n);r&&(S.fill(0),e.rasterizeGlyph(r,w,C,v,0,v,S),o(P,S,t*C,0,C,v))}for(let t=0;t<x.length;t++){const n=x[t],r=m.glyphs.get(n);r&&(S.fill(0),e.rasterizeGlyph(r,w,C,v,0,v,S),o(P,S,(p+t)*C,0,C,v))}const D=t.buildCharTable(C,v,f,d),M=a(x,p,C,v);return i(await l(T),C,v,f,d,D,M)}(f,d,h,g,u,m)};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FontAtlas } from './atlas';
|
|
2
|
+
/** Configuration for generating a font atlas at runtime. */
|
|
3
|
+
export interface FontAtlasOptions {
|
|
4
|
+
family: string;
|
|
5
|
+
size: number;
|
|
6
|
+
fontUrl?: string;
|
|
7
|
+
fontData?: ArrayBuffer;
|
|
8
|
+
firstChar?: number;
|
|
9
|
+
lastChar?: number;
|
|
10
|
+
extraChars?: string;
|
|
11
|
+
bold?: boolean;
|
|
12
|
+
italic?: boolean;
|
|
13
|
+
pixelPerfect?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a monospace font atlas from either a TTF/OTF source or the system font stack.
|
|
17
|
+
* When pixelPerfect is true and a font source is given, glyphs are rasterized via
|
|
18
|
+
* scanline fill rather than Canvas text rendering.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createFontAtlas(options: FontAtlasOptions): Promise<FontAtlas>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{buildCharTable as t}from"./atlas.mjs";import{rasterizeGlyph as e}from"./glyph-rasterizer.mjs";import{parseTTF as n}from"./ttf-parser.mjs";async function o(o){const{family:s,size:f,fontUrl:h,fontData:g,firstChar:d=32,lastChar:m=126,extraChars:u="",bold:w=!1,italic:p=!1,pixelPerfect:x=!1}=o;return!x||void 0===h&&void 0===g?async function(e,n,o,i,s,f,h){await document.fonts.ready;const g=`${h?"italic":"normal"} ${f?"bold":"normal"} ${n}px "${e}", monospace`,d=document.createElement("canvas");d.width=1,d.height=1;const m=d.getContext("2d");m.font=g,m.textBaseline="top";const u=Math.ceil(m.measureText("M").width),w=n,p=i-o+1,x=r(s,o,i),y=(p+x.length)*u,C=w,v=document.createElement("canvas");v.width=y,v.height=C;const E=v.getContext("2d");E.imageSmoothingEnabled=!1,E.font=g,E.textBaseline="top",E.fillStyle="#ffffff";for(let t=0;t<p;t++)E.fillText(String.fromCharCode(o+t),t*u,0);for(let t=0;t<x.length;t++)E.fillText(String.fromCodePoint(x[t]),(p+t)*u,0);const P=t(u,w,o,i),D=a(x,p,u,w);return l(await c(v),u,w,o,i,P,D)}(s,f,d,m,u,w,p):async function(o,s,f,h,g,d){if(!o&&!s)throw new Error("Pixel perfect atlas requires fontUrl or fontData");const m=s??o;if(!m)throw new Error("Invalid font source");const u=await n(m),w=f/u.unitsPerEm,p=g-h+1,x=r(d,h,g),y=p+x.length,C=Math.round(u.advanceWidth*w),v=f,E=y*C,P=v,D=document.createElement("canvas");D.width=E,D.height=P;const S=D.getContext("2d");if(!S)throw new Error("Failed to create atlas canvas context");const b=new Uint8Array(C*v);for(let t=0;t<p;t++){const n=h+t,o=u.glyphs.get(n);o&&(b.fill(0),e(o,w,C,v,0,v,b),i(S,b,t*C,0,C,v))}for(let t=0;t<x.length;t++){const n=x[t],o=u.glyphs.get(n);o&&(b.fill(0),e(o,w,C,v,0,v,b),i(S,b,(p+t)*C,0,C,v))}const I=t(C,v,h,g),M=a(x,p,C,v);return l(await c(D),C,v,h,g,I,M)}(h,g,f,d,m,u)}function r(t,e,n){const o=[],r=/* @__PURE__ */new Set;for(let a=0;a<t.length;a++){const i=t.codePointAt(a);void 0!==i&&(i>65535&&a++,i>=e&&i<=n||r.has(i)||(r.add(i),o.push(i)))}return o}function a(t,e,n,o){const r=/* @__PURE__ */new Map;for(let a=0;a<t.length;a++)r.set(t[a],{x:(e+a)*n,y:0,width:n,height:o});return r}function i(t,e,n,o,r,a){const i=t.createImageData(r,a),l=i.data;for(let t=0;t<e.length;t++){const n=e[t],o=4*t;l[o]=n,l[o+1]=n,l[o+2]=n,l[o+3]=n}t.putImageData(i,n,o)}function l(t,e,n,o,r,a,i){return{image:t,charWidth:e,lineHeight:n,firstChar:o,lastChar:r,charTable:a,getRegion:t=>null,getCharRegion:/* @__PURE__ */(()=>{const t={x:0,y:0,width:e,height:n};return n=>n>=o&&n<=r?(t.x=(n-o)*e,t):i.get(n)??null})()}}function c(t){return new Promise((e,n)=>{const o=new Image;o.onload=()=>e(o),o.onerror=()=>n(new Error("Failed to convert atlas canvas to image")),o.src=t.toDataURL()})}export{o as createFontAtlas};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function t(t,o,n,e){const s=t.points;if(0===s.length)return[];const r=[],l=[];for(let t=0;t<s.length;t++){const o=s[t],n=s[(t+1)%s.length];l.push(o),o.onCurve||n.onCurve||l.push({x:(o.x+n.x)/2,y:(o.y+n.y)/2,onCurve:!0})}for(let t=0;t<l.length;t++){const s=l[t];if(s.onCurve)r.push({x:s.x*o+n,y:-s.y*o+e});else{const c=l[(t-1+l.length)%l.length],h=l[(t+1)%l.length];if(c.onCurve&&h.onCurve){const t=4;for(let l=1;l<=t;l++){const u=l/t,y=1-u,f=y*y*c.x+2*y*u*s.x+u*u*h.x,x=y*y*c.y+2*y*u*s.y+u*u*h.y;r.push({x:f*o+n,y:-x*o+e})}}}}return r}Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),exports.rasterizeGlyph=function(o,n,e,s,r,l,c){const h=[];for(const e of o.contours){const o=t(e,n,r,l);for(let t=0;t<o.length;t++){const n=o[t],e=o[(t+1)%o.length];n.y!==e.y&&h.push({x1:n.x,y1:n.y,x2:e.x,y2:e.y})}}for(let t=0;t<s;t++){const o=t+.5,n=[];for(const t of h){const{x1:e,y1:s,x2:r,y2:l}=t,c=Math.min(s,l),h=Math.max(s,l);if(o<c||o>=h)continue;const u=(o-s)/(l-s);n.push(e+u*(r-e))}n.sort((t,o)=>t-o);for(let o=0;o<n.length-1;o+=2){const s=Math.max(0,Math.ceil(n[o])),r=Math.min(e,Math.floor(n[o+1]));for(let o=s;o<r;o++)c[t*e+o]=255}}};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TTFGlyph } from './ttf-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Rasterizes a single glyph outline into a coverage buffer, writing 255 for
|
|
4
|
+
* pixels inside the outline and leaving the rest at 0, with the scanline
|
|
5
|
+
* polygon fill algorithm (Foley and van Dam, Computer Graphics: Principles and
|
|
6
|
+
* Practice, 1990). Non-horizontal contour edges are collected once, then each
|
|
7
|
+
* pixel row is filled between sorted edge crossings under the even-odd rule.
|
|
8
|
+
* offsetX and offsetY place the glyph, with offsetY acting as the baseline.
|
|
9
|
+
*/
|
|
10
|
+
export declare function rasterizeGlyph(glyph: TTFGlyph, scale: number, width: number, height: number, offsetX: number, offsetY: number, buffer: Uint8Array): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(t,o,e,s,r,h,c){const l=[];for(const e of t.contours){const t=n(e,o,r,h);for(let n=0;n<t.length;n++){const o=t[n],e=t[(n+1)%t.length];o.y!==e.y&&l.push({x1:o.x,y1:o.y,x2:e.x,y2:e.y})}}for(let t=0;t<s;t++){const n=t+.5,o=[];for(const t of l){const{x1:e,y1:s,x2:r,y2:h}=t,c=Math.min(s,h),l=Math.max(s,h);if(n<c||n>=l)continue;const f=(n-s)/(h-s);o.push(e+f*(r-e))}o.sort((t,n)=>t-n);for(let n=0;n<o.length-1;n+=2){const s=Math.max(0,Math.ceil(o[n])),r=Math.min(e,Math.floor(o[n+1]));for(let n=s;n<r;n++)c[t*e+n]=255}}}function n(t,n,o,e){const s=t.points;if(0===s.length)return[];const r=[],h=[];for(let t=0;t<s.length;t++){const n=s[t],o=s[(t+1)%s.length];h.push(n),n.onCurve||o.onCurve||h.push({x:(n.x+o.x)/2,y:(n.y+o.y)/2,onCurve:!0})}for(let t=0;t<h.length;t++){const s=h[t];if(s.onCurve)r.push({x:s.x*n+o,y:-s.y*n+e});else{const c=h[(t-1+h.length)%h.length],l=h[(t+1)%h.length];if(c.onCurve&&l.onCurve){const t=4;for(let h=1;h<=t;h++){const f=h/t,u=1-f,x=u*u*c.x+2*u*f*s.x+f*f*l.x,y=u*u*c.y+2*u*f*s.y+f*f*l.y;r.push({x:x*n+o,y:-y*n+e})}}}}return r}export{t as rasterizeGlyph};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),exports.InputDispatcher=class{surface;focusedWidget=null;activePointers=Array.from({length:10},()=>({id:0,widget:null}));activePointerCount=0;cachedRect=null;rectFrameId=-1;_localX=0;_localY=0;oldHoverChain=new Array(64).fill(null);newHoverChain=new Array(64).fill(null);oldHoverLength=0;newHoverLength=0;onPointerDownBound;onPointerUpBound;onPointerMoveBound;onPointerCancelBound;onPointerLeaveBound;onWheelBound;onKeyDownBound;onKeyUpBound;constructor(e){this.surface=e,this.onPointerDownBound=this.handlePointerDown.bind(this),this.onPointerUpBound=this.handlePointerUp.bind(this),this.onPointerMoveBound=this.handlePointerMove.bind(this),this.onPointerCancelBound=this.handlePointerCancel.bind(this),this.onPointerLeaveBound=this.clearHoverChain.bind(this),this.onWheelBound=this.handleWheel.bind(this),this.onKeyDownBound=this.handleKeyDown.bind(this),this.onKeyUpBound=this.handleKeyUp.bind(this);const t=e.canvas;t.style.touchAction="none",t.addEventListener("pointerdown",this.onPointerDownBound),t.addEventListener("pointerup",this.onPointerUpBound),t.addEventListener("pointermove",this.onPointerMoveBound),t.addEventListener("pointercancel",this.onPointerCancelBound),t.addEventListener("pointerleave",this.onPointerLeaveBound),t.addEventListener("wheel",this.onWheelBound,{passive:!1}),t.addEventListener("keydown",this.onKeyDownBound),t.addEventListener("keyup",this.onKeyUpBound),t.tabIndex=0}setFocus(e){this.focusedWidget!==e&&(this.focusedWidget&&(this.focusedWidget.focused=!1,this.focusedWidget.onBlur()),this.focusedWidget=e,e&&(e.focused=!0,e.onFocus()))}getActivePointerCount(){return this.activePointerCount}dispose(){const e=this.surface.canvas;e.removeEventListener("pointerdown",this.onPointerDownBound),e.removeEventListener("pointerup",this.onPointerUpBound),e.removeEventListener("pointermove",this.onPointerMoveBound),e.removeEventListener("pointercancel",this.onPointerCancelBound),e.removeEventListener("pointerleave",this.onPointerLeaveBound),e.removeEventListener("wheel",this.onWheelBound),e.removeEventListener("keydown",this.onKeyDownBound),e.removeEventListener("keyup",this.onKeyUpBound),this.activePointerCount=0,this.clearHoverChain()}getRect(e=!1){const t=this.surface.frameId;return!e&&this.cachedRect&&this.rectFrameId===t||(this.cachedRect=this.surface.canvas.getBoundingClientRect(),this.rectFrameId=t),this.cachedRect}computeLocalCoords(e,t=!1){const n=this.getRect(t);this._localX=e.clientX-n.left,this._localY=e.clientY-n.top}findActivePointer(e){for(let t=0;t<this.activePointerCount;t++)if(this.activePointers[t].id===e)return t;return-1}buildChain(e,t){let n=0,i=e;for(;i&&n<64;)t[n++]=i,i=i.parent;for(let e=0,i=n-1;e<i;e++,i--){const n=t[e];t[e]=t[i],t[i]=n}return n}updateHover(e){this.newHoverLength=this.buildChain(e,this.newHoverChain);const t=Math.min(this.oldHoverLength,this.newHoverLength);let n=0;for(;n<t&&this.oldHoverChain[n]===this.newHoverChain[n];)n++;for(let e=this.oldHoverLength-1;e>=n;e--){const t=this.oldHoverChain[e];t&&t.onPointerLeave()}for(let e=n;e<this.newHoverLength;e++){const t=this.newHoverChain[e];t&&t.onPointerEnter()}const i=this.oldHoverChain;this.oldHoverChain=this.newHoverChain,this.newHoverChain=i,this.oldHoverLength=this.newHoverLength;for(let e=0;e<this.newHoverLength;e++)this.newHoverChain[e]=null}clearHoverChain(){for(let e=this.oldHoverLength-1;e>=0;e--){const t=this.oldHoverChain[e];t&&t.onPointerLeave(),this.oldHoverChain[e]=null}this.oldHoverLength=0}handlePointerDown(e){if(this.activePointerCount>=10)return;this.computeLocalCoords(e,!0);const t=this._localX,n=this._localY,i=this.findHit(t,n);if(!i)return;let o=i;for(;o;){const i=t-this.widgetGlobalX(o),s=n-this.widgetGlobalY(o);if(o.onPointerDown(e.pointerId,i,s,e.button)){this.surface.canvas.setPointerCapture(e.pointerId);const t=this.activePointers[this.activePointerCount];return t.id=e.pointerId,t.widget=o,this.activePointerCount++,void(o.focusable&&this.setFocus(o))}o=o.parent}}handlePointerUp(e){const t=this.findActivePointer(e.pointerId);if(-1===t)return;const n=this.activePointers[t];this.computeLocalCoords(e);const i=this._localX,o=this._localY,s=n.widget;if(s.onPointerUp(e.pointerId,i-this.widgetGlobalX(s),o-this.widgetGlobalY(s),e.button),this.activePointerCount--,t<this.activePointerCount){const e=this.activePointers[this.activePointerCount];this.activePointers[t].id=e.id,this.activePointers[t].widget=e.widget}this.activePointers[this.activePointerCount].widget=null,this.surface.canvas.releasePointerCapture(e.pointerId)}handlePointerMove(e){this.computeLocalCoords(e);const t=this._localX,n=this._localY,i=this.findActivePointer(e.pointerId);if(-1!==i){const o=this.activePointers[i];return void o.widget.onPointerMove(e.pointerId,t-this.widgetGlobalX(o.widget),n-this.widgetGlobalY(o.widget))}const o=this.findHit(t,n);this.updateHover(o),o&&o.onPointerMove(e.pointerId,t-this.widgetGlobalX(o),n-this.widgetGlobalY(o))}handlePointerCancel(e){const t=this.findActivePointer(e.pointerId);if(-1!==t){if(this.activePointerCount--,t<this.activePointerCount){const e=this.activePointers[this.activePointerCount];this.activePointers[t].id=e.id,this.activePointers[t].widget=e.widget}this.activePointers[this.activePointerCount].widget=null}}handleWheel(e){const t=this.getRect(),n=e.clientX-t.left,i=e.clientY-t.top;let o=this.findHit(n,i);for(;o;){if(o.onWheel(e.deltaX,e.deltaY))return void e.preventDefault();o=o.parent}}handleKeyDown(e){let t=this.focusedWidget;for(;t;){if(t.onKeyDown(e.key,e.code,e.ctrlKey||e.metaKey,e.shiftKey,e.altKey))return void e.preventDefault();t=t.parent}}handleKeyUp(e){this.focusedWidget&&this.focusedWidget.onKeyUp(e.key,e.code)}findHit(e,t){return this.surface.getRoot()?.hitTest(e,t)??null}widgetGlobalX(e){return e.absoluteX}widgetGlobalY(e){return e.absoluteY}};
|