@nordcraft/runtime 1.0.96 → 1.0.97

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.
package/package.json CHANGED
@@ -9,8 +9,8 @@
9
9
  "directory": "packages/runtime"
10
10
  },
11
11
  "dependencies": {
12
- "@nordcraft/core": "1.0.96",
13
- "@nordcraft/std-lib": "1.0.96",
12
+ "@nordcraft/core": "1.0.97",
13
+ "@nordcraft/std-lib": "1.0.97",
14
14
  "fast-deep-equal": "3.1.3",
15
15
  "path-to-regexp": "6.3.0"
16
16
  },
@@ -23,5 +23,5 @@
23
23
  "files": ["dist", "src"],
24
24
  "main": "dist/page.main.js",
25
25
  "types": "dist/page.main.d.ts",
26
- "version": "1.0.96"
26
+ "version": "1.0.97"
27
27
  }
@@ -291,16 +291,20 @@ export function createAPI({
291
291
  },
292
292
  })
293
293
 
294
- ctx.reportFormulaEvaluation?.(['apis', api.name], {
295
- isLoading: false,
296
- data: data.body,
297
- error: null,
298
- response: {
299
- status: data.status,
300
- headers: data.headers,
301
- performance,
294
+ ctx.reportFormulaEvaluation?.(
295
+ ['apis', api.name],
296
+ {
297
+ isLoading: false,
298
+ data: data.body,
299
+ error: null,
300
+ response: {
301
+ status: data.status,
302
+ headers: data.headers,
303
+ performance,
304
+ },
302
305
  },
303
- })
306
+ ctx,
307
+ )
304
308
 
305
309
  const appliedRedirectRule = handleRedirectRules(api, componentData)
306
310
  if (appliedRedirectRule) {
@@ -366,16 +370,20 @@ export function createAPI({
366
370
  },
367
371
  })
368
372
 
369
- ctx.reportFormulaEvaluation?.(['apis', api.name], {
370
- isLoading: false,
371
- data: null,
372
- error: data.body,
373
- response: {
374
- status: data.status,
375
- headers: data.headers,
376
- performance,
373
+ ctx.reportFormulaEvaluation?.(
374
+ ['apis', api.name],
375
+ {
376
+ isLoading: false,
377
+ data: null,
378
+ error: data.body,
379
+ response: {
380
+ status: data.status,
381
+ headers: data.headers,
382
+ performance,
383
+ },
377
384
  },
378
- })
385
+ ctx,
386
+ )
379
387
 
380
388
  const appliedRedirectRule = handleRedirectRules(api, componentData)
381
389
  if (appliedRedirectRule) {
@@ -433,11 +441,15 @@ export function createAPI({
433
441
  },
434
442
  })
435
443
 
436
- ctx.reportFormulaEvaluation?.(['apis', api.name], {
437
- isLoading: true,
438
- data: ctx.dataSignal.get().Apis?.[api.name]?.data ?? null,
439
- error: null,
440
- })
444
+ ctx.reportFormulaEvaluation?.(
445
+ ['apis', api.name],
446
+ {
447
+ isLoading: true,
448
+ data: ctx.dataSignal.get().Apis?.[api.name]?.data ?? null,
449
+ error: null,
450
+ },
451
+ ctx,
452
+ )
441
453
 
442
454
  let response
443
455
 
@@ -1030,7 +1042,7 @@ export function createAPI({
1030
1042
  if (ctx.reportFormulaEvaluation) {
1031
1043
  const apiStatus = data.Apis?.[api.name]
1032
1044
  if (apiStatus) {
1033
- ctx.reportFormulaEvaluation(['apis', api.name], apiStatus)
1045
+ ctx.reportFormulaEvaluation(['apis', api.name], apiStatus, ctx)
1034
1046
  }
1035
1047
  }
1036
1048
  return {
@@ -75,10 +75,14 @@ export function createComponent({
75
75
  return mapObject(node.attrs, ([attr, value]) => [
76
76
  attr,
77
77
  value?.type !== 'value'
78
- ? applyFormula(value, {
79
- ...formulaCtx,
80
- data,
81
- })
78
+ ? applyFormula(
79
+ value,
80
+ {
81
+ ...formulaCtx,
82
+ data,
83
+ },
84
+ ['attrs', attr],
85
+ )
82
86
  : value?.value,
83
87
  ])
84
88
  })
@@ -94,11 +98,15 @@ export function createComponent({
94
98
  data: null,
95
99
  isLoading:
96
100
  api!.autoFetch &&
97
- applyFormula(api!.autoFetch, {
98
- ...formulaCtx,
99
- component,
100
- data: dataSignal.get(),
101
- })
101
+ applyFormula(
102
+ api!.autoFetch,
103
+ {
104
+ ...formulaCtx,
105
+ component,
106
+ data: dataSignal.get(),
107
+ },
108
+ ['apis', name, 'autoFetch'],
109
+ )
102
110
  ? true
103
111
  : false,
104
112
  error: null,
@@ -125,12 +133,16 @@ export function createComponent({
125
133
  ...data,
126
134
  Variables: mapObject(component.variables ?? {}, ([name, variable]) => [
127
135
  name,
128
- applyFormula(variable.initialValue, {
129
- // Initial value
130
- ...formulaCtx,
131
- component,
132
- data: componentDataSignal.get(),
133
- }),
136
+ applyFormula(
137
+ variable.initialValue,
138
+ {
139
+ // Initial value
140
+ ...formulaCtx,
141
+ component,
142
+ data: componentDataSignal.get(),
143
+ },
144
+ ['variables', name],
145
+ ),
134
146
  ]),
135
147
  }))
136
148
  registerComponentToLogState(component, componentDataSignal)
@@ -140,7 +152,7 @@ export function createComponent({
140
152
  componentDataSignal.subscribe(
141
153
  (data) => {
142
154
  Object.entries(data.Variables ?? {}).forEach(([name, value]) => {
143
- ctx.reportFormulaEvaluation?.(['variables', name], value)
155
+ ctx.reportFormulaEvaluation?.(['variables', name], value, ctx)
144
156
  })
145
157
  },
146
158
  {
@@ -332,10 +344,14 @@ export function createComponent({
332
344
  }),
333
345
  signal: dataSignal.map((data) =>
334
346
  appendUnit(
335
- applyFormula(customProperty.formula, {
336
- ...formulaCtx,
337
- data,
338
- }),
347
+ applyFormula(
348
+ customProperty.formula,
349
+ {
350
+ ...formulaCtx,
351
+ data,
352
+ },
353
+ ['customProperties', customPropertyName, 'formula'],
354
+ ),
339
355
  customProperty.unit,
340
356
  ),
341
357
  ),
@@ -355,10 +371,14 @@ export function createComponent({
355
371
  }),
356
372
  signal: dataSignal.map((data) =>
357
373
  appendUnit(
358
- applyFormula(customProperty.formula, {
359
- ...formulaCtx,
360
- data,
361
- }),
374
+ applyFormula(
375
+ customProperty.formula,
376
+ {
377
+ ...formulaCtx,
378
+ data,
379
+ },
380
+ ['customProperties', customPropertyName, 'formula'],
381
+ ),
362
382
  customProperty.unit,
363
383
  ),
364
384
  ),
@@ -118,7 +118,7 @@ export function createElement({
118
118
  },
119
119
  attrPath,
120
120
  )
121
- ctx.reportFormulaEvaluation?.(attrPath, val)
121
+ ctx.reportFormulaEvaluation?.(attrPath, val, ctx)
122
122
  return val
123
123
  })
124
124
  o.subscribe((val) => {
@@ -155,7 +155,7 @@ export function createElement({
155
155
  },
156
156
  styleVarPath,
157
157
  )
158
- ctx.reportFormulaEvaluation?.(styleVarPath, value)
158
+ ctx.reportFormulaEvaluation?.(styleVarPath, value, ctx)
159
159
  return unit ? value + unit : value
160
160
  })
161
161
 
@@ -189,7 +189,7 @@ export function createElement({
189
189
  },
190
190
  cpPath,
191
191
  )
192
- ctx.reportFormulaEvaluation?.(cpPath, val)
192
+ ctx.reportFormulaEvaluation?.(cpPath, val, ctx)
193
193
  return appendUnit(val, unit)
194
194
  }),
195
195
  root: ctx.root,
@@ -224,7 +224,7 @@ export function createElement({
224
224
  },
225
225
  variantCpPath,
226
226
  )
227
- ctx.reportFormulaEvaluation?.(variantCpPath, val)
227
+ ctx.reportFormulaEvaluation?.(variantCpPath, val, ctx)
228
228
  return appendUnit(val, unit)
229
229
  }),
230
230
  root: ctx.root,
@@ -63,6 +63,9 @@ export function createNode({
63
63
  ...ctx,
64
64
  package:
65
65
  node.package ?? (isLocalComponent ? undefined : ctx.package),
66
+ // Skip sub-component formula evaluation for now as editor only needs the scope for the selected component
67
+ // TODO: Letting the AI get the state of a deep component may be useful in the future, but we need a better way at precising scope for it to not overwhelm it.
68
+ reportFormulaEvaluation: undefined,
66
69
  },
67
70
  parentElement,
68
71
  })
@@ -106,8 +109,6 @@ export function createNode({
106
109
  ),
107
110
  )
108
111
 
109
- ctx.reportFormulaEvaluation?.(conditionPath, show)
110
-
111
112
  return show
112
113
  })
113
114
 
@@ -208,8 +209,6 @@ export function createNode({
208
209
  listPath,
209
210
  )
210
211
 
211
- ctx.reportFormulaEvaluation?.(listPath, list)
212
-
213
212
  if (typeof list !== 'object') {
214
213
  return []
215
214
  }
@@ -237,21 +236,21 @@ export function createNode({
237
236
  }
238
237
  const repeatKeyPath = ['nodes', id, 'repeatKey']
239
238
  let childKey = node?.repeatKey
240
- ? applyFormula(node.repeatKey, {
241
- data: childData,
242
- component: ctx.component,
243
- formulaCache: ctx.formulaCache,
244
- root: ctx.root,
245
- package: ctx.package,
246
- toddle: ctx.toddle,
247
- env: ctx.env,
248
- })
239
+ ? applyFormula(
240
+ node.repeatKey,
241
+ {
242
+ data: childData,
243
+ component: ctx.component,
244
+ formulaCache: ctx.formulaCache,
245
+ root: ctx.root,
246
+ package: ctx.package,
247
+ toddle: ctx.toddle,
248
+ env: ctx.env,
249
+ },
250
+ repeatKeyPath,
251
+ )
249
252
  : Key
250
253
 
251
- if (node?.repeatKey) {
252
- ctx.reportFormulaEvaluation?.(repeatKeyPath, childKey)
253
- }
254
-
255
254
  if (seenKeys.has(childKey)) {
256
255
  console.warn(
257
256
  `Duplicate key "${childKey}" found in repeat. Fallback to index as key. This will cause a re-render of the duplicated children on every change.`,
@@ -72,6 +72,7 @@ export function createText({
72
72
  ctx.reportFormulaEvaluation?.(
73
73
  [...(ctx.jsonPath ?? []), 'value'],
74
74
  value.value,
75
+ ctx,
75
76
  )
76
77
  elem.innerText = String(value.value)
77
78
  }
@@ -11,3 +11,18 @@ export const debounce = (func: () => void, wait: number, immediate = false) => {
11
11
  }
12
12
  }
13
13
  }
14
+
15
+ export const throttleToIdleCallback = (func: () => void) => {
16
+ let scheduled = false
17
+ return () => {
18
+ if (!scheduled) {
19
+ scheduled = true
20
+ ;(globalThis.requestIdleCallback ?? globalThis.requestAnimationFrame)(
21
+ () => {
22
+ func()
23
+ scheduled = false
24
+ },
25
+ )
26
+ }
27
+ }
28
+ }
@@ -64,7 +64,7 @@ import { dragEnded } from './editor/drag-drop/dragEnded'
64
64
  import { dragMove } from './editor/drag-drop/dragMove'
65
65
  import { dragReorder } from './editor/drag-drop/dragReorder'
66
66
  import { dragStarted } from './editor/drag-drop/dragStarted'
67
- import { debounce } from './editor/editorUtils'
67
+ import { throttleToIdleCallback } from './editor/editorUtils'
68
68
  import { introspectApiRequest } from './editor/graphql'
69
69
  import { isInputTarget } from './editor/input'
70
70
  import { updateComponentLinks } from './editor/links'
@@ -251,22 +251,35 @@ export const createRoot = (
251
251
  let component: Component | null = null
252
252
  let componentFormulaData: Record<string, any> = {}
253
253
 
254
- const reportFormulaEvaluation: FormulaEvaluationReporter = (path, data) => {
255
- componentFormulaData[path.join('/')] = data
256
- reportComponentFormulaData()
254
+ const reportFormulaEvaluation: FormulaEvaluationReporter = (
255
+ path,
256
+ data,
257
+ ctx,
258
+ ) => {
259
+ if (
260
+ data !== undefined &&
261
+ path.length > 0 &&
262
+ // We are currently skipping all children formulas to lower the scope of reporting to what the user can see in the canvas
263
+ ctx.component?.name === component?.name
264
+ ) {
265
+ try {
266
+ componentFormulaData[path.join('/')] = JSON.parse(JSON.stringify(data))
267
+ } catch {
268
+ componentFormulaData[path.join('/')] =
269
+ `[Unserializable value of type ${typeof data}]`
270
+ } finally {
271
+ reportComponentFormulaData()
272
+ }
273
+ }
257
274
  }
258
- const reportComponentFormulaData = debounce(
259
- () => {
260
- postMessageToEditor({
261
- type: 'componentFormulaData',
262
- data: componentFormulaData,
263
- component: component?.name,
264
- })
265
- componentFormulaData = {}
266
- },
267
- 5, // 5ms debounce to batch multiple rapid updates
268
- true,
269
- )
275
+ const reportComponentFormulaData = throttleToIdleCallback(() => {
276
+ postMessageToEditor({
277
+ type: 'componentFormulaData',
278
+ data: componentFormulaData,
279
+ component: component?.name,
280
+ })
281
+ componentFormulaData = {}
282
+ })
270
283
  let selectedNodeId: string | null = null
271
284
  let highlightedNodeId: string | null = null
272
285
  let styleVariantSelection: {
@@ -1427,7 +1440,6 @@ body[data-mode="design"] [data-id="${animationState.animatedElementId}"], body[d
1427
1440
  applyFormula(variable.initialValue, formulaContext, [
1428
1441
  'variables',
1429
1442
  name,
1430
- 'initialValue',
1431
1443
  ]),
1432
1444
  ],
1433
1445
  )
@@ -1477,7 +1489,7 @@ body[data-mode="design"] [data-id="${animationState.animatedElementId}"], body[d
1477
1489
  jsonPath: ctx?.jsonPath,
1478
1490
  reportFormulaEvaluation,
1479
1491
  },
1480
- ['variables', name, 'initialValue'],
1492
+ ['variables', name],
1481
1493
  ),
1482
1494
  ],
1483
1495
  )
@@ -66,21 +66,17 @@ export function handleAction(
66
66
  break
67
67
  }
68
68
  case 'SetVariable': {
69
- const value = applyFormula(
70
- action.data,
71
- {
72
- data,
73
- component: ctx.component,
74
- formulaCache: ctx.formulaCache,
75
- root: ctx.root,
76
- package: ctx.package,
77
- toddle: ctx.toddle,
78
- env: ctx.env,
79
- jsonPath: ctx.jsonPath,
80
- reportFormulaEvaluation: ctx.reportFormulaEvaluation,
81
- },
82
- ['data'],
83
- )
69
+ const value = applyFormula(action.data, {
70
+ data,
71
+ component: ctx.component,
72
+ formulaCache: ctx.formulaCache,
73
+ root: ctx.root,
74
+ package: ctx.package,
75
+ toddle: ctx.toddle,
76
+ env: ctx.env,
77
+ jsonPath: ctx.jsonPath,
78
+ })
79
+
84
80
  ctx.dataSignal.update((data) => {
85
81
  return {
86
82
  ...data,
@@ -424,19 +420,21 @@ export function handleAction(
424
420
  event,
425
421
  (callbackName, callbackData) => {
426
422
  const callback = callbacks?.[callbackName]
427
- callback?.actions?.forEach((action) =>
428
- handleAction(
429
- action,
430
- {
431
- ...data,
432
- ...ctx.dataSignal.get(),
433
- Parameters: parameters,
434
- Event: callbackData,
435
- },
436
- ctx,
437
- event,
438
- workflowCallback,
439
- ),
423
+ callback?.actions?.forEach(
424
+ (action) =>
425
+ action &&
426
+ handleAction(
427
+ action,
428
+ {
429
+ ...data,
430
+ ...ctx.dataSignal.get(),
431
+ Parameters: parameters,
432
+ Event: callbackData,
433
+ },
434
+ ctx,
435
+ event,
436
+ workflowCallback,
437
+ ),
440
438
  )
441
439
  },
442
440
  ),
@@ -464,19 +462,21 @@ export function handleAction(
464
462
  event,
465
463
  (callbackName, callbackData) => {
466
464
  const callback = callbacks?.[callbackName]
467
- callback?.actions?.forEach((action) =>
468
- handleAction(
469
- action,
470
- {
471
- ...data,
472
- ...ctx.dataSignal.get(),
473
- Parameters: parameters,
474
- Event: callbackData,
475
- },
476
- ctx,
477
- event,
478
- workflowCallback,
479
- ),
465
+ callback?.actions?.forEach(
466
+ (action) =>
467
+ action &&
468
+ handleAction(
469
+ action,
470
+ {
471
+ ...data,
472
+ ...ctx.dataSignal.get(),
473
+ Parameters: parameters,
474
+ Event: callbackData,
475
+ },
476
+ ctx,
477
+ event,
478
+ workflowCallback,
479
+ ),
480
480
  )
481
481
  },
482
482
  ),