@blokkli/editor 2.0.0-alpha.21 → 2.0.0-alpha.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +97 -169
  3. package/dist/runtime/blokkliPlugins/MenuButton/index.vue +3 -1
  4. package/dist/runtime/blokkliPlugins/Sidebar/Detached/index.vue +20 -14
  5. package/dist/runtime/blokkliPlugins/Sidebar/Detached/index.vue.d.ts +8 -4
  6. package/dist/runtime/blokkliPlugins/Sidebar/index.vue +55 -25
  7. package/dist/runtime/blokkliPlugins/Sidebar/index.vue.d.ts +4 -3
  8. package/dist/runtime/blokkliPlugins/ToolbarButton/index.vue +4 -1
  9. package/dist/runtime/blokkliPlugins/ViewOption/index.vue +3 -1
  10. package/dist/runtime/blokkliPlugins/index.d.ts +1 -3
  11. package/dist/runtime/blokkliPlugins/index.js +0 -4
  12. package/dist/runtime/components/Blocks/FromLibrary/index.vue +0 -5
  13. package/dist/runtime/components/BlokkliEditable.vue +6 -1
  14. package/dist/runtime/components/BlokkliField.vue +1 -1
  15. package/dist/runtime/components/BlokkliProvider.vue +4 -4
  16. package/dist/runtime/components/Edit/Actions/ItemDropdown.vue +66 -0
  17. package/dist/runtime/components/Edit/Actions/ItemDropdown.vue.d.ts +6 -0
  18. package/dist/runtime/components/Edit/Actions/index.vue +7 -49
  19. package/dist/runtime/components/Edit/AnimationCanvas/index.vue +23 -68
  20. package/dist/runtime/components/Edit/BlockProxy/index.vue +2 -8
  21. package/dist/runtime/components/Edit/BlokkliErrorBoundary.vue +33 -49
  22. package/dist/runtime/components/Edit/BlokkliErrorBoundary.vue.d.ts +14 -1
  23. package/dist/runtime/components/Edit/BlokkliRootErrorBoundary.vue +93 -0
  24. package/dist/runtime/components/Edit/BlokkliRootErrorBoundary.vue.d.ts +12 -0
  25. package/dist/runtime/components/Edit/DraggableList.vue +5 -25
  26. package/dist/runtime/components/Edit/EditProvider.vue +24 -11
  27. package/dist/runtime/components/Edit/Features/{BlockAddList → AddList/Blocks}/index.vue +26 -62
  28. package/dist/runtime/components/Edit/Features/{Hover/Overlay → AddList/Blocks}/index.vue.d.ts +2 -2
  29. package/dist/runtime/components/Edit/Features/AddList/index.vue +29 -8
  30. package/dist/runtime/components/Edit/Features/Analyze/{Renderer.vue → Main.vue} +4 -8
  31. package/dist/runtime/components/Edit/Features/Analyze/{Overlay → Renderer}/fragment.glsl +10 -6
  32. package/dist/runtime/components/Edit/Features/Analyze/{Overlay → Renderer}/index.vue +43 -15
  33. package/dist/runtime/components/Edit/Features/Analyze/{Overlay → Renderer}/index.vue.d.ts +0 -2
  34. package/dist/runtime/components/Edit/Features/Analyze/{Overlay → Renderer}/vertex.glsl +11 -9
  35. package/dist/runtime/components/Edit/Features/Analyze/Results/ResultsItemNodesTarget.vue +15 -3
  36. package/dist/runtime/components/Edit/Features/Analyze/analyzers/helpers/collectTextElements.js +3 -0
  37. package/dist/runtime/components/Edit/Features/Analyze/index.vue +7 -3
  38. package/dist/runtime/components/Edit/Features/Anchors/index.vue +2 -2
  39. package/dist/runtime/components/Edit/Features/Clipboard/List/index.vue +21 -10
  40. package/dist/runtime/components/Edit/Features/Clipboard/index.vue +18 -11
  41. package/dist/runtime/components/Edit/Features/Conversions/index.vue +16 -9
  42. package/dist/runtime/components/Edit/Features/Debug/DebugSection.vue +24 -0
  43. package/dist/runtime/components/Edit/Features/Debug/DebugSection.vue.d.ts +16 -0
  44. package/dist/runtime/components/Edit/Features/Debug/Main.vue +80 -0
  45. package/dist/runtime/components/Edit/Features/Debug/Section/Features.vue +34 -0
  46. package/dist/runtime/components/Edit/Features/Debug/Section/Icons.vue +15 -0
  47. package/dist/runtime/components/Edit/Features/Debug/Section/Icons.vue.d.ts +2 -0
  48. package/dist/runtime/components/Edit/Features/Debug/Section/Keyboard.vue +17 -0
  49. package/dist/runtime/components/Edit/Features/Debug/Section/Keyboard.vue.d.ts +2 -0
  50. package/dist/runtime/components/Edit/Features/Debug/Section/Logging.vue +66 -0
  51. package/dist/runtime/components/Edit/Features/{Selection/OverlayFallback/index.vue.d.ts → Debug/Section/Logging.vue.d.ts} +2 -1
  52. package/dist/runtime/components/Edit/Features/Debug/Section/Rendering.vue +96 -0
  53. package/dist/runtime/components/Edit/Features/Debug/Section/Rendering.vue.d.ts +2 -0
  54. package/dist/runtime/components/Edit/Features/Debug/Section/Selection.vue +25 -0
  55. package/dist/runtime/components/Edit/Features/Debug/Section/Selection.vue.d.ts +2 -0
  56. package/dist/runtime/components/Edit/Features/Debug/index.vue +2 -2
  57. package/dist/runtime/components/Edit/Features/DraggingOverlay/DragItems/index.vue +20 -27
  58. package/dist/runtime/components/Edit/Features/DraggingOverlay/Renderer/fragment.glsl +80 -0
  59. package/dist/runtime/components/Edit/Features/DraggingOverlay/{DropTargets → Renderer}/index.vue +100 -87
  60. package/dist/runtime/components/Edit/Features/DraggingOverlay/{DropTargets → Renderer}/vertex.glsl +51 -8
  61. package/dist/runtime/components/Edit/Features/DraggingOverlay/index.vue +3 -3
  62. package/dist/runtime/components/Edit/Features/EditableField/Overlay/index.vue +0 -6
  63. package/dist/runtime/components/Edit/Features/EditableField/index.vue +6 -2
  64. package/dist/runtime/components/Edit/Features/Fragments/Dialog/Item/index.vue +1 -5
  65. package/dist/runtime/components/Edit/Features/Hover/Renderer/fragment.glsl +141 -0
  66. package/dist/runtime/components/Edit/Features/Hover/{Overlay → Renderer}/index.vue +225 -38
  67. package/dist/runtime/components/Edit/Features/Hover/Renderer/index.vue.d.ts +2 -0
  68. package/dist/runtime/components/Edit/Features/Hover/{Overlay → Renderer}/vertex.glsl +49 -11
  69. package/dist/runtime/components/Edit/Features/Hover/index.vue +9 -6
  70. package/dist/runtime/components/Edit/Features/Library/LibraryDialog/Item/index.vue +1 -5
  71. package/dist/runtime/components/Edit/Features/Library/index.vue +24 -12
  72. package/dist/runtime/components/Edit/Features/MediaLibrary/Library/Item.vue +1 -1
  73. package/dist/runtime/components/Edit/Features/MediaLibrary/Library/index.vue +26 -13
  74. package/dist/runtime/components/Edit/Features/MediaLibrary/Library/index.vue.d.ts +0 -1
  75. package/dist/runtime/components/Edit/Features/MediaLibrary/index.vue +3 -47
  76. package/dist/runtime/components/Edit/Features/MultiSelect/{Overlay → Renderer}/fragment.glsl +13 -26
  77. package/dist/runtime/components/Edit/Features/MultiSelect/{Overlay → Renderer}/index.vue +130 -72
  78. package/dist/runtime/components/Edit/Features/MultiSelect/{Overlay → Renderer}/index.vue.d.ts +0 -1
  79. package/dist/runtime/components/Edit/Features/MultiSelect/{Overlay → Renderer}/vertex.glsl +27 -2
  80. package/dist/runtime/components/Edit/Features/MultiSelect/index.vue +25 -12
  81. package/dist/runtime/components/Edit/Features/Ownership/index.vue +2 -2
  82. package/dist/runtime/components/Edit/Features/ResponsivePreview/index.vue +1 -1
  83. package/dist/runtime/components/Edit/Features/Search/Overlay/Results/Content/index.vue +23 -6
  84. package/dist/runtime/components/Edit/Features/Selection/AddButtons/Overlay/index.vue.d.ts +2 -2
  85. package/dist/runtime/components/Edit/Features/Selection/AddButtons/Renderer/fragment.glsl +19 -17
  86. package/dist/runtime/components/Edit/Features/Selection/AddButtons/Renderer/index.vue +242 -131
  87. package/dist/runtime/components/Edit/Features/Selection/AddButtons/Renderer/vertex.glsl +29 -12
  88. package/dist/runtime/components/Edit/Features/Selection/AddButtons/index.vue +12 -4
  89. package/dist/runtime/components/Edit/Features/Selection/{Overlay → Renderer}/fragment.glsl +23 -14
  90. package/dist/runtime/components/Edit/Features/Selection/{Overlay → Renderer}/index.vue +121 -28
  91. package/dist/runtime/components/Edit/Features/Selection/{Overlay → Renderer}/index.vue.d.ts +0 -2
  92. package/dist/runtime/components/Edit/Features/Selection/{Overlay → Renderer}/vertex.glsl +23 -14
  93. package/dist/runtime/components/Edit/Features/Selection/index.vue +46 -59
  94. package/dist/runtime/components/Edit/Features/Transform/Dialog/index.vue +5 -5
  95. package/dist/runtime/components/Edit/Features/Transform/index.vue +29 -22
  96. package/dist/runtime/components/Edit/Form/Checkboxes/index.vue +43 -0
  97. package/dist/runtime/components/Edit/Form/Checkboxes/index.vue.d.ts +20 -0
  98. package/dist/runtime/components/Edit/Messages/Item/index.vue.d.ts +1 -1
  99. package/dist/runtime/components/Edit/PreviewProvider.vue +1 -1
  100. package/dist/runtime/components/Edit/Sortli/index.vue +5 -8
  101. package/dist/runtime/components/Edit/Sortli/index.vue.d.ts +2 -0
  102. package/dist/runtime/components/Edit/Toolbar/index.vue +1 -4
  103. package/dist/runtime/components/Edit/ViewportBlockingRect/index.vue +9 -2
  104. package/dist/runtime/components/Edit/ViewportBlockingRect/index.vue.d.ts +4 -1
  105. package/dist/runtime/components/Edit/index.d.ts +6 -4
  106. package/dist/runtime/components/Edit/index.js +11 -7
  107. package/dist/runtime/css/output.css +1 -1
  108. package/dist/runtime/helpers/animationProvider.d.ts +58 -9
  109. package/dist/runtime/helpers/animationProvider.js +318 -75
  110. package/dist/runtime/helpers/composables/defineItemDropdownAction.d.ts +2 -0
  111. package/dist/runtime/helpers/composables/defineItemDropdownAction.js +10 -0
  112. package/dist/runtime/helpers/composables/defineRenderer.d.ts +5 -2
  113. package/dist/runtime/helpers/composables/defineRenderer.js +3 -2
  114. package/dist/runtime/helpers/composables/useGlobalBlokkliObject.d.ts +16 -0
  115. package/dist/runtime/helpers/composables/useGlobalBlokkliObject.js +36 -0
  116. package/dist/runtime/helpers/debugProvider.d.ts +13 -3
  117. package/dist/runtime/helpers/debugProvider.js +80 -12
  118. package/dist/runtime/helpers/domProvider.js +46 -32
  119. package/dist/runtime/helpers/dropTargets/index.js +1 -1
  120. package/dist/runtime/helpers/eventBus.d.ts +1 -1
  121. package/dist/runtime/helpers/index.d.ts +1 -6
  122. package/dist/runtime/helpers/index.js +0 -81
  123. package/dist/runtime/helpers/pluginProvider.d.ts +16 -0
  124. package/dist/runtime/helpers/pluginProvider.js +30 -1
  125. package/dist/runtime/helpers/providers/directive.d.ts +4 -1
  126. package/dist/runtime/helpers/providers/directive.js +9 -2
  127. package/dist/runtime/helpers/stateProvider.js +1 -0
  128. package/dist/runtime/helpers/uiProvider.d.ts +5 -1
  129. package/dist/runtime/helpers/uiProvider.js +41 -7
  130. package/dist/runtime/helpers/webgl/index.d.ts +2 -3
  131. package/dist/runtime/helpers/webgl/index.js +9 -14
  132. package/dist/runtime/icons/dock-left.svg +1 -0
  133. package/dist/runtime/icons/dock-right.svg +1 -0
  134. package/dist/runtime/icons/dock-window.svg +1 -0
  135. package/dist/runtime/plugins/blokkliDirectives.js +1 -1
  136. package/dist/runtime/types/index.d.ts +4 -21
  137. package/package.json +1 -1
  138. package/dist/runtime/blokkliPlugins/DroppableEdit/index.vue +0 -56
  139. package/dist/runtime/blokkliPlugins/DroppableEdit/index.vue.d.ts +0 -24
  140. package/dist/runtime/blokkliPlugins/ItemDropdown/index.vue +0 -76
  141. package/dist/runtime/blokkliPlugins/ItemDropdown/index.vue.d.ts +0 -31
  142. package/dist/runtime/components/Edit/Features/BlockAddList/docs.md +0 -15
  143. package/dist/runtime/components/Edit/Features/Debug/Renderer.vue +0 -240
  144. package/dist/runtime/components/Edit/Features/DraggingOverlay/DropTargets/fragment.glsl +0 -96
  145. package/dist/runtime/components/Edit/Features/Hover/Overlay/fragment.glsl +0 -139
  146. package/dist/runtime/components/Edit/Features/Selection/OverlayFallback/index.vue +0 -42
  147. /package/dist/runtime/components/Edit/Features/Analyze/{Renderer.vue.d.ts → Main.vue.d.ts} +0 -0
  148. /package/dist/runtime/components/Edit/Features/Anchors/{Renderer.vue → Overlay/index.vue} +0 -0
  149. /package/dist/runtime/components/Edit/Features/{BlockAddList → Anchors/Overlay}/index.vue.d.ts +0 -0
  150. /package/dist/runtime/components/Edit/Features/Debug/{Renderer.vue.d.ts → Main.vue.d.ts} +0 -0
  151. /package/dist/runtime/components/Edit/Features/{Anchors/Renderer.vue.d.ts → Debug/Section/Features.vue.d.ts} +0 -0
  152. /package/dist/runtime/components/Edit/Features/DraggingOverlay/{DropTargets → Renderer}/index.vue.d.ts +0 -0
  153. /package/dist/runtime/components/Edit/Features/Ownership/{Renderer.vue → Banner/index.vue} +0 -0
  154. /package/dist/runtime/components/Edit/Features/Ownership/{Renderer.vue.d.ts → Banner/index.vue.d.ts} +0 -0
@@ -5,11 +5,13 @@ import type { StorageProvider } from './storageProvider.js';
5
5
  import type { CursorKeyword } from './dom/index.js';
6
6
  import type { CanvasDrawEvent, Coord } from '#blokkli/types';
7
7
  import type { SelectionProvider } from './selectionProvider.js';
8
- import type { ElementProvider } from './providers/element.js';
8
+ import type { RectangleBufferCollector } from './webgl/index.js';
9
+ import type { DebugProvider } from './debugProvider.js';
9
10
  export type RenderContext = CanvasDrawEvent & {
10
- gl: WebGLRenderingContext;
11
+ changeOptionsTransition: number;
11
12
  };
12
- export type Renderer = {
13
+ type PreferredRenderingMode = 'auto' | 'webgl' | '2d';
14
+ export type Renderer<T = RectangleBufferCollector<any>> = {
13
15
  id: string;
14
16
  zIndex: number;
15
17
  enabled?: () => boolean;
@@ -19,29 +21,72 @@ export type Renderer = {
19
21
  mouse: Coord;
20
22
  mouseArtboard: Coord;
21
23
  }) => boolean | undefined;
22
- render: (ctx: RenderContext) => void;
24
+ collector: () => T;
25
+ program?: () => {
26
+ shaders: [string, string];
27
+ };
28
+ render: (ctx: RenderContext, gl: WebGLRenderingContext, program: ProgramInfo) => void;
29
+ renderFallback?: (ctx: RenderContext, ctx2d: CanvasRenderingContext2D) => void;
23
30
  };
24
31
  export type AnimationProvider = {
25
32
  /**
26
33
  * Request an animation loop. Should be called when UI state changes.
27
34
  */
28
35
  requestDraw: () => void;
36
+ /**
37
+ * Reset the animation state and force a remount of all renderer components.
38
+ */
39
+ reset: () => void;
29
40
  /**
30
41
  * Get the WebGL rendering context.
42
+ * Returns undefined if context is lost or not available.
31
43
  */
32
44
  gl: () => WebGLRenderingContext | undefined;
45
+ /**
46
+ * Get the raw WebGL context even if it's lost.
47
+ * For debugging purposes only (e.g., context loss testing).
48
+ */
49
+ getRawGL: () => WebGLRenderingContext | null;
33
50
  setSharedUniforms: (gl: WebGLRenderingContext, programInfo: ProgramInfo) => void;
34
51
  dpi: ComputedRef<number>;
35
52
  webglSupported: ComputedRef<boolean | null>;
36
53
  webglEnabled: WritableComputedRef<boolean>;
37
- getCanvasElement: () => HTMLCanvasElement;
54
+ preferredRenderingMode: WritableComputedRef<PreferredRenderingMode>;
55
+ /**
56
+ * Reactive property that indicates if we're currently rendering with WebGL.
57
+ * True when WebGL context exists and is not lost.
58
+ */
59
+ isRenderingWebGL: ComputedRef<boolean>;
60
+ /**
61
+ * Reactive property that indicates if we have a WebGL context.
62
+ * True even if the context is lost (for debugging purposes).
63
+ */
64
+ hasWebGLContext: ComputedRef<boolean>;
65
+ /**
66
+ * Reactive key that increments when WebGL context is restored.
67
+ * Use this as a component key to force remounting renderer components on context loss/restore.
68
+ */
69
+ renderKey: ComputedRef<number>;
70
+ /**
71
+ * Reactive key that changes when switching between WebGL and 2D rendering modes.
72
+ * Use this as the canvas element key to force creating a new canvas with the appropriate context.
73
+ */
74
+ canvasKey: ComputedRef<string>;
75
+ /**
76
+ * Set the canvas element to use for rendering.
77
+ * This initializes the appropriate context based on webglEnabled.
78
+ */
79
+ setCanvasElement: (canvas: HTMLCanvasElement) => void;
80
+ /**
81
+ * Remove the canvas element and clean up contexts.
82
+ */
83
+ removeCanvasElement: () => void;
38
84
  /**
39
85
  * Register a WebGL program.
40
86
  *
41
87
  * The programs are cached by the given ID.
42
88
  */
43
89
  registerProgram: (id: string, gl: WebGLRenderingContext, shaders: string[]) => ProgramInfo;
44
- setMouseCoords: (x: number, y: number) => void;
45
90
  cursor: ComputedRef<CursorKeyword>;
46
91
  /**
47
92
  * Handle a click event by calling onClick handlers on renderers.
@@ -50,12 +95,16 @@ export type AnimationProvider = {
50
95
  handleClick: (x: number, y: number) => boolean;
51
96
  /**
52
97
  * Register a WebGL renderer with a specific zIndex.
53
- * Returns an unregister function.
98
+ * Returns an object with the collector instance and an unregister function.
54
99
  */
55
- registerRenderer: (id: string, config: Omit<Renderer, 'id'>) => () => void;
100
+ registerRenderer: <T = RectangleBufferCollector<any>>(id: string, config: Omit<Renderer<T>, 'id'>) => {
101
+ collector: T;
102
+ unregister: () => void;
103
+ };
56
104
  /**
57
105
  * Unregister a WebGL renderer.
58
106
  */
59
107
  unregisterRenderer: (id: string) => void;
60
108
  };
61
- export default function (ui: UiProvider, storage: StorageProvider, selection: SelectionProvider, element: ElementProvider): AnimationProvider;
109
+ export default function (ui: UiProvider, storage: StorageProvider, selection: SelectionProvider, debug: DebugProvider): AnimationProvider;
110
+ export {};
@@ -3,22 +3,143 @@ import useAnimationFrame from "./composables/useAnimationFrame.js";
3
3
  import {
4
4
  ref,
5
5
  computed,
6
+ watch,
6
7
  onMounted,
7
8
  onBeforeUnmount
8
9
  } from "#imports";
9
10
  import { eventBus } from "#blokkli/helpers/eventBus";
10
11
  import { createProgramInfo } from "twgl.js";
11
- export default function(ui, storage, selection, element) {
12
- const webglEnabled = storage.use("webglEnabled", true);
12
+ import { useTransitionedValue } from "./useTransitionedValue.js";
13
+ function configureWebGLContext(gl) {
14
+ gl.enable(gl.BLEND);
15
+ gl.disable(gl.DEPTH_TEST);
16
+ gl.clearColor(0, 0, 0, 0);
17
+ gl.blendFunc(gl.SRC_ALPHA_SATURATE, gl.ONE);
18
+ gl.blendEquation(gl.FUNC_ADD);
19
+ }
20
+ export default function(ui, storage, selection, debug) {
21
+ const logger = debug.createLogger("Animation");
22
+ const preferredRenderingMode = storage.use(
23
+ "preferredRenderingMode",
24
+ "auto"
25
+ );
26
+ const webglSupported = ref(null);
27
+ const webglEnabled = computed({
28
+ get: () => {
29
+ if (preferredRenderingMode.value === "2d") {
30
+ return false;
31
+ }
32
+ if (preferredRenderingMode.value === "webgl") {
33
+ return true;
34
+ }
35
+ return webglSupported.value !== false;
36
+ },
37
+ set: (value) => {
38
+ preferredRenderingMode.value = value ? "webgl" : "2d";
39
+ }
40
+ });
13
41
  const currentCursor = ref("default");
14
42
  const cursor = computed(() => currentCursor.value);
43
+ const renderKey = ref(0);
44
+ const canvasKey = computed(() => webglEnabled.value ? "webgl" : "2d");
45
+ const isRenderingWebGL = computed(
46
+ () => hasGLContext.value && !isContextLost.value
47
+ );
15
48
  const renderers = /* @__PURE__ */ new Map();
49
+ const rendererPrograms = /* @__PURE__ */ new Map();
50
+ const rendererCollectors = /* @__PURE__ */ new Map();
51
+ const rendererFailures = /* @__PURE__ */ new Map();
52
+ const rendererCooldowns = /* @__PURE__ */ new Map();
53
+ const renderersPermanentlyDisabled = /* @__PURE__ */ new Set();
54
+ function shouldSkipRenderer(id) {
55
+ if (renderersPermanentlyDisabled.has(id)) {
56
+ return true;
57
+ }
58
+ const cooldownEnd = rendererCooldowns.get(id);
59
+ if (cooldownEnd) {
60
+ const now = Date.now();
61
+ if (now < cooldownEnd) {
62
+ return true;
63
+ } else {
64
+ rendererCooldowns.delete(id);
65
+ }
66
+ }
67
+ return false;
68
+ }
69
+ function handleRendererFailure(id) {
70
+ const failures = (rendererFailures.get(id) || 0) + 1;
71
+ rendererFailures.set(id, failures);
72
+ if (failures === 6) {
73
+ renderersPermanentlyDisabled.add(id);
74
+ rendererFailures.delete(id);
75
+ rendererCooldowns.delete(id);
76
+ logger.error(
77
+ `Renderer "${id}" has been permanently disabled due to repeated failures.`
78
+ );
79
+ return;
80
+ }
81
+ if (failures === 5) {
82
+ const cooldownEnd = Date.now() + 5e3;
83
+ rendererCooldowns.set(id, cooldownEnd);
84
+ logger.error(
85
+ `Renderer "${id}" failed 5 times in a row. Skipping for 5 seconds.`
86
+ );
87
+ }
88
+ }
89
+ function handleRendererSuccess(id) {
90
+ rendererFailures.delete(id);
91
+ }
92
+ function executeRenderer(renderer, ctx, ctx2dContext2) {
93
+ if (!renderer.enabled || renderer.enabled()) {
94
+ const glContext2 = gl();
95
+ if (glContext2 && webglEnabled.value && !shouldSkipRenderer(renderer.id)) {
96
+ const program = rendererPrograms.get(renderer.id);
97
+ if (program) {
98
+ try {
99
+ renderer.render(ctx, glContext2, program);
100
+ handleRendererSuccess(renderer.id);
101
+ } catch (error) {
102
+ handleRendererFailure(renderer.id);
103
+ logger.error(`Renderer "${renderer.id}" failed:`, error);
104
+ }
105
+ }
106
+ } else if (ctx2dContext2 && renderer.renderFallback) {
107
+ try {
108
+ renderer.renderFallback(ctx, ctx2dContext2);
109
+ handleRendererSuccess(renderer.id);
110
+ } catch (error) {
111
+ handleRendererFailure(renderer.id);
112
+ logger.error(`Renderer "${renderer.id}" (2D fallback) failed:`, error);
113
+ }
114
+ }
115
+ }
116
+ }
16
117
  function registerRenderer(id, config) {
17
- renderers.set(id, { id, ...config });
18
- return () => unregisterRenderer(id);
118
+ logger.log("Registered Renderer: " + id);
119
+ const renderer = { id, ...config };
120
+ renderers.set(id, renderer);
121
+ const collector = config.collector();
122
+ rendererCollectors.set(id, collector);
123
+ if (renderer.program) {
124
+ const glContext2 = gl();
125
+ if (glContext2) {
126
+ const { shaders } = renderer.program();
127
+ const programInfo = registerProgram(id, glContext2, shaders);
128
+ rendererPrograms.set(id, programInfo);
129
+ }
130
+ }
131
+ return {
132
+ collector,
133
+ unregister: () => unregisterRenderer(id)
134
+ };
19
135
  }
20
136
  function unregisterRenderer(id) {
21
137
  renderers.delete(id);
138
+ rendererPrograms.delete(id);
139
+ rendererCollectors.delete(id);
140
+ rendererFailures.delete(id);
141
+ rendererCooldowns.delete(id);
142
+ renderersPermanentlyDisabled.delete(id);
22
143
  }
23
144
  function handleClick(x, y) {
24
145
  const sortedRenderers = Array.from(renderers.values()).filter((renderer) => !renderer.enabled || renderer.enabled()).sort((a, b) => b.zIndex - a.zIndex);
@@ -44,54 +165,170 @@ export default function(ui, storage, selection, element) {
44
165
  let mouseX = 0;
45
166
  let mouseY = 0;
46
167
  let iterator = 120;
47
- const webglSupported = ref(null);
48
168
  const maxCanvasWidth = ref(16384);
49
169
  const maxCanvasHeight = ref(16384);
50
170
  let webglLimitsQueried = false;
51
171
  let canvasElement = null;
52
- function getCanvasElement() {
53
- if (canvasElement) {
54
- return canvasElement;
172
+ let glContext = null;
173
+ let ctx2dContext = null;
174
+ const isContextLost = ref(false);
175
+ const hasGLContext = ref(false);
176
+ let lastCanvasWidth = 0;
177
+ let lastCanvasHeight = 0;
178
+ function initializeContexts() {
179
+ if (!canvasElement) {
180
+ glContext = null;
181
+ ctx2dContext = null;
182
+ isContextLost.value = false;
183
+ hasGLContext.value = false;
184
+ return;
55
185
  }
56
- const el = element.query(
57
- document.documentElement,
58
- "#bk-animation-canvas-webgl",
59
- "Find animation canvas element."
186
+ canvasElement.removeEventListener("webglcontextlost", handleContextLost);
187
+ canvasElement.removeEventListener(
188
+ "webglcontextrestored",
189
+ handleContextRestored
60
190
  );
61
- if (!(el instanceof HTMLCanvasElement)) {
62
- throw new TypeError("Failed to locate WebGL canvas.");
191
+ if (webglEnabled.value && webglSupported.value !== false) {
192
+ const gl2 = canvasElement.getContext("webgl2", {
193
+ premultipliedAlpha: true
194
+ });
195
+ if (gl2) {
196
+ glContext = gl2;
197
+ ctx2dContext = null;
198
+ isContextLost.value = false;
199
+ hasGLContext.value = true;
200
+ webglSupported.value = true;
201
+ if (!webglLimitsQueried) {
202
+ const maxViewportDims = gl2.getParameter(
203
+ gl2.MAX_VIEWPORT_DIMS
204
+ );
205
+ maxCanvasWidth.value = maxViewportDims[0] || 16384;
206
+ maxCanvasHeight.value = maxViewportDims[1] || 16384;
207
+ webglLimitsQueried = true;
208
+ }
209
+ configureWebGLContext(gl2);
210
+ canvasElement.addEventListener(
211
+ "webglcontextlost",
212
+ handleContextLost,
213
+ false
214
+ );
215
+ canvasElement.addEventListener(
216
+ "webglcontextrestored",
217
+ handleContextRestored,
218
+ false
219
+ );
220
+ } else {
221
+ webglSupported.value = false;
222
+ glContext = null;
223
+ hasGLContext.value = false;
224
+ ctx2dContext = canvasElement.getContext("2d");
225
+ }
226
+ } else {
227
+ glContext = null;
228
+ hasGLContext.value = false;
229
+ ctx2dContext = canvasElement.getContext("2d");
63
230
  }
64
- canvasElement = el;
65
- return el;
66
231
  }
67
- function gl() {
68
- if (!webglEnabled.value) {
69
- return;
232
+ watch(webglEnabled, () => {
233
+ const programCount = registeredPrograms.size;
234
+ registeredPrograms.clear();
235
+ rendererPrograms.clear();
236
+ removeCanvasElement();
237
+ logger.log(
238
+ `Cleared ${programCount} WebGL programs and stopped rendering due to mode change to ${webglEnabled.value ? "WebGL" : "2D"}`
239
+ );
240
+ });
241
+ function handleContextLost(event) {
242
+ event.preventDefault();
243
+ logger.error("WebGL context lost");
244
+ isContextLost.value = true;
245
+ const programCount = registeredPrograms.size;
246
+ registeredPrograms.clear();
247
+ rendererPrograms.clear();
248
+ logger.log(`Cleared ${programCount} invalidated WebGL programs`);
249
+ }
250
+ function handleContextRestored() {
251
+ logger.log("WebGL context restored");
252
+ isContextLost.value = false;
253
+ const restoredGL = glContext;
254
+ if (restoredGL) {
255
+ configureWebGLContext(restoredGL);
256
+ logger.log("Re-configured WebGL context settings");
70
257
  }
71
- if (webglSupported.value === false) {
72
- return;
258
+ renderKey.value++;
259
+ logger.log(
260
+ `Incremented renderKey to ${renderKey.value} to force renderer remount`
261
+ );
262
+ requestDraw();
263
+ }
264
+ function reset() {
265
+ isContextLost.value = true;
266
+ registeredPrograms.clear();
267
+ rendererPrograms.clear();
268
+ renderKey.value++;
269
+ isContextLost.value = false;
270
+ }
271
+ function setCanvasElement(canvas) {
272
+ canvasElement = canvas;
273
+ initializeContexts();
274
+ updateCanvasSize();
275
+ renderKey.value++;
276
+ logger.log(
277
+ `Canvas element set with ${webglEnabled.value ? "WebGL" : "2D"} context, renderKey = ${renderKey.value}`
278
+ );
279
+ }
280
+ function removeCanvasElement() {
281
+ if (canvasElement) {
282
+ canvasElement.removeEventListener("webglcontextlost", handleContextLost);
283
+ canvasElement.removeEventListener(
284
+ "webglcontextrestored",
285
+ handleContextRestored
286
+ );
73
287
  }
74
- const canvas = getCanvasElement();
75
- const glContext = canvas.getContext("webgl2", {
76
- premultipliedAlpha: true
77
- });
78
- if (!glContext) {
79
- webglSupported.value = false;
288
+ canvasElement = null;
289
+ glContext = null;
290
+ ctx2dContext = null;
291
+ isContextLost.value = false;
292
+ hasGLContext.value = false;
293
+ lastCanvasWidth = 0;
294
+ lastCanvasHeight = 0;
295
+ }
296
+ function updateCanvasSize() {
297
+ if (!canvasElement) {
80
298
  return;
81
299
  }
82
- webglSupported.value = true;
83
- if (!webglLimitsQueried) {
84
- const maxViewportDims = glContext.getParameter(
85
- glContext.MAX_VIEWPORT_DIMS
86
- );
87
- maxCanvasWidth.value = maxViewportDims[0] || 16384;
88
- maxCanvasHeight.value = maxViewportDims[1] || 16384;
89
- webglLimitsQueried = true;
300
+ const canvasWidth = ui.viewport.value.width * dpi.value;
301
+ const canvasHeight = ui.viewport.value.height * dpi.value;
302
+ if (canvasWidth !== lastCanvasWidth || canvasHeight !== lastCanvasHeight) {
303
+ canvasElement.width = canvasWidth;
304
+ canvasElement.height = canvasHeight;
305
+ if (glContext) {
306
+ glContext.viewport(0, 0, canvasWidth, canvasHeight);
307
+ }
308
+ lastCanvasWidth = canvasWidth;
309
+ lastCanvasHeight = canvasHeight;
310
+ }
311
+ }
312
+ function gl() {
313
+ if (isContextLost.value) {
314
+ return void 0;
90
315
  }
316
+ return glContext || void 0;
317
+ }
318
+ function getRawGL() {
91
319
  return glContext;
92
320
  }
321
+ const getChangeOptionsTransition = useTransitionedValue(
322
+ () => {
323
+ return selection.isChangingOptions.value ? 0 : 1;
324
+ },
325
+ {
326
+ duration: 150
327
+ }
328
+ );
93
329
  useAnimationFrame((time) => {
94
330
  const selectedUuids = [...selection.uuids.value];
331
+ const changeOptionsTransition = getChangeOptionsTransition();
95
332
  if (iterator < 1) {
96
333
  return;
97
334
  }
@@ -103,27 +340,22 @@ export default function(ui, storage, selection, element) {
103
340
  fieldAreas: [],
104
341
  time
105
342
  });
106
- const glContext = gl();
343
+ updateCanvasSize();
344
+ if (!canvasElement) {
345
+ return;
346
+ }
107
347
  if (glContext) {
108
- glContext.enable(glContext.BLEND);
109
- glContext.blendFunc(glContext.SRC_ALPHA_SATURATE, glContext.ONE);
110
- glContext.blendEquation(glContext.FUNC_ADD);
111
348
  glContext.clearColor(0, 0, 0, 0);
112
349
  glContext.clear(glContext.COLOR_BUFFER_BIT);
350
+ } else if (ctx2dContext) {
351
+ ctx2dContext.clearRect(0, 0, canvasElement.width, canvasElement.height);
113
352
  }
114
- const sortedRenderers = Array.from(renderers.values()).sort(
115
- (a, b) => a.zIndex - b.zIndex
116
- );
117
- let onlyRenderer = null;
118
- for (const renderer of sortedRenderers) {
119
- if (!renderer.enabled || renderer.enabled()) {
120
- const onlyValue = typeof renderer.only === "function" ? renderer.only() : renderer.only;
121
- if (onlyValue) {
122
- onlyRenderer = renderer;
123
- break;
124
- }
353
+ const sortedRenderers = Array.from(renderers.values()).sort((a, b) => {
354
+ if (glContext) {
355
+ return a.zIndex - b.zIndex;
125
356
  }
126
- }
357
+ return b.zIndex - a.zIndex;
358
+ });
127
359
  const artboardOffset = ui.artboardOffset.value;
128
360
  const artboardScale = ui.artboardScale.value;
129
361
  const artboardSize = ui.artboardSize.value;
@@ -132,7 +364,6 @@ export default function(ui, storage, selection, element) {
132
364
  y: (mouseY - artboardOffset.y) / artboardScale
133
365
  };
134
366
  const ctx = {
135
- gl: glContext,
136
367
  time,
137
368
  mouseX,
138
369
  mouseY,
@@ -140,22 +371,26 @@ export default function(ui, storage, selection, element) {
140
371
  artboardOffset,
141
372
  artboardScale,
142
373
  artboardSize,
143
- selectedUuids
374
+ selectedUuids,
375
+ dpi: dpi.value,
376
+ changeOptionsTransition
144
377
  };
145
- if (onlyRenderer) {
146
- const glContext2 = gl();
147
- if (glContext2) {
148
- onlyRenderer.render(ctx);
378
+ let onlyRenderer = null;
379
+ for (const renderer of sortedRenderers) {
380
+ if (!renderer.enabled || renderer.enabled()) {
381
+ const onlyValue = typeof renderer.only === "function" ? renderer.only() : renderer.only;
382
+ if (onlyValue) {
383
+ onlyRenderer = renderer;
384
+ break;
385
+ }
149
386
  }
387
+ }
388
+ if (onlyRenderer) {
389
+ executeRenderer(onlyRenderer, ctx, ctx2dContext);
150
390
  } else {
151
391
  for (let i = sortedRenderers.length - 1; i >= 0; i--) {
152
392
  const renderer = sortedRenderers[i];
153
- if (!renderer.enabled || renderer.enabled()) {
154
- const glContext2 = gl();
155
- if (glContext2) {
156
- renderer.render(ctx);
157
- }
158
- }
393
+ executeRenderer(renderer, ctx, ctx2dContext);
159
394
  }
160
395
  }
161
396
  let newCursor = "default";
@@ -197,6 +432,12 @@ export default function(ui, storage, selection, element) {
197
432
  const dpi = computed(() => {
198
433
  const viewportWidth = ui.viewport.value.width;
199
434
  const viewportHeight = ui.viewport.value.height;
435
+ if (!webglEnabled.value) {
436
+ const MAX_2D_CANVAS_SIZE = 4096;
437
+ const maxDpiByWidth2 = MAX_2D_CANVAS_SIZE / viewportWidth;
438
+ const maxDpiByHeight2 = MAX_2D_CANVAS_SIZE / viewportHeight;
439
+ return Math.min(maxDpiByWidth2, maxDpiByHeight2, 1);
440
+ }
200
441
  const deviceRatio = window.devicePixelRatio;
201
442
  const maxDpiByWidth = maxCanvasWidth.value / viewportWidth;
202
443
  const maxDpiByHeight = maxCanvasHeight.value / viewportHeight;
@@ -234,31 +475,33 @@ export default function(ui, storage, selection, element) {
234
475
  );
235
476
  gl2.uniform1f(gl2.getUniformLocation(programInfo.program, "u_dpi"), dpi.value);
236
477
  }
237
- const registeredPrograms = {};
478
+ const registeredPrograms = /* @__PURE__ */ new Map();
238
479
  function registerProgram(id, gl2, shaders) {
239
- if (!registeredPrograms[id]) {
240
- registeredPrograms[id] = createProgramInfo(gl2, shaders);
480
+ if (!registeredPrograms.has(id)) {
481
+ registeredPrograms.set(id, createProgramInfo(gl2, shaders));
241
482
  }
242
- return registeredPrograms[id];
243
- }
244
- function setMouseCoords(x, y) {
245
- mouseX = x;
246
- mouseY = y;
247
- iterator = 120;
483
+ return registeredPrograms.get(id);
248
484
  }
249
485
  return {
250
486
  requestDraw,
251
487
  gl,
488
+ getRawGL,
252
489
  setSharedUniforms,
253
490
  dpi,
254
491
  registerProgram,
255
- setMouseCoords,
256
492
  webglSupported: computed(() => webglSupported.value && webglEnabled.value),
257
493
  webglEnabled,
258
- getCanvasElement,
494
+ preferredRenderingMode,
495
+ isRenderingWebGL,
496
+ hasWebGLContext: computed(() => hasGLContext.value),
497
+ renderKey: computed(() => renderKey.value),
498
+ canvasKey,
499
+ setCanvasElement,
500
+ removeCanvasElement,
259
501
  cursor,
260
502
  handleClick,
261
503
  registerRenderer,
262
- unregisterRenderer
504
+ unregisterRenderer,
505
+ reset
263
506
  };
264
507
  }
@@ -0,0 +1,2 @@
1
+ import type { ItemDropdownAction } from '../pluginProvider.js';
2
+ export default function (cb: () => ItemDropdownAction | ItemDropdownAction[] | undefined): void;
@@ -0,0 +1,10 @@
1
+ import { onBeforeUnmount, onMounted, useBlokkli } from "#imports";
2
+ export default function(cb) {
3
+ const { plugins } = useBlokkli();
4
+ onMounted(() => {
5
+ plugins.addItemDropdownAction(cb);
6
+ });
7
+ onBeforeUnmount(() => {
8
+ plugins.removeItemDropdownAction(cb);
9
+ });
10
+ }
@@ -3,6 +3,9 @@ import type { Renderer } from '../animationProvider.js';
3
3
  * Register a WebGL renderer with automatic cleanup on unmount.
4
4
  *
5
5
  * @param id - Unique identifier for the renderer
6
- * @param config - Renderer configuration (zIndex, enabled, render)
6
+ * @param config - Renderer configuration (zIndex, enabled, render, collector)
7
+ * @returns Object containing the collector instance with inferred type
7
8
  */
8
- export default function defineRenderer(id: string, config: Omit<Renderer, 'id'>): void;
9
+ export default function defineRenderer<T>(id: string, config: Omit<Renderer<T>, 'id'>): {
10
+ collector: T;
11
+ };
@@ -1,8 +1,9 @@
1
1
  import { onBeforeUnmount, useBlokkli } from "#imports";
2
2
  export default function defineRenderer(id, config) {
3
3
  const { animation } = useBlokkli();
4
- const unregisterRenderer = animation.registerRenderer(id, config);
4
+ const { collector, unregister } = animation.registerRenderer(id, config);
5
5
  onBeforeUnmount(() => {
6
- unregisterRenderer();
6
+ unregister();
7
7
  });
8
+ return { collector };
8
9
  }
@@ -0,0 +1,16 @@
1
+ import type { LogMessage } from '../debugProvider.js';
2
+ type BlokkliGlobalWindowObject = {
3
+ messages: LogMessage[];
4
+ };
5
+ export declare function useGlobalBlokkliObject(): {
6
+ init: () => void;
7
+ pushMessage: (message: LogMessage) => void;
8
+ getMessages: () => LogMessage[];
9
+ cleanup: () => void;
10
+ };
11
+ declare global {
12
+ interface Window {
13
+ __BLOKKLI__?: BlokkliGlobalWindowObject;
14
+ }
15
+ }
16
+ export {};