@hatiolab/figure-model 0.3.4 → 0.4.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 (46) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +3 -0
  3. package/dist/asset.d.ts +12 -0
  4. package/dist/asset.d.ts.map +1 -1
  5. package/dist/asset.js +15 -3
  6. package/dist/asset.js.map +1 -1
  7. package/dist/command-preview.d.ts +6 -0
  8. package/dist/command-preview.d.ts.map +1 -1
  9. package/dist/command-preview.js +35 -2
  10. package/dist/command-preview.js.map +1 -1
  11. package/dist/editor-workspace.d.ts +48 -401
  12. package/dist/editor-workspace.d.ts.map +1 -1
  13. package/dist/editor-workspace.js +55 -13
  14. package/dist/editor-workspace.js.map +1 -1
  15. package/dist/index.d.ts +5 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +5 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/kernel-version.d.ts +1 -1
  20. package/dist/kernel-version.js +1 -1
  21. package/dist/names.d.ts +19 -0
  22. package/dist/names.d.ts.map +1 -0
  23. package/dist/names.js +47 -0
  24. package/dist/names.js.map +1 -0
  25. package/dist/package.d.ts +86 -0
  26. package/dist/package.d.ts.map +1 -0
  27. package/dist/package.js +202 -0
  28. package/dist/package.js.map +1 -0
  29. package/dist/render-lod.d.ts +12 -0
  30. package/dist/render-lod.d.ts.map +1 -0
  31. package/dist/render-lod.js +32 -0
  32. package/dist/render-lod.js.map +1 -0
  33. package/dist/shape-ops.d.ts +1 -1
  34. package/dist/shape-ops.d.ts.map +1 -1
  35. package/dist/shape-ops.js.map +1 -1
  36. package/dist/words.d.ts +6 -6
  37. package/dist/words.js +8 -8
  38. package/dist/words.js.map +1 -1
  39. package/dist/work-counter.d.ts +16 -0
  40. package/dist/work-counter.d.ts.map +1 -0
  41. package/dist/work-counter.js +13 -0
  42. package/dist/work-counter.js.map +1 -0
  43. package/docs/evaluation-performance-summary-2026-10-05.md +18 -0
  44. package/docs/performance-followup-2026-10-05.md +578 -0
  45. package/docs/shape-dimension-contract.md +10 -0
  46. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import type { compileFigureAsset } from './asset.ts';
1
2
  import type { FigureEditRequest, FigureEditControl } from './authoring-session.ts';
2
3
  import type { FigureBoxCreation, FigurePrimitiveCreation } from './create.ts';
3
4
  import { type FigureProposalStep } from './proposal.ts';
@@ -20,98 +21,38 @@ export interface FigureLeaveOptions {
20
21
  export interface FigureOpenOptions extends FigureLeaveOptions {
21
22
  baseline?: string;
22
23
  }
24
+ type ClosedView = {
25
+ context: null;
26
+ name: null;
27
+ fields: never[];
28
+ geometry: never[];
29
+ bindings: never[];
30
+ dirty: false;
31
+ revision: null;
32
+ canUndo?: undefined;
33
+ canRedo?: undefined;
34
+ palette?: undefined;
35
+ };
36
+ type Rendered = ReturnType<ReturnType<typeof compileFigureAsset>['evaluate']>;
37
+ type OpenView = {
38
+ context: string;
39
+ name: string;
40
+ revision: number;
41
+ dirty: boolean;
42
+ canUndo: boolean;
43
+ canRedo: boolean;
44
+ fields: Array<FigureAsset['document']['model']['inputs'][number] & {
45
+ value: number;
46
+ }>;
47
+ geometry: Rendered['geometry'];
48
+ bindings: Rendered['bindings'];
49
+ palette: Rendered['palette'];
50
+ };
23
51
  /** One history for structural and input edits. No persistence or authentication is implied. */
24
52
  export declare function createFigureEditorWorkspace(): {
25
- read: () => {
26
- context: null;
27
- name: null;
28
- fields: never[];
29
- geometry: never[];
30
- bindings: never[];
31
- dirty: boolean;
32
- revision: null;
33
- canUndo?: undefined;
34
- canRedo?: undefined;
35
- palette?: undefined;
36
- } | {
37
- context: string;
38
- name: string;
39
- revision: number;
40
- dirty: boolean;
41
- canUndo: boolean;
42
- canRedo: boolean;
43
- fields: {
44
- value: number;
45
- id: string;
46
- unit: import("./graph-types.ts").FigureUnit;
47
- min: number;
48
- max: number;
49
- role?: "design" | "state";
50
- }[];
51
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
52
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
53
- palette: Record<string, string>;
54
- };
55
- open(text: string, { discardDesignChanges, baseline }?: FigureOpenOptions): {
56
- context: null;
57
- name: null;
58
- fields: never[];
59
- geometry: never[];
60
- bindings: never[];
61
- dirty: boolean;
62
- revision: null;
63
- canUndo?: undefined;
64
- canRedo?: undefined;
65
- palette?: undefined;
66
- } | {
67
- context: string;
68
- name: string;
69
- revision: number;
70
- dirty: boolean;
71
- canUndo: boolean;
72
- canRedo: boolean;
73
- fields: {
74
- value: number;
75
- id: string;
76
- unit: import("./graph-types.ts").FigureUnit;
77
- min: number;
78
- max: number;
79
- role?: "design" | "state";
80
- }[];
81
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
82
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
83
- palette: Record<string, string>;
84
- };
85
- close({ discardDesignChanges }?: FigureLeaveOptions): {
86
- context: null;
87
- name: null;
88
- fields: never[];
89
- geometry: never[];
90
- bindings: never[];
91
- dirty: boolean;
92
- revision: null;
93
- canUndo?: undefined;
94
- canRedo?: undefined;
95
- palette?: undefined;
96
- } | {
97
- context: string;
98
- name: string;
99
- revision: number;
100
- dirty: boolean;
101
- canUndo: boolean;
102
- canRedo: boolean;
103
- fields: {
104
- value: number;
105
- id: string;
106
- unit: import("./graph-types.ts").FigureUnit;
107
- min: number;
108
- max: number;
109
- role?: "design" | "state";
110
- }[];
111
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
112
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
113
- palette: Record<string, string>;
114
- };
53
+ read: () => ClosedView | OpenView;
54
+ open(text: string, { discardDesignChanges, baseline }?: FigureOpenOptions): ClosedView | OpenView;
55
+ close({ discardDesignChanges }?: FigureLeaveOptions): ClosedView | OpenView;
115
56
  preview({ context, ...q }: FigureEditRequest & {
116
57
  context: string;
117
58
  }): {
@@ -145,143 +86,27 @@ export declare function createFigureEditorWorkspace(): {
145
86
  };
146
87
  apply({ context, ...q }: FigureEditRequest & {
147
88
  context: string;
148
- }): {
149
- context: null;
150
- name: null;
151
- fields: never[];
152
- geometry: never[];
153
- bindings: never[];
154
- dirty: boolean;
155
- revision: null;
156
- canUndo?: undefined;
157
- canRedo?: undefined;
158
- palette?: undefined;
159
- } | {
160
- context: string;
161
- name: string;
162
- revision: number;
163
- dirty: boolean;
164
- canUndo: boolean;
165
- canRedo: boolean;
166
- fields: {
167
- value: number;
168
- id: string;
169
- unit: import("./graph-types.ts").FigureUnit;
170
- min: number;
171
- max: number;
172
- role?: "design" | "state";
173
- }[];
174
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
175
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
176
- palette: Record<string, string>;
177
- };
89
+ }): ClosedView | OpenView;
178
90
  addBox({ context, box, ...q }: FigureEditControl & {
179
91
  context: string;
180
92
  box: FigureBoxCreation;
181
- }): {
182
- context: null;
183
- name: null;
184
- fields: never[];
185
- geometry: never[];
186
- bindings: never[];
187
- dirty: boolean;
188
- revision: null;
189
- canUndo?: undefined;
190
- canRedo?: undefined;
191
- palette?: undefined;
192
- } | {
193
- context: string;
194
- name: string;
195
- revision: number;
196
- dirty: boolean;
197
- canUndo: boolean;
198
- canRedo: boolean;
199
- fields: {
200
- value: number;
201
- id: string;
202
- unit: import("./graph-types.ts").FigureUnit;
203
- min: number;
204
- max: number;
205
- role?: "design" | "state";
206
- }[];
207
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
208
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
209
- palette: Record<string, string>;
210
- };
93
+ }): ClosedView | OpenView;
211
94
  addPrimitive({ context, part, ...q }: FigureEditControl & {
212
95
  context: string;
213
96
  part: FigurePrimitiveCreation;
214
- }): {
215
- context: null;
216
- name: null;
217
- fields: never[];
218
- geometry: never[];
219
- bindings: never[];
220
- dirty: boolean;
221
- revision: null;
222
- canUndo?: undefined;
223
- canRedo?: undefined;
224
- palette?: undefined;
225
- } | {
226
- context: string;
227
- name: string;
228
- revision: number;
229
- dirty: boolean;
230
- canUndo: boolean;
231
- canRedo: boolean;
232
- fields: {
233
- value: number;
234
- id: string;
235
- unit: import("./graph-types.ts").FigureUnit;
236
- min: number;
237
- max: number;
238
- role?: "design" | "state";
239
- }[];
240
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
241
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
242
- palette: Record<string, string>;
243
- };
97
+ }): ClosedView | OpenView;
244
98
  /** A part edit, one step of the history. Removing a part also says what else that changed (`removal`). */
245
99
  editPart({ context, edit, ...q }: FigureEditControl & {
246
100
  context: string;
247
101
  edit: FigurePartEdit;
248
- }): {
249
- context: null;
250
- name: null;
251
- fields: never[];
252
- geometry: never[];
253
- bindings: never[];
254
- dirty: boolean;
255
- revision: null;
256
- canUndo?: undefined;
257
- canRedo?: undefined;
258
- palette?: undefined;
259
- } | {
260
- context: string;
261
- name: string;
262
- revision: number;
263
- dirty: boolean;
264
- canUndo: boolean;
265
- canRedo: boolean;
266
- fields: {
267
- value: number;
268
- id: string;
269
- unit: import("./graph-types.ts").FigureUnit;
270
- min: number;
271
- max: number;
272
- role?: "design" | "state";
273
- }[];
274
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
275
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
276
- palette: Record<string, string>;
277
- } | {
102
+ }): ClosedView | OpenView | {
278
103
  removal: import("./authoring-actions.ts").RemovalNotice;
279
104
  context: null;
280
105
  name: null;
281
106
  fields: never[];
282
107
  geometry: never[];
283
108
  bindings: never[];
284
- dirty: boolean;
109
+ dirty: false;
285
110
  revision: null;
286
111
  canUndo?: undefined;
287
112
  canRedo?: undefined;
@@ -294,17 +119,12 @@ export declare function createFigureEditorWorkspace(): {
294
119
  dirty: boolean;
295
120
  canUndo: boolean;
296
121
  canRedo: boolean;
297
- fields: {
122
+ fields: Array<FigureAsset["document"]["model"]["inputs"][number] & {
298
123
  value: number;
299
- id: string;
300
- unit: import("./graph-types.ts").FigureUnit;
301
- min: number;
302
- max: number;
303
- role?: "design" | "state";
304
- }[];
305
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
306
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
307
- palette: Record<string, string>;
124
+ }>;
125
+ geometry: Rendered["geometry"];
126
+ bindings: Rendered["bindings"];
127
+ palette: Rendered["palette"];
308
128
  };
309
129
  /**
310
130
  * One authoring command — a dimension that follows, a fastening, a motion, the room the figure takes up —
@@ -318,36 +138,7 @@ export declare function createFigureEditorWorkspace(): {
318
138
  command({ context, action, ...q }: FigureEditControl & {
319
139
  context: string;
320
140
  action: FigureAuthoringAction;
321
- }): {
322
- context: null;
323
- name: null;
324
- fields: never[];
325
- geometry: never[];
326
- bindings: never[];
327
- dirty: boolean;
328
- revision: null;
329
- canUndo?: undefined;
330
- canRedo?: undefined;
331
- palette?: undefined;
332
- } | {
333
- context: string;
334
- name: string;
335
- revision: number;
336
- dirty: boolean;
337
- canUndo: boolean;
338
- canRedo: boolean;
339
- fields: {
340
- value: number;
341
- id: string;
342
- unit: import("./graph-types.ts").FigureUnit;
343
- min: number;
344
- max: number;
345
- role?: "design" | "state";
346
- }[];
347
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
348
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
349
- palette: Record<string, string>;
350
- };
141
+ }): ClosedView | OpenView;
351
142
  /**
352
143
  * What a command would do, seen before it is done (ADR-0097 §5.5): the same command on a copy, with the values the
353
144
  * author is looking at. No revision, no history, no clip, no consent is touched; the answer names the figure and
@@ -361,36 +152,7 @@ export declare function createFigureEditorWorkspace(): {
361
152
  attachment?: Extract<FigureAuthoringAction, {
362
153
  kind: "attach";
363
154
  }>;
364
- }): {
365
- context: null;
366
- name: null;
367
- fields: never[];
368
- geometry: never[];
369
- bindings: never[];
370
- dirty: boolean;
371
- revision: null;
372
- canUndo?: undefined;
373
- canRedo?: undefined;
374
- palette?: undefined;
375
- } | {
376
- context: string;
377
- name: string;
378
- revision: number;
379
- dirty: boolean;
380
- canUndo: boolean;
381
- canRedo: boolean;
382
- fields: {
383
- value: number;
384
- id: string;
385
- unit: import("./graph-types.ts").FigureUnit;
386
- min: number;
387
- max: number;
388
- role?: "design" | "state";
389
- }[];
390
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
391
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
392
- palette: Record<string, string>;
393
- };
155
+ }): ClosedView | OpenView;
394
156
  previewCommand({ context, action }: {
395
157
  context: string;
396
158
  action: FigureAuthoringAction;
@@ -410,135 +172,20 @@ export declare function createFigureEditorWorkspace(): {
410
172
  applyProposal({ context, steps, ...q }: FigureEditControl & {
411
173
  context: string;
412
174
  steps: readonly FigureProposalStep[];
413
- }): {
414
- context: null;
415
- name: null;
416
- fields: never[];
417
- geometry: never[];
418
- bindings: never[];
419
- dirty: boolean;
420
- revision: null;
421
- canUndo?: undefined;
422
- canRedo?: undefined;
423
- palette?: undefined;
424
- } | {
425
- context: string;
426
- name: string;
427
- revision: number;
428
- dirty: boolean;
429
- canUndo: boolean;
430
- canRedo: boolean;
431
- fields: {
432
- value: number;
433
- id: string;
434
- unit: import("./graph-types.ts").FigureUnit;
435
- min: number;
436
- max: number;
437
- role?: "design" | "state";
438
- }[];
439
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
440
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
441
- palette: Record<string, string>;
442
- };
175
+ }): ClosedView | OpenView;
443
176
  undo({ context, ...q }: FigureEditControl & {
444
177
  context: string;
445
- }): {
446
- context: null;
447
- name: null;
448
- fields: never[];
449
- geometry: never[];
450
- bindings: never[];
451
- dirty: boolean;
452
- revision: null;
453
- canUndo?: undefined;
454
- canRedo?: undefined;
455
- palette?: undefined;
456
- } | {
457
- context: string;
458
- name: string;
459
- revision: number;
460
- dirty: boolean;
461
- canUndo: boolean;
462
- canRedo: boolean;
463
- fields: {
464
- value: number;
465
- id: string;
466
- unit: import("./graph-types.ts").FigureUnit;
467
- min: number;
468
- max: number;
469
- role?: "design" | "state";
470
- }[];
471
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
472
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
473
- palette: Record<string, string>;
474
- };
178
+ }): ClosedView | OpenView;
475
179
  redo({ context, ...q }: FigureEditControl & {
476
180
  context: string;
477
- }): {
478
- context: null;
479
- name: null;
480
- fields: never[];
481
- geometry: never[];
482
- bindings: never[];
483
- dirty: boolean;
484
- revision: null;
485
- canUndo?: undefined;
486
- canRedo?: undefined;
487
- palette?: undefined;
488
- } | {
489
- context: string;
490
- name: string;
491
- revision: number;
492
- dirty: boolean;
493
- canUndo: boolean;
494
- canRedo: boolean;
495
- fields: {
496
- value: number;
497
- id: string;
498
- unit: import("./graph-types.ts").FigureUnit;
499
- min: number;
500
- max: number;
501
- role?: "design" | "state";
502
- }[];
503
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
504
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
505
- palette: Record<string, string>;
506
- };
181
+ }): ClosedView | OpenView;
507
182
  export({ context, captureStateDefaults }: {
508
183
  context: string;
509
184
  captureStateDefaults?: boolean;
510
185
  }): FigureWorkspaceExport;
511
- acknowledgeSaved({ context, revision, text }: FigureWorkspaceExport): {
512
- context: null;
513
- name: null;
514
- fields: never[];
515
- geometry: never[];
516
- bindings: never[];
517
- dirty: boolean;
518
- revision: null;
519
- canUndo?: undefined;
520
- canRedo?: undefined;
521
- palette?: undefined;
522
- } | {
523
- context: string;
524
- name: string;
525
- revision: number;
526
- dirty: boolean;
527
- canUndo: boolean;
528
- canRedo: boolean;
529
- fields: {
530
- value: number;
531
- id: string;
532
- unit: import("./graph-types.ts").FigureUnit;
533
- min: number;
534
- max: number;
535
- role?: "design" | "state";
536
- }[];
537
- geometry: import("./asset-types.ts").FigureAssetGeometry[];
538
- bindings: import("./asset-types.ts").FigureEvaluatedBinding[];
539
- palette: Record<string, string>;
540
- };
186
+ acknowledgeSaved({ context, revision, text }: FigureWorkspaceExport): ClosedView | OpenView;
541
187
  };
542
188
  export type FigureEditorWorkspace = ReturnType<typeof createFigureEditorWorkspace>;
543
189
  export type FigureWorkspaceView = ReturnType<FigureEditorWorkspace['read']>;
190
+ export {};
544
191
  //# sourceMappingURL=editor-workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor-workspace.d.ts","sourceRoot":"","sources":["../src/editor-workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor-workspace.d.ts","sourceRoot":"","sources":["../src/editor-workspace.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { previewFigureCommand } from "./command-preview.js";
2
- import { parseFigureAsset, serializeFigureAsset, compileFigureAsset, captureFigureAuthoredInputs } from "./asset.js";
2
+ import { parseFigureAsset, serializeFigureAsset, captureFigureAuthoredInputs, captureAndCompileFigureAsset } from "./asset.js";
3
3
  import { createFigureAuthoringSession } from "./authoring-session.js";
4
4
  import { addFigureBox, addFigurePrimitive } from "./create.js";
5
5
  import { editFigurePart, removePart } from "./part-edit.js";
@@ -28,8 +28,33 @@ export function createFigureEditorWorkspace() {
28
28
  if (q.expectedRevision !== a.revision)
29
29
  fail('REVISION_CONFLICT', 'editor changed since request');
30
30
  }
31
+ /*
32
+ The same figure state is asked about by the read, the validation of the edit that made it, the next command and the
33
+ export. What is derived from an asset and its inputs -- the asset with them captured, its evaluation, its text -- is
34
+ worked out once for that pair and held by the objects themselves, which a change replaces and never edits.
35
+ */
36
+ const texts = new WeakMap();
37
+ const textOf = (asset) => {
38
+ let text = texts.get(asset);
39
+ if (text === undefined)
40
+ texts.set(asset, (text = JSON.stringify(asset)));
41
+ return text;
42
+ };
43
+ const derived = new WeakMap();
44
+ /** The asset with the live inputs and state captured (what a command sees) and what it evaluates to. Throws what a compile throws. */
45
+ function derive(asset, inputs) {
46
+ let byInputs = derived.get(asset);
47
+ if (!byInputs)
48
+ derived.set(asset, (byInputs = new WeakMap()));
49
+ let one = byInputs.get(inputs);
50
+ if (!one) {
51
+ const { asset: seen, compiled } = captureAndCompileFigureAsset(asset, inputs, { captureStateDefaults: true });
52
+ byInputs.set(inputs, (one = { seen, rendered: compiled.evaluate() }));
53
+ }
54
+ return one;
55
+ }
31
56
  function dirty() {
32
- return !!active && serializeFigureAsset(active.asset) !== serializeFigureAsset(active.baseline);
57
+ return !!active && textOf(active.asset) !== textOf(active.baseline);
33
58
  }
34
59
  function leave(discard) {
35
60
  if (dirty() && !discard)
@@ -37,7 +62,7 @@ export function createFigureEditorWorkspace() {
37
62
  }
38
63
  const snapshot = (a) => structuredClone({ asset: a.asset, inputs: a.inputs });
39
64
  function validate(s) {
40
- compileFigureAsset(captureFigureAuthoredInputs(s.asset, s.inputs, { captureStateDefaults: true })).evaluate();
65
+ derive(s.asset, s.inputs);
41
66
  }
42
67
  function install(a, next) {
43
68
  validate(next);
@@ -49,10 +74,25 @@ export function createFigureEditorWorkspace() {
49
74
  a.inputs = next.inputs;
50
75
  a.revision++;
51
76
  }
77
+ /*
78
+ Compiling and evaluating the figure and serializing it twice for `dirty` is the cost of a read, and a modeller reads
79
+ several times for one selection or edit. Every change replaces `asset`, `inputs` or `baseline` rather than editing
80
+ them in place, so the references and the history lengths say whether the last answer still holds.
81
+ */
82
+ let exported = null;
83
+ let cached = null;
52
84
  function read() {
53
85
  if (!active)
54
86
  return { context: null, name: null, fields: [], geometry: [], bindings: [], dirty: false, revision: null };
55
- const a = active, rendered = compileFigureAsset(captureFigureAuthoredInputs(a.asset, a.inputs, { captureStateDefaults: true })).evaluate();
87
+ const a = active;
88
+ if (cached && cached.a === a && cached.asset === a.asset && cached.inputs === a.inputs && cached.baseline === a.baseline && cached.undo === a.undo.length && cached.redo === a.redo.length && cached.revision === a.revision)
89
+ return { ...cached.view };
90
+ const view = build(a);
91
+ cached = { a, asset: a.asset, inputs: a.inputs, baseline: a.baseline, undo: a.undo.length, redo: a.redo.length, revision: a.revision, view };
92
+ return { ...view };
93
+ }
94
+ function build(a) {
95
+ const { rendered } = derive(a.asset, a.inputs);
56
96
  return {
57
97
  context: a.context,
58
98
  name: a.asset.name,
@@ -148,7 +188,7 @@ export function createFigureEditorWorkspace() {
148
188
  command({ context, action, ...q }) {
149
189
  const a = current(context);
150
190
  control(a, q);
151
- const seen = captureFigureAuthoredInputs(a.asset, a.inputs, { captureStateDefaults: true });
191
+ const seen = derive(a.asset, a.inputs).seen;
152
192
  const asset = applyFigureAuthoring(seen, action, q);
153
193
  const inputs = Object.fromEntries(asset.document.model.inputs.map(i => [i.id, asset.designInputs[i.id] ?? asset.stateDefaults[i.id]]));
154
194
  install(a, { asset, inputs });
@@ -163,7 +203,7 @@ export function createFigureEditorWorkspace() {
163
203
  relocateAttached({ context, part, position, attachment, ...q }) {
164
204
  const a = current(context);
165
205
  control(a, q);
166
- const seen = captureFigureAuthoredInputs(a.asset, a.inputs, { captureStateDefaults: true });
206
+ const seen = derive(a.asset, a.inputs).seen;
167
207
  const joint = seen.joints?.find(j => j.body1.part === part);
168
208
  if (!joint || joint.type !== 'fixed' || seen.document.model.nodes.some(n => n.id === `${part}.motion`))
169
209
  fail('EDIT_TARGET', 'only a fixed attachment can be relocated by this drag');
@@ -187,7 +227,7 @@ export function createFigureEditorWorkspace() {
187
227
  },
188
228
  previewCommand({ context, action }) {
189
229
  const a = current(context);
190
- const seen = captureFigureAuthoredInputs(a.asset, a.inputs, { captureStateDefaults: true });
230
+ const seen = derive(a.asset, a.inputs).seen;
191
231
  return { context, revision: a.revision, ...previewFigureCommand(seen, action, a.inputs) };
192
232
  },
193
233
  /**
@@ -197,7 +237,7 @@ export function createFigureEditorWorkspace() {
197
237
  applyProposal({ context, steps, ...q }) {
198
238
  const a = current(context);
199
239
  control(a, q);
200
- const seen = captureFigureAuthoredInputs(a.asset, a.inputs, { captureStateDefaults: true });
240
+ const seen = derive(a.asset, a.inputs).seen;
201
241
  const asset = applyFigureProposal(seen, steps, q);
202
242
  const inputs = Object.fromEntries(asset.document.model.inputs.map(i => [i.id, asset.designInputs[i.id] ?? asset.stateDefaults[i.id]]));
203
243
  install(a, { asset, inputs });
@@ -233,11 +273,13 @@ export function createFigureEditorWorkspace() {
233
273
  },
234
274
  export({ context, captureStateDefaults = false }) {
235
275
  const a = current(context);
236
- return {
237
- context,
238
- revision: a.revision,
239
- text: serializeFigureAsset(captureFigureAuthoredInputs(a.asset, a.inputs, { captureStateDefaults }))
240
- };
276
+ const held = exported;
277
+ if (held && held.asset === a.asset && held.inputs === a.inputs && held.defaults === captureStateDefaults)
278
+ return { context, revision: a.revision, text: held.text };
279
+ /* The workspace's asset was compiled when it was opened or written: it is not compiled again to be exported. */
280
+ const text = JSON.stringify(captureAndCompileFigureAsset(a.asset, a.inputs, { captureStateDefaults }, true).asset);
281
+ exported = { asset: a.asset, inputs: a.inputs, defaults: captureStateDefaults, text };
282
+ return { context, revision: a.revision, text };
241
283
  },
242
284
  acknowledgeSaved({ context, revision, text }) {
243
285
  const a = current(context);