@nordcraft/runtime 1.0.57 → 1.0.59

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 (39) hide show
  1. package/dist/components/createComponent.js +12 -13
  2. package/dist/components/createComponent.js.map +1 -1
  3. package/dist/components/createElement.js +9 -7
  4. package/dist/components/createElement.js.map +1 -1
  5. package/dist/components/createNode.js.map +1 -1
  6. package/dist/custom-element.main.esm.js +27 -27
  7. package/dist/custom-element.main.esm.js.map +4 -4
  8. package/dist/editor/input.d.ts +1 -0
  9. package/dist/editor/input.js +16 -0
  10. package/dist/editor/input.js.map +1 -0
  11. package/dist/editor/links.d.ts +6 -0
  12. package/dist/editor/links.js +15 -0
  13. package/dist/editor/links.js.map +1 -0
  14. package/dist/editor/overlay.d.ts +12 -0
  15. package/dist/editor/overlay.js +20 -0
  16. package/dist/editor/overlay.js.map +1 -0
  17. package/dist/editor/types.d.ts +254 -0
  18. package/dist/editor/types.js +42 -0
  19. package/dist/editor/types.js.map +1 -0
  20. package/dist/editor-preview.main.js +147 -198
  21. package/dist/editor-preview.main.js.map +1 -1
  22. package/dist/page.main.esm.js +3 -3
  23. package/dist/page.main.esm.js.map +4 -4
  24. package/dist/page.main.js +20 -0
  25. package/dist/page.main.js.map +1 -1
  26. package/dist/styles/CustomPropertyStyleSheet.test.js +12 -3
  27. package/dist/styles/CustomPropertyStyleSheet.test.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/components/createComponent.ts +27 -20
  30. package/src/components/createElement.ts +29 -21
  31. package/src/components/createNode.ts +0 -1
  32. package/src/editor/input.ts +17 -0
  33. package/src/editor/links.ts +16 -0
  34. package/src/editor/overlay.ts +21 -0
  35. package/src/editor/types.ts +271 -0
  36. package/src/editor-preview.main.ts +180 -411
  37. package/src/page.main.ts +23 -0
  38. package/src/styles/CustomPropertyStyleSheet.test.ts +12 -3
  39. package/src/editor/types.d.ts +0 -36
@@ -3,9 +3,9 @@
3
3
  /* eslint-disable no-case-declarations */
4
4
  /* eslint-disable no-fallthrough */
5
5
  import { isLegacyApi } from '@nordcraft/core/dist/api/api'
6
+ import { isLegacyPluginAction } from '@nordcraft/core/dist/component/actionUtils'
6
7
  import {
7
8
  HeadTagTypes,
8
- type AnimationKeyframe,
9
9
  type Component,
10
10
  type ComponentData,
11
11
  type MetaEntry,
@@ -24,8 +24,8 @@ import {
24
24
  type PluginFormula,
25
25
  type ToddleFormula,
26
26
  } from '@nordcraft/core/dist/formula/formulaTypes'
27
- import { valueFormula } from '@nordcraft/core/dist/formula/formulaUtils'
28
27
  import { getClassName } from '@nordcraft/core/dist/styling/className'
28
+ import { appendUnit } from '@nordcraft/core/dist/styling/customProperty'
29
29
  import type { OldTheme, Theme } from '@nordcraft/core/dist/styling/theme'
30
30
  import { getThemeCss, renderTheme } from '@nordcraft/core/dist/styling/theme'
31
31
  import type { StyleVariant } from '@nordcraft/core/dist/styling/variantSelector'
@@ -41,6 +41,7 @@ import type {
41
41
  } from '@nordcraft/core/dist/types'
42
42
  import { mapObject, omitKeys } from '@nordcraft/core/dist/utils/collections'
43
43
  import { safeFunctionName } from '@nordcraft/core/dist/utils/handlerUtils'
44
+ import { isDefined } from '@nordcraft/core/dist/utils/util'
44
45
  import * as libActions from '@nordcraft/std-lib/dist/actions'
45
46
  import * as libFormulas from '@nordcraft/std-lib/dist/formulas'
46
47
  import fastDeepEqual from 'fast-deep-equal'
@@ -55,7 +56,15 @@ import { dragMove } from './editor/drag-drop/dragMove'
55
56
  import { dragReorder } from './editor/drag-drop/dragReorder'
56
57
  import { dragStarted } from './editor/drag-drop/dragStarted'
57
58
  import { introspectApiRequest } from './editor/graphql'
58
- import type { DragState } from './editor/types'
59
+ import { isInputTarget } from './editor/input'
60
+ import { updateComponentLinks } from './editor/links'
61
+ import { getRectData } from './editor/overlay'
62
+ import {
63
+ TextNodeComputedStyles,
64
+ type DragState,
65
+ type EditorPostMessageType,
66
+ type NordcraftPreviewEvent,
67
+ } from './editor/types'
59
68
  import { handleAction } from './events/handleAction'
60
69
  import type { Signal } from './signal/signal'
61
70
  import { signal } from './signal/signal'
@@ -75,130 +84,6 @@ import {
75
84
  storeScrollState,
76
85
  } from './utils/storeScrollState'
77
86
 
78
- type ToddlePreviewEvent =
79
- | {
80
- type: 'style_variant_changed'
81
- variantIndex: number | null
82
- }
83
- | {
84
- type: 'component'
85
- component: Component
86
- }
87
- | { type: 'components'; components: Component[] }
88
- | {
89
- type: 'packages'
90
- packages: Record<
91
- string,
92
- {
93
- components: Record<string, Component>
94
- formulas: Record<
95
- string,
96
- PluginFormula<FormulaHandlerV2> | PluginFormula<string>
97
- >
98
- actions: Record<string, PluginActionV2 | PluginAction>
99
- manifest: {
100
- name: string
101
- // commit represents the commit hash (version) of the package
102
- commit: string
103
- }
104
- }
105
- >
106
- }
107
- | {
108
- type: 'global_formulas'
109
- formulas: Record<
110
- string,
111
- PluginFormula<FormulaHandlerV2> | PluginFormula<string>
112
- >
113
- }
114
- | {
115
- type: 'global_actions'
116
- actions: Record<string, PluginActionV2 | PluginAction>
117
- }
118
- | { type: 'theme'; theme: Record<string, OldTheme | Theme> }
119
- | { type: 'mode'; mode: 'design' | 'test' }
120
- | { type: 'attrs'; attrs: Record<string, unknown> }
121
- | { type: 'selection'; selectedNodeId: string | null }
122
- | { type: 'highlight'; highlightedNodeId: string | null }
123
- | {
124
- type: 'click' | 'mousemove' | 'dblclick'
125
- metaKey: boolean
126
- x: number
127
- y: number
128
- }
129
- | { type: 'report_document_scroll_size' }
130
- | { type: 'update_inner_text'; innerText: string }
131
- | { type: 'reload' }
132
- | { type: 'fetch_api'; apiKey: string }
133
- | { type: 'introspect_qraphql_api'; apiKey: string }
134
- | { type: 'drag-started'; x: number; y: number }
135
- | { type: 'drag-ended'; canceled?: true }
136
- | { type: 'keydown'; key: string; altKey: boolean; metaKey: boolean }
137
- | { type: 'keyup'; key: string; altKey: boolean; metaKey: boolean }
138
- | {
139
- type: 'get_computed_style'
140
- styles?: string[]
141
- }
142
- | {
143
- type: 'set_timeline_keyframes'
144
- keyframes: Record<string, AnimationKeyframe> | null
145
- }
146
- | {
147
- type: 'set_timeline_time'
148
- time: number | null
149
- timingFunction:
150
- | 'linear'
151
- | 'ease'
152
- | 'ease-in'
153
- | 'ease-out'
154
- | 'ease-in-out'
155
- | 'step-start'
156
- | 'step-end'
157
- | string
158
- | undefined
159
- fillMode: 'none' | 'forwards' | 'backwards' | 'both' | undefined
160
- }
161
- | {
162
- type: 'preview_style'
163
- styles: Record<string, string> | null
164
- theme?: {
165
- key: string
166
- value: Theme
167
- }
168
- }
169
- | {
170
- type: 'preview_theme'
171
- theme: string | null
172
- }
173
-
174
- /**
175
- * Styles required for rendering the same exact text again somewhere else (on a overlay rect in the editor)
176
- */
177
- enum TextNodeComputedStyles {
178
- // Caret color is important as it is the only visible part of the text node (when text is not highlighted)
179
- CARET_COLOR = 'caret-color',
180
- FONT_FAMILY = 'font-family',
181
- FONT_SIZE = 'font-size',
182
- FONT_WEIGHT = 'font-weight',
183
- FONT_STYLE = 'font-style',
184
- FONT_VARIANT = 'font-variant',
185
- FONT_STRETCH = 'font-stretch',
186
- LINE_HEIGHT = 'line-height',
187
- TEXT_ALIGN = 'text-align',
188
- TEXT_TRANSFORM = 'text-transform',
189
- LETTER_SPACING = 'letter-spacing',
190
- WHITE_SPACE = 'white-space',
191
- WORD_SPACING = 'word-spacing',
192
- TEXT_INDENT = 'text-indent',
193
- TEXT_OVERFLOW = 'text-overflow',
194
- TEXT_RENDERING = 'text-rendering',
195
- WORD_BREAK = 'word-break',
196
- WORD_WRAP = 'word-wrap',
197
- DIRECTION = 'direction',
198
- UNICODE_BIDI = 'unicode-bidi',
199
- VERTICAL_ALIGN = 'vertical-align',
200
- }
201
-
202
87
  let env: ToddleEnv
203
88
 
204
89
  export const initGlobalObject = () => {
@@ -297,6 +182,18 @@ export const initGlobalObject = () => {
297
182
  )
298
183
  }
299
184
 
185
+ const EMPTY_COMPONENT_DATA: ComponentData = {
186
+ Location: {
187
+ query: {},
188
+ params: {},
189
+ page: '/',
190
+ path: '/',
191
+ hash: '',
192
+ },
193
+ Attributes: {},
194
+ Variables: {},
195
+ }
196
+
300
197
  // imported by "/.toddle/preview" (see worker/src/preview.ts)
301
198
  export const createRoot = (
302
199
  domNode: HTMLElement | null = document.getElementById('App'),
@@ -304,35 +201,8 @@ export const createRoot = (
304
201
  if (!domNode) {
305
202
  throw new Error('Cant find root domNode')
306
203
  }
307
- const isInputTarget = (event: Event) => {
308
- const target = event.target
309
- if (target instanceof HTMLElement) {
310
- if (
311
- target.tagName === 'INPUT' ||
312
- target.tagName === 'TEXTAREA' ||
313
- target.tagName === 'SELECT' ||
314
- target.tagName === 'STYLE-EDITOR'
315
- ) {
316
- return true
317
- }
318
- if (target.contentEditable?.toLocaleLowerCase() === 'true') {
319
- return true
320
- }
321
- }
322
- return false
323
- }
324
204
 
325
- const dataSignal = signal<ComponentData>({
326
- Location: {
327
- query: {},
328
- params: {},
329
- page: '/',
330
- path: '/',
331
- hash: '',
332
- },
333
- Attributes: {},
334
- Variables: {},
335
- })
205
+ const dataSignal = signal(EMPTY_COMPONENT_DATA)
336
206
  let ctxDataSignal: Signal<ComponentData> | undefined
337
207
 
338
208
  let ctx: ComponentContext | null = null
@@ -372,87 +242,26 @@ export const createRoot = (
372
242
  let previewStyleAnimationFrame = -1
373
243
  let timelineTimeAnimationFrame = -1
374
244
 
375
- /**
376
- * Modifies all link nodes on a component
377
- * NOTE: alters in place
378
- */
379
- const updateComponentLinks = (component: Component) => {
380
- // Find all links and add target="_blank" to them
381
- Object.entries(component.nodes ?? {}).forEach(([_, node]) => {
382
- if (node.type === 'element' && node.tag === 'a') {
383
- node.attrs['target'] = valueFormula('_blank')
384
- }
385
- })
386
- return component
387
- }
388
-
389
- const registerActions = (
390
- allActions: Record<string, PluginActionV2 | PluginAction>,
391
- packageName?: string,
392
- ) => {
393
- const actions: Record<string, PluginActionV2> = {}
394
- Object.entries(allActions ?? {}).forEach(([name, action]) => {
395
- if (typeof action.name === 'string' && action.version === undefined) {
396
- // Legacy actions are self-registering. We need to execute them to register them
397
- Function(action.handler)()
398
- return
399
- }
400
- // We need to convert the handler string into a real function
401
- actions[name] = {
402
- ...(action as PluginActionV2),
403
- handler:
404
- typeof action.handler === 'string'
405
- ? (new Function(
406
- 'args, ctx',
407
- `${action.handler}
408
- return ${safeFunctionName(action.name)}(args, ctx)`,
409
- ) as ActionHandlerV2)
410
- : action.handler,
411
- }
412
- })
413
- window.toddle.actions[packageName ?? window.__toddle.project] = actions
414
- }
415
-
416
- const registerFormulas = (
417
- allFormulas: Record<
418
- string,
419
- ToddleFormula | CodeFormula<FormulaHandlerV2> | CodeFormula<string>
420
- >,
421
- packageName?: string,
422
- ) => {
423
- const formulas: Record<string, PluginFormula<FormulaHandlerV2>> = {}
424
- Object.entries(allFormulas ?? {}).forEach(([name, formula]) => {
425
- if (
426
- !isToddleFormula<FormulaHandlerV2 | string>(formula) &&
427
- typeof formula.name === 'string' &&
428
- formula.version === undefined
429
- ) {
430
- // Legacy formulas are self-registering. We need to execute them to register them
431
- Function(formula.handler as unknown as string)()
432
- return
433
- } else if (!isToddleFormula<FormulaHandlerV2 | string>(formula)) {
434
- // For code formulas we need to convert the handler string into a real function
435
- formulas[name] = {
436
- ...formula,
437
- handler:
438
- typeof formula.handler === 'string'
439
- ? (new Function(
440
- 'args, ctx',
441
- `${formula.handler}
442
- return ${safeFunctionName(formula.name)}(args, ctx)`,
443
- ) as FormulaHandlerV2)
444
- : formula.handler,
245
+ const setupDataSignalSubscribers = () => {
246
+ dataSignal.subscribe((data) => {
247
+ if (component && components && packageComponents && data) {
248
+ try {
249
+ postMessageToEditor({ type: 'data', data })
250
+ } catch {
251
+ // If we're unable to send the data, let's try to JSON serialize it
252
+ postMessageToEditor({
253
+ type: 'data',
254
+ data: JSON.parse(JSON.stringify(data)),
255
+ })
445
256
  }
446
- return
447
257
  }
448
- formulas[name] = formula as PluginFormula<FormulaHandlerV2>
449
258
  })
450
- window.toddle.formulas[packageName ?? window.__toddle.project] = formulas
451
259
  }
260
+ setupDataSignalSubscribers()
452
261
 
453
262
  window.addEventListener(
454
263
  'message',
455
- async (message: MessageEvent<ToddlePreviewEvent>) => {
264
+ async (message: MessageEvent<NordcraftPreviewEvent>) => {
456
265
  if (!message.isTrusted) {
457
266
  console.error('UNTRUSTED MESSAGE')
458
267
  }
@@ -466,11 +275,22 @@ export const createRoot = (
466
275
  | undefined
467
276
 
468
277
  if (message.data.component.name !== component?.name) {
278
+ // Store scroll state for the previous component
469
279
  storeScrollState(component?.name)
280
+ // Remove all subscribers from the previous showSignal
470
281
  showSignal.cleanSubscribers()
282
+ // Clear any previously overridden conditional elements
283
+ showSignal.set({ displayedNodes: [], testMode: mode === 'test' })
284
+ // Restore scroll state for the new component
471
285
  scrollStateRestorer = getScrollStateRestorer(
472
286
  message.data.component.name,
473
287
  )
288
+ // Destroy the dataSignal (including subscribers) for the previous component
289
+ dataSignal.destroy()
290
+ // Re-subscribe all dataSignal subscribers
291
+ setupDataSignalSubscribers()
292
+ // Re-initialize the data signal for the new component
293
+ ctxDataSignal?.destroy()
474
294
  }
475
295
 
476
296
  component = updateComponentLinks(message.data.component)
@@ -1249,26 +1069,31 @@ export const createRoot = (
1249
1069
  ] ?? ({ style: {} } as StyleVariant)
1250
1070
  // Add a style element specific to the selected element which
1251
1071
  // is only applied when the preview is in design mode
1252
- const styleVariantCustomProperties = Object.entries(
1253
- (selectedStyleVariant as StyleVariant).customProperties ?? {},
1072
+ const styleVariantCustomProperties = Object.fromEntries(
1073
+ Object.entries(
1074
+ (selectedStyleVariant as StyleVariant).customProperties ?? {},
1075
+ )
1076
+ .map(([customPropertyName, customProperty]) => [
1077
+ customPropertyName,
1078
+ appendUnit(
1079
+ applyFormula(customProperty.formula, {
1080
+ data: {
1081
+ Attributes: dataSignal.get().Attributes,
1082
+ Variables: dataSignal.get().Variables,
1083
+ Contexts: ctxDataSignal?.get().Contexts ?? {},
1084
+ },
1085
+ component: getCurrentComponent(),
1086
+ root: ctx?.root,
1087
+ formulaCache: {},
1088
+ package: ctx?.package,
1089
+ toddle: window.toddle,
1090
+ env,
1091
+ } as FormulaContext),
1092
+ customProperty.unit,
1093
+ ),
1094
+ ])
1095
+ .filter(([, value]) => isDefined(value)),
1254
1096
  )
1255
- .map(([customPropertyName, customProperty]) => ({
1256
- name: customPropertyName,
1257
- value: applyFormula(customProperty.formula, {
1258
- data: {
1259
- Attributes: dataSignal.get().Attributes,
1260
- Variables: dataSignal.get().Variables,
1261
- Contexts: ctxDataSignal?.get().Contexts ?? {},
1262
- },
1263
- component: getCurrentComponent(),
1264
- root: ctx?.root,
1265
- formulaCache: {},
1266
- package: ctx?.package,
1267
- toddle: window.toddle,
1268
- env,
1269
- } as FormulaContext),
1270
- }))
1271
- .filter(({ value }) => value !== undefined)
1272
1097
 
1273
1098
  const styleElem = document.createElement('style')
1274
1099
  const pseudoElement = selectedStyleVariant.pseudoElement
@@ -1281,11 +1106,7 @@ export const createRoot = (
1281
1106
  ${styleToCss({
1282
1107
  ...(!pseudoElement && nodeLookup.node.style),
1283
1108
  ...selectedStyleVariant.style,
1284
- ...Object.fromEntries(
1285
- styleVariantCustomProperties.map(
1286
- ({ name, value }) => [name, value],
1287
- ),
1288
- ),
1109
+ ...styleVariantCustomProperties,
1289
1110
  })}
1290
1111
  }
1291
1112
  `),
@@ -1742,68 +1563,7 @@ export const createRoot = (
1742
1563
  return ctx
1743
1564
  }
1744
1565
 
1745
- document.addEventListener('keydown', (event) => {
1746
- if (isInputTarget(event)) {
1747
- return
1748
- }
1749
- switch (event.key) {
1750
- case 'k':
1751
- if (event.metaKey) {
1752
- event.preventDefault()
1753
- }
1754
- }
1755
- postMessageToEditor({
1756
- type: 'keydown',
1757
- event: {
1758
- key: event.key,
1759
- metaKey: event.metaKey,
1760
- shiftKey: event.shiftKey,
1761
- altKey: event.altKey,
1762
- },
1763
- })
1764
- })
1765
- document.addEventListener('keyup', (event) => {
1766
- if (isInputTarget(event)) {
1767
- return
1768
- }
1769
- postMessageToEditor({
1770
- type: 'keyup',
1771
- event: {
1772
- key: event.key,
1773
- metaKey: event.metaKey,
1774
- shiftKey: event.shiftKey,
1775
- altKey: event.altKey,
1776
- },
1777
- })
1778
- })
1779
- document.addEventListener('keypress', (event) => {
1780
- if (isInputTarget(event)) {
1781
- return
1782
- }
1783
- postMessageToEditor({
1784
- type: 'keypress',
1785
- event: {
1786
- key: event.key,
1787
- metaKey: event.metaKey,
1788
- shiftKey: event.shiftKey,
1789
- altKey: event.altKey,
1790
- },
1791
- })
1792
- })
1793
-
1794
- dataSignal.subscribe((data) => {
1795
- if (component && components && packageComponents && data) {
1796
- try {
1797
- postMessageToEditor({ type: 'data', data })
1798
- } catch {
1799
- // If we're unable to send the data, let's try to JSON serialize it
1800
- postMessageToEditor({
1801
- type: 'data',
1802
- data: JSON.parse(JSON.stringify(data)),
1803
- })
1804
- }
1805
- }
1806
- })
1566
+ initKeyListeners()
1807
1567
 
1808
1568
  const clearSelectedStyleVariant = () => {
1809
1569
  if (styleVariantSelection) {
@@ -1864,28 +1624,6 @@ export const createRoot = (
1864
1624
  })()
1865
1625
  }
1866
1626
 
1867
- function getRectData(selectedNode: Element | null | undefined) {
1868
- if (!selectedNode) {
1869
- return null
1870
- }
1871
-
1872
- const { borderRadius, rotate } = window.getComputedStyle(selectedNode)
1873
- const rect: DOMRect = selectedNode.getBoundingClientRect()
1874
-
1875
- return {
1876
- left: rect.left,
1877
- right: rect.right,
1878
- top: rect.top,
1879
- bottom: rect.bottom,
1880
- width: rect.width,
1881
- height: rect.height,
1882
- x: rect.x,
1883
- y: rect.y,
1884
- borderRadius: borderRadius.split(' '),
1885
- rotate,
1886
- }
1887
- }
1888
-
1889
1627
  const insertOrReplaceHeadNode = (id: string, node: Node) => {
1890
1628
  const existing = document.head.querySelector(`[data-meta-id="${id}"]`)
1891
1629
  if (existing) {
@@ -1988,90 +1726,121 @@ const insertTheme = (
1988
1726
  parent.appendChild(styleElem)
1989
1727
  }
1990
1728
 
1991
- type PostMessageType =
1992
- | {
1993
- type: 'textComputedStyle'
1994
- computedStyle: Record<string, string>
1995
- }
1996
- | {
1997
- type: 'selection'
1998
- selectedNodeId: string | null
1999
- }
2000
- | {
2001
- type: 'highlight'
2002
- highlightedNodeId: string | null
2003
- }
2004
- | {
2005
- type: 'navigate'
2006
- name: string
2007
- }
2008
- | {
2009
- type: 'documentScrollSize'
2010
- scrollHeight: number
2011
- scrollWidth: number
2012
- }
2013
- | {
2014
- type: 'nodeMoved'
2015
- copy: boolean
2016
- parent?: string | null
2017
- index?: number
2018
- }
2019
- | {
2020
- type: 'computedStyle'
2021
- computedStyle: Record<string, string>
2022
- }
2023
- | {
2024
- type: 'style'
2025
- time: string
1729
+ const initKeyListeners = () => {
1730
+ document.addEventListener('keydown', (event) => {
1731
+ if (isInputTarget(event)) {
1732
+ return
2026
1733
  }
2027
- | {
2028
- type: 'component event'
2029
- event: any
2030
- time: string
2031
- data: any
1734
+ switch (event.key) {
1735
+ case 'k':
1736
+ if (event.metaKey) {
1737
+ event.preventDefault()
1738
+ }
2032
1739
  }
2033
- | {
2034
- type: 'keydown'
1740
+ postMessageToEditor({
1741
+ type: 'keydown',
2035
1742
  event: {
2036
- key: string
2037
- metaKey: boolean
2038
- shiftKey: boolean
2039
- altKey: boolean
2040
- }
1743
+ key: event.key,
1744
+ metaKey: event.metaKey,
1745
+ shiftKey: event.shiftKey,
1746
+ altKey: event.altKey,
1747
+ },
1748
+ })
1749
+ })
1750
+ document.addEventListener('keyup', (event) => {
1751
+ if (isInputTarget(event)) {
1752
+ return
2041
1753
  }
2042
- | {
2043
- type: 'keyup'
1754
+ postMessageToEditor({
1755
+ type: 'keyup',
2044
1756
  event: {
2045
- key: string
2046
- metaKey: boolean
2047
- shiftKey: boolean
2048
- altKey: boolean
2049
- }
1757
+ key: event.key,
1758
+ metaKey: event.metaKey,
1759
+ shiftKey: event.shiftKey,
1760
+ altKey: event.altKey,
1761
+ },
1762
+ })
1763
+ })
1764
+ document.addEventListener('keypress', (event) => {
1765
+ if (isInputTarget(event)) {
1766
+ return
2050
1767
  }
2051
- | {
2052
- type: 'keypress'
1768
+ postMessageToEditor({
1769
+ type: 'keypress',
2053
1770
  event: {
2054
- key: string
2055
- metaKey: boolean
2056
- shiftKey: boolean
2057
- altKey: boolean
2058
- }
2059
- }
2060
- | { type: 'data'; data: ComponentData }
2061
- | {
2062
- type: 'selectionRect'
2063
- rect: ReturnType<typeof getRectData>
1771
+ key: event.key,
1772
+ metaKey: event.metaKey,
1773
+ shiftKey: event.shiftKey,
1774
+ altKey: event.altKey,
1775
+ },
1776
+ })
1777
+ })
1778
+ }
1779
+
1780
+ const registerActions = (
1781
+ allActions: Record<string, PluginAction>,
1782
+ packageName?: string,
1783
+ ) => {
1784
+ const actions: Record<string, PluginActionV2> = {}
1785
+ Object.entries(allActions ?? {}).forEach(([name, action]) => {
1786
+ if (isLegacyPluginAction(action)) {
1787
+ // Legacy actions are self-registering. We need to execute them to register them
1788
+ Function(action.handler)()
1789
+ return
2064
1790
  }
2065
- | {
2066
- type: 'highlightRect'
2067
- rect: ReturnType<typeof getRectData>
1791
+ // We need to convert the handler string into a real function
1792
+ actions[name] = {
1793
+ ...(action as PluginActionV2),
1794
+ handler:
1795
+ typeof action.handler === 'string'
1796
+ ? (new Function(
1797
+ 'args, ctx',
1798
+ `${action.handler}
1799
+ return ${safeFunctionName(action.name)}(args, ctx)`,
1800
+ ) as ActionHandlerV2)
1801
+ : action.handler,
2068
1802
  }
2069
- | {
2070
- type: 'introspectionResult'
2071
- data: any
2072
- apiKey: string
1803
+ })
1804
+ window.toddle.actions[packageName ?? window.__toddle.project] = actions
1805
+ }
1806
+
1807
+ const registerFormulas = (
1808
+ allFormulas: Record<
1809
+ string,
1810
+ ToddleFormula | CodeFormula<FormulaHandlerV2> | CodeFormula<string>
1811
+ >,
1812
+ packageName?: string,
1813
+ ) => {
1814
+ const formulas: Record<string, PluginFormula<FormulaHandlerV2>> = {}
1815
+ Object.entries(allFormulas ?? {}).forEach(([name, formula]) => {
1816
+ if (
1817
+ !isToddleFormula<FormulaHandlerV2 | string>(formula) &&
1818
+ typeof formula.name === 'string' &&
1819
+ formula.version === undefined
1820
+ ) {
1821
+ // Legacy formulas are self-registering. We need to execute them to register them
1822
+ Function(formula.handler as unknown as string)()
1823
+ return
1824
+ } else if (!isToddleFormula<FormulaHandlerV2 | string>(formula)) {
1825
+ // For code formulas we need to convert the handler string into a real function
1826
+ formulas[name] = {
1827
+ ...formula,
1828
+ handler:
1829
+ typeof formula.handler === 'string'
1830
+ ? (new Function(
1831
+ 'args, ctx',
1832
+ `${formula.handler}
1833
+ return ${safeFunctionName(formula.name)}(args, ctx)`,
1834
+ ) as FormulaHandlerV2)
1835
+ : formula.handler,
1836
+ }
1837
+ return
2073
1838
  }
1839
+ formulas[name] = formula as PluginFormula<FormulaHandlerV2>
1840
+ })
1841
+ window.toddle.formulas[packageName ?? window.__toddle.project] = formulas
1842
+ }
2074
1843
 
2075
- const postMessageToEditor = (message: PostMessageType) => {
1844
+ const postMessageToEditor = (message: EditorPostMessageType) => {
2076
1845
  window.parent?.postMessage(message, '*')
2077
1846
  }