@juo/blocks 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/ExtensionRoot-CRJx0Dtr.js +378 -0
  2. package/dist/block.d.ts.map +1 -1
  3. package/dist/{bridge-w5KQ-czl.js → bridge-D838SWmN.js} +146 -32
  4. package/dist/context.d.ts.map +1 -1
  5. package/dist/core.js +775 -35
  6. package/dist/{juo-workflow-BlG9CcAG.js → css-runtime-DWytAzZ5.js} +466 -611
  7. package/dist/editor/bridge.d.ts +8 -0
  8. package/dist/editor/bridge.d.ts.map +1 -1
  9. package/dist/editor/canvas-mode-signal.d.ts +22 -0
  10. package/dist/editor/canvas-mode-signal.d.ts.map +1 -0
  11. package/dist/editor/canvas-mode.d.ts +15 -0
  12. package/dist/editor/canvas-mode.d.ts.map +1 -0
  13. package/dist/editor/canvas-mode.test.d.ts +2 -0
  14. package/dist/editor/canvas-mode.test.d.ts.map +1 -0
  15. package/dist/editor/components.d.ts +113 -0
  16. package/dist/editor/components.d.ts.map +1 -1
  17. package/dist/editor/components.test.d.ts.map +1 -1
  18. package/dist/editor/handler.d.ts.map +1 -1
  19. package/dist/editor/handler.test.d.ts +2 -0
  20. package/dist/editor/handler.test.d.ts.map +1 -0
  21. package/dist/editor/layout-command-emitter.d.ts +31 -0
  22. package/dist/editor/layout-command-emitter.d.ts.map +1 -0
  23. package/dist/editor/layout-command-emitter.test.d.ts +2 -0
  24. package/dist/editor/layout-command-emitter.test.d.ts.map +1 -0
  25. package/dist/editor/layout-system-signal.d.ts +21 -0
  26. package/dist/editor/layout-system-signal.d.ts.map +1 -0
  27. package/dist/editor/main.d.ts +1 -0
  28. package/dist/editor/main.d.ts.map +1 -1
  29. package/dist/editor/messages.d.ts +90 -3
  30. package/dist/editor/messages.d.ts.map +1 -1
  31. package/dist/editor/messages.test.d.ts +2 -0
  32. package/dist/editor/messages.test.d.ts.map +1 -0
  33. package/dist/editor/surface-context.d.ts +7 -0
  34. package/dist/editor/surface-context.d.ts.map +1 -0
  35. package/dist/editor/surface-context.test.d.ts +2 -0
  36. package/dist/editor/surface-context.test.d.ts.map +1 -0
  37. package/dist/editor/wrapper-shared.d.ts +31 -0
  38. package/dist/editor/wrapper-shared.d.ts.map +1 -0
  39. package/dist/editor.js +863 -45
  40. package/dist/gridstack-adapter-DreG3XnB.js +5490 -0
  41. package/dist/layout/css-runtime.d.ts +41 -0
  42. package/dist/layout/css-runtime.d.ts.map +1 -0
  43. package/dist/layout/css-runtime.test.d.ts +2 -0
  44. package/dist/layout/css-runtime.test.d.ts.map +1 -0
  45. package/dist/layout/derive.d.ts +4 -0
  46. package/dist/layout/derive.d.ts.map +1 -0
  47. package/dist/layout/derive.test.d.ts +2 -0
  48. package/dist/layout/derive.test.d.ts.map +1 -0
  49. package/dist/layout/dividers.d.ts +34 -0
  50. package/dist/layout/dividers.d.ts.map +1 -0
  51. package/dist/layout/dividers.test.d.ts +2 -0
  52. package/dist/layout/dividers.test.d.ts.map +1 -0
  53. package/dist/layout/engine.d.ts +3 -0
  54. package/dist/layout/engine.d.ts.map +1 -0
  55. package/dist/layout/engine.test.d.ts +2 -0
  56. package/dist/layout/engine.test.d.ts.map +1 -0
  57. package/dist/layout/grid-utils.d.ts +57 -0
  58. package/dist/layout/grid-utils.d.ts.map +1 -0
  59. package/dist/layout/grid-utils.test.d.ts +2 -0
  60. package/dist/layout/grid-utils.test.d.ts.map +1 -0
  61. package/dist/layout/index.d.ts +12 -0
  62. package/dist/layout/index.d.ts.map +1 -0
  63. package/dist/layout/index.test.d.ts +2 -0
  64. package/dist/layout/index.test.d.ts.map +1 -0
  65. package/dist/layout/insert.d.ts +31 -0
  66. package/dist/layout/insert.d.ts.map +1 -0
  67. package/dist/layout/insert.test.d.ts +2 -0
  68. package/dist/layout/insert.test.d.ts.map +1 -0
  69. package/dist/layout/project-css.d.ts +6 -0
  70. package/dist/layout/project-css.d.ts.map +1 -0
  71. package/dist/layout/project-css.test.d.ts +2 -0
  72. package/dist/layout/project-css.test.d.ts.map +1 -0
  73. package/dist/layout/project-gridstack.d.ts +50 -0
  74. package/dist/layout/project-gridstack.d.ts.map +1 -0
  75. package/dist/layout/project-gridstack.test.d.ts +2 -0
  76. package/dist/layout/project-gridstack.test.d.ts.map +1 -0
  77. package/dist/layout/reconcile.d.ts +22 -0
  78. package/dist/layout/reconcile.d.ts.map +1 -0
  79. package/dist/layout/reconcile.test.d.ts +2 -0
  80. package/dist/layout/reconcile.test.d.ts.map +1 -0
  81. package/dist/layout/types.d.ts +118 -0
  82. package/dist/layout/types.d.ts.map +1 -0
  83. package/dist/layout-mode-adapter-BBd4JRQd.js +21 -0
  84. package/dist/main.d.ts +7 -1
  85. package/dist/main.d.ts.map +1 -1
  86. package/dist/morph-Dj-xvrwt.js +182 -0
  87. package/dist/preact.js +1 -1
  88. package/dist/project-gridstack-DYLMKSH1.js +52 -0
  89. package/dist/react.js +1 -1
  90. package/dist/theme-state.d.ts +16 -0
  91. package/dist/theme-state.d.ts.map +1 -1
  92. package/dist/theme-state.layout.test.d.ts +2 -0
  93. package/dist/theme-state.layout.test.d.ts.map +1 -0
  94. package/dist/theme-state.test.d.ts +2 -0
  95. package/dist/theme-state.test.d.ts.map +1 -0
  96. package/dist/vue.js +1 -1
  97. package/dist/web-components/ExtensionRoot.d.ts +50 -2
  98. package/dist/web-components/ExtensionRoot.d.ts.map +1 -1
  99. package/dist/web-components/editor/ExtensionRoot.d.ts +11 -0
  100. package/dist/web-components/editor/ExtensionRoot.d.ts.map +1 -0
  101. package/dist/web-components/editor/ExtensionRoot.test.d.ts +2 -0
  102. package/dist/web-components/editor/ExtensionRoot.test.d.ts.map +1 -0
  103. package/dist/web-components/editor/InlineText.d.ts +1 -0
  104. package/dist/web-components/editor/InlineText.d.ts.map +1 -1
  105. package/dist/web-components/editor/editor-layout-controller.d.ts +28 -0
  106. package/dist/web-components/editor/editor-layout-controller.d.ts.map +1 -0
  107. package/dist/web-components/editor/gridstack-adapter.d.ts +3 -0
  108. package/dist/web-components/editor/gridstack-adapter.d.ts.map +1 -0
  109. package/dist/web-components/editor/gridstack-adapter.test.d.ts +2 -0
  110. package/dist/web-components/editor/gridstack-adapter.test.d.ts.map +1 -0
  111. package/dist/web-components/editor/morph.d.ts +32 -0
  112. package/dist/web-components/editor/morph.d.ts.map +1 -0
  113. package/dist/web-components/editor/morph.test.d.ts +2 -0
  114. package/dist/web-components/editor/morph.test.d.ts.map +1 -0
  115. package/dist/web-components/layout/layout-controller.d.ts +55 -0
  116. package/dist/web-components/layout/layout-controller.d.ts.map +1 -0
  117. package/dist/web-components/layout/view-layout-controller.d.ts +17 -0
  118. package/dist/web-components/layout/view-layout-controller.d.ts.map +1 -0
  119. package/dist/web-components/layout/view-layout-controller.test.d.ts +2 -0
  120. package/dist/web-components/layout/view-layout-controller.test.d.ts.map +1 -0
  121. package/dist/web-components/layout-grid.d.ts +47 -0
  122. package/dist/web-components/layout-grid.d.ts.map +1 -0
  123. package/dist/web-components/layout-grid.test.d.ts +2 -0
  124. package/dist/web-components/layout-grid.test.d.ts.map +1 -0
  125. package/dist/web-components/layout-mode-adapter.d.ts +51 -0
  126. package/dist/web-components/layout-mode-adapter.d.ts.map +1 -0
  127. package/dist/web-components/layout-mode-adapter.test.d.ts +2 -0
  128. package/dist/web-components/layout-mode-adapter.test.d.ts.map +1 -0
  129. package/dist/web-components-editor.js +125 -17
  130. package/dist/web-components-runtime.js +4 -3
  131. package/package.json +3 -2
  132. package/dist/ExtensionRoot-2xTGTu3K.js +0 -219
@@ -2,7 +2,6 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { reportBlockError, reportBlockWarning } from "./observability.js";
5
- import { a as createEditorBridge, e as createSelectBlockMessage } from "./bridge-w5KQ-czl.js";
6
5
  const createContext = (key) => key;
7
6
  class ContextRequestEvent extends Event {
8
7
  constructor(context, callback, subscribe) {
@@ -21,13 +20,11 @@ class ContextProvideEvent extends Event {
21
20
  }
22
21
  }
23
22
  function provideContext(target, context, init) {
24
- console.log("context:provide", context, target);
25
23
  const ev = new ContextProvideEvent(context, init);
26
24
  target.dispatchEvent(ev);
27
25
  }
28
26
  function injectContext(target, context) {
29
27
  let value = void 0;
30
- console.log("context:inject", context, target);
31
28
  const ev = new ContextRequestEvent(context, (received) => {
32
29
  value = received;
33
30
  });
@@ -39,7 +36,6 @@ function injectContext(target, context) {
39
36
  }
40
37
  function subscribeContext(target, context, callback) {
41
38
  let unsubscribeFunction = null;
42
- console.log("context:subscribe", context, target);
43
39
  const protocolCallback = (value, unsubscribe) => {
44
40
  if (unsubscribe) {
45
41
  unsubscribeFunction = unsubscribe;
@@ -63,10 +59,8 @@ class ContextRoot extends HTMLElement {
63
59
  connectedCallback() {
64
60
  this.addEventListener("context-provide", (ev) => {
65
61
  ev.stopPropagation();
66
- console.log("context-root:provide", ev.context, ev.value);
67
62
  this.store.set(ev.context, ev.value);
68
63
  const registeredCallbacks = this.callbacks.get(ev.context) ?? [];
69
- console.log("callbacks", registeredCallbacks);
70
64
  registeredCallbacks.forEach((cb) => {
71
65
  const unsubscribe = () => {
72
66
  const callbacks = this.callbacks.get(ev.context);
@@ -76,14 +70,12 @@ class ContextRoot extends HTMLElement {
76
70
  callbacks.splice(cbIndex, 1);
77
71
  }
78
72
  }
79
- console.log("context-root:unsubscribed", ev.context);
80
73
  };
81
74
  cb(ev.value, unsubscribe);
82
75
  });
83
76
  });
84
77
  this.addEventListener("context-request", (ev) => {
85
78
  ev.stopPropagation();
86
- console.log("context-root:request", ev.context, ev);
87
79
  const storedValue = this.store.get(ev.context);
88
80
  if (ev.subscribe) {
89
81
  let callbacks = this.callbacks.get(ev.context);
@@ -99,7 +91,6 @@ class ContextRoot extends HTMLElement {
99
91
  currentCallbacks.splice(index, 1);
100
92
  }
101
93
  }
102
- console.log("context-root:unsubscribed", ev.context);
103
94
  };
104
95
  callbacks.push(ev.callback);
105
96
  if (storedValue !== void 0) {
@@ -113,7 +104,6 @@ class ContextRoot extends HTMLElement {
113
104
  }
114
105
  }
115
106
  });
116
- console.log("context-root:ready");
117
107
  }
118
108
  }
119
109
  var i = Symbol.for("preact-signals");
@@ -121,22 +111,22 @@ function t() {
121
111
  if (!(s > 1)) {
122
112
  var i2, t2 = false;
123
113
  while (void 0 !== h) {
124
- var r = h;
114
+ var r2 = h;
125
115
  h = void 0;
126
116
  f++;
127
- while (void 0 !== r) {
128
- var o2 = r.o;
129
- r.o = void 0;
130
- r.f &= -3;
131
- if (!(8 & r.f) && c(r)) try {
132
- r.c();
133
- } catch (r2) {
117
+ while (void 0 !== r2) {
118
+ var o2 = r2.o;
119
+ r2.o = void 0;
120
+ r2.f &= -3;
121
+ if (!(8 & r2.f) && c(r2)) try {
122
+ r2.c();
123
+ } catch (r3) {
134
124
  if (!t2) {
135
- i2 = r2;
125
+ i2 = r3;
136
126
  t2 = true;
137
127
  }
138
128
  }
139
- r = o2;
129
+ r2 = o2;
140
130
  }
141
131
  }
142
132
  f = 0;
@@ -144,6 +134,15 @@ function t() {
144
134
  if (t2) throw i2;
145
135
  } else s--;
146
136
  }
137
+ function r(i2) {
138
+ if (s > 0) return i2();
139
+ s++;
140
+ try {
141
+ return i2();
142
+ } finally {
143
+ t();
144
+ }
145
+ }
147
146
  var o = void 0;
148
147
  function n(i2) {
149
148
  var t2 = o;
@@ -192,11 +191,11 @@ u.prototype.h = function() {
192
191
  return true;
193
192
  };
194
193
  u.prototype.S = function(i2) {
195
- var t2 = this, r = this.t;
196
- if (r !== i2 && void 0 === i2.e) {
197
- i2.x = r;
194
+ var t2 = this, r2 = this.t;
195
+ if (r2 !== i2 && void 0 === i2.e) {
196
+ i2.x = r2;
198
197
  this.t = i2;
199
- if (void 0 !== r) r.e = i2;
198
+ if (void 0 !== r2) r2.e = i2;
200
199
  else n(function() {
201
200
  var i3;
202
201
  null == (i3 = t2.W) || i3.call(t2);
@@ -206,13 +205,13 @@ u.prototype.S = function(i2) {
206
205
  u.prototype.U = function(i2) {
207
206
  var t2 = this;
208
207
  if (void 0 !== this.t) {
209
- var r = i2.e, o2 = i2.x;
210
- if (void 0 !== r) {
211
- r.x = o2;
208
+ var r2 = i2.e, o2 = i2.x;
209
+ if (void 0 !== r2) {
210
+ r2.x = o2;
212
211
  i2.e = void 0;
213
212
  }
214
213
  if (void 0 !== o2) {
215
- o2.e = r;
214
+ o2.e = r2;
216
215
  i2.x = void 0;
217
216
  }
218
217
  if (i2 === this.t) {
@@ -227,10 +226,10 @@ u.prototype.U = function(i2) {
227
226
  u.prototype.subscribe = function(i2) {
228
227
  var t2 = this;
229
228
  return E(function() {
230
- var r = t2.value, n2 = o;
229
+ var r2 = t2.value, n2 = o;
231
230
  o = void 0;
232
231
  try {
233
- i2(r);
232
+ i2(r2);
234
233
  } finally {
235
234
  o = n2;
236
235
  }
@@ -266,7 +265,7 @@ Object.defineProperty(u.prototype, "value", { get: function() {
266
265
  v++;
267
266
  s++;
268
267
  try {
269
- for (var r = this.t; void 0 !== r; r = r.x) r.t.N();
268
+ for (var r2 = this.t; void 0 !== r2; r2 = r2.x) r2.t.N();
270
269
  } finally {
271
270
  t();
272
271
  }
@@ -281,8 +280,8 @@ function c(i2) {
281
280
  }
282
281
  function a(i2) {
283
282
  for (var t2 = i2.s; void 0 !== t2; t2 = t2.n) {
284
- var r = t2.S.n;
285
- if (void 0 !== r) t2.r = r;
283
+ var r2 = t2.S.n;
284
+ if (void 0 !== r2) t2.r = r2;
286
285
  t2.S.n = t2;
287
286
  t2.i = -1;
288
287
  if (void 0 === t2.n) {
@@ -292,19 +291,19 @@ function a(i2) {
292
291
  }
293
292
  }
294
293
  function l(i2) {
295
- var t2 = i2.s, r = void 0;
294
+ var t2 = i2.s, r2 = void 0;
296
295
  while (void 0 !== t2) {
297
296
  var o2 = t2.p;
298
297
  if (-1 === t2.i) {
299
298
  t2.S.U(t2);
300
299
  if (void 0 !== o2) o2.n = t2.n;
301
300
  if (void 0 !== t2.n) t2.n.p = o2;
302
- } else r = t2;
301
+ } else r2 = t2;
303
302
  t2.S.n = t2.r;
304
303
  if (void 0 !== t2.r) t2.r = void 0;
305
304
  t2 = o2;
306
305
  }
307
- i2.s = r;
306
+ i2.s = r2;
308
307
  }
309
308
  function y(i2, t2) {
310
309
  u.call(this, void 0);
@@ -382,14 +381,14 @@ function w(i2, t2) {
382
381
  return new y(i2, t2);
383
382
  }
384
383
  function _(i2) {
385
- var r = i2.u;
384
+ var r2 = i2.u;
386
385
  i2.u = void 0;
387
- if ("function" == typeof r) {
386
+ if ("function" == typeof r2) {
388
387
  s++;
389
388
  var n2 = o;
390
389
  o = void 0;
391
390
  try {
392
- r();
391
+ r2();
393
392
  } catch (t2) {
394
393
  i2.f &= -2;
395
394
  i2.f |= 8;
@@ -466,14 +465,19 @@ function E(i2) {
466
465
  t2.d();
467
466
  throw i3;
468
467
  }
469
- var r = t2.d.bind(t2);
470
- r[Symbol.dispose] = r;
471
- return r;
468
+ var r2 = t2.d.bind(t2);
469
+ r2[Symbol.dispose] = r2;
470
+ return r2;
472
471
  }
473
472
  function isEditorPreview() {
474
473
  if (typeof window === "undefined") return false;
475
474
  return new URLSearchParams(window.location.search).get("editor") === "true";
476
475
  }
476
+ const layoutSystemEnabled = d(false);
477
+ function setLayoutSystemEnabled(enabled) {
478
+ if (layoutSystemEnabled.peek() === enabled) return;
479
+ layoutSystemEnabled.value = enabled;
480
+ }
477
481
  function hasRenderedContent(scope) {
478
482
  var _a;
479
483
  for (const child of scope.children) {
@@ -581,7 +585,9 @@ function withWrapper(fn) {
581
585
  }
582
586
  if (wrapper === null) {
583
587
  if (isEditorPreview()) {
584
- wrapper = document.createElement("juo-editor-block-wrapper");
588
+ wrapper = document.createElement(
589
+ layoutSystemEnabled.value ? "juo-editor-layout-block-wrapper" : "juo-editor-block-wrapper"
590
+ );
585
591
  wrapper.setAttribute("data-block-id", block.id);
586
592
  (_a = wrapper.setBlock) == null ? void 0 : _a.call(wrapper, block);
587
593
  wrapper.appendChild(rendered);
@@ -895,8 +901,14 @@ function indexOverrides(overrides) {
895
901
  function createThemeState(deps) {
896
902
  var _a;
897
903
  const surfaces = {
898
- page: { blocks: d([]) },
899
- overlay: { blocks: d([]) }
904
+ page: {
905
+ blocks: d([]),
906
+ layout: d(null)
907
+ },
908
+ overlay: {
909
+ blocks: d([]),
910
+ layout: d(null)
911
+ }
900
912
  };
901
913
  const locale = d("en");
902
914
  const catalogs = d(null);
@@ -906,6 +918,9 @@ function createThemeState(deps) {
906
918
  function setGlobalStyles(styles) {
907
919
  globalStyles.value = styles;
908
920
  }
921
+ E(() => {
922
+ setLayoutSystemEnabled(globalStyles.value.layoutEngine === "grid-v1");
923
+ });
909
924
  async function collectBlockDefaults(loc) {
910
925
  const nextDefaults = {};
911
926
  const nextModes = {};
@@ -949,10 +964,13 @@ function createThemeState(deps) {
949
964
  }
950
965
  async function resolve(surface, path) {
951
966
  var _a2, _b, _c, _d;
952
- surfaces[surface].blocks.value = [];
967
+ r(() => {
968
+ surfaces[surface].blocks.value = [];
969
+ surfaces[surface].layout.value = null;
970
+ });
953
971
  const stored = await deps.resolve(surface, path);
954
972
  if (stored != null) {
955
- surfaces[surface].blocks.value = stored.blocks.flatMap((block) => {
973
+ const hydratedBlocks = stored.blocks.flatMap((block) => {
956
974
  try {
957
975
  return [createBlockInstanceFromObject(block)];
958
976
  } catch (error) {
@@ -967,6 +985,10 @@ function createThemeState(deps) {
967
985
  return [];
968
986
  }
969
987
  });
988
+ r(() => {
989
+ surfaces[surface].blocks.value = hydratedBlocks;
990
+ surfaces[surface].layout.value = stored.layout ?? null;
991
+ });
970
992
  return stored;
971
993
  }
972
994
  const blockToUse = surface === "page" ? (_b = (_a2 = deps.routerService) == null ? void 0 : _a2.getPages().find((p2) => p2.path === path)) == null ? void 0 : _b.block : (_c = getDefinedBlocks().find((b2) => b2.name === path)) == null ? void 0 : _c.name;
@@ -1068,610 +1090,443 @@ function createTranslationService(locales, options = {}) {
1068
1090
  }
1069
1091
  };
1070
1092
  }
1071
- function getDefaultBlockForAction(actionType, actionConfig, blockId) {
1072
- const blockName = `action:${actionType}`;
1073
- const definedBlocks = getDefinedBlocks();
1074
- const exists = definedBlocks.some((b2) => b2.name === blockName);
1075
- if (!exists) {
1076
- throw new Error(
1077
- `No default block registered for action type "${actionType}". Expected a block named "${blockName}" to be registered in blocks-extensions.`
1078
- );
1079
- }
1080
- return createBlockInstance(blockName, { props: actionConfig, id: blockId });
1093
+ const GRID_V1_ENGINE_ID = "grid-v1";
1094
+ const PAGE_ROOT_SLOT = "content";
1095
+ const DEFAULT_GRID = { cols: 12, gap: 16, rowUnit: 40 };
1096
+ const MAX_BLOCKS_PER_ROW = 3;
1097
+ const DEFAULT_BREAKPOINTS = [
1098
+ { id: "desktop", cols: 12 },
1099
+ { id: "mobile", cols: 1, query: { maxWidth: 600 } }
1100
+ ];
1101
+ function sharesRowBand$1(a2, b2) {
1102
+ return a2.r < b2.r + b2.h && a2.r + a2.h > b2.r;
1081
1103
  }
1082
- function hasDefaultBlockForAction(actionType) {
1083
- const blockName = `action:${actionType}`;
1084
- return getDefinedBlocks().some((b2) => b2.name === blockName);
1104
+ function minFillCols(cols) {
1105
+ return Math.max(1, Math.ceil(Math.max(1, cols) / MAX_BLOCKS_PER_ROW));
1085
1106
  }
1086
- const WorkflowServiceContext = createContext("services/workflow");
1087
- const RUN_SCOPED_RESET = {
1088
- firstPollReason: null,
1089
- feedbackCollected: false
1090
- };
1091
- function createWorkflowApiAdapter(sdk) {
1092
- return {
1093
- async start(flowType, context, workflowId, dryRun) {
1094
- const { data } = await sdk.post("/customer/v1/workflows/start", {
1095
- flowType,
1096
- context,
1097
- ...workflowId ? { workflowId } : {},
1098
- ...dryRun ? { dryRun: true } : {}
1099
- });
1100
- return data;
1101
- },
1102
- async respond(runId, interactionId, responseType, responseData, expectedRunVersion, eventId) {
1103
- const { data } = await sdk.post(
1104
- "/customer/v1/workflows/{runId}/respond",
1105
- {
1106
- runId,
1107
- interactionId,
1108
- responseType,
1109
- responseData,
1110
- expectedRunVersion,
1111
- eventId
1112
- }
1113
- );
1114
- return data;
1115
- },
1116
- async getState(runId) {
1117
- const { data } = await sdk.get("/customer/v1/workflows/{runId}", {
1118
- runId
1119
- });
1120
- return data;
1121
- },
1122
- async cancel(runId) {
1123
- const { data } = await sdk.post("/customer/v1/workflows/{runId}/cancel", {
1124
- runId
1125
- });
1126
- return data;
1127
- }
1128
- };
1107
+ function clampRect(rect, cols) {
1108
+ const w2 = Math.max(1, Math.min(rect.w, cols));
1109
+ const c2 = Math.max(0, Math.min(rect.c, cols - w2));
1110
+ const h2 = Math.max(1, rect.h);
1111
+ const r2 = Math.max(0, rect.r);
1112
+ return { ...rect, c: c2, r: r2, w: w2, h: h2 };
1129
1113
  }
1130
- function createWorkflowService(deps) {
1131
- const { api } = deps;
1132
- const editor = createEditorBridge();
1133
- const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
1134
- const previewWorkflowTrigger = (urlParams == null ? void 0 : urlParams.get("trigger")) ?? null;
1135
- const previewStepId = (urlParams == null ? void 0 : urlParams.get("step-id")) ?? null;
1136
- const previewWorkflowId = (urlParams == null ? void 0 : urlParams.get("workflow-id")) ?? null;
1137
- const isEditorMode = editor.isEditorMode || previewWorkflowTrigger !== null;
1138
- const _state = d({
1139
- status: "idle",
1140
- flowType: null,
1141
- workflowId: null,
1142
- workflowVersion: null,
1143
- runId: null,
1144
- runVersion: null,
1145
- currentStepId: null,
1146
- currentInteraction: null,
1147
- error: null,
1148
- result: null,
1149
- flowParams: null,
1150
- invalidatedResources: [],
1151
- outcome: null,
1152
- completionData: null,
1153
- dryRun: null,
1154
- ...RUN_SCOPED_RESET
1155
- });
1156
- const _backendInteractionId = d(null);
1157
- const _registeredRootBlocks = /* @__PURE__ */ new Set();
1158
- const isActive = w(() => _state.value.status === "active");
1159
- const isLoading = w(() => _state.value.status === "loading");
1160
- const isCompleted = w(() => _state.value.status === "completed");
1161
- const hasError = w(() => _state.value.status === "error");
1162
- const currentInteraction = w(() => _state.value.currentInteraction);
1163
- const canRespond = w(() => {
1164
- const interaction = _state.value.currentInteraction;
1165
- const backendId = _backendInteractionId.value;
1166
- if (!interaction || !backendId) return false;
1167
- return interaction.interactionId === backendId;
1168
- });
1169
- const currentStepInfo = w(() => {
1170
- const interaction = _state.value.currentInteraction;
1171
- if (!interaction) return null;
1172
- const actionConfig = deps.shopCurrencyCode ? { ...interaction.actionConfig, currency: deps.shopCurrencyCode } : interaction.actionConfig;
1173
- return {
1174
- interactionId: interaction.interactionId,
1175
- stepId: interaction.stepId,
1176
- actionType: interaction.actionType,
1177
- actionConfig,
1178
- availableResponses: interaction.availableResponses
1179
- };
1114
+ function rectsOverlap(a2, b2) {
1115
+ return a2.c < b2.c + b2.w && a2.c + a2.w > b2.c && a2.r < b2.r + b2.h && a2.r + a2.h > b2.r;
1116
+ }
1117
+ function readingOrder(items) {
1118
+ return Object.keys(items).sort((idA, idB) => {
1119
+ const a2 = items[idA];
1120
+ const b2 = items[idB];
1121
+ if (a2.r !== b2.r) return a2.r - b2.r;
1122
+ return a2.c - b2.c;
1180
1123
  });
1181
- function updateState(updates) {
1182
- _state.value = { ..._state.value, ...updates };
1183
- }
1184
- function hashString(value) {
1185
- let hash = 2166136261;
1186
- for (let index = 0; index < value.length; index += 1) {
1187
- hash ^= value.charCodeAt(index);
1188
- hash = Math.imul(hash, 16777619);
1124
+ }
1125
+ function compactItems(items, cols) {
1126
+ const columnBottoms = new Array(Math.max(1, cols)).fill(0);
1127
+ const compacted = {};
1128
+ for (const id of readingOrder(items)) {
1129
+ const rect = clampRect(items[id], cols);
1130
+ let top = 0;
1131
+ for (let c2 = rect.c; c2 < rect.c + rect.w; c2++) {
1132
+ top = Math.max(top, columnBottoms[c2] ?? 0);
1133
+ }
1134
+ compacted[id] = { ...rect, r: top };
1135
+ for (let c2 = rect.c; c2 < rect.c + rect.w; c2++) {
1136
+ columnBottoms[c2] = top + rect.h;
1189
1137
  }
1190
- return (hash >>> 0).toString(36);
1191
- }
1192
- function shortenId(value, slugLength = 6) {
1193
- const compact = value.toLowerCase().replace(/[^a-z0-9]/g, "");
1194
- const slug = (compact || "id").slice(0, slugLength);
1195
- return `${slug}-${hashString(value)}`;
1196
1138
  }
1197
- function createStableStepBlockId(workflowId, stepId) {
1198
- return `wf-${shortenId(workflowId)}-${shortenId(stepId)}`;
1139
+ return compacted;
1140
+ }
1141
+ function rowBandBounds(items, target, cols, excludeId) {
1142
+ let left = 0;
1143
+ let right = Math.max(1, cols);
1144
+ for (const [id, rect] of Object.entries(items)) {
1145
+ if (id === excludeId) continue;
1146
+ const sharesRow = rect.r < target.r + target.h && rect.r + rect.h > target.r;
1147
+ if (!sharesRow) continue;
1148
+ const rectRight = rect.c + rect.w;
1149
+ if (rectRight <= target.c) {
1150
+ left = Math.max(left, rectRight);
1151
+ } else if (rect.c >= target.c + target.w) {
1152
+ right = Math.min(right, rect.c);
1153
+ }
1199
1154
  }
1200
- function handleFlowResponse(data) {
1201
- var _a, _b, _c, _d, _e, _f, _g;
1202
- const mergedInvalidated = [
1203
- ..._state.value.invalidatedResources,
1204
- ...data.invalidatedResources ?? []
1205
- ];
1206
- if (data.status === "waiting_interaction") {
1207
- const currentStepId = ((_a = data.pendingInteraction) == null ? void 0 : _a.stepId) ?? (isEditorMode ? (_c = (_b = data.interactiveSteps) == null ? void 0 : _b[0]) == null ? void 0 : _c.stepId : null);
1208
- _backendInteractionId.value = ((_d = data.pendingInteraction) == null ? void 0 : _d.interactionId) ?? null;
1209
- const isInitialResponse = _state.value.runId == null;
1210
- updateState({
1211
- status: "active",
1212
- workflowId: data.workflowId,
1213
- workflowVersion: data.workflowVersion,
1214
- runId: data.runId,
1215
- runVersion: data.runVersion,
1216
- currentStepId,
1217
- currentInteraction: data.pendingInteraction,
1218
- invalidatedResources: mergedInvalidated,
1219
- outcome: null
1220
- });
1221
- if (editor.isEditorMode) {
1222
- const displayStepId = isInitialResponse && previewStepId && ((_e = data.interactiveSteps) == null ? void 0 : _e.some((s2) => s2.stepId === previewStepId)) ? previewStepId : currentStepId;
1223
- const overlayBlocks = deps.themeState.overlay.blocks.value;
1224
- const rootBlock = overlayBlocks[0];
1225
- const stepSlot = displayStepId ? (_f = rootBlock == null ? void 0 : rootBlock.slots) == null ? void 0 : _f[displayStepId] : null;
1226
- const stepBlock = Array.isArray(stepSlot) ? stepSlot[0] : null;
1227
- const blockId = stepBlock && typeof stepBlock === "object" && "id" in stepBlock ? stepBlock.id : null;
1228
- if (blockId) {
1229
- editor.sendMessage(
1230
- createSelectBlockMessage({ blockId }, blockId)
1231
- );
1232
- }
1233
- }
1234
- } else if (data.status === "completed") {
1235
- _backendInteractionId.value = null;
1236
- updateState({
1237
- status: "completed",
1238
- currentStepId: null,
1239
- currentInteraction: null,
1240
- runVersion: data.runVersion,
1241
- result: data.result || null,
1242
- invalidatedResources: mergedInvalidated,
1243
- outcome: data.outcome || null,
1244
- completionData: data.completionData || null
1245
- });
1246
- } else if (data.status === "failed") {
1247
- _backendInteractionId.value = null;
1248
- handleError(new Error(((_g = data.error) == null ? void 0 : _g.message) || "Flow failed"));
1249
- } else if (data.status === "cancelled") {
1250
- _backendInteractionId.value = null;
1251
- updateState({
1252
- status: "idle",
1253
- flowType: null,
1254
- workflowId: null,
1255
- workflowVersion: null,
1256
- runId: null,
1257
- runVersion: null,
1258
- currentStepId: null,
1259
- currentInteraction: null,
1260
- error: null,
1261
- result: null,
1262
- invalidatedResources: [],
1263
- outcome: null,
1264
- completionData: null,
1265
- ...RUN_SCOPED_RESET
1266
- });
1155
+ return { left, right };
1156
+ }
1157
+ function fillSegmentBounds(target, nonFill, cols) {
1158
+ let left = 0;
1159
+ let right = Math.max(1, cols);
1160
+ for (const rect of nonFill) {
1161
+ if (!sharesRowBand$1(rect, target)) continue;
1162
+ const rectRight = rect.c + rect.w;
1163
+ if (rectRight <= target.c) {
1164
+ left = Math.max(left, rectRight);
1165
+ } else if (rect.c >= target.c) {
1166
+ right = Math.min(right, rect.c);
1167
+ } else {
1168
+ left = Math.max(left, rectRight);
1267
1169
  }
1268
1170
  }
1269
- function handleError(error) {
1270
- const message = error instanceof Error ? error.message : "Unknown error";
1271
- updateState({
1272
- status: "error",
1273
- error: message
1274
- });
1171
+ return { left, right: Math.max(left, right) };
1172
+ }
1173
+ function applyFillExpansion(items, cols) {
1174
+ const next = {};
1175
+ for (const id of readingOrder(items)) {
1176
+ next[id] = items[id];
1275
1177
  }
1276
- function maybeApplyPreviewStep(flowType, data) {
1277
- var _a;
1278
- if (!previewWorkflowTrigger) return;
1279
- if (flowType !== previewWorkflowTrigger) return;
1280
- if (data.status !== "waiting_interaction") return;
1281
- const step = (_a = data.interactiveSteps) == null ? void 0 : _a.find(
1282
- (interactiveStep) => interactiveStep.stepId === previewStepId
1178
+ const fillIds = readingOrder(next).filter((id) => next[id].fill === true);
1179
+ if (fillIds.length === 0) return next;
1180
+ const nonFill = Object.values(next).filter((rect) => rect.fill !== true);
1181
+ const groups = [];
1182
+ for (const id of fillIds) {
1183
+ const rect = next[id];
1184
+ const seg = fillSegmentBounds(rect, nonFill, cols);
1185
+ const group = groups.find(
1186
+ (g2) => g2.left === seg.left && g2.right === seg.right && g2.members.some((memberId) => sharesRowBand$1(next[memberId], rect))
1283
1187
  );
1284
- if (!step) return;
1285
- updateState({
1286
- currentStepId: step.stepId,
1287
- currentInteraction: {
1288
- interactionId: `preview-${data.runId}-${step.stepId}`,
1289
- stepId: step.stepId,
1290
- actionType: step.actionType,
1291
- actionBehavior: "input",
1292
- actionConfig: step.actionConfig,
1293
- availableResponses: step.availableResponses,
1294
- presentedAt: /* @__PURE__ */ new Date()
1295
- }
1296
- });
1188
+ if (group) group.members.push(id);
1189
+ else groups.push({ left: seg.left, right: seg.right, members: [id] });
1297
1190
  }
1298
- function initWorkflowRootBlock(data) {
1299
- if (_registeredRootBlocks.has(data.workflowId)) {
1300
- return;
1301
- }
1302
- const allSteps = data.interactiveSteps ?? (data.pendingInteraction ? [
1303
- {
1304
- stepId: data.pendingInteraction.stepId,
1305
- actionType: data.pendingInteraction.actionType,
1306
- actionConfig: data.pendingInteraction.actionConfig,
1307
- availableResponses: data.pendingInteraction.availableResponses
1308
- }
1309
- ] : []);
1310
- const slotProperties = {};
1311
- for (const step of allSteps) {
1312
- slotProperties[step.stepId] = {};
1313
- }
1314
- const presets = {};
1315
- const baseLabelFor = (step) => {
1316
- var _a;
1317
- return ((_a = getDefinedBlocks().find((b2) => b2.name === `action:${step.actionType}`)) == null ? void 0 : _a.displayName) ?? step.stepId;
1318
- };
1319
- const totalCounts = /* @__PURE__ */ new Map();
1320
- for (const step of allSteps) {
1321
- const baseLabel = baseLabelFor(step);
1322
- totalCounts.set(baseLabel, (totalCounts.get(baseLabel) ?? 0) + 1);
1323
- }
1324
- const seenCounts = /* @__PURE__ */ new Map();
1325
- for (const step of allSteps) {
1326
- const baseLabel = baseLabelFor(step);
1327
- const occurrence = (seenCounts.get(baseLabel) ?? 0) + 1;
1328
- seenCounts.set(baseLabel, occurrence);
1329
- const isDuplicated = (totalCounts.get(baseLabel) ?? 0) > 1;
1330
- const label = isDuplicated ? `${baseLabel} (${occurrence})` : baseLabel;
1331
- presets[step.stepId] = {
1332
- label,
1333
- description: step.actionType,
1334
- state: step.stepId,
1335
- // @ts-expect-error Untyped
1336
- context: step
1337
- };
1191
+ const minCols = minFillCols(cols);
1192
+ const overflow = [];
1193
+ for (const group of groups) {
1194
+ const span = Math.max(1, group.right - group.left);
1195
+ const fit = Math.min(group.members.length, Math.floor(span / minCols));
1196
+ if (fit <= 0) {
1197
+ overflow.push(...group.members);
1198
+ continue;
1338
1199
  }
1339
- registerBlock(
1340
- defineBlock(`workflow:${data.workflowId}`, {
1341
- group: "workflow",
1342
- schema: {
1343
- type: "object",
1344
- properties: {
1345
- props: { type: "object" },
1346
- slots: {
1347
- type: "object",
1348
- properties: slotProperties
1349
- }
1350
- },
1351
- required: ["props", "slots"],
1352
- additionalProperties: true
1353
- },
1354
- initialValue: () => ({
1355
- props: {},
1356
- slots: Object.fromEntries(
1357
- allSteps.map((step) => [
1358
- step.stepId,
1359
- [
1360
- getDefaultBlockForAction(
1361
- step.actionType,
1362
- step.actionConfig,
1363
- createStableStepBlockId(data.workflowId, step.stepId)
1364
- )
1365
- ]
1366
- ])
1367
- )
1368
- }),
1369
- presets,
1370
- renderer() {
1371
- return null;
1372
- }
1373
- })
1374
- );
1375
- _registeredRootBlocks.add(data.workflowId);
1376
- }
1377
- async function initWorkflowState(data) {
1378
- const { blocks } = await deps.themeState.resolve(
1379
- "overlay",
1380
- `workflow:${data.workflowId}`
1381
- );
1382
- if (blocks.length === 0) return;
1383
- editor.onBlockPresetChange(blocks[0].id, (preset) => {
1384
- if (!preset) {
1385
- return;
1386
- }
1387
- updateState({
1388
- // @ts-expect-error Untyped
1389
- currentStepId: preset.state,
1390
- // @ts-expect-error Untyped
1391
- currentInteraction: { ...preset == null ? void 0 : preset.context }
1392
- });
1200
+ const staying = group.members.slice(0, fit);
1201
+ overflow.push(...group.members.slice(fit));
1202
+ const base = Math.floor(span / fit);
1203
+ const remainder = span - base * fit;
1204
+ let pos = group.left;
1205
+ staying.forEach((id, index) => {
1206
+ const w2 = base + (index < remainder ? 1 : 0);
1207
+ next[id] = { ...next[id], c: pos, w: Math.max(1, w2) };
1208
+ pos += w2;
1393
1209
  });
1394
1210
  }
1395
- const service = {
1396
- // State signals (readonly)
1397
- state: _state,
1398
- /** True when the portal is running inside the Blocks Editor (editorMode dep
1399
- * or ?trigger=... URL param). Consumers use this to suppress auto-start. */
1400
- isPreviewMode: isEditorMode,
1401
- // Computed signals
1402
- isActive,
1403
- isLoading,
1404
- isCompleted,
1405
- hasError,
1406
- canRespond,
1407
- currentInteraction,
1408
- currentStepInfo,
1409
- // Methods
1410
- async startFlow(flowType, context, workflowId) {
1411
- const isDryRun = isEditorMode;
1412
- updateState({
1413
- status: "loading",
1414
- error: null,
1415
- flowParams: context,
1416
- flowType,
1417
- dryRun: isDryRun,
1418
- ...RUN_SCOPED_RESET
1419
- });
1420
- try {
1421
- const data = await api.start(
1422
- flowType,
1423
- context,
1424
- workflowId,
1425
- isDryRun || void 0
1426
- );
1427
- if (data.status === "no_workflow") {
1428
- service.reset();
1429
- return;
1430
- }
1431
- if (data.status === "completed_silently") {
1432
- updateState({
1433
- status: "completed",
1434
- currentStepId: null,
1435
- currentInteraction: null,
1436
- result: null,
1437
- invalidatedResources: data.invalidatedResources ?? [],
1438
- outcome: data.outcome || null,
1439
- completionData: data.completionData || null
1440
- });
1441
- return;
1442
- }
1443
- initWorkflowRootBlock(data);
1444
- await initWorkflowState(data);
1445
- handleFlowResponse(data);
1446
- maybeApplyPreviewStep(flowType, data);
1447
- } catch (error) {
1448
- handleError(error);
1449
- }
1450
- },
1451
- async respond(responseType, responseData) {
1452
- const { runId, runVersion, currentInteraction: currentInteraction2 } = _state.value;
1453
- if (!runId || !currentInteraction2) {
1454
- throw new Error("No active interaction to respond to");
1455
- }
1456
- updateState({ status: "loading" });
1457
- try {
1458
- const data = await api.respond(
1459
- runId,
1460
- currentInteraction2.interactionId,
1461
- responseType,
1462
- responseData,
1463
- runVersion ?? void 0
1464
- );
1465
- if (_state.value.firstPollReason == null && currentInteraction2.actionType === "poll") {
1466
- const reason = responseData == null ? void 0 : responseData.reason;
1467
- if (typeof reason === "string" && reason.length > 0) {
1468
- updateState({ firstPollReason: reason });
1469
- }
1470
- }
1471
- if (!_state.value.feedbackCollected && currentInteraction2.actionType === "collect_feedback" && responseType === "submitted") {
1472
- const feedback = responseData == null ? void 0 : responseData.feedback;
1473
- if (typeof feedback === "string" && feedback.trim().length > 0) {
1474
- updateState({ feedbackCollected: true });
1475
- }
1476
- }
1477
- handleFlowResponse(data);
1478
- } catch (error) {
1479
- handleError(error);
1480
- }
1481
- },
1482
- async cancel() {
1483
- const { runId, status } = _state.value;
1484
- const isTerminal = status === "completed" || status === "error";
1485
- if (runId && !isTerminal) {
1486
- try {
1487
- const data = await api.cancel(runId);
1488
- if (data.outcome) {
1489
- updateState({
1490
- status: "completed",
1491
- flowType: null,
1492
- workflowId: null,
1493
- workflowVersion: null,
1494
- runId: null,
1495
- runVersion: null,
1496
- currentStepId: null,
1497
- currentInteraction: null,
1498
- error: null,
1499
- result: null,
1500
- flowParams: null,
1501
- invalidatedResources: data.invalidatedResources ?? [],
1502
- outcome: data.outcome || null,
1503
- dryRun: null
1504
- });
1505
- return;
1506
- }
1507
- } catch {
1508
- }
1211
+ if (overflow.length === 0) return next;
1212
+ for (const id of overflow) {
1213
+ const rect = next[id];
1214
+ let bandBottom = rect.r;
1215
+ for (const [otherId, other] of Object.entries(next)) {
1216
+ if (otherId === id || overflow.includes(otherId)) continue;
1217
+ if (sharesRowBand$1(other, rect)) {
1218
+ bandBottom = Math.max(bandBottom, other.r + other.h);
1509
1219
  }
1510
- service.reset();
1511
- },
1512
- reset() {
1513
- _backendInteractionId.value = null;
1514
- updateState({
1515
- status: "idle",
1516
- flowType: null,
1517
- workflowId: null,
1518
- workflowVersion: null,
1519
- runId: null,
1520
- runVersion: null,
1521
- currentStepId: null,
1522
- currentInteraction: null,
1523
- error: null,
1524
- result: null,
1525
- flowParams: null,
1526
- invalidatedResources: [],
1527
- outcome: null,
1528
- completionData: null,
1529
- ...RUN_SCOPED_RESET
1530
- });
1531
- },
1532
- async refresh() {
1533
- const { runId } = _state.value;
1534
- if (!runId) return;
1535
- try {
1536
- const data = await api.getState(runId);
1537
- handleFlowResponse(data);
1538
- } catch (error) {
1539
- handleError(error);
1220
+ }
1221
+ next[id] = { ...next[id], c: 0, r: bandBottom, w: cols };
1222
+ }
1223
+ return compactItems(next, cols);
1224
+ }
1225
+ function findFreeRect(items, size, cols) {
1226
+ const w2 = Math.max(1, Math.min(size.w, cols));
1227
+ const h2 = Math.max(1, size.h);
1228
+ const existing = Object.values(items);
1229
+ for (let r2 = 0; ; r2++) {
1230
+ for (let c2 = 0; c2 <= cols - w2; c2++) {
1231
+ const candidate = { c: c2, r: r2, w: w2, h: h2 };
1232
+ if (!existing.some((rect) => rectsOverlap(candidate, rect))) {
1233
+ return candidate;
1540
1234
  }
1541
1235
  }
1542
- };
1543
- if (previewWorkflowTrigger) {
1544
- void service.startFlow(
1545
- previewWorkflowTrigger,
1546
- {},
1547
- previewWorkflowId ?? void 0
1548
- );
1549
1236
  }
1550
- return service;
1551
1237
  }
1552
- class JuoWorkflowElement extends HTMLElement {
1553
- constructor() {
1554
- super(...arguments);
1555
- __publicField(this, "workflowService", null);
1556
- __publicField(this, "unsubscribeContext", null);
1557
- __publicField(this, "unsubscribeEffect", null);
1558
- /** Tracks which step is currently mounted to avoid unnecessary DOM teardown. */
1559
- __publicField(this, "renderedStepId", null);
1560
- /**
1561
- * Optional function to wrap rendered content (step blocks).
1562
- * Must be set before workflow becomes active for correct behavior.
1563
- */
1564
- __publicField(this, "blockWrapper", null);
1238
+ function deriveLayout(base, target) {
1239
+ const cols = target.cols;
1240
+ const items = {};
1241
+ let r2 = 0;
1242
+ for (const id of readingOrder(base.items)) {
1243
+ const h2 = Math.max(1, base.items[id].h);
1244
+ items[id] = clampRect({ c: 0, r: r2, w: cols, h: h2 }, cols);
1245
+ r2 += h2;
1565
1246
  }
1566
- connectedCallback() {
1567
- this.style.display = "contents";
1568
- this.unsubscribeContext = subscribeContext(
1569
- this,
1570
- WorkflowServiceContext,
1571
- (service) => {
1572
- this.workflowService = service;
1573
- this.setupSubscription();
1574
- }
1575
- );
1247
+ return { grid: { ...base.grid, cols }, items };
1248
+ }
1249
+ function resolveLayout(doc, breakpointId) {
1250
+ var _a;
1251
+ const present = doc.layouts[breakpointId];
1252
+ if (present) return present;
1253
+ const baseId = (_a = doc.breakpoints[0]) == null ? void 0 : _a.id;
1254
+ const baseLayout = baseId ? doc.layouts[baseId] : void 0;
1255
+ const target = doc.breakpoints.find((b2) => b2.id === breakpointId);
1256
+ if (!baseLayout || !target) {
1257
+ return { grid: { ...DEFAULT_GRID, cols: (target == null ? void 0 : target.cols) ?? 1 }, items: {} };
1576
1258
  }
1577
- disconnectedCallback() {
1578
- var _a, _b;
1579
- (_a = this.unsubscribeContext) == null ? void 0 : _a.call(this);
1580
- (_b = this.unsubscribeEffect) == null ? void 0 : _b.call(this);
1581
- this.unsubscribeContext = null;
1582
- this.unsubscribeEffect = null;
1583
- this.renderedStepId = null;
1259
+ return deriveLayout(baseLayout, target);
1260
+ }
1261
+ function containerRule(scope, grid) {
1262
+ return `${scope}{display:grid;grid-template-columns:repeat(${grid.cols},minmax(0,1fr));--juo-layout-cols:${grid.cols};--juo-layout-gap:var(--juo-layout-spacing,${grid.gap}px);column-gap:var(--juo-layout-gap);row-gap:0;margin-block-end:calc(-1 * var(--juo-layout-gap));}`;
1263
+ }
1264
+ function baseSpacingRule(scope) {
1265
+ return `${scope} > [data-block-id]{margin-block-end:var(--juo-layout-gap);}`;
1266
+ }
1267
+ function collapseRule(scope) {
1268
+ return `${scope} > [data-empty],${scope} > :has(> [data-empty]){margin-block-end:0;}`;
1269
+ }
1270
+ function itemRule(scope, blockId, rect) {
1271
+ return `${scope} > [data-block-id="${blockId}"]{grid-column:${rect.c + 1}/span ${rect.w};grid-row:${rect.r + 1}/span ${rect.h};--juo-layout-c:${rect.c};}`;
1272
+ }
1273
+ function breakpointCss(scope, layout) {
1274
+ const rules = [containerRule(scope, layout.grid)];
1275
+ for (const blockId of Object.keys(layout.items)) {
1276
+ rules.push(itemRule(scope, blockId, layout.items[blockId]));
1584
1277
  }
1585
- setupSubscription() {
1586
- var _a;
1587
- if (!this.workflowService) return;
1588
- (_a = this.unsubscribeEffect) == null ? void 0 : _a.call(this);
1589
- this.unsubscribeEffect = E(() => {
1590
- var _a2, _b;
1591
- const status = ((_a2 = this.workflowService) == null ? void 0 : _a2.state.value.status) ?? "idle";
1592
- const stepInfo = (_b = this.workflowService) == null ? void 0 : _b.currentStepInfo.value;
1593
- const stepId = (stepInfo == null ? void 0 : stepInfo.stepId) ?? null;
1594
- queueMicrotask(() => {
1595
- if (this.isConnected) {
1596
- this.render(status, stepId);
1597
- }
1598
- });
1599
- });
1278
+ return rules.join("");
1279
+ }
1280
+ function sharesRowBand(a2, b2) {
1281
+ return a2.r < b2.r + b2.h && a2.r + a2.h > b2.r;
1282
+ }
1283
+ function nonEmptySubsets(ids) {
1284
+ const out = [];
1285
+ for (let mask = 1; mask < 1 << ids.length; mask++) {
1286
+ const subset = [];
1287
+ for (let i2 = 0; i2 < ids.length; i2++) {
1288
+ if (mask & 1 << i2) subset.push(ids[i2]);
1289
+ }
1290
+ out.push(subset);
1600
1291
  }
1601
- render(status, stepId) {
1602
- var _a;
1603
- if ((status === "active" || status === "loading") && stepId) {
1604
- if (this.renderedStepId !== stepId) {
1605
- this.innerHTML = "";
1606
- this.renderedStepId = stepId;
1607
- const extensionRoot = document.createElement(
1608
- "juo-extension-root"
1609
- );
1610
- extensionRoot.setAttribute("surface", "overlay");
1611
- extensionRoot.setAttribute("name", stepId);
1612
- extensionRoot.blockWrapper = this.blockWrapper;
1613
- this.appendChild(extensionRoot);
1292
+ return out;
1293
+ }
1294
+ function reclaimSelector(scope, fillId, emptyMates, domIndex) {
1295
+ const fillIdx = domIndex.get(fillId) ?? 0;
1296
+ const byDom = (a2, b2) => (domIndex.get(a2) ?? 0) - (domIndex.get(b2) ?? 0);
1297
+ const before = emptyMates.filter((id) => (domIndex.get(id) ?? 0) < fillIdx).sort(byDom);
1298
+ const after = emptyMates.filter((id) => (domIndex.get(id) ?? 0) > fillIdx).sort(byDom);
1299
+ const emptyBefore = (id) => `[data-block-id="${id}"]:is([data-empty],:has(> [data-empty]))`;
1300
+ const emptyAfterHas = (id) => `~ [data-block-id="${id}"][data-empty],~ [data-block-id="${id}"] > [data-empty]`;
1301
+ let sel = "";
1302
+ for (const id of before) sel += `${emptyBefore(id)} ~ `;
1303
+ sel += `[data-block-id="${fillId}"]`;
1304
+ for (const id of after) sel += `:has(${emptyAfterHas(id)})`;
1305
+ return `${scope} > ${sel}`;
1306
+ }
1307
+ function reclaimCss(scope, layout, domOrder) {
1308
+ const items = layout.items;
1309
+ const cols = layout.grid.cols;
1310
+ const domIndex = new Map(domOrder.map((id, index) => [id, index]));
1311
+ const rules = [];
1312
+ for (const [fillId, fillRect] of Object.entries(items)) {
1313
+ if (fillRect.fill !== true) continue;
1314
+ const mateIds = Object.entries(items).filter(([id, rect]) => id !== fillId && sharesRowBand(rect, fillRect)).map(([id]) => id);
1315
+ if (mateIds.length === 0 || mateIds.length > 4) continue;
1316
+ for (const emptyMates of nonEmptySubsets(mateIds)) {
1317
+ const remaining = {};
1318
+ for (const [id, rect] of Object.entries(items)) {
1319
+ if (id === fillId || emptyMates.includes(id)) continue;
1320
+ remaining[id] = rect;
1614
1321
  }
1615
- return;
1322
+ const { left, right } = rowBandBounds(remaining, fillRect, cols);
1323
+ if (left >= fillRect.c && right <= fillRect.c + fillRect.w) continue;
1324
+ const selector = reclaimSelector(scope, fillId, emptyMates, domIndex);
1325
+ rules.push(
1326
+ `${selector}{grid-column:${left + 1}/span ${Math.max(1, right - left)};--juo-layout-c:${left};}`
1327
+ );
1328
+ }
1329
+ }
1330
+ return rules.join("");
1331
+ }
1332
+ function projectToCss(doc, opts) {
1333
+ const base = doc.breakpoints[0];
1334
+ if (!base) return "";
1335
+ const baseLayout = resolveLayout(doc, base.id);
1336
+ const domOrder = opts.domOrder ?? Object.keys(baseLayout.items);
1337
+ const parts = [
1338
+ breakpointCss(opts.scope, baseLayout),
1339
+ baseSpacingRule(opts.scope),
1340
+ collapseRule(opts.scope),
1341
+ reclaimCss(opts.scope, baseLayout, domOrder)
1342
+ ];
1343
+ for (const bp of doc.breakpoints.slice(1)) {
1344
+ if (!bp.query) continue;
1345
+ parts.push(
1346
+ `@container (max-width:${bp.query.maxWidth}px){` + breakpointCss(opts.scope, resolveLayout(doc, bp.id)) + `}`
1347
+ );
1348
+ }
1349
+ return parts.join("");
1350
+ }
1351
+ function createDefaultLayoutDocument(blockIds) {
1352
+ const items = {};
1353
+ blockIds.forEach((id, index) => {
1354
+ items[id] = { c: 0, r: index, w: DEFAULT_GRID.cols, h: 1 };
1355
+ });
1356
+ return {
1357
+ version: 1,
1358
+ engine: GRID_V1_ENGINE_ID,
1359
+ breakpoints: structuredClone([...DEFAULT_BREAKPOINTS]),
1360
+ layouts: {
1361
+ [DEFAULT_BREAKPOINTS[0].id]: { grid: { ...DEFAULT_GRID }, items }
1616
1362
  }
1617
- this.innerHTML = "";
1618
- this.renderedStepId = null;
1619
- if (status === "error") {
1620
- const error = ((_a = this.workflowService) == null ? void 0 : _a.state.value.error) ?? "Unknown error";
1621
- this.dispatchEvent(
1622
- new CustomEvent("juo-workflow-error", {
1623
- bubbles: true,
1624
- composed: true,
1625
- detail: { error }
1626
- })
1363
+ };
1364
+ }
1365
+ function reconcileLayout(doc, blockIds) {
1366
+ var _a;
1367
+ if (!doc) return createDefaultLayoutDocument(blockIds);
1368
+ const next = structuredClone(doc);
1369
+ const baseId = (_a = next.breakpoints[0]) == null ? void 0 : _a.id;
1370
+ if (!baseId) return createDefaultLayoutDocument(blockIds);
1371
+ const layout = next.layouts[baseId] ?? { grid: { ...DEFAULT_GRID }, items: {} };
1372
+ const known = new Set(blockIds);
1373
+ for (const id of Object.keys(layout.items)) {
1374
+ if (!known.has(id)) delete layout.items[id];
1375
+ }
1376
+ for (const id of blockIds) {
1377
+ if (!layout.items[id]) {
1378
+ layout.items[id] = findFreeRect(
1379
+ layout.items,
1380
+ { w: layout.grid.cols, h: 1 },
1381
+ layout.grid.cols
1627
1382
  );
1628
1383
  }
1629
1384
  }
1385
+ next.layouts[baseId] = layout;
1386
+ return next;
1630
1387
  }
1631
- if (typeof customElements !== "undefined" && !customElements.get("juo-workflow")) {
1632
- customElements.define("juo-workflow", JuoWorkflowElement);
1388
+ function compactLayoutDocument(doc) {
1389
+ var _a;
1390
+ if (!doc) return doc;
1391
+ const next = structuredClone(doc);
1392
+ const baseId = (_a = next.breakpoints[0]) == null ? void 0 : _a.id;
1393
+ if (!baseId) return next;
1394
+ const layout = next.layouts[baseId];
1395
+ if (!layout) return next;
1396
+ layout.items = compactItems(layout.items, layout.grid.cols);
1397
+ next.layouts[baseId] = layout;
1398
+ return next;
1399
+ }
1400
+ function expandFillLayoutDocument(doc) {
1401
+ var _a;
1402
+ if (!doc) return doc;
1403
+ const next = structuredClone(doc);
1404
+ const baseId = (_a = next.breakpoints[0]) == null ? void 0 : _a.id;
1405
+ if (!baseId) return next;
1406
+ const layout = next.layouts[baseId];
1407
+ if (!layout) return next;
1408
+ layout.items = applyFillExpansion(layout.items, layout.grid.cols);
1409
+ next.layouts[baseId] = layout;
1410
+ return next;
1411
+ }
1412
+ function classifyRowDividers(items, blockIds) {
1413
+ const leaderBlockIds = /* @__PURE__ */ new Set();
1414
+ const rowBlockIds = /* @__PURE__ */ new Set();
1415
+ const placed = blockIds.filter((id) => items[id] != null);
1416
+ if (placed.length === 0) return { leaderBlockIds, rowBlockIds };
1417
+ const rects = placed.map((id) => items[id]);
1418
+ const minRow = Math.min(...rects.map((rect) => rect.r));
1419
+ const boundaryIsCrossed = (boundary) => rects.some((rect) => rect.r < boundary && rect.r + rect.h > boundary);
1420
+ const ordered = [...placed].sort((a2, b2) => {
1421
+ const ra = items[a2];
1422
+ const rb = items[b2];
1423
+ return ra.r - rb.r || ra.c - rb.c;
1424
+ });
1425
+ let lastLeaderRow = null;
1426
+ for (const id of ordered) {
1427
+ const { r: r2 } = items[id];
1428
+ if (r2 <= minRow || boundaryIsCrossed(r2)) continue;
1429
+ rowBlockIds.add(id);
1430
+ if (r2 !== lastLeaderRow) {
1431
+ leaderBlockIds.add(id);
1432
+ lastLeaderRow = r2;
1433
+ }
1434
+ }
1435
+ return { leaderBlockIds, rowBlockIds };
1436
+ }
1437
+ const LAYOUT_SCOPE_ATTR = "data-juo-layout";
1438
+ function layoutScopeSelector(scopeId) {
1439
+ return `[${LAYOUT_SCOPE_ATTR}="${scopeId}"]`;
1440
+ }
1441
+ function buildLayoutCss(layout, blockIds, scopeId) {
1442
+ const reconciled = reconcileLayout(layout, blockIds);
1443
+ return projectToCss(reconciled, {
1444
+ scope: layoutScopeSelector(scopeId),
1445
+ domOrder: blockIds
1446
+ });
1447
+ }
1448
+ function buildGridLayout(layout, blockIds, scopeId) {
1449
+ var _a, _b;
1450
+ const scope = layoutScopeSelector(scopeId);
1451
+ const reconciled = reconcileLayout(layout, blockIds);
1452
+ const baseId = (_a = reconciled.breakpoints[0]) == null ? void 0 : _a.id;
1453
+ const layoutItems = (baseId != null ? (_b = reconciled.layouts[baseId]) == null ? void 0 : _b.items : null) ?? {};
1454
+ const { leaderBlockIds, rowBlockIds } = classifyRowDividers(
1455
+ layoutItems,
1456
+ blockIds
1457
+ );
1458
+ const fullWidthBlockIds = /* @__PURE__ */ new Set();
1459
+ for (const [id, rect] of Object.entries(layoutItems)) {
1460
+ if (rect.fill === true) fullWidthBlockIds.add(id);
1461
+ }
1462
+ return {
1463
+ css: projectToCss(reconciled, { scope, domOrder: blockIds }),
1464
+ dividerBlockIds: leaderBlockIds,
1465
+ dividerRowBlockIds: rowBlockIds,
1466
+ fullWidthBlockIds
1467
+ };
1633
1468
  }
1634
1469
  export {
1635
- isEditorPreview as A,
1470
+ reconcileLayout as $,
1471
+ createDefaultLayoutDocument as A,
1636
1472
  BlockWrapperEvent as B,
1637
1473
  ContextProvideEvent as C,
1638
- DEFAULT_GLOBAL_STYLES as D,
1474
+ DEFAULT_GRID as D,
1639
1475
  E,
1640
- isStubDefinition as F,
1641
- onRegisterBlock as G,
1642
- provideContext as H,
1643
- serializeBlocks as I,
1644
- subscribeContext as J,
1645
- n as K,
1646
- withWrapper as L,
1647
- ContextRoot as M,
1476
+ createSelectedBlock as F,
1477
+ GRID_V1_ENGINE_ID as G,
1478
+ createThemeState as H,
1479
+ createTranslationService as I,
1480
+ defineBlockRuntime as J,
1481
+ deriveLayout as K,
1482
+ LAYOUT_SCOPE_ATTR as L,
1483
+ expandFillLayoutDocument as M,
1484
+ hasRenderedContent as N,
1485
+ indexOverrides as O,
1486
+ PAGE_ROOT_SLOT as P,
1487
+ injectContext as Q,
1488
+ isEditorPreview as R,
1648
1489
  SelectedBlockContext as S,
1649
1490
  ThemeStateContext as T,
1650
- WorkflowServiceContext as W,
1651
- ContextRequestEvent as a,
1652
- TranslationContext as b,
1491
+ isStubDefinition as U,
1492
+ layoutScopeSelector as V,
1493
+ layoutSystemEnabled as W,
1494
+ onRegisterBlock as X,
1495
+ projectToCss as Y,
1496
+ provideContext as Z,
1497
+ readingOrder as _,
1498
+ createBlockInstance as a,
1499
+ rectsOverlap as a0,
1500
+ resolveLayout as a1,
1501
+ serializeBlocks as a2,
1502
+ setLayoutSystemEnabled as a3,
1503
+ n as a4,
1504
+ withWrapper as a5,
1505
+ ContextRoot as a6,
1506
+ MAX_BLOCKS_PER_ROW as a7,
1507
+ defineBlock as b,
1653
1508
  createContext as c,
1654
1509
  d,
1655
- attachEmptyTracking as e,
1656
- backfillInstanceDefaults as f,
1657
- createBlockInstance as g,
1658
- createBlockInstanceFromJSON as h,
1659
- createBlockInstanceFromObject as i,
1660
- createCeRenderer as j,
1661
- createSelectedBlock as k,
1662
- createThemeState as l,
1663
- createTranslationService as m,
1664
- createWorkflowApiAdapter as n,
1665
- createWorkflowService as o,
1666
- defineBlock as p,
1667
- defineBlockRuntime as q,
1510
+ clampRect as e,
1511
+ applyFillExpansion as f,
1512
+ getDefinedBlocks as g,
1513
+ compactItems as h,
1514
+ findFreeRect as i,
1515
+ ContextRequestEvent as j,
1516
+ DEFAULT_BREAKPOINTS as k,
1517
+ DEFAULT_GLOBAL_STYLES as l,
1518
+ TranslationContext as m,
1519
+ attachEmptyTracking as n,
1520
+ backfillInstanceDefaults as o,
1521
+ buildGridLayout as p,
1522
+ buildLayoutCss as q,
1668
1523
  registerBlock as r,
1669
- getDefaultBlockForAction as s,
1670
- getDefinedBlocks as t,
1524
+ subscribeContext as s,
1525
+ classifyRowDividers as t,
1671
1526
  u,
1672
- hasDefaultBlockForAction as v,
1527
+ compactLayoutDocument as v,
1673
1528
  w,
1674
- hasRenderedContent as x,
1675
- indexOverrides as y,
1676
- injectContext as z
1529
+ createBlockInstanceFromJSON as x,
1530
+ createBlockInstanceFromObject as y,
1531
+ createCeRenderer as z
1677
1532
  };