@kubohiroya/turbowarp-extension-data-editor 0.1.0 → 0.2.1

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 (123) hide show
  1. package/README.md +30 -6
  2. package/dist/build-info.json +7 -2
  3. package/dist/descendants.js +11 -2
  4. package/dist/fixed-input-branches.d.ts +12 -0
  5. package/dist/fixed-input-branches.js +180 -0
  6. package/dist/fixed-input-editor.d.ts +39 -0
  7. package/dist/fixed-input-editor.js +538 -0
  8. package/dist/fixed-input-schema.d.ts +15 -0
  9. package/dist/fixed-input-schema.js +21 -0
  10. package/dist/fixed-input-snapshot.d.ts +7 -0
  11. package/dist/fixed-input-snapshot.js +180 -0
  12. package/dist/fixed-input-types.d.ts +138 -0
  13. package/dist/fixed-input-types.js +23 -0
  14. package/dist/fixed-input-view.d.ts +14 -0
  15. package/dist/fixed-input-view.js +168 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.js +4 -0
  18. package/dist/json-value.d.ts +4 -0
  19. package/dist/json-value.js +12 -0
  20. package/dist/prepared-save.d.ts +5 -0
  21. package/dist/prepared-save.js +47 -0
  22. package/dist/prepared-types.d.ts +13 -0
  23. package/dist/prepared-types.js +1 -0
  24. package/dist/react.css +42 -0
  25. package/dist/react.d.ts +3 -0
  26. package/dist/react.js +10 -1
  27. package/dist/registry.d.ts +6 -0
  28. package/dist/registry.js +98 -48
  29. package/dist/rjsf.d.ts +48 -1
  30. package/dist/rjsf.js +92 -8
  31. package/dist/schema-compose.d.ts +6 -0
  32. package/dist/schema-compose.js +103 -0
  33. package/dist/schema-engine.d.ts +18 -0
  34. package/dist/schema-engine.js +298 -0
  35. package/dist/schema-json.d.ts +10 -0
  36. package/dist/schema-json.js +68 -0
  37. package/dist/schema-types.d.ts +161 -0
  38. package/dist/schema-types.js +2 -0
  39. package/dist/schema-view.d.ts +7 -0
  40. package/dist/schema-view.js +102 -0
  41. package/dist/schema.d.ts +10 -0
  42. package/dist/schema.js +87 -0
  43. package/dist/session.d.ts +3 -1
  44. package/dist/session.js +220 -9
  45. package/dist/source-document.d.ts +3 -0
  46. package/dist/source-document.js +415 -0
  47. package/dist/source-editor.d.ts +13 -0
  48. package/dist/source-editor.js +235 -0
  49. package/dist/source-schema-hints.d.ts +3 -0
  50. package/dist/source-schema-hints.js +13 -0
  51. package/dist/source-types.d.ts +47 -0
  52. package/dist/source-types.js +1 -0
  53. package/dist/source-view.d.ts +3 -0
  54. package/dist/source-view.js +318 -0
  55. package/dist/turbowarp/bridge.d.ts +2 -1
  56. package/dist/turbowarp/bridge.js +34 -5
  57. package/dist/turbowarp/fixed-fields.d.ts +82 -0
  58. package/dist/turbowarp/fixed-fields.js +311 -0
  59. package/dist/turbowarp/fixed-transaction.d.ts +18 -0
  60. package/dist/turbowarp/fixed-transaction.js +460 -0
  61. package/dist/turbowarp/host.d.ts +3 -8
  62. package/dist/turbowarp/host.js +68 -1
  63. package/dist/turbowarp.d.ts +4 -0
  64. package/dist/turbowarp.js +1 -1
  65. package/dist/types.d.ts +7 -4
  66. package/dist/view.js +4 -0
  67. package/dist/webpack4/index.js +24 -5
  68. package/dist/webpack4/metafile.json +9886 -8710
  69. package/dist/webpack4/react.js +11 -2
  70. package/dist/webpack4/react.js.map +2 -2
  71. package/dist/webpack4/rjsf.js +15205 -20951
  72. package/dist/webpack4/rjsf.js.map +4 -4
  73. package/dist/webpack4/schema.js +20 -0
  74. package/dist/webpack4/shared-75QIWLZY.js +8375 -0
  75. package/dist/webpack4/shared-75QIWLZY.js.map +7 -0
  76. package/dist/webpack4/shared-BKTRZUZH.js +42 -0
  77. package/dist/webpack4/{shared-KIFRVTSM.js.map → shared-BKTRZUZH.js.map} +1 -1
  78. package/dist/webpack4/shared-FYUILHED.js +1 -0
  79. package/dist/webpack4/shared-FYUILHED.js.map +7 -0
  80. package/dist/webpack4/shared-GQLPPRT2.js +210 -0
  81. package/dist/webpack4/shared-GQLPPRT2.js.map +7 -0
  82. package/dist/webpack4/shared-P2JBRZFP.js +2206 -0
  83. package/dist/webpack4/shared-P2JBRZFP.js.map +7 -0
  84. package/dist/webpack4/{shared-KIFRVTSM.js → shared-RN7GOT65.js} +2 -41
  85. package/dist/webpack4/shared-RN7GOT65.js.map +7 -0
  86. package/dist/webpack4/turbowarp.js +912 -9
  87. package/dist/webpack4/turbowarp.js.map +4 -4
  88. package/docs/README.md +1 -0
  89. package/docs/block-editor-e2e.md +98 -16
  90. package/docs/ci-success-gate.md +84 -0
  91. package/docs/editor/issue-12-public-validation/README.md +61 -0
  92. package/docs/editor/issue-12-public-validation/dark-invalid.png +0 -0
  93. package/docs/editor/issue-12-public-validation/dark-styles.json +173 -0
  94. package/docs/editor/issue-12-public-validation/dark-valid.png +0 -0
  95. package/docs/editor/issue-12-public-validation/deployment-target.json +5 -0
  96. package/docs/editor/issue-12-public-validation/deployment-wait.json +7 -0
  97. package/docs/editor/issue-12-public-validation/light-invalid.png +0 -0
  98. package/docs/editor/issue-12-public-validation/light-styles.json +173 -0
  99. package/docs/editor/issue-12-public-validation/light-valid.png +0 -0
  100. package/docs/editor/issue-12-public-validation/provenance.json +88 -0
  101. package/docs/editor/issue-12-public-validation/results.json +677 -0
  102. package/docs/editor/issue-12-public-validation/summary.json +206 -0
  103. package/docs/fixed-input-contracts.md +77 -0
  104. package/docs/github-pages.md +48 -41
  105. package/docs/package-distribution.md +2 -2
  106. package/docs/schema-stage1-browser.json +10 -0
  107. package/docs/schema-stage1-contracts.json +242 -0
  108. package/docs/schema-stage1-dependencies.json +202 -0
  109. package/docs/schema-stage1-git-artifact-20261018.ja.md +17 -0
  110. package/docs/schema-stage1-profile-compat.json +30 -0
  111. package/docs/schema-stage1-review-fixes-20261018.ja.md +36 -0
  112. package/docs/schema-stage1-verification.json +541 -0
  113. package/docs/schema-stage1.ja.md +80 -0
  114. package/docs/schema.md +43 -0
  115. package/docs/source-forms.ja.md +19 -0
  116. package/docs/stage-2-publication.ja.md +35 -0
  117. package/docs/template-fixture-separation.ja.md +43 -0
  118. package/package.json +18 -3
  119. package/webpack4.cjs +1 -1
  120. package/dist/webpack4/shared-CQB3NG2A.js +0 -1
  121. package/dist/webpack4/shared-WHOAW2VF.js +0 -957
  122. package/dist/webpack4/shared-WHOAW2VF.js.map +0 -7
  123. /package/dist/webpack4/{shared-CQB3NG2A.js.map → schema.js.map} +0 -0
@@ -1,12 +1,23 @@
1
- import "./shared-CQB3NG2A.js";
2
- import "./shared-WHOAW2VF.js";
1
+ import "./shared-FYUILHED.js";
2
+ import {
3
+ FIXED_INPUTS_CAPABILITY,
4
+ captureFixedInputDeclaration,
5
+ captureFixedSnapshot,
6
+ sameFixedSnapshot,
7
+ supportsFixedInputEditing,
8
+ validateFixedBranchChanges
9
+ } from "./shared-P2JBRZFP.js";
3
10
  import {
4
11
  BlockEditorConflictError,
5
12
  BlockEditorStructureError,
6
13
  DEFAULT_DESCENDANT_LIMITS,
7
14
  DESCENDANT_EDITING_CAPABILITY,
8
15
  supportsDescendantEditing
9
- } from "./shared-KIFRVTSM.js";
16
+ } from "./shared-BKTRZUZH.js";
17
+ import {
18
+ snapshotJson
19
+ } from "./shared-75QIWLZY.js";
20
+ import "./shared-RN7GOT65.js";
10
21
 
11
22
  // dist/turbowarp/subtree.js
12
23
  var collectBlockEditorSubtree = ({ ScratchBlocks, reference, inspect, generation }) => {
@@ -66,17 +77,827 @@ var collectBlockEditorSubtree = ({ ScratchBlocks, reference, inspect, generation
66
77
  };
67
78
  var subtree_default = collectBlockEditorSubtree;
68
79
 
69
- // dist/turbowarp/host.js
80
+ // dist/turbowarp/fixed-fields.js
70
81
  var owns = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
71
- var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDescendants = false, onError = console.error }) => {
82
+ var own = (object, key) => {
83
+ const descriptor = object && Object.getOwnPropertyDescriptor(object, key);
84
+ return descriptor && "value" in descriptor ? descriptor.value : void 0;
85
+ };
86
+ var primitive = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
87
+ var token = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
88
+ var inherited = (object, name) => {
89
+ for (let current = object; current; current = Object.getPrototypeOf(current)) {
90
+ const descriptor = Object.getOwnPropertyDescriptor(current, name);
91
+ if (descriptor)
92
+ return "value" in descriptor ? descriptor.value : void 0;
93
+ }
94
+ return void 0;
95
+ };
96
+ var storage = ["text_", "value_", "colour_", "validator_", "sourceBlock_", "hue_", "saturation_", "brightness_"];
97
+ var methods = [
98
+ "getValue",
99
+ "getText",
100
+ "setValue",
101
+ "setText",
102
+ "getValidator",
103
+ "classValidator",
104
+ "callValidator",
105
+ "getOptions",
106
+ "forceRerender",
107
+ "updateSliderHandles_",
108
+ "updateDom_"
109
+ ];
110
+ var trustedClasses = /* @__PURE__ */ new WeakMap();
111
+ function initializeFixedFields(ScratchBlocks) {
112
+ for (const key of ["FieldTextInput", "FieldNumber", "FieldDropdown", "FieldColourSlider"]) {
113
+ const Class = ScratchBlocks[key];
114
+ if (typeof Class !== "function" || trustedClasses.has(Class))
115
+ continue;
116
+ const chain = [];
117
+ for (let prototype = Class.prototype; prototype && prototype !== Object.prototype; prototype = Object.getPrototypeOf(prototype)) {
118
+ chain.push({ prototype, own: methods.map((name) => [name, Object.getOwnPropertyDescriptor(prototype, name)]) });
119
+ }
120
+ trustedClasses.set(Class, {
121
+ prototype: Class.prototype,
122
+ chain,
123
+ methods: methods.map((name) => [name, inherited(Class.prototype, name)])
124
+ });
125
+ }
126
+ }
127
+ var safeField = (field, Class) => {
128
+ const trusted = Class && trustedClasses.get(Class);
129
+ if (!trusted || Class.prototype !== trusted.prototype || Object.getPrototypeOf(field) !== trusted.prototype)
130
+ return false;
131
+ let currentPrototype = trusted.prototype;
132
+ for (const entry of trusted.chain) {
133
+ if (currentPrototype !== entry.prototype)
134
+ return false;
135
+ for (const [name, expected] of entry.own) {
136
+ const actual = Object.getOwnPropertyDescriptor(currentPrototype, name);
137
+ if (Boolean(actual) !== Boolean(expected) || actual && (!("value" in actual) || !expected || !("value" in expected) || actual.value !== expected.value))
138
+ return false;
139
+ }
140
+ currentPrototype = Object.getPrototypeOf(currentPrototype);
141
+ }
142
+ if (currentPrototype !== Object.prototype && currentPrototype !== null)
143
+ return false;
144
+ for (const name of storage) {
145
+ for (let current = field; current; current = Object.getPrototypeOf(current)) {
146
+ const descriptor = Object.getOwnPropertyDescriptor(current, name);
147
+ if (!descriptor)
148
+ continue;
149
+ if (!("value" in descriptor))
150
+ return false;
151
+ break;
152
+ }
153
+ }
154
+ for (const [name, expected] of trusted.methods) {
155
+ if (owns(field, name) || inherited(field, name) !== expected)
156
+ return false;
157
+ }
158
+ for (let current = field; current; current = Object.getPrototypeOf(current)) {
159
+ const descriptor = Object.getOwnPropertyDescriptor(current, "validator_");
160
+ if (descriptor)
161
+ return "value" in descriptor && descriptor.value == null;
162
+ }
163
+ return false;
164
+ };
165
+ var arrayData = (value) => {
166
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype)
167
+ return null;
168
+ const result = [];
169
+ for (let index = 0; index < value.length; index++) {
170
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
171
+ if (!descriptor || !("value" in descriptor))
172
+ return null;
173
+ result.push(descriptor.value);
174
+ }
175
+ return result;
176
+ };
177
+ var staticValues = (field) => {
178
+ const items = arrayData(own(field, "menuGenerator_"));
179
+ if (!items || items.length === 0)
180
+ return null;
181
+ const values = [];
182
+ for (const item of items) {
183
+ const tuple = arrayData(item);
184
+ if (!tuple || tuple.length !== 2 || typeof tuple[0] !== "string" || typeof tuple[1] !== "string")
185
+ return null;
186
+ values.push(tuple[1]);
187
+ }
188
+ return Object.freeze(values);
189
+ };
190
+ var rawKeys = ["text_", "value_", "colour_", "hue_", "saturation_", "brightness_"];
191
+ var ownState = (object, keys) => keys.map((name) => [name, Object.getOwnPropertyDescriptor(object, name)]);
192
+ var restoreOwnState = (object, state) => {
193
+ for (const [name, descriptor] of state) {
194
+ if (descriptor)
195
+ Object.defineProperty(object, name, descriptor);
196
+ else
197
+ delete object[name];
198
+ }
199
+ };
200
+ var vmFieldsIdentity = (fields, identify) => Object.keys(fields || {}).sort().map((name) => {
201
+ const descriptor = Object.getOwnPropertyDescriptor(fields, name);
202
+ if (!descriptor || !("value" in descriptor))
203
+ return [name, "accessor"];
204
+ const stored = descriptor.value;
205
+ if (!stored || typeof stored !== "object")
206
+ return [name, typeof stored, String(stored)];
207
+ return [name, identify(stored), ...Object.keys(stored).sort().map((key) => {
208
+ const item = Object.getOwnPropertyDescriptor(stored, key);
209
+ if (!item || !("value" in item))
210
+ return [key, "accessor"];
211
+ const value = item.value;
212
+ return [key, typeof value, primitive(value) ? Object.is(value, -0) ? "-0" : String(value) : value && (typeof value === "object" || typeof value === "function") ? identify(value) : String(value)];
213
+ })];
214
+ });
215
+ var colorState = (block, field) => {
216
+ const colors = ["getColour", "getColourSecondary", "getColourTertiary", "getColourQuaternary"];
217
+ return {
218
+ colors: colors.map((name) => typeof block[name] === "function" ? block[name]() : void 0),
219
+ hsv: ["hue_", "saturation_", "brightness_"].map((name) => [name, own(field, name)]),
220
+ blockProperties: ownState(block, ["colour_", "colourSecondary_", "colourTertiary_", "colourQuaternary_"])
221
+ };
222
+ };
223
+ function inspectFixedArgument({ ScratchBlocks, block, vmBlock, target, argument, role, identify, extensionId }) {
224
+ initializeFixedFields(ScratchBlocks);
225
+ const sourceIdentity = [argument, identify(block), identify(vmBlock)];
226
+ let observedFields = [];
227
+ const unavailable = (source, reason, raw2) => ({
228
+ slot: primitive(raw2) ? { kind: "literal", value: raw2 } : { kind: "reporter" },
229
+ metadata: { role, source, identity: JSON.stringify([...sourceIdentity, source]), reason },
230
+ signatureParts: [...sourceIdentity, observedFields]
231
+ });
232
+ const direct = block.getField(argument);
233
+ const input = block.getInput(argument);
234
+ const connected = input && input.connection && input.connection.targetBlock();
235
+ const vmInput = vmBlock.inputs && vmBlock.inputs[argument];
236
+ if ((connected ? connected.id : null) !== (vmInput && vmInput.block ? vmInput.block : null))
237
+ return null;
238
+ if (direct && connected)
239
+ return null;
240
+ if (connected && !connected.isShadow()) {
241
+ const vmReporter = target.blocks.getBlock(connected.id);
242
+ if (!vmReporter || vmReporter.opcode !== connected.type || vmReporter.parent !== block.id)
243
+ return null;
244
+ sourceIdentity.push(identify(connected), identify(vmReporter), connected.id, connected.type);
245
+ return {
246
+ slot: { kind: "reporter" },
247
+ metadata: { role, source: "reporter", identity: JSON.stringify(sourceIdentity) },
248
+ signatureParts: sourceIdentity
249
+ };
250
+ }
251
+ let owner = direct ? block : connected;
252
+ if (!owner) {
253
+ if (vmBlock.fields && owns(vmBlock.fields, argument))
254
+ return null;
255
+ return unavailable("missing", "The declared field or input is missing.");
256
+ }
257
+ const vmOwner = direct ? vmBlock : target.blocks.getBlock(owner.id);
258
+ if (!vmOwner || vmOwner.opcode !== owner.type)
259
+ return null;
260
+ sourceIdentity.push(identify(owner), identify(vmOwner), owner.id, owner.type);
261
+ observedFields = vmFieldsIdentity(vmOwner.fields, identify);
262
+ if (!direct && (!vmInput || vmInput.shadow !== owner.id || vmOwner.parent !== block.id))
263
+ return null;
264
+ let kind;
265
+ let name;
266
+ let Class;
267
+ if (direct) {
268
+ kind = "menu";
269
+ name = argument;
270
+ Class = ScratchBlocks.FieldDropdown;
271
+ } else if (owner.type === "text") {
272
+ kind = "text";
273
+ name = "TEXT";
274
+ Class = ScratchBlocks.FieldTextInput;
275
+ } else if (owner.type === "math_number") {
276
+ kind = "number";
277
+ name = "NUM";
278
+ Class = ScratchBlocks.FieldNumber;
279
+ } else if (owner.type === "colour_picker") {
280
+ kind = "color";
281
+ name = "COLOUR";
282
+ Class = ScratchBlocks.FieldColourSlider;
283
+ } else if (typeof extensionId === "string" && owner.type.startsWith(`${extensionId}_menu_`)) {
284
+ kind = "menu";
285
+ Class = ScratchBlocks.FieldDropdown;
286
+ const names = [];
287
+ for (const row of owner.inputList)
288
+ for (const candidate of row.fieldRow) {
289
+ const candidateName = own(candidate, "name");
290
+ if (typeof candidateName === "string" && candidateName)
291
+ names.push(candidateName);
292
+ }
293
+ if (names.length !== 1)
294
+ return unavailable("unsupported", "The menu shadow does not have one standard field.");
295
+ [name] = names;
296
+ } else
297
+ return unavailable("unsupported", "The shadow field family is unsupported.");
298
+ const field = direct || owner.getField(name);
299
+ const stored = vmOwner.fields && vmOwner.fields[name];
300
+ if (!field || !stored)
301
+ return null;
302
+ sourceIdentity.push(identify(field), name);
303
+ const raw = own(stored, "value");
304
+ if (!safeField(field, Class))
305
+ return unavailable("unsupported", "The field class, methods or validator is unsupported.", raw);
306
+ const value = inherited(Class.prototype, "getValue").call(field);
307
+ if (!Object.is(value, raw))
308
+ return null;
309
+ if (typeof value !== "string")
310
+ return unavailable("unsupported", "The native field value is not a string.", raw);
311
+ const allowedValues = kind === "menu" ? staticValues(field) : void 0;
312
+ if (kind === "menu" && !allowedValues)
313
+ return unavailable("unsupported", "Dynamic or nonstandard menus are unsupported.", raw);
314
+ sourceIdentity.push(kind, ...allowedValues || []);
315
+ const binding = {
316
+ block: owner,
317
+ field,
318
+ fieldName: name,
319
+ value,
320
+ vmBlock: vmOwner,
321
+ kind,
322
+ Class,
323
+ prototype: Class.prototype,
324
+ allowedValues,
325
+ restoreValue: inherited(Class.prototype, "setValue"),
326
+ rawState: ownState(field, rawKeys),
327
+ restoreHandles: inherited(Class.prototype, "updateSliderHandles_"),
328
+ restoreDom: inherited(Class.prototype, "updateDom_"),
329
+ ...kind === "color" ? { restoreState: colorState(owner, field) } : {}
330
+ };
331
+ return {
332
+ slot: { kind: "literal", value },
333
+ metadata: {
334
+ role,
335
+ source: direct ? "field" : "shadow",
336
+ identity: JSON.stringify(sourceIdentity),
337
+ field: { blockId: owner.id, name, type: { text: "FieldTextInput", number: "FieldNumber", menu: "FieldDropdown", color: "FieldColourSlider" }[kind] }
338
+ },
339
+ ...role === "editable" ? { binding } : {},
340
+ signatureParts: [...sourceIdentity, observedFields]
341
+ };
342
+ }
343
+ function prepareFixedChange(binding, value) {
344
+ if (!binding || !safeField(binding.field, binding.Class) || typeof value !== "string") {
345
+ throw new TypeError("Only standard native string field writes are supported.");
346
+ }
347
+ if (binding.kind === "menu") {
348
+ const currentValues = staticValues(binding.field);
349
+ if (!currentValues || currentValues.length !== binding.allowedValues.length || currentValues.some((item, index) => item !== binding.allowedValues[index])) {
350
+ throw new TypeError("The static menu changed before preparing the write.");
351
+ }
352
+ }
353
+ if (value !== binding.value) {
354
+ if (binding.kind === "number" && (!token.test(value.trim()) || !Number.isFinite(Number(value)))) {
355
+ throw new TypeError("Expected a finite numeric field token.");
356
+ }
357
+ if (binding.kind === "color" && !/^#(?:[a-f0-9]{3}|[a-f0-9]{6})$/iu.test(value)) {
358
+ throw new TypeError("Expected a hexadecimal color field token.");
359
+ }
360
+ if (binding.kind === "menu" && !binding.allowedValues.includes(value)) {
361
+ throw new TypeError("Expected a declared static menu value.");
362
+ }
363
+ }
364
+ return { ...binding, next: value };
365
+ }
366
+ function captureFixedFieldState(binding) {
367
+ if (!binding || !safeField(binding.field, binding.Class)) {
368
+ throw new TypeError("Cannot capture an unsupported native field.");
369
+ }
370
+ const value = inherited(binding.prototype, "getValue").call(binding.field);
371
+ if (typeof value !== "string")
372
+ throw new TypeError("Expected a native string field.");
373
+ return {
374
+ ...binding,
375
+ value,
376
+ rawState: ownState(binding.field, rawKeys),
377
+ ...binding.kind === "color" ? { restoreState: colorState(binding.block, binding.field) } : {}
378
+ };
379
+ }
380
+ function restoreFixedField(binding) {
381
+ let failure;
382
+ try {
383
+ binding.restoreValue.call(binding.field, binding.value);
384
+ } catch (error) {
385
+ failure = error;
386
+ }
387
+ restoreOwnState(binding.field, binding.rawState);
388
+ if (binding.restoreState) {
389
+ const colors = binding.restoreState.colors;
390
+ try {
391
+ if (colors.every((value) => typeof value === "string"))
392
+ binding.block.setColour(...colors);
393
+ } catch (error) {
394
+ failure = failure || error;
395
+ }
396
+ restoreOwnState(binding.block, binding.restoreState.blockProperties);
397
+ try {
398
+ binding.restoreHandles.call(binding.field);
399
+ } catch (error) {
400
+ failure = failure || error;
401
+ }
402
+ try {
403
+ binding.restoreDom.call(binding.field);
404
+ } catch (error) {
405
+ failure = failure || error;
406
+ }
407
+ }
408
+ if (failure)
409
+ throw failure;
410
+ }
411
+
412
+ // dist/turbowarp/fixed-transaction.js
413
+ var own2 = (object, name) => {
414
+ const descriptor = object && Object.getOwnPropertyDescriptor(object, name);
415
+ if (!descriptor || !Object.prototype.hasOwnProperty.call(descriptor, "value")) {
416
+ throw new TypeError("Expected an own data property.");
417
+ }
418
+ return descriptor.value;
419
+ };
420
+ var inheritedData = (object, key) => {
421
+ for (let item = object; item; item = Object.getPrototypeOf(item)) {
422
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
423
+ if (descriptor) {
424
+ if (!("value" in descriptor))
425
+ throw new TypeError("Unsupported undo configuration.");
426
+ return descriptor.value;
427
+ }
428
+ }
429
+ return void 0;
430
+ };
431
+ var ensureNativeHistory = (stack) => {
432
+ if (!Array.isArray(stack) || !Object.isExtensible(stack) || Object.getOwnPropertySymbols(stack).length || !Object.getOwnPropertyDescriptor(stack, "length").writable) {
433
+ throw new TypeError("Unsupported undo history.");
434
+ }
435
+ if (Object.getOwnPropertyNames(stack).length !== stack.length + 1)
436
+ throw new TypeError("Unsupported undo history.");
437
+ for (let index = 0; index < stack.length; index++) {
438
+ const descriptor = Object.getOwnPropertyDescriptor(stack, String(index));
439
+ if (!descriptor || !("value" in descriptor) || !descriptor.writable || !descriptor.configurable) {
440
+ throw new TypeError("Unsupported undo history.");
441
+ }
442
+ }
443
+ };
444
+ var copyHistory = (stack) => {
445
+ const copy = [];
446
+ for (let index = 0; index < stack.length; index++)
447
+ copy.push(own2(stack, String(index)));
448
+ return copy;
449
+ };
450
+ var replaceHistory = (stack, values) => {
451
+ stack.length = 0;
452
+ for (const value of values)
453
+ Array.prototype.push.call(stack, value);
454
+ };
455
+ var conflict = () => {
456
+ throw new BlockEditorConflictError();
457
+ };
458
+ var aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
459
+ var isAborted = (signal) => aborted.call(signal);
460
+ var optionalOwn = (object, name) => {
461
+ const descriptor = object && Object.getOwnPropertyDescriptor(object, name);
462
+ if (descriptor && !Object.prototype.hasOwnProperty.call(descriptor, "value")) {
463
+ throw new TypeError("VM state must contain data properties.");
464
+ }
465
+ return descriptor && descriptor.value;
466
+ };
467
+ var stateToken = (value, identify, seen = /* @__PURE__ */ new Set()) => {
468
+ if (typeof value === "number")
469
+ return ["number", Object.is(value, -0) ? "-0" : String(value)];
470
+ if (value === void 0)
471
+ return ["undefined"];
472
+ if (value === null || typeof value === "string" || typeof value === "boolean")
473
+ return value;
474
+ if (typeof value !== "object" || seen.has(value) || seen.size > 64)
475
+ throw new TypeError("Unsupported VM state.");
476
+ seen.add(value);
477
+ const result = Object.getOwnPropertyNames(value).sort().map((key) => [key, stateToken(own2(value, key), identify, seen)]);
478
+ seen.delete(value);
479
+ return ["object", identify(value), result];
480
+ };
481
+ var untouchedState = (block, vmBlock, target, bindings, identify) => {
482
+ const omitted = /* @__PURE__ */ new Map();
483
+ for (const binding of bindings.values()) {
484
+ if (!omitted.has(binding.vmBlock))
485
+ omitted.set(binding.vmBlock, /* @__PURE__ */ new Set());
486
+ omitted.get(binding.vmBlock).add(binding.fieldName);
487
+ }
488
+ const seen = /* @__PURE__ */ new Set();
489
+ const records = [];
490
+ const capture = (item) => {
491
+ if (!item || seen.has(item))
492
+ return;
493
+ seen.add(item);
494
+ const fields = item.fields || {};
495
+ records.push([identify(item), ["id", "opcode", "parent", "next", "inputs", "mutation", "shadow", "topLevel"].map((key) => [key, stateToken(optionalOwn(item, key), identify)]), identify(fields), Object.keys(fields).sort().map((key) => {
496
+ var _a;
497
+ const field = own2(fields, key);
498
+ if (!((_a = omitted.get(item)) === null || _a === void 0 ? void 0 : _a.has(key)))
499
+ return [key, stateToken(field, identify)];
500
+ return [key, identify(field), Object.getOwnPropertyNames(field).sort().filter((property) => property !== "value").map((property) => [property, stateToken(own2(field, property), identify)])];
501
+ })]);
502
+ };
503
+ capture(vmBlock);
504
+ const connections = [];
505
+ for (const input of block.inputList) {
506
+ const connected = input.connection && input.connection.targetBlock();
507
+ connections.push([input.name || "", identify(input), input.connection ? identify(input.connection) : null, connected ? [connected.id, connected.type, identify(connected)] : null]);
508
+ if (connected)
509
+ capture(target.blocks.getBlock(connected.id));
510
+ }
511
+ const statementConnections = ["nextConnection", "previousConnection", "outputConnection"].map((name) => {
512
+ const connection = block[name];
513
+ const connected = connection && connection.targetBlock();
514
+ return [
515
+ name,
516
+ connection ? identify(connection) : null,
517
+ connection && connection.targetConnection ? identify(connection.targetConnection) : null,
518
+ connected ? [connected.id, connected.type, identify(connected)] : null
519
+ ];
520
+ });
521
+ return JSON.stringify([records, connections, statementConnections]);
522
+ };
523
+ var sameDeclaration = (left, right) => JSON.stringify(captureFixedInputDeclaration(left)) === JSON.stringify(captureFixedInputDeclaration(right));
524
+ function createFixedInputPort({ ScratchBlocks, workspace, vm, registry, identify, getState, begin, markEvent, finish, onError, subscribeScope }) {
525
+ initializeFixedFields(ScratchBlocks);
526
+ const inspect = (reference, suppliedDeclaration) => {
527
+ const state = getState();
528
+ const target = vm.editingTarget;
529
+ if (state.disposed || !target || target.id !== reference.targetId || workspace.isFlyout || workspace.options.readOnly || workspace.isDragging && workspace.isDragging())
530
+ return null;
531
+ const block = workspace.getBlockById(reference.blockId);
532
+ if (!block || block.isInFlyout || block.isShadow() || !block.isEditable())
533
+ return null;
534
+ const descriptor = registry.resolve(block.type);
535
+ const declaration = registry.resolveFixed(block.type);
536
+ if (!descriptor || !declaration || !sameDeclaration(declaration, suppliedDeclaration))
537
+ return null;
538
+ const vmBlock = target.blocks.getBlock(block.id);
539
+ const nextBlock = block.nextConnection && block.nextConnection.targetBlock();
540
+ if (!vmBlock || (nextBlock ? nextBlock.id : null) !== (vmBlock.next || null) || vmBlock.opcode !== block.type || (block.getParent() ? block.getParent().id : null) !== (vmBlock.parent || null))
541
+ return null;
542
+ const arguments_ = /* @__PURE__ */ Object.create(null);
543
+ const inputs = /* @__PURE__ */ Object.create(null);
544
+ const bindings = /* @__PURE__ */ new Map();
545
+ const signature = [state.revision, identify(target), identify(block), identify(vmBlock), identify(declaration)];
546
+ for (const [role, names] of [["editable", declaration.editableArguments], ["readonly", declaration.readOnlyArguments]]) {
547
+ for (const argument of names) {
548
+ const item = inspectFixedArgument({
549
+ ScratchBlocks,
550
+ block,
551
+ vmBlock,
552
+ target,
553
+ argument,
554
+ role,
555
+ identify,
556
+ extensionId: descriptor.extensionId
557
+ });
558
+ if (!item)
559
+ return null;
560
+ arguments_[argument] = item.slot;
561
+ inputs[argument] = item.metadata;
562
+ if (role === "editable" && item.binding)
563
+ bindings.set(argument, item.binding);
564
+ signature.push(argument, item.signatureParts);
565
+ }
566
+ }
567
+ const untouched = untouchedState(block, vmBlock, target, bindings, identify);
568
+ signature.push(untouched);
569
+ const snapshot = captureFixedSnapshot({
570
+ targetId: target.id,
571
+ blockId: block.id,
572
+ opcode: block.type,
573
+ revision: JSON.stringify(signature),
574
+ arguments: arguments_,
575
+ fixedContext: {
576
+ contractId: declaration.contractId,
577
+ contractRevision: declaration.contractRevision,
578
+ generation: String(state.generation),
579
+ readOnlyArguments: declaration.readOnlyArguments,
580
+ dependencies: declaration.dependencies,
581
+ inputs
582
+ }
583
+ }, reference, declaration);
584
+ return { snapshot, bindings, declaration, target, block, vmBlock, state, untouched, runtime: vm.runtime };
585
+ };
586
+ const commit = (request) => {
587
+ var _a;
588
+ const expectedInput = own2(request, "expected");
589
+ const changesInput = own2(request, "changes");
590
+ const signal = own2(request, "signal");
591
+ try {
592
+ const contextInput = own2(request, "context");
593
+ const context = snapshotJson({
594
+ consumerId: own2(contextInput, "consumerId"),
595
+ nowMs: own2(contextInput, "nowMs"),
596
+ settings: own2(contextInput, "settings")
597
+ });
598
+ if (typeof context.consumerId !== "string" || !context.consumerId || typeof context.nowMs !== "number" || !Number.isFinite(context.nowMs)) {
599
+ throw new TypeError("Invalid fixed validation context.");
600
+ }
601
+ } catch (_b) {
602
+ throw new TypeError("Invalid fixed validation context.");
603
+ }
604
+ if (isAborted(signal))
605
+ conflict();
606
+ const expected = captureFixedSnapshot(expectedInput);
607
+ const current = inspect(expected, registry.resolveFixed(expected.opcode));
608
+ if (!current || !sameFixedSnapshot(expected, current.snapshot))
609
+ conflict();
610
+ const declaration = current.declaration;
611
+ captureFixedSnapshot(expected, expected, declaration);
612
+ if (!Array.isArray(changesInput))
613
+ throw new TypeError("Expected a fixed argument patch array.");
614
+ const prepared = [];
615
+ const seenNames = /* @__PURE__ */ new Set();
616
+ const seenFields = /* @__PURE__ */ new Set();
617
+ for (let i = 0; i < changesInput.length; i++) {
618
+ const change = own2(changesInput, String(i));
619
+ const name = own2(change, "argument");
620
+ const value = own2(change, "value");
621
+ if (typeof name !== "string" || seenNames.has(name) || !declaration.editableArguments.includes(name) || !current.bindings.has(name)) {
622
+ throw new TypeError("Only distinct editable standard fields may be changed.");
623
+ }
624
+ seenNames.add(name);
625
+ const binding = current.bindings.get(name);
626
+ if (seenFields.has(binding.field))
627
+ throw new TypeError("Duplicate field write.");
628
+ seenFields.add(binding.field);
629
+ if (Object.is(value, binding.value))
630
+ continue;
631
+ prepared.push({ ...prepareFixedChange(binding, value), argument: name });
632
+ }
633
+ if (validateFixedBranchChanges(declaration, expected, prepared.map((item) => ({ argument: item.argument, value: item.next }))).length) {
634
+ throw new TypeError("Fixed branch changes require a valid canonical branch policy.");
635
+ }
636
+ const guard = () => {
637
+ const state = getState();
638
+ if (isAborted(signal) || state.disposed || state.generation !== current.state.generation || state.externalRevision !== current.state.externalRevision || vm.editingTarget !== current.target || vm.runtime !== current.runtime || registry.resolveFixed(expected.opcode) !== declaration)
639
+ conflict();
640
+ };
641
+ guard();
642
+ const beforeWrite = inspect(expected, declaration);
643
+ if (!beforeWrite || !sameFixedSnapshot(expected, beforeWrite.snapshot))
644
+ conflict();
645
+ guard();
646
+ if (!prepared.length)
647
+ return;
648
+ const Events = ScratchBlocks.Events;
649
+ if (!Events.isEnabled() || !Events.recordUndo || Events.getGroup() || !Array.isArray(workspace.undoStack_) || !Array.isArray(workspace.redoStack_)) {
650
+ throw new Error("The workspace is busy or its undo API is unsupported.");
651
+ }
652
+ const undoStack = workspace.undoStack_;
653
+ const redoStack = workspace.redoStack_;
654
+ ensureNativeHistory(undoStack);
655
+ ensureNativeHistory(redoStack);
656
+ const configuredMaxUndo = inheritedData(workspace, "MAX_UNDO");
657
+ const maxUndo = configuredMaxUndo === void 0 ? Infinity : configuredMaxUndo;
658
+ if (maxUndo !== Infinity && (!Number.isSafeInteger(maxUndo) || maxUndo < 0)) {
659
+ throw new TypeError("Unsupported undo configuration.");
660
+ }
661
+ const undo = copyHistory(undoStack);
662
+ const redo = copyHistory(redoStack);
663
+ const oldGroup = Events.getGroup();
664
+ const runtime = current.runtime;
665
+ const dirty = runtime.emitProjectChanged;
666
+ const dirtyDescriptor = Object.getOwnPropertyDescriptor(runtime, "emitProjectChanged");
667
+ const savedVmFields = prepared.map((item) => ({
668
+ binding: item,
669
+ block: item.vmBlock,
670
+ name: item.fieldName,
671
+ fields: item.vmBlock.fields,
672
+ field: item.vmBlock.fields[item.fieldName],
673
+ descriptors: Object.getOwnPropertyDescriptors(item.vmBlock.fields[item.fieldName])
674
+ }));
675
+ let succeeded = false;
676
+ let failure;
677
+ let activeEvent = null;
678
+ const pendingHistory = [];
679
+ const externalWrites = /* @__PURE__ */ new Map();
680
+ const byField = new Map(prepared.map((item) => [JSON.stringify([item.block.id, item.fieldName]), item]));
681
+ let externalDirty = false;
682
+ const ownedEvents = /* @__PURE__ */ new WeakSet();
683
+ const originalDelivery = workspace.fireChangeListener;
684
+ const deliveryDescriptor = Object.getOwnPropertyDescriptor(workspace, "fireChangeListener");
685
+ const stableScope = () => {
686
+ const state = getState();
687
+ return state.generation === current.state.generation && vm.editingTarget === current.target && vm.runtime === runtime;
688
+ };
689
+ let begun = false;
690
+ try {
691
+ runtime.emitProjectChanged = () => {
692
+ if (!activeEvent || !ownedEvents.has(activeEvent) || !stableScope())
693
+ externalDirty = true;
694
+ };
695
+ workspace.fireChangeListener = function(event) {
696
+ const previous = activeEvent;
697
+ const external = event && !event.isUiEvent && !ownedEvents.has(event);
698
+ const edited = external && event.type === "change" && event.element === "field" && byField.get(JSON.stringify([event.blockId, event.name]));
699
+ if (edited && !externalWrites.has(edited.field)) {
700
+ if (Object.is(event.oldValue, edited.next))
701
+ event.oldValue = edited.value;
702
+ externalWrites.set(edited.field, null);
703
+ }
704
+ activeEvent = event;
705
+ try {
706
+ return originalDelivery.call(this, event);
707
+ } finally {
708
+ if (edited) {
709
+ const fields = edited.vmBlock.fields;
710
+ const field = fields && fields[edited.fieldName];
711
+ externalWrites.set(edited.field, {
712
+ binding: captureFixedFieldState(edited),
713
+ fields,
714
+ field,
715
+ descriptors: field && Object.getOwnPropertyDescriptors(field)
716
+ });
717
+ }
718
+ activeEvent = previous;
719
+ }
720
+ };
721
+ Events.setGroup(true);
722
+ Events.disable();
723
+ begin();
724
+ begun = true;
725
+ guard();
726
+ for (const item of prepared) {
727
+ guard();
728
+ item.field.setValue(item.next);
729
+ if (!Object.is(item.field.getValue(), item.next))
730
+ throw new Error("The field rejected its value.");
731
+ guard();
732
+ }
733
+ for (const item of prepared) {
734
+ guard();
735
+ const event = new Events.Change(item.block, "field", item.fieldName, item.value, item.next);
736
+ ownedEvents.add(event);
737
+ markEvent(event);
738
+ const recordUndo = event.recordUndo;
739
+ event.recordUndo = false;
740
+ try {
741
+ workspace.fireChangeListener(event);
742
+ } finally {
743
+ event.recordUndo = recordUndo;
744
+ }
745
+ if (recordUndo)
746
+ pendingHistory.push(event);
747
+ guard();
748
+ }
749
+ const after = inspect(expected, declaration);
750
+ if (!after || after.untouched !== current.untouched)
751
+ conflict();
752
+ const wantedArguments = { ...expected.arguments };
753
+ for (const item of prepared)
754
+ wantedArguments[item.argument] = { kind: "literal", value: item.next };
755
+ if (!sameFixedSnapshot({ ...expected, revision: after.snapshot.revision, arguments: wantedArguments }, after.snapshot))
756
+ conflict();
757
+ guard();
758
+ if (workspace.undoStack_ !== undoStack || workspace.redoStack_ !== redoStack || undoStack.length !== undo.length || redoStack.length !== redo.length || undo.some((entry, index) => undoStack[index] !== entry) || redo.some((entry, index) => redoStack[index] !== entry))
759
+ conflict();
760
+ ensureNativeHistory(undoStack);
761
+ ensureNativeHistory(redoStack);
762
+ try {
763
+ for (const event of pendingHistory)
764
+ Array.prototype.push.call(undoStack, event);
765
+ if (undoStack.length > maxUndo) {
766
+ Array.prototype.copyWithin.call(undoStack, 0, undoStack.length - maxUndo);
767
+ undoStack.length = maxUndo;
768
+ }
769
+ redoStack.length = 0;
770
+ } catch (error) {
771
+ replaceHistory(undoStack, undo);
772
+ replaceHistory(redoStack, redo);
773
+ throw error;
774
+ }
775
+ succeeded = true;
776
+ } catch (error) {
777
+ failure = error;
778
+ for (const item of begun ? prepared : []) {
779
+ try {
780
+ restoreFixedField(((_a = externalWrites.get(item.field)) === null || _a === void 0 ? void 0 : _a.binding) || item);
781
+ } catch (restoreError) {
782
+ try {
783
+ onError(restoreError);
784
+ } catch (_c) {
785
+ }
786
+ }
787
+ }
788
+ for (const saved of begun ? savedVmFields : []) {
789
+ const outside = externalWrites.get(saved.binding.field);
790
+ const fields = saved.block.fields;
791
+ if (!fields)
792
+ continue;
793
+ const field = outside ? outside.field : saved.field;
794
+ const descriptors = outside ? outside.descriptors : saved.descriptors;
795
+ if (!field) {
796
+ delete fields[saved.name];
797
+ continue;
798
+ }
799
+ fields[saved.name] = field;
800
+ for (const key of Object.keys(field))
801
+ if (!Object.hasOwn(descriptors, key))
802
+ delete field[key];
803
+ Object.defineProperties(field, descriptors);
804
+ }
805
+ if (begun && typeof current.target.blocks.resetCache === "function")
806
+ current.target.blocks.resetCache();
807
+ } finally {
808
+ try {
809
+ if (begun)
810
+ finish(succeeded);
811
+ } finally {
812
+ try {
813
+ if (!Events.isEnabled())
814
+ Events.enable();
815
+ } finally {
816
+ try {
817
+ Events.setGroup(oldGroup);
818
+ } finally {
819
+ if (deliveryDescriptor)
820
+ Object.defineProperty(workspace, "fireChangeListener", deliveryDescriptor);
821
+ else
822
+ delete workspace.fireChangeListener;
823
+ if (dirtyDescriptor)
824
+ Object.defineProperty(runtime, "emitProjectChanged", dirtyDescriptor);
825
+ else
826
+ delete runtime.emitProjectChanged;
827
+ }
828
+ }
829
+ }
830
+ }
831
+ if (succeeded || externalDirty) {
832
+ try {
833
+ dirty.call(runtime);
834
+ } catch (error) {
835
+ try {
836
+ onError(error);
837
+ } catch (_d) {
838
+ }
839
+ }
840
+ }
841
+ if (!succeeded)
842
+ throw failure;
843
+ };
844
+ return Object.freeze({
845
+ capability: FIXED_INPUTS_CAPABILITY,
846
+ readBlock: (reference, declaration) => {
847
+ var _a;
848
+ return ((_a = inspect(reference, declaration)) === null || _a === void 0 ? void 0 : _a.snapshot) || null;
849
+ },
850
+ commit,
851
+ subscribeInvalidation: (reference, declaration, listener) => {
852
+ const captured = inspect(reference, declaration);
853
+ if (!captured || typeof subscribeScope !== "function") {
854
+ listener(new BlockEditorConflictError());
855
+ return () => {
856
+ };
857
+ }
858
+ let active = true;
859
+ const check = () => {
860
+ const state = getState();
861
+ if (!active || !state.disposed && state.generation === captured.state.generation && vm.editingTarget === captured.target && vm.runtime === captured.runtime && workspace.getBlockById(reference.blockId) === captured.block && captured.target.blocks.getBlock(reference.blockId) === captured.vmBlock && registry.resolveFixed(captured.snapshot.opcode) === captured.declaration)
862
+ return;
863
+ active = false;
864
+ listener(new BlockEditorConflictError());
865
+ };
866
+ const unsubscribe = subscribeScope(check);
867
+ return () => {
868
+ active = false;
869
+ unsubscribe();
870
+ };
871
+ }
872
+ });
873
+ }
874
+
875
+ // dist/turbowarp/host.js
876
+ var owns2 = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
877
+ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDescendants = false, enableFixedInputs = false, onError = console.error }) => {
72
878
  let disposed = false;
73
879
  let committing = false;
74
880
  let revision = 0;
75
881
  let identity = 0;
76
882
  let generation = 1;
883
+ let externalRevision = 0;
884
+ let fixedTransaction = null;
77
885
  let lastTarget = vm.editingTarget;
78
886
  const versions = /* @__PURE__ */ new Map();
79
887
  const invalidations = /* @__PURE__ */ new Set();
888
+ const fixedScopeListeners = /* @__PURE__ */ new Set();
889
+ const notifyFixedScopes = () => {
890
+ for (const listener of [...fixedScopeListeners]) {
891
+ try {
892
+ listener();
893
+ } catch (error) {
894
+ try {
895
+ onError(error);
896
+ } catch (_a) {
897
+ }
898
+ }
899
+ }
900
+ };
80
901
  let notifyInvalidations = () => {
81
902
  };
82
903
  const identities = /* @__PURE__ */ new WeakMap();
@@ -89,6 +910,16 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
89
910
  if (event && event.isUiEvent)
90
911
  return;
91
912
  revision++;
913
+ if (fixedTransaction && event && fixedTransaction.events.has(event)) {
914
+ fixedTransaction.revisions++;
915
+ if (enableDescendants) {
916
+ for (const id of /* @__PURE__ */ new Set([event.blockId, ...event.ids || []])) {
917
+ if (id)
918
+ fixedTransaction.versions.set(id, (fixedTransaction.versions.get(id) || 0) + 1);
919
+ }
920
+ }
921
+ } else
922
+ externalRevision++;
92
923
  if (enableDescendants && event) {
93
924
  const ids = /* @__PURE__ */ new Set([event.blockId, ...event.ids || []]);
94
925
  for (const id of ids)
@@ -97,6 +928,7 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
97
928
  }
98
929
  if (!committing)
99
930
  notifyInvalidations();
931
+ notifyFixedScopes();
100
932
  };
101
933
  const renew = () => {
102
934
  generation++;
@@ -122,6 +954,8 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
122
954
  if (!block || block.isInFlyout || block.isShadow())
123
955
  return null;
124
956
  const descriptor = registry.resolve(block.type);
957
+ if (typeof registry.resolveFixed === "function" && registry.resolveFixed(block.type))
958
+ return null;
125
959
  const vmBlock = target.blocks.getBlock(block.id);
126
960
  if (!structural && !descriptor || !block.isEditable() && (!structural || descriptor) || !vmBlock || vmBlock.opcode !== block.type)
127
961
  return null;
@@ -278,7 +1112,7 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
278
1112
  return changes.map((change) => {
279
1113
  const record = change && records.get(change.blockId);
280
1114
  const key = change && JSON.stringify([change.blockId, change.argument]);
281
- if (!record || typeof change.value !== "string" || seen.has(key) || !owns(record.snapshot.arguments, change.argument) || !record.fields.has(change.argument)) {
1115
+ if (!record || typeof change.value !== "string" || seen.has(key) || !owns2(record.snapshot.arguments, change.argument) || !record.fields.has(change.argument)) {
282
1116
  throw new TypeError("Only distinct standard text shadow changes are supported.");
283
1117
  }
284
1118
  seen.add(key);
@@ -292,10 +1126,17 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
292
1126
  const host = {
293
1127
  isCommitting: () => committing,
294
1128
  readBlock: (reference) => {
1129
+ const block = workspace.getBlockById(reference.blockId);
1130
+ const declaration = block && typeof registry.resolveFixed === "function" && registry.resolveFixed(block.type);
1131
+ if (declaration)
1132
+ return host.fixedInputs ? host.fixedInputs.readBlock(reference, declaration) : null;
295
1133
  const result = inspect(reference);
296
1134
  return result ? result.snapshot : null;
297
1135
  },
298
1136
  commit: ({ expected, changes }) => {
1137
+ if (typeof registry.resolveFixed === "function" && registry.resolveFixed(expected.opcode)) {
1138
+ throw new TypeError("Fixed input editors require the fixed transaction port.");
1139
+ }
299
1140
  const current = inspect(expected);
300
1141
  if (!current || JSON.stringify(current.snapshot) !== JSON.stringify(expected)) {
301
1142
  throw new BlockEditorConflictError();
@@ -305,6 +1146,8 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
305
1146
  invalidate: renew,
306
1147
  dispose: () => {
307
1148
  disposed = true;
1149
+ notifyFixedScopes();
1150
+ fixedScopeListeners.clear();
308
1151
  notifyInvalidations();
309
1152
  invalidations.clear();
310
1153
  workspace.removeChangeListener(invalidate);
@@ -338,6 +1181,42 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
338
1181
  }
339
1182
  };
340
1183
  }
1184
+ if (enableFixedInputs === true) {
1185
+ host.fixedInputs = createFixedInputPort({
1186
+ ScratchBlocks,
1187
+ workspace,
1188
+ vm,
1189
+ registry,
1190
+ identify,
1191
+ onError,
1192
+ subscribeScope: (listener) => {
1193
+ fixedScopeListeners.add(listener);
1194
+ return () => {
1195
+ fixedScopeListeners.delete(listener);
1196
+ };
1197
+ },
1198
+ getState: () => ({ disposed, revision, generation, externalRevision }),
1199
+ begin: () => {
1200
+ committing = true;
1201
+ fixedTransaction = { events: /* @__PURE__ */ new WeakSet(), revisions: 0, versions: /* @__PURE__ */ new Map() };
1202
+ },
1203
+ markEvent: (event) => fixedTransaction.events.add(event),
1204
+ finish: (succeeded) => {
1205
+ if (!succeeded) {
1206
+ revision -= fixedTransaction.revisions;
1207
+ for (const [id, delta] of fixedTransaction.versions) {
1208
+ const remaining = (versions.get(id) || 0) - delta;
1209
+ if (remaining > 0)
1210
+ versions.set(id, remaining);
1211
+ else
1212
+ versions.delete(id);
1213
+ }
1214
+ }
1215
+ fixedTransaction = null;
1216
+ committing = false;
1217
+ }
1218
+ });
1219
+ }
341
1220
  return host;
342
1221
  };
343
1222
  var host_default = createBlockEditorHost;
@@ -495,14 +1374,21 @@ var createBlockEditorErrorSession = (reference, error) => {
495
1374
  var error_session_default = createBlockEditorErrorSession;
496
1375
 
497
1376
  // dist/turbowarp/bridge.js
498
- var openBlockEditorSession = ({ registry, reference, host, descendants }) => descendants && supportsDescendantEditing(host) ? registry.openDescendants(reference, host) : registry.open(reference, host);
499
- var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, canOpen, onOpen, onError, descendants = false }) => {
1377
+ var fixedDeclaration = (registry, snapshot) => snapshot && typeof registry.resolveFixed === "function" && registry.resolveFixed(snapshot.opcode);
1378
+ var openBlockEditorSession = ({ registry, reference, host, descendants }) => {
1379
+ const snapshot = host.readBlock ? host.readBlock(reference) : null;
1380
+ if (fixedDeclaration(registry, snapshot))
1381
+ return supportsFixedInputEditing(host) ? registry.open(reference, host) : void 0;
1382
+ return descendants && supportsDescendantEditing(host) ? registry.openDescendants(reference, host) : registry.open(reference, host);
1383
+ };
1384
+ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, canOpen, onOpen, onError, descendants = false, fixedInputs = false }) => {
500
1385
  const host = host_default({
501
1386
  ScratchBlocks,
502
1387
  workspace,
503
1388
  vm,
504
1389
  registry: service.registry,
505
1390
  enableDescendants: descendants,
1391
+ enableFixedInputs: fixedInputs,
506
1392
  onError
507
1393
  });
508
1394
  const decorations = /* @__PURE__ */ new Map();
@@ -510,6 +1396,7 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
510
1396
  let timer = null;
511
1397
  let activeSession = null;
512
1398
  let openingSnapshot = null;
1399
+ let openingFixedDeclaration = null;
513
1400
  let stopWaiting = null;
514
1401
  const endSession = () => {
515
1402
  const session = activeSession;
@@ -532,11 +1419,17 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
532
1419
  }
533
1420
  };
534
1421
  const checkSession = () => {
1422
+ var _a;
535
1423
  if (host.isCommitting())
536
1424
  return;
537
1425
  if (!activeSession || !openingSnapshot || !["editing", "saving"].includes(activeSession.getState().status))
538
1426
  return;
539
1427
  const current = typeof activeSession.getNodes === "function" ? host.descendants.readSubtree(activeSession.reference) : host.readBlock(activeSession.reference);
1428
+ if (openingFixedDeclaration) {
1429
+ if (!current || ((_a = current.fixedContext) === null || _a === void 0 ? void 0 : _a.generation) !== openingSnapshot.fixedContext.generation || fixedDeclaration(service.registry, current) !== openingFixedDeclaration)
1430
+ endSession();
1431
+ return;
1432
+ }
540
1433
  if (!current || JSON.stringify(current) !== JSON.stringify(openingSnapshot))
541
1434
  endSession();
542
1435
  };
@@ -544,6 +1437,8 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
544
1437
  if (disposed || !vm.editingTarget || !canOpen() || !service.registry.resolve(block.type))
545
1438
  return false;
546
1439
  const snapshot = host.readBlock({ targetId: vm.editingTarget.id, blockId: block.id });
1440
+ if (fixedDeclaration(service.registry, snapshot))
1441
+ return supportsFixedInputEditing(host);
547
1442
  return snapshot && Object.values(snapshot.arguments).every((slot) => slot.kind === "literal");
548
1443
  };
549
1444
  const open = (block) => {
@@ -558,6 +1453,7 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
558
1453
  ScratchBlocks.hideChaff();
559
1454
  activeSession = session;
560
1455
  openingSnapshot = typeof session.getNodes === "function" ? host.descendants.readSubtree(session.reference) : host.readBlock(session.reference);
1456
+ openingFixedDeclaration = fixedDeclaration(service.registry, openingSnapshot) || null;
561
1457
  const labels = {};
562
1458
  const appearances = /* @__PURE__ */ Object.create(null);
563
1459
  if (typeof session.getNodes === "function") {
@@ -574,6 +1470,7 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
574
1470
  onError(error);
575
1471
  activeSession = error_session_default(reference, error);
576
1472
  openingSnapshot = null;
1473
+ openingFixedDeclaration = null;
577
1474
  onOpen(activeSession);
578
1475
  }
579
1476
  };
@@ -622,7 +1519,12 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
622
1519
  timer = setTimeout(refresh, 0);
623
1520
  };
624
1521
  const unregister = service.subscribe(() => {
625
- if (!activeSession || typeof activeSession.getNodes !== "function") {
1522
+ if (openingFixedDeclaration && activeSession) {
1523
+ if (service.registry.resolveFixed(openingSnapshot.opcode) !== openingFixedDeclaration) {
1524
+ host.invalidate();
1525
+ endSession();
1526
+ }
1527
+ } else if (!activeSession || typeof activeSession.getNodes !== "function") {
626
1528
  host.invalidate();
627
1529
  endSession();
628
1530
  }
@@ -661,6 +1563,7 @@ function attachTurboWarpBlockEditors(options) {
661
1563
  const bridge = bridge_default({
662
1564
  ...options,
663
1565
  descendants: options.descendants === true,
1566
+ fixedInputs: options.fixedInputs === true,
664
1567
  onError: (_a = options.onError) !== null && _a !== void 0 ? _a : console.error
665
1568
  });
666
1569
  let disposed = false;