@kubohiroya/turbowarp-extension-data-editor 0.2.0 → 0.2.2

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 (68) hide show
  1. package/README.md +9 -6
  2. package/dist/build-info.json +3 -3
  3. package/dist/fixed-input-branches.d.ts +12 -0
  4. package/dist/fixed-input-branches.js +180 -0
  5. package/dist/fixed-input-editor.d.ts +39 -0
  6. package/dist/fixed-input-editor.js +538 -0
  7. package/dist/fixed-input-schema.d.ts +15 -0
  8. package/dist/fixed-input-schema.js +21 -0
  9. package/dist/fixed-input-snapshot.d.ts +7 -0
  10. package/dist/fixed-input-snapshot.js +180 -0
  11. package/dist/fixed-input-types.d.ts +138 -0
  12. package/dist/fixed-input-types.js +23 -0
  13. package/dist/fixed-input-view.d.ts +14 -0
  14. package/dist/fixed-input-view.js +168 -0
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.js +4 -0
  17. package/dist/react.css +168 -0
  18. package/dist/react.d.ts +8 -0
  19. package/dist/react.js +19 -2
  20. package/dist/registry.d.ts +4 -0
  21. package/dist/registry.js +45 -7
  22. package/dist/rjsf-templates.d.ts +11 -0
  23. package/dist/rjsf-templates.js +129 -0
  24. package/dist/rjsf.d.ts +24 -1
  25. package/dist/rjsf.js +35 -3
  26. package/dist/schema.d.ts +2 -0
  27. package/dist/schema.js +1 -0
  28. package/dist/session.d.ts +3 -1
  29. package/dist/session.js +205 -7
  30. package/dist/turbowarp/bridge.d.ts +2 -1
  31. package/dist/turbowarp/bridge.js +37 -5
  32. package/dist/turbowarp/fixed-fields.d.ts +82 -0
  33. package/dist/turbowarp/fixed-fields.js +311 -0
  34. package/dist/turbowarp/fixed-transaction.d.ts +18 -0
  35. package/dist/turbowarp/fixed-transaction.js +460 -0
  36. package/dist/turbowarp/host.d.ts +3 -8
  37. package/dist/turbowarp/host.js +68 -1
  38. package/dist/turbowarp.d.ts +4 -0
  39. package/dist/turbowarp.js +1 -1
  40. package/dist/types.d.ts +5 -0
  41. package/dist/view.js +4 -0
  42. package/dist/webpack4/index.js +22 -5
  43. package/dist/webpack4/metafile.json +592 -299
  44. package/dist/webpack4/react.js +20 -6
  45. package/dist/webpack4/react.js.map +2 -2
  46. package/dist/webpack4/rjsf.js +450 -60
  47. package/dist/webpack4/rjsf.js.map +4 -4
  48. package/dist/webpack4/schema.js +6 -3
  49. package/dist/webpack4/{shared-KQOUMWJ4.js → shared-75QIWLZY.js} +1129 -1244
  50. package/dist/webpack4/{shared-KQOUMWJ4.js.map → shared-75QIWLZY.js.map} +4 -4
  51. package/dist/webpack4/shared-FYUILHED.js +1 -0
  52. package/dist/webpack4/shared-GQLPPRT2.js +210 -0
  53. package/dist/webpack4/shared-GQLPPRT2.js.map +7 -0
  54. package/dist/webpack4/shared-P2JBRZFP.js +2206 -0
  55. package/dist/webpack4/shared-P2JBRZFP.js.map +7 -0
  56. package/dist/webpack4/turbowarp.js +913 -9
  57. package/dist/webpack4/turbowarp.js.map +4 -4
  58. package/docs/api.md +7 -0
  59. package/docs/fixed-input-contracts.md +77 -0
  60. package/docs/package-distribution.md +5 -0
  61. package/docs/stage-2-publication.ja.md +6 -0
  62. package/package.json +2 -1
  63. package/dist/webpack4/shared-CQB3NG2A.js +0 -1
  64. package/dist/webpack4/shared-MRPGBJIH.js +0 -75
  65. package/dist/webpack4/shared-MRPGBJIH.js.map +0 -7
  66. package/dist/webpack4/shared-N6YQE2X6.js +0 -1029
  67. package/dist/webpack4/shared-N6YQE2X6.js.map +0 -7
  68. /package/dist/webpack4/{shared-CQB3NG2A.js.map → shared-FYUILHED.js.map} +0 -0
@@ -1,5 +1,12 @@
1
- import "./shared-CQB3NG2A.js";
2
- import "./shared-N6YQE2X6.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,
@@ -7,7 +14,9 @@ import {
7
14
  DESCENDANT_EDITING_CAPABILITY,
8
15
  supportsDescendantEditing
9
16
  } from "./shared-BKTRZUZH.js";
10
- import "./shared-MRPGBJIH.js";
17
+ import {
18
+ snapshotJson
19
+ } from "./shared-75QIWLZY.js";
11
20
  import "./shared-RN7GOT65.js";
12
21
 
13
22
  // dist/turbowarp/subtree.js
@@ -68,17 +77,827 @@ var collectBlockEditorSubtree = ({ ScratchBlocks, reference, inspect, generation
68
77
  };
69
78
  var subtree_default = collectBlockEditorSubtree;
70
79
 
71
- // dist/turbowarp/host.js
80
+ // dist/turbowarp/fixed-fields.js
72
81
  var owns = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
73
- 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 }) => {
74
878
  let disposed = false;
75
879
  let committing = false;
76
880
  let revision = 0;
77
881
  let identity = 0;
78
882
  let generation = 1;
883
+ let externalRevision = 0;
884
+ let fixedTransaction = null;
79
885
  let lastTarget = vm.editingTarget;
80
886
  const versions = /* @__PURE__ */ new Map();
81
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
+ };
82
901
  let notifyInvalidations = () => {
83
902
  };
84
903
  const identities = /* @__PURE__ */ new WeakMap();
@@ -91,6 +910,16 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
91
910
  if (event && event.isUiEvent)
92
911
  return;
93
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++;
94
923
  if (enableDescendants && event) {
95
924
  const ids = /* @__PURE__ */ new Set([event.blockId, ...event.ids || []]);
96
925
  for (const id of ids)
@@ -99,6 +928,7 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
99
928
  }
100
929
  if (!committing)
101
930
  notifyInvalidations();
931
+ notifyFixedScopes();
102
932
  };
103
933
  const renew = () => {
104
934
  generation++;
@@ -124,6 +954,8 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
124
954
  if (!block || block.isInFlyout || block.isShadow())
125
955
  return null;
126
956
  const descriptor = registry.resolve(block.type);
957
+ if (typeof registry.resolveFixed === "function" && registry.resolveFixed(block.type))
958
+ return null;
127
959
  const vmBlock = target.blocks.getBlock(block.id);
128
960
  if (!structural && !descriptor || !block.isEditable() && (!structural || descriptor) || !vmBlock || vmBlock.opcode !== block.type)
129
961
  return null;
@@ -280,7 +1112,7 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
280
1112
  return changes.map((change) => {
281
1113
  const record = change && records.get(change.blockId);
282
1114
  const key = change && JSON.stringify([change.blockId, change.argument]);
283
- 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)) {
284
1116
  throw new TypeError("Only distinct standard text shadow changes are supported.");
285
1117
  }
286
1118
  seen.add(key);
@@ -294,10 +1126,17 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
294
1126
  const host = {
295
1127
  isCommitting: () => committing,
296
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;
297
1133
  const result = inspect(reference);
298
1134
  return result ? result.snapshot : null;
299
1135
  },
300
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
+ }
301
1140
  const current = inspect(expected);
302
1141
  if (!current || JSON.stringify(current.snapshot) !== JSON.stringify(expected)) {
303
1142
  throw new BlockEditorConflictError();
@@ -307,6 +1146,8 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
307
1146
  invalidate: renew,
308
1147
  dispose: () => {
309
1148
  disposed = true;
1149
+ notifyFixedScopes();
1150
+ fixedScopeListeners.clear();
310
1151
  notifyInvalidations();
311
1152
  invalidations.clear();
312
1153
  workspace.removeChangeListener(invalidate);
@@ -340,6 +1181,42 @@ var createBlockEditorHost = ({ ScratchBlocks, workspace, vm, registry, enableDes
340
1181
  }
341
1182
  };
342
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
+ }
343
1220
  return host;
344
1221
  };
345
1222
  var host_default = createBlockEditorHost;
@@ -497,14 +1374,21 @@ var createBlockEditorErrorSession = (reference, error) => {
497
1374
  var error_session_default = createBlockEditorErrorSession;
498
1375
 
499
1376
  // dist/turbowarp/bridge.js
500
- var openBlockEditorSession = ({ registry, reference, host, descendants }) => descendants && supportsDescendantEditing(host) ? registry.openDescendants(reference, host) : registry.open(reference, host);
501
- 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 }) => {
502
1385
  const host = host_default({
503
1386
  ScratchBlocks,
504
1387
  workspace,
505
1388
  vm,
506
1389
  registry: service.registry,
507
1390
  enableDescendants: descendants,
1391
+ enableFixedInputs: fixedInputs,
508
1392
  onError
509
1393
  });
510
1394
  const decorations = /* @__PURE__ */ new Map();
@@ -512,6 +1396,7 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
512
1396
  let timer = null;
513
1397
  let activeSession = null;
514
1398
  let openingSnapshot = null;
1399
+ let openingFixedDeclaration = null;
515
1400
  let stopWaiting = null;
516
1401
  const endSession = () => {
517
1402
  const session = activeSession;
@@ -534,11 +1419,17 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
534
1419
  }
535
1420
  };
536
1421
  const checkSession = () => {
1422
+ var _a;
537
1423
  if (host.isCommitting())
538
1424
  return;
539
1425
  if (!activeSession || !openingSnapshot || !["editing", "saving"].includes(activeSession.getState().status))
540
1426
  return;
541
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
+ }
542
1433
  if (!current || JSON.stringify(current) !== JSON.stringify(openingSnapshot))
543
1434
  endSession();
544
1435
  };
@@ -546,6 +1437,8 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
546
1437
  if (disposed || !vm.editingTarget || !canOpen() || !service.registry.resolve(block.type))
547
1438
  return false;
548
1439
  const snapshot = host.readBlock({ targetId: vm.editingTarget.id, blockId: block.id });
1440
+ if (fixedDeclaration(service.registry, snapshot))
1441
+ return supportsFixedInputEditing(host);
549
1442
  return snapshot && Object.values(snapshot.arguments).every((slot) => slot.kind === "literal");
550
1443
  };
551
1444
  const open = (block) => {
@@ -560,8 +1453,12 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
560
1453
  ScratchBlocks.hideChaff();
561
1454
  activeSession = session;
562
1455
  openingSnapshot = typeof session.getNodes === "function" ? host.descendants.readSubtree(session.reference) : host.readBlock(session.reference);
1456
+ openingFixedDeclaration = fixedDeclaration(service.registry, openingSnapshot) || null;
563
1457
  const labels = {};
564
1458
  const appearances = /* @__PURE__ */ Object.create(null);
1459
+ const rootAppearance = appearance_default(block, ScratchBlocks);
1460
+ appearances[reference.blockId] = rootAppearance;
1461
+ labels[reference.blockId] = rootAppearance.name || block.type;
565
1462
  if (typeof session.getNodes === "function") {
566
1463
  for (const node of session.getNodes()) {
567
1464
  const source = workspace.getBlockById(node.reference.blockId);
@@ -576,6 +1473,7 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
576
1473
  onError(error);
577
1474
  activeSession = error_session_default(reference, error);
578
1475
  openingSnapshot = null;
1476
+ openingFixedDeclaration = null;
579
1477
  onOpen(activeSession);
580
1478
  }
581
1479
  };
@@ -624,7 +1522,12 @@ var createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, c
624
1522
  timer = setTimeout(refresh, 0);
625
1523
  };
626
1524
  const unregister = service.subscribe(() => {
627
- if (!activeSession || typeof activeSession.getNodes !== "function") {
1525
+ if (openingFixedDeclaration && activeSession) {
1526
+ if (service.registry.resolveFixed(openingSnapshot.opcode) !== openingFixedDeclaration) {
1527
+ host.invalidate();
1528
+ endSession();
1529
+ }
1530
+ } else if (!activeSession || typeof activeSession.getNodes !== "function") {
628
1531
  host.invalidate();
629
1532
  endSession();
630
1533
  }
@@ -663,6 +1566,7 @@ function attachTurboWarpBlockEditors(options) {
663
1566
  const bridge = bridge_default({
664
1567
  ...options,
665
1568
  descendants: options.descendants === true,
1569
+ fixedInputs: options.fixedInputs === true,
666
1570
  onError: (_a = options.onError) !== null && _a !== void 0 ? _a : console.error
667
1571
  });
668
1572
  let disposed = false;