@tableslayer/ui 0.1.3 → 0.1.4

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 (205) hide show
  1. package/package.json +2 -13
  2. package/src/lib/components/Avatar/Avatar.svelte +82 -0
  3. package/src/lib/components/Avatar/AvatarFileInput.svelte +85 -0
  4. package/src/lib/components/Avatar/AvatarPopover.svelte +34 -0
  5. package/src/lib/components/Avatar/index.ts +4 -0
  6. package/src/lib/components/Avatar/types.ts +24 -0
  7. package/src/lib/components/BrushSizeSlider/BrushSizeSlider.svelte +174 -0
  8. package/src/lib/components/BrushSizeSlider/index.ts +1 -0
  9. package/src/lib/components/Button/Button.svelte +182 -0
  10. package/src/lib/components/Button/ConfirmActionButton.svelte +98 -0
  11. package/src/lib/components/Button/IconButton.svelte +121 -0
  12. package/src/lib/components/Button/RadioButton.svelte +93 -0
  13. package/src/lib/components/Button/index.ts +5 -0
  14. package/src/lib/components/Button/types.ts +54 -0
  15. package/src/lib/components/CardFan/CardFan.svelte +165 -0
  16. package/src/lib/components/CardFan/index.ts +2 -0
  17. package/src/lib/components/CardFan/types.ts +6 -0
  18. package/src/lib/components/CodeBlock/Code.svelte +7 -0
  19. package/src/lib/components/CodeBlock/CodeBlock.svelte +102 -0
  20. package/src/lib/components/CodeBlock/index.ts +3 -0
  21. package/src/lib/components/CodeBlock/types.ts +10 -0
  22. package/src/lib/components/ColorMode/ColorMode.svelte +8 -0
  23. package/src/lib/components/ColorMode/index.ts +2 -0
  24. package/src/lib/components/ColorMode/types.ts +12 -0
  25. package/src/lib/components/ColorPicker/ColorPicker.svelte +838 -0
  26. package/src/lib/components/ColorPicker/ColorPickerSwatch.svelte +32 -0
  27. package/src/lib/components/ColorPicker/index.ts +3 -0
  28. package/src/lib/components/ColorPicker/types.ts +51 -0
  29. package/src/lib/components/ContextMenu/ContextMenu.svelte +86 -0
  30. package/src/lib/components/ContextMenu/index.ts +2 -0
  31. package/src/lib/components/ContextMenu/types.ts +15 -0
  32. package/src/lib/components/DrawingSliders/DrawingSliders.svelte +379 -0
  33. package/src/lib/components/DrawingSliders/index.ts +1 -0
  34. package/src/lib/components/Editor/Editor.svelte +825 -0
  35. package/src/lib/components/Editor/index.ts +1 -0
  36. package/src/lib/components/FogSliders/FogSliders.svelte +33 -0
  37. package/src/lib/components/FogSliders/index.ts +1 -0
  38. package/src/lib/components/Hr/Hr.svelte +15 -0
  39. package/src/lib/components/Hr/index.ts +1 -0
  40. package/src/lib/components/Icon/Icon.svelte +6 -0
  41. package/src/lib/components/Icon/index.ts +2 -0
  42. package/src/lib/components/Icon/types.ts +20 -0
  43. package/src/lib/components/Input/DualInputSlider.svelte +126 -0
  44. package/src/lib/components/Input/FileInput.svelte +176 -0
  45. package/src/lib/components/Input/FormControl.svelte +150 -0
  46. package/src/lib/components/Input/FormError.svelte +37 -0
  47. package/src/lib/components/Input/Input.svelte +56 -0
  48. package/src/lib/components/Input/InputCheckbox.svelte +99 -0
  49. package/src/lib/components/Input/InputSlider.svelte +86 -0
  50. package/src/lib/components/Input/Label.svelte +19 -0
  51. package/src/lib/components/Input/index.ts +9 -0
  52. package/src/lib/components/Input/types.ts +39 -0
  53. package/src/lib/components/Link/Link.svelte +41 -0
  54. package/src/lib/components/Link/LinkBox.svelte +20 -0
  55. package/src/lib/components/Link/LinkOverlay.svelte +23 -0
  56. package/src/lib/components/Link/index.ts +4 -0
  57. package/src/lib/components/Link/types.ts +17 -0
  58. package/src/lib/components/Loading/Loader.svelte +60 -0
  59. package/src/lib/components/Loading/Skeleton.svelte +9 -0
  60. package/src/lib/components/Loading/index.ts +2 -0
  61. package/src/lib/components/Logo/Logo.svelte +16 -0
  62. package/src/lib/components/Logo/index.ts +1 -0
  63. package/src/lib/components/MarkerTooltip/MarkerTooltip.svelte +435 -0
  64. package/src/lib/components/MarkerTooltip/index.ts +1 -0
  65. package/src/lib/components/Menu/SelectorMenu.svelte +280 -0
  66. package/src/lib/components/Menu/index.ts +2 -0
  67. package/src/lib/components/Menu/types.ts +17 -0
  68. package/src/lib/components/MyCounterButton.svelte +11 -0
  69. package/src/lib/components/Panel/index.ts +2 -0
  70. package/src/lib/components/Panel/panel.svelte +18 -0
  71. package/src/lib/components/Panel/types.ts +8 -0
  72. package/src/lib/components/PersistButton/PersistButton.svelte +100 -0
  73. package/src/lib/components/PersistButton/index.ts +1 -0
  74. package/src/lib/components/Popover/Popover.svelte +81 -0
  75. package/src/lib/components/Popover/index.ts +2 -0
  76. package/src/lib/components/Popover/types.ts +19 -0
  77. package/src/lib/components/PropsTable/PropsTable.svelte +107 -0
  78. package/src/lib/components/RadialMenu/EffectPreview.svelte +36 -0
  79. package/src/lib/components/RadialMenu/EffectPreviewScene.svelte +194 -0
  80. package/src/lib/components/RadialMenu/RadialMenu.svelte +503 -0
  81. package/src/lib/components/RadialMenu/RadialMenuItem.svelte +176 -0
  82. package/src/lib/components/RadialMenu/index.ts +2 -0
  83. package/src/lib/components/RadialMenu/types.ts +35 -0
  84. package/src/lib/components/Select/Select.svelte +342 -0
  85. package/src/lib/components/Select/index.ts +2 -0
  86. package/src/lib/components/Select/types.ts +22 -0
  87. package/src/lib/components/Spacer/Spacer.svelte +14 -0
  88. package/src/lib/components/Spacer/index.ts +2 -0
  89. package/src/lib/components/Spacer/types.ts +5 -0
  90. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationLayer.svelte +445 -0
  91. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationMaterial.svelte +167 -0
  92. package/src/lib/components/Stage/components/AnnotationLayer/types.ts +196 -0
  93. package/src/lib/components/Stage/components/CursorLayer/CursorLayer.svelte +148 -0
  94. package/src/lib/components/Stage/components/CursorLayer/cursor.svg +26 -0
  95. package/src/lib/components/Stage/components/CursorLayer/index.ts +2 -0
  96. package/src/lib/components/Stage/components/CursorLayer/types.ts +23 -0
  97. package/src/lib/components/Stage/components/DrawingLayer/DrawingMaterial.svelte +364 -0
  98. package/src/lib/components/Stage/components/DrawingLayer/types.ts +65 -0
  99. package/src/lib/components/Stage/components/EdgeOverlayLayer/EdgeOverlayLayer.svelte +72 -0
  100. package/src/lib/components/Stage/components/EdgeOverlayLayer/types.ts +34 -0
  101. package/src/lib/components/Stage/components/FogLayer/FogLayer.svelte +75 -0
  102. package/src/lib/components/Stage/components/FogLayer/types.ts +51 -0
  103. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarLayer.svelte +249 -0
  104. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarMaterial.svelte +200 -0
  105. package/src/lib/components/Stage/components/FogOfWarLayer/types.ts +116 -0
  106. package/src/lib/components/Stage/components/GridLayer/GridLayer.svelte +20 -0
  107. package/src/lib/components/Stage/components/GridLayer/GridMaterial.svelte +69 -0
  108. package/src/lib/components/Stage/components/GridLayer/types.ts +79 -0
  109. package/src/lib/components/Stage/components/LayerInput/LayerInput.svelte +300 -0
  110. package/src/lib/components/Stage/components/MapLayer/MapLayer.svelte +196 -0
  111. package/src/lib/components/Stage/components/MapLayer/dataSources/GifDataSource.ts +265 -0
  112. package/src/lib/components/Stage/components/MapLayer/dataSources/IMapDataSource.ts +55 -0
  113. package/src/lib/components/Stage/components/MapLayer/dataSources/ImageDataSource.ts +87 -0
  114. package/src/lib/components/Stage/components/MapLayer/dataSources/VideoDataSource.ts +150 -0
  115. package/src/lib/components/Stage/components/MapLayer/dataSources/dataSourceFactory.ts +48 -0
  116. package/src/lib/components/Stage/components/MapLayer/dataSources/index.ts +16 -0
  117. package/src/lib/components/Stage/components/MapLayer/types.ts +58 -0
  118. package/src/lib/components/Stage/components/MarkerLayer/MarkerLayer.svelte +398 -0
  119. package/src/lib/components/Stage/components/MarkerLayer/MarkerToken.svelte +262 -0
  120. package/src/lib/components/Stage/components/MarkerLayer/types.ts +126 -0
  121. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementLayer.svelte +364 -0
  122. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementManager.svelte +473 -0
  123. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BaseMeasurement.ts +427 -0
  124. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BeamMeasurement.ts +105 -0
  125. package/src/lib/components/Stage/components/MeasurementLayer/measurements/CircleMeasurement.ts +98 -0
  126. package/src/lib/components/Stage/components/MeasurementLayer/measurements/ConeMeasurement.ts +163 -0
  127. package/src/lib/components/Stage/components/MeasurementLayer/measurements/LineMeasurement.ts +102 -0
  128. package/src/lib/components/Stage/components/MeasurementLayer/measurements/RectangleMeasurement.ts +120 -0
  129. package/src/lib/components/Stage/components/MeasurementLayer/measurements/index.ts +7 -0
  130. package/src/lib/components/Stage/components/MeasurementLayer/types.ts +94 -0
  131. package/src/lib/components/Stage/components/MeasurementLayer/utils/canvasDrawing.ts +357 -0
  132. package/src/lib/components/Stage/components/MeasurementLayer/utils/distanceCalculations.ts +170 -0
  133. package/src/lib/components/Stage/components/ParticleSystem/ParticleSystem.svelte +220 -0
  134. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/ash.png +0 -0
  135. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/leaves.png +0 -0
  136. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/rain.png +0 -0
  137. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/snow.png +0 -0
  138. package/src/lib/components/Stage/components/ParticleSystem/rng.js +20 -0
  139. package/src/lib/components/Stage/components/ParticleSystem/types.ts +95 -0
  140. package/src/lib/components/Stage/components/PerformanceDebugger/PerformanceDebugger.svelte +144 -0
  141. package/src/lib/components/Stage/components/PerformanceDebugger/index.ts +1 -0
  142. package/src/lib/components/Stage/components/PerformanceOverlay/PerformanceOverlay.svelte +208 -0
  143. package/src/lib/components/Stage/components/PerformanceOverlay/index.ts +1 -0
  144. package/src/lib/components/Stage/components/PointerInputManager/PointerInputManager.svelte +201 -0
  145. package/src/lib/components/Stage/components/Scene/Scene.svelte +651 -0
  146. package/src/lib/components/Stage/components/Scene/luts.ts +24 -0
  147. package/src/lib/components/Stage/components/Scene/types.ts +225 -0
  148. package/src/lib/components/Stage/components/Stage/Stage.svelte +332 -0
  149. package/src/lib/components/Stage/components/Stage/types.ts +136 -0
  150. package/src/lib/components/Stage/components/WeatherLayer/WeatherLayer.svelte +135 -0
  151. package/src/lib/components/Stage/components/WeatherLayer/presets/AshPreset.ts +71 -0
  152. package/src/lib/components/Stage/components/WeatherLayer/presets/LeavesPreset.ts +70 -0
  153. package/src/lib/components/Stage/components/WeatherLayer/presets/RainPreset.ts +68 -0
  154. package/src/lib/components/Stage/components/WeatherLayer/presets/SnowPreset.ts +70 -0
  155. package/src/lib/components/Stage/components/WeatherLayer/presets/index.ts +6 -0
  156. package/src/lib/components/Stage/components/WeatherLayer/types.ts +35 -0
  157. package/src/lib/components/Stage/helpers/clippingPlaneStore.svelte.ts +28 -0
  158. package/src/lib/components/Stage/helpers/debugState.svelte.ts +18 -0
  159. package/src/lib/components/Stage/helpers/grid.ts +548 -0
  160. package/src/lib/components/Stage/helpers/lazyBrush.ts +171 -0
  161. package/src/lib/components/Stage/helpers/performanceMetrics.svelte.ts +220 -0
  162. package/src/lib/components/Stage/helpers/utils.ts +21 -0
  163. package/src/lib/components/Stage/index.ts +49 -0
  164. package/src/lib/components/Stage/shaders/AnnotationEffects.frag +1070 -0
  165. package/src/lib/components/Stage/shaders/Annotations.frag +29 -0
  166. package/src/lib/components/Stage/shaders/Drawing.frag +83 -0
  167. package/src/lib/components/Stage/shaders/Drawing.vert +5 -0
  168. package/src/lib/components/Stage/shaders/Fog.frag +147 -0
  169. package/src/lib/components/Stage/shaders/FractalNoise.frag +96 -0
  170. package/src/lib/components/Stage/shaders/GridShader.frag +174 -0
  171. package/src/lib/components/Stage/shaders/Overlay.frag +23 -0
  172. package/src/lib/components/Stage/shaders/Overlay.vert +0 -0
  173. package/src/lib/components/Stage/shaders/Particles.frag +27 -0
  174. package/src/lib/components/Stage/shaders/Particles.vert +51 -0
  175. package/src/lib/components/Stage/shaders/ToolOutline.frag +59 -0
  176. package/src/lib/components/Stage/shaders/default.vert +8 -0
  177. package/src/lib/components/Stage/types.ts +4 -0
  178. package/src/lib/components/Table/Table.svelte +16 -0
  179. package/src/lib/components/Table/Td.svelte +17 -0
  180. package/src/lib/components/Table/Th.svelte +18 -0
  181. package/src/lib/components/Table/index.ts +4 -0
  182. package/src/lib/components/Table/types.ts +14 -0
  183. package/src/lib/components/Text/Text.svelte +23 -0
  184. package/src/lib/components/Text/index.ts +2 -0
  185. package/src/lib/components/Text/types.ts +12 -0
  186. package/src/lib/components/Title/Title.svelte +54 -0
  187. package/src/lib/components/Title/index.ts +2 -0
  188. package/src/lib/components/Title/types.ts +9 -0
  189. package/src/lib/components/Toast/Toast.svelte +155 -0
  190. package/src/lib/components/Toast/index.ts +5 -0
  191. package/src/lib/components/Toast/toastCookie.ts +24 -0
  192. package/src/lib/components/Toast/types.ts +6 -0
  193. package/src/lib/components/ToolTip/ToolTip.svelte +70 -0
  194. package/src/lib/components/ToolTip/index.ts +2 -0
  195. package/src/lib/components/ToolTip/types.ts +14 -0
  196. package/src/lib/components/index.ts +32 -0
  197. package/src/lib/components/types.ts +0 -0
  198. package/src/lib/index.ts +2 -0
  199. package/src/lib/styles/globals.css +108 -0
  200. package/src/lib/styles/normalize.css +9 -0
  201. package/src/lib/styles/reset.css +133 -0
  202. package/src/lib/styles/utilities.css +179 -0
  203. package/src/lib/styles/vars.css +1103 -0
  204. package/src/lib/types/awareness.ts +17 -0
  205. package/src/lib/utils/rle.ts +217 -0
@@ -0,0 +1,225 @@
1
+ import type { ToneMappingMode } from 'postprocessing';
2
+
3
+ export enum SceneLoadingState {
4
+ LoadingMap = 1,
5
+ Resizing = 2,
6
+ Rendering = 3,
7
+ Initialized = 4
8
+ }
9
+
10
+ export enum SceneRotation {
11
+ Deg0 = 0,
12
+ Deg90 = 90,
13
+ Deg180 = 180,
14
+ Deg270 = 270
15
+ }
16
+
17
+ export enum SceneLayer {
18
+ Main = 0,
19
+ Overlay = 1,
20
+ Input = 2
21
+ }
22
+
23
+ export enum SceneLayerOrder {
24
+ Background = 0,
25
+ Map = 10,
26
+ Fog = 20,
27
+ Weather = 30,
28
+ EffectAnnotation = 35, // Effect annotations render under fog of war
29
+ Marker = 37, // Markers render above effect annotations but below fog of war
30
+ FogOfWar = 40,
31
+ Grid = 50,
32
+ EdgeOverlay = 70,
33
+ Annotation = 80, // Color annotations render over fog of war
34
+ Measurement = 90,
35
+ Cursor = 100
36
+ }
37
+
38
+ export interface SceneLayerProps {
39
+ /**
40
+ * Whether the scene layer should auto fit
41
+ */
42
+ autoFit: boolean;
43
+
44
+ /**
45
+ * The position of the scene layer within the canvas
46
+ */
47
+ offset: { x: number; y: number };
48
+
49
+ /**
50
+ * The rotation of the scene layer in degrees
51
+ */
52
+ rotation: SceneRotation;
53
+
54
+ /**
55
+ * The scale of the scene layer
56
+ */
57
+ zoom: number;
58
+ }
59
+
60
+ import type { Marker } from '../MarkerLayer/types';
61
+
62
+ export interface SceneExports {
63
+ fill: () => void;
64
+ fit: () => void;
65
+ generateThumbnail: () => Promise<Blob>;
66
+ getMarkerScreenPosition: (marker: Marker) => { x: number; y: number } | null;
67
+
68
+ annotations: {
69
+ clear: (layerId: string) => void;
70
+ toRLE: () => Promise<Uint8Array>;
71
+ fromRLE: (rleData: Uint8Array, width: number, height: number) => Promise<void>;
72
+ loadMask: (layerId: string, rleData: Uint8Array) => Promise<void>;
73
+ isDrawing: () => boolean;
74
+ };
75
+
76
+ fogOfWar: {
77
+ clear: () => void;
78
+ reset: () => void;
79
+ toPng: () => Promise<Blob>;
80
+ toRLE: () => Promise<Uint8Array>;
81
+ fromRLE: (rleData: Uint8Array, width: number, height: number) => Promise<void>;
82
+ isDrawing: () => boolean;
83
+ };
84
+
85
+ map: {
86
+ fit: () => void;
87
+ fill: () => void;
88
+ getSize: () => { width: number; height: number } | null;
89
+ };
90
+
91
+ markers: {
92
+ isHoveringMarker: boolean;
93
+ isDraggingMarker: boolean;
94
+ hoveredMarker: Marker | null;
95
+ selectedMarker: Marker | null;
96
+ maintainHover: (maintain: boolean) => void;
97
+ onSceneChange: () => void;
98
+ };
99
+
100
+ getMarkerSizeInScreenSpace: (markerSize: number) => number;
101
+
102
+ measurement: {
103
+ getCurrentMeasurement: () => {
104
+ startPoint: { x: number; y: number } | null;
105
+ endPoint: { x: number; y: number } | null;
106
+ type: number;
107
+ } | null;
108
+ isDrawing: () => boolean;
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Properties for post-processing effects
114
+ */
115
+ export interface PostProcessingProps {
116
+ /**
117
+ * Whether post-processing is enabled
118
+ */
119
+ enabled: boolean;
120
+
121
+ /**
122
+ * Bloom effect properties
123
+ */
124
+ bloom: {
125
+ /**
126
+ * Whether the bloom effect is enabled
127
+ */
128
+ enabled: boolean;
129
+
130
+ /**
131
+ * Overall intensity of the bloom effect (0-1)
132
+ */
133
+ intensity: number;
134
+
135
+ /**
136
+ * Luminance threshold for what pixels will bloom (0-1)
137
+ */
138
+ threshold: number;
139
+
140
+ /**
141
+ * How smoothly the bloom transitions at the threshold (0-1)
142
+ */
143
+ smoothing: number;
144
+
145
+ /**
146
+ * Radius of the bloom effect
147
+ */
148
+ radius: number;
149
+
150
+ /**
151
+ * Levels of the bloom effect
152
+ */
153
+ levels: number;
154
+
155
+ /**
156
+ * Whether to apply mipmap blur
157
+ */
158
+ mipmapBlur: boolean;
159
+ };
160
+
161
+ /**
162
+ * Chromatic aberration effect properties
163
+ */
164
+ chromaticAberration: {
165
+ /**
166
+ * Whether the chromatic aberration effect is enabled
167
+ */
168
+ enabled: boolean;
169
+
170
+ /**
171
+ * The offset of the chromatic aberration effect
172
+ */
173
+ offset: number;
174
+ };
175
+
176
+ /**
177
+ * LUT effect properties
178
+ */
179
+ lut: {
180
+ /**
181
+ * Whether the LUT3D effect is enabled
182
+ */
183
+ enabled: boolean;
184
+
185
+ /**
186
+ * The URL of the LUT to use
187
+ */
188
+ url: string | null;
189
+ };
190
+
191
+ /**
192
+ * Tone mapping effect properties
193
+ */
194
+ toneMapping: {
195
+ /**
196
+ * Whether the tone mapping effect is enabled
197
+ */
198
+ enabled: boolean;
199
+
200
+ /**
201
+ * The mode of the tone mapping effect
202
+ */
203
+ mode: ToneMappingMode;
204
+ };
205
+
206
+ /**
207
+ * Vignette effect properties
208
+ */
209
+ vignette: {
210
+ /**
211
+ * Whether the vignette effect is enabled
212
+ */
213
+ enabled: boolean;
214
+
215
+ /**
216
+ * How far from the center the vignette starts (0-1)
217
+ */
218
+ offset: number;
219
+
220
+ /**
221
+ * How dark the vignette effect is (0-1)
222
+ */
223
+ darkness: number;
224
+ };
225
+ }
@@ -0,0 +1,332 @@
1
+ <script lang="ts">
2
+ import { Canvas, T } from '@threlte/core';
3
+ import * as THREE from 'three';
4
+ import type { Callbacks, StageProps } from './types';
5
+ import Scene from '../Scene/Scene.svelte';
6
+ import { type SceneExports, SceneLayerOrder } from '../Scene/types';
7
+ import { setContext } from 'svelte';
8
+ import { PerfMonitor } from '@threlte/extras';
9
+ import { MarkerTooltip } from '../../../MarkerTooltip';
10
+
11
+ import type { CursorData } from './types';
12
+
13
+ /**
14
+ * Creates a custom WebGL renderer with high-performance settings.
15
+ * This fixes Chrome/Mac performance issues by:
16
+ * - Requesting high-performance GPU mode (critical for macOS)
17
+ * - Disabling unnecessary features like stencil buffer
18
+ */
19
+ const createRenderer = (canvas: HTMLCanvasElement) => {
20
+ return new THREE.WebGLRenderer({
21
+ canvas,
22
+ powerPreference: 'high-performance',
23
+ antialias: true,
24
+ alpha: false,
25
+ stencil: false,
26
+ depth: true
27
+ });
28
+ };
29
+
30
+ interface Props {
31
+ props: StageProps;
32
+ callbacks: Callbacks;
33
+ receivedMeasurement?: {
34
+ startPoint: { x: number; y: number };
35
+ endPoint: { x: number; y: number };
36
+ type: number;
37
+ beamWidth?: number;
38
+ coneAngle?: number;
39
+ // Visual properties
40
+ color?: string;
41
+ thickness?: number;
42
+ outlineColor?: string;
43
+ outlineThickness?: number;
44
+ opacity?: number;
45
+ markerSize?: number;
46
+ // Timing properties
47
+ autoHideDelay?: number;
48
+ fadeoutTime?: number;
49
+ // Distance properties
50
+ showDistance?: boolean;
51
+ snapToGrid?: boolean;
52
+ enableDMG252?: boolean;
53
+ } | null;
54
+ cursors?: CursorData[];
55
+ trackLocalCursor?: boolean;
56
+ hoveredMarkerId?: string | null;
57
+ pinnedMarkerIds?: string[];
58
+ onPinToggle?: (markerId: string, pinned: boolean) => void;
59
+ }
60
+
61
+ let {
62
+ props,
63
+ callbacks,
64
+ receivedMeasurement = null,
65
+ cursors = [],
66
+ trackLocalCursor = false,
67
+ hoveredMarkerId = null,
68
+ pinnedMarkerIds = [],
69
+ onPinToggle
70
+ }: Props = $props();
71
+
72
+ let sceneRef = $state<SceneExports>();
73
+ let containerElement = $state<HTMLDivElement>();
74
+ let tooltipPosition = $state<{ x: number; y: number } | null>(null);
75
+ interface MarkerData {
76
+ id: string;
77
+ title?: string;
78
+ note?: unknown;
79
+ visibility?: number;
80
+ size?: number;
81
+ tooltip?: {
82
+ title?: string;
83
+ content?: unknown;
84
+ };
85
+ }
86
+
87
+ let hoveredMarkerData = $state<MarkerData | null>(null);
88
+ let markerSizeInPixels = $state<number>(40);
89
+
90
+ let pinnedTooltips = $state<
91
+ Array<{
92
+ marker: MarkerData;
93
+ position: { x: number; y: number };
94
+ preferredPlacement: 'top' | 'bottom' | 'left' | 'right';
95
+ }>
96
+ >([]);
97
+
98
+ const renderedTooltips = new Map<string, { element: HTMLElement; bounds: DOMRect }>();
99
+
100
+ let stageContext = $state({ mode: props.mode, hoveredMarkerId, pinnedMarkerIds });
101
+ setContext('stage', stageContext);
102
+
103
+ $effect(() => {
104
+ stageContext.mode = props.mode;
105
+ stageContext.hoveredMarkerId = hoveredMarkerId;
106
+ stageContext.pinnedMarkerIds = pinnedMarkerIds;
107
+ });
108
+
109
+ setContext('callbacks', callbacks);
110
+
111
+ export const annotations = {
112
+ clear: (layerId: string) => sceneRef?.annotations.clear(layerId),
113
+ toRLE: () => sceneRef?.annotations?.toRLE() ?? Promise.resolve(new Uint8Array()),
114
+ fromRLE: (rleData: Uint8Array, width: number, height: number) =>
115
+ sceneRef?.annotations?.fromRLE(rleData, width, height) ?? Promise.resolve(),
116
+ loadMask: (layerId: string, rleData: Uint8Array) =>
117
+ sceneRef?.annotations?.loadMask(layerId, rleData) ?? Promise.resolve(),
118
+ isDrawing: () => sceneRef?.annotations?.isDrawing() ?? false
119
+ };
120
+
121
+ export const map = {
122
+ fill: () => sceneRef?.map.fill(),
123
+ fit: () => sceneRef?.map.fit(),
124
+ getSize: () => sceneRef?.map?.getSize?.() ?? null
125
+ };
126
+
127
+ export const fogOfWar = {
128
+ clear: () => sceneRef?.fogOfWar.clear(),
129
+ reset: () => sceneRef?.fogOfWar.reset(),
130
+ toPng: () => sceneRef?.fogOfWar.toPng() ?? Promise.resolve(new Blob()),
131
+ toRLE: () => sceneRef?.fogOfWar.toRLE() ?? Promise.resolve(new Uint8Array()),
132
+ fromRLE: (rleData: Uint8Array, width: number, height: number) =>
133
+ sceneRef?.fogOfWar.fromRLE(rleData, width, height) ?? Promise.resolve(),
134
+ isDrawing: () => sceneRef?.fogOfWar?.isDrawing() ?? false
135
+ };
136
+
137
+ export const scene = {
138
+ fill: () => sceneRef?.fill(),
139
+ fit: () => sceneRef?.fit(),
140
+ generateThumbnail: () => sceneRef?.generateThumbnail() ?? Promise.resolve(new Blob())
141
+ };
142
+
143
+ export const markers = {
144
+ get isHoveringMarker() {
145
+ return sceneRef?.markers?.isHoveringMarker ?? false;
146
+ },
147
+ get isDraggingMarker() {
148
+ return sceneRef?.markers?.isDraggingMarker ?? false;
149
+ }
150
+ };
151
+
152
+ export const measurement = {
153
+ getCurrentMeasurement: () => sceneRef?.measurement?.getCurrentMeasurement() ?? null,
154
+ isDrawing: () => sceneRef?.measurement?.isDrawing() ?? false
155
+ };
156
+
157
+ /**
158
+ * Called when the scene changes to clear all transient state.
159
+ * Clears tooltips, marker hover/selection, and other scene-specific state.
160
+ */
161
+ export function onSceneChange() {
162
+ // Clear tooltip state
163
+ hoveredMarkerData = null;
164
+ tooltipPosition = null;
165
+
166
+ // Clear marker interaction state
167
+ sceneRef?.markers?.onSceneChange?.();
168
+ }
169
+
170
+ $effect(() => {
171
+ // Update marker size when zoom changes or marker data changes
172
+ const zoom = props.scene.zoom;
173
+ if (sceneRef?.getMarkerSizeInScreenSpace) {
174
+ const markerSize = hoveredMarkerData?.size || 1;
175
+ markerSizeInPixels = sceneRef.getMarkerSizeInScreenSpace(markerSize);
176
+ }
177
+ // Use zoom to avoid unused variable warning
178
+ void zoom;
179
+ });
180
+
181
+ $effect(() => {
182
+ // Only show pinned tooltips in DM mode when activeLayer is None (0) or Marker (2)
183
+ // In player mode, always show pinned tooltips
184
+ const shouldShowPinnedTooltips = props.mode === 1 || props.activeLayer === 0 || props.activeLayer === 2;
185
+
186
+ if (
187
+ pinnedMarkerIds &&
188
+ pinnedMarkerIds.length > 0 &&
189
+ containerElement &&
190
+ sceneRef?.getMarkerScreenPosition &&
191
+ shouldShowPinnedTooltips
192
+ ) {
193
+ const newPinnedTooltips = [];
194
+
195
+ const placementPatterns = [
196
+ ['top', 'bottom', 'left', 'right'],
197
+ ['bottom', 'top', 'right', 'left'],
198
+ ['left', 'right', 'top', 'bottom'],
199
+ ['right', 'left', 'bottom', 'top']
200
+ ];
201
+
202
+ for (let i = 0; i < pinnedMarkerIds.length; i++) {
203
+ const markerId = pinnedMarkerIds[i];
204
+ const marker = props.marker.markers.find((m) => m.id === markerId);
205
+ if (marker) {
206
+ const screenPos = sceneRef.getMarkerScreenPosition(marker);
207
+ if (screenPos) {
208
+ const pattern = placementPatterns[i % placementPatterns.length];
209
+ const preferredPlacement = pattern[0] as 'top' | 'bottom' | 'left' | 'right';
210
+
211
+ newPinnedTooltips.push({ marker, position: screenPos, preferredPlacement });
212
+ }
213
+ }
214
+ }
215
+ pinnedTooltips = newPinnedTooltips;
216
+ } else {
217
+ pinnedTooltips = [];
218
+ }
219
+ });
220
+
221
+ $effect(() => {
222
+ let markerForTooltip = null;
223
+
224
+ if (props.mode === 0) {
225
+ const hoveredMarker = sceneRef?.markers?.hoveredMarker;
226
+ const isDragging = sceneRef?.markers?.isDraggingMarker;
227
+ // Only show tooltips when activeLayer is None (0) or Marker (2)
228
+ // MapLayerType: None = 0, FogOfWar = 1, Marker = 2, Annotation = 3, Measurement = 4
229
+ const isMarkerOrNoneLayer = props.activeLayer === 0 || props.activeLayer === 2;
230
+ if (hoveredMarker && !pinnedMarkerIds.includes(hoveredMarker.id) && !isDragging && isMarkerOrNoneLayer) {
231
+ // Look up the current marker from the markers array by ID to handle Y.js updates
232
+ // This prevents stale marker references from breaking tooltips after sync
233
+ const currentMarker = props.marker.markers.find((m) => m.id === hoveredMarker.id);
234
+ markerForTooltip = currentMarker || hoveredMarker;
235
+ }
236
+ } else if (props.mode === 1) {
237
+ // Player mode - only show tooltips for markers where:
238
+ // 1. The marker is pinned (handled separately in pinnedTooltips)
239
+ // 2. The marker visibility is "On hover" (MarkerVisibility.Hover = 3) AND DM is hovering
240
+ // 3. The marker visibility is "Everyone" (MarkerVisibility.Always = 0) AND DM is hovering
241
+ let dmBroadcastMarker = null;
242
+ if (hoveredMarkerId) {
243
+ if (!pinnedMarkerIds.includes(hoveredMarkerId)) {
244
+ const marker = props.marker.markers.find((m) => m.id === hoveredMarkerId);
245
+ // Only show if marker visibility is "On hover" or "Everyone"
246
+ // MarkerVisibility: Always = 0, DM = 1, Player = 2, Hover = 3
247
+ if (marker && (marker.visibility === 0 || marker.visibility === 3)) {
248
+ dmBroadcastMarker = marker;
249
+ }
250
+ }
251
+ }
252
+
253
+ // In player mode, also check if player has selected a marker
254
+ // But only show tooltip if it has appropriate visibility (not DM-only)
255
+ const selectedByPlayer = sceneRef?.markers?.selectedMarker;
256
+ const isDragging = sceneRef?.markers?.isDraggingMarker;
257
+ let selectedNotPinned = null;
258
+ if (selectedByPlayer && !pinnedMarkerIds.includes(selectedByPlayer.id) && !isDragging) {
259
+ // Only show if marker visibility is not DM-only (i.e., not visibility = 1)
260
+ // MarkerVisibility: Always = 0, DM = 1, Player = 2, Hover = 3
261
+ if (selectedByPlayer.visibility !== 1) {
262
+ selectedNotPinned = selectedByPlayer;
263
+ }
264
+ }
265
+
266
+ markerForTooltip = dmBroadcastMarker || selectedNotPinned;
267
+ }
268
+
269
+ if (markerForTooltip && containerElement) {
270
+ hoveredMarkerData = markerForTooltip;
271
+ const screenPos = sceneRef?.getMarkerScreenPosition?.(markerForTooltip);
272
+ if (screenPos) {
273
+ tooltipPosition = screenPos;
274
+ }
275
+ } else {
276
+ hoveredMarkerData = null;
277
+ tooltipPosition = null;
278
+ }
279
+ });
280
+ </script>
281
+
282
+ <div bind:this={containerElement} style="height: 100%; width: 100%; position: relative;">
283
+ <Canvas {createRenderer} renderMode="always">
284
+ <T.Mesh scale={[100000, 100000, 1]} layers={[SceneLayerOrder.Background]}>
285
+ <T.PlaneGeometry />
286
+ <T.MeshBasicMaterial color={props.backgroundColor} />
287
+ </T.Mesh>
288
+
289
+ <Scene bind:this={sceneRef} {props} {receivedMeasurement} {cursors} {trackLocalCursor} />
290
+ {#if props.debug.enableStats}
291
+ <PerfMonitor logsPerSecond={props.debug.loggingRate} anchorX={'right'} anchorY={'bottom'} />
292
+ {/if}
293
+ </Canvas>
294
+
295
+ {#each pinnedTooltips as { marker, position, preferredPlacement }, index}
296
+ <MarkerTooltip
297
+ {marker}
298
+ {position}
299
+ {containerElement}
300
+ markerDiameter={sceneRef?.getMarkerSizeInScreenSpace ? sceneRef.getMarkerSizeInScreenSpace(marker.size || 1) : 40}
301
+ isDM={props.mode === 0}
302
+ isPinned={true}
303
+ {onPinToggle}
304
+ {preferredPlacement}
305
+ existingTooltips={Array.from(renderedTooltips.values()).filter((t, i) => i < index)}
306
+ onTooltipMount={(element, bounds) => {
307
+ renderedTooltips.set(marker.id, { element, bounds });
308
+ }}
309
+ onTooltipUnmount={() => {
310
+ renderedTooltips.delete(marker.id);
311
+ }}
312
+ />
313
+ {/each}
314
+
315
+ {#if hoveredMarkerData}
316
+ <MarkerTooltip
317
+ marker={hoveredMarkerData}
318
+ position={tooltipPosition}
319
+ {containerElement}
320
+ markerDiameter={markerSizeInPixels}
321
+ isDM={props.mode === 0}
322
+ isPinned={false}
323
+ {onPinToggle}
324
+ existingTooltips={Array.from(renderedTooltips.values())}
325
+ onTooltipHover={(isHovering) => {
326
+ if (props.mode === 0 && sceneRef?.markers?.maintainHover) {
327
+ sceneRef.markers.maintainHover(isHovering);
328
+ }
329
+ }}
330
+ />
331
+ {/if}
332
+ </div>
@@ -0,0 +1,136 @@
1
+ import type { AnnotationsLayerProps } from '../AnnotationLayer/types';
2
+ import type { EdgeOverlayProps } from '../EdgeOverlayLayer/types';
3
+ import type { FogLayerProps } from '../FogLayer/types';
4
+ import type { FogOfWarLayerProps } from '../FogOfWarLayer/types';
5
+ import type { GridLayerProps } from '../GridLayer/types';
6
+ import type { MapLayerProps, MapLayerType } from '../MapLayer/types';
7
+ import type { Marker, MarkerLayerProps } from '../MarkerLayer/types';
8
+ import type { MeasurementLayerProps } from '../MeasurementLayer/types';
9
+ import type { PostProcessingProps, SceneLayerProps } from '../Scene/types';
10
+ import type { WeatherLayerProps } from '../WeatherLayer/types';
11
+ export type { CursorData } from '../CursorLayer/types';
12
+
13
+ export interface Callbacks {
14
+ onAnnotationUpdate: (layerId: string, blob: Promise<Blob>, endPosition?: { x: number; y: number }) => void;
15
+ onFogUpdate: (blob: Promise<Blob>) => void;
16
+ onMapUpdate: (offset: { x: number; y: number }, zoom: number) => void;
17
+ onSceneUpdate: (offset: { x: number; y: number }, zoom: number) => void;
18
+ onStageLoading: () => void;
19
+ onStageInitialized: () => void;
20
+ onMarkerAdded: (marker: Marker) => void;
21
+ onMarkerMoved: (marker: Marker, position: { x: number; y: number }) => void;
22
+ onMarkerSelected: (marker: Marker | null) => void;
23
+ onMarkerContextMenu: (marker: Marker, event: MouseEvent | TouchEvent) => void;
24
+ onMeasurementStart?: (startPoint: { x: number; y: number }, type: number) => void;
25
+ onMeasurementUpdate?: (
26
+ startPoint: { x: number; y: number },
27
+ endPoint: { x: number; y: number },
28
+ type: number
29
+ ) => void;
30
+ onMeasurementEnd?: () => void;
31
+ onCursorMove?: (worldPosition: { x: number; y: number; z: number }) => void;
32
+ onMarkerHover?: (marker: Marker | null) => void;
33
+ }
34
+
35
+ export enum StageMode {
36
+ DM = 0,
37
+ Player = 1
38
+ }
39
+
40
+ export interface DisplayProps {
41
+ /**
42
+ * The minimum padding around the grid relative to the edge of the scene.
43
+ * The actual padding may be greater depending on the grid layout.
44
+ */
45
+ padding: {
46
+ x: number;
47
+ y: number;
48
+ };
49
+
50
+ /**
51
+ * The size of the display in inches
52
+ */
53
+ size: { x: number; y: number };
54
+
55
+ /**
56
+ * The resolution of the display in pixels
57
+ */
58
+ resolution: { x: number; y: number };
59
+
60
+ /**
61
+ * Maximum device pixel ratio to use for rendering.
62
+ * Caps the pixel ratio to improve performance on high-DPI devices with weak GPUs.
63
+ * Defaults to 2 if not specified.
64
+ */
65
+ maxPixelRatio?: number;
66
+ }
67
+
68
+ /**
69
+ * Properties for the Stage component
70
+ */
71
+ export type StageProps = {
72
+ mode: StageMode;
73
+ activeLayer: MapLayerType;
74
+ annotations: AnnotationsLayerProps;
75
+ backgroundColor: string;
76
+ debug: {
77
+ enableStats: boolean;
78
+ loggingRate: number;
79
+ enableMetrics: boolean;
80
+ logMetricsToConsole: boolean;
81
+ disabledLayers: string[];
82
+ };
83
+ display: DisplayProps;
84
+ fog: FogLayerProps;
85
+ edgeOverlay: EdgeOverlayProps;
86
+ fogOfWar: FogOfWarLayerProps;
87
+ grid: GridLayerProps;
88
+ map: MapLayerProps;
89
+ marker: MarkerLayerProps;
90
+ measurement: MeasurementLayerProps;
91
+ postProcessing: PostProcessingProps;
92
+ scene: SceneLayerProps;
93
+ weather: WeatherLayerProps;
94
+ };
95
+
96
+ export interface StageExports {
97
+ annotations: {
98
+ clear: (layerId: string) => void;
99
+ toRLE: () => Promise<Uint8Array>;
100
+ fromRLE: (rleData: Uint8Array, width: number, height: number) => Promise<void>;
101
+ loadMask: (layerId: string, rleData: Uint8Array) => Promise<void>;
102
+ isDrawing: () => boolean;
103
+ };
104
+
105
+ fogOfWar: {
106
+ clear: () => void;
107
+ reset: () => void;
108
+ toPng: () => Promise<Blob>;
109
+ toRLE: () => Promise<Uint8Array>;
110
+ fromRLE: (rleData: Uint8Array, width: number, height: number) => Promise<void>;
111
+ isDrawing: () => boolean;
112
+ };
113
+ map: {
114
+ fit: () => void;
115
+ fill: () => void;
116
+ getSize: () => { width: number; height: number } | null;
117
+ };
118
+ scene: {
119
+ fit: () => void;
120
+ fill: () => void;
121
+ generateThumbnail: () => Promise<Blob>;
122
+ };
123
+ markers: {
124
+ isHoveringMarker: boolean;
125
+ isDraggingMarker: boolean;
126
+ };
127
+ measurement: {
128
+ getCurrentMeasurement: () => {
129
+ startPoint: { x: number; y: number } | null;
130
+ endPoint: { x: number; y: number } | null;
131
+ type: number;
132
+ } | null;
133
+ isDrawing: () => boolean;
134
+ };
135
+ onSceneChange: () => void;
136
+ }