@contractspec/lib.example-shared-ui 6.0.6 → 6.0.7

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 (81) hide show
  1. package/.turbo/turbo-build.log +90 -84
  2. package/AGENTS.md +43 -25
  3. package/README.md +63 -35
  4. package/dist/EvolutionDashboard.js +9 -9
  5. package/dist/EvolutionSidebar.js +15 -15
  6. package/dist/LocalDataIndicator.js +3 -3
  7. package/dist/MarkdownView.d.ts +0 -7
  8. package/dist/MarkdownView.js +76 -172
  9. package/dist/PersonalizationInsights.js +12 -12
  10. package/dist/SaveToStudioButton.js +2 -2
  11. package/dist/SpecDrivenTemplateShell.d.ts +1 -1
  12. package/dist/SpecDrivenTemplateShell.js +10 -10
  13. package/dist/SpecEditorPanel.js +3 -3
  14. package/dist/TemplateShell.js +10 -10
  15. package/dist/browser/EvolutionDashboard.js +9 -9
  16. package/dist/browser/EvolutionSidebar.js +15 -15
  17. package/dist/browser/LocalDataIndicator.js +3 -3
  18. package/dist/browser/MarkdownView.js +76 -172
  19. package/dist/browser/PersonalizationInsights.js +12 -12
  20. package/dist/browser/SaveToStudioButton.js +2 -2
  21. package/dist/browser/SpecDrivenTemplateShell.js +10 -10
  22. package/dist/browser/SpecEditorPanel.js +3 -3
  23. package/dist/browser/TemplateShell.js +10 -10
  24. package/dist/browser/hooks/index.js +29 -29
  25. package/dist/browser/index.js +193 -286
  26. package/dist/browser/lib/component-registry.js +1 -1
  27. package/dist/browser/markdown/formatPresentationName.js +9 -0
  28. package/dist/browser/markdown/useMarkdownPresentation.js +65 -0
  29. package/dist/hooks/index.d.ts +3 -3
  30. package/dist/hooks/index.js +29 -29
  31. package/dist/index.d.ts +12 -11
  32. package/dist/index.js +193 -286
  33. package/dist/lib/component-registry.js +1 -1
  34. package/dist/markdown/formatPresentationName.d.ts +1 -0
  35. package/dist/markdown/formatPresentationName.js +10 -0
  36. package/dist/markdown/useMarkdownPresentation.d.ts +21 -0
  37. package/dist/markdown/useMarkdownPresentation.js +66 -0
  38. package/dist/node/EvolutionDashboard.js +9 -9
  39. package/dist/node/EvolutionSidebar.js +15 -15
  40. package/dist/node/LocalDataIndicator.js +3 -3
  41. package/dist/node/MarkdownView.js +76 -172
  42. package/dist/node/PersonalizationInsights.js +12 -12
  43. package/dist/node/SaveToStudioButton.js +2 -2
  44. package/dist/node/SpecDrivenTemplateShell.js +10 -10
  45. package/dist/node/SpecEditorPanel.js +3 -3
  46. package/dist/node/TemplateShell.js +10 -10
  47. package/dist/node/hooks/index.js +29 -29
  48. package/dist/node/index.js +193 -286
  49. package/dist/node/lib/component-registry.js +1 -1
  50. package/dist/node/markdown/formatPresentationName.js +9 -0
  51. package/dist/node/markdown/useMarkdownPresentation.js +65 -0
  52. package/dist/utils/index.d.ts +1 -1
  53. package/package.json +38 -11
  54. package/src/EvolutionDashboard.tsx +415 -415
  55. package/src/EvolutionSidebar.tsx +245 -245
  56. package/src/LocalDataIndicator.tsx +28 -28
  57. package/src/MarkdownView.tsx +119 -372
  58. package/src/OverlayContextProvider.tsx +272 -272
  59. package/src/PersonalizationInsights.tsx +232 -232
  60. package/src/SaveToStudioButton.tsx +51 -51
  61. package/src/SpecDrivenTemplateShell.tsx +59 -59
  62. package/src/SpecEditorPanel.tsx +138 -138
  63. package/src/TemplateShell.tsx +50 -50
  64. package/src/bundles/ExampleTemplateBundle.ts +78 -78
  65. package/src/hooks/index.ts +3 -3
  66. package/src/hooks/useBehaviorTracking.ts +252 -252
  67. package/src/hooks/useEvolution.ts +437 -437
  68. package/src/hooks/useRegistryTemplates.ts +42 -42
  69. package/src/hooks/useSpecContent.ts +214 -214
  70. package/src/hooks/useWorkflowComposer.ts +567 -567
  71. package/src/index.ts +12 -11
  72. package/src/lib/component-registry.tsx +40 -40
  73. package/src/lib/runtime-context.tsx +31 -31
  74. package/src/lib/types.ts +57 -57
  75. package/src/markdown/formatPresentationName.ts +9 -0
  76. package/src/markdown/useMarkdownPresentation.ts +107 -0
  77. package/src/overlay-types.ts +15 -15
  78. package/src/utils/fetchPresentationData.ts +13 -13
  79. package/src/utils/generateSpecFromTemplate.ts +29 -29
  80. package/src/utils/index.ts +1 -1
  81. package/tsconfig.json +8 -8
@@ -8,59 +8,59 @@ import type { TemplateId } from './lib/types';
8
8
  * Overlay modification operation types (for OverlayContextProvider)
9
9
  */
10
10
  export type OverlayModificationOp =
11
- | { op: 'hide' }
12
- | { op: 'relabel'; label: string }
13
- | { op: 'reorder'; position: number }
14
- | { op: 'restyle'; className?: string; variant?: string }
15
- | { op: 'set-default'; value: unknown };
11
+ | { op: 'hide' }
12
+ | { op: 'relabel'; label: string }
13
+ | { op: 'reorder'; position: number }
14
+ | { op: 'restyle'; className?: string; variant?: string }
15
+ | { op: 'set-default'; value: unknown };
16
16
 
17
17
  /**
18
18
  * Overlay spec for a field or component
19
19
  */
20
20
  export interface OverlaySpec {
21
- id: string;
22
- target: string; // path to the field/component
23
- modifications: OverlayModificationOp[];
24
- conditions?: {
25
- role?: string[];
26
- device?: 'mobile' | 'desktop' | 'any';
27
- featureFlags?: string[];
28
- };
21
+ id: string;
22
+ target: string; // path to the field/component
23
+ modifications: OverlayModificationOp[];
24
+ conditions?: {
25
+ role?: string[];
26
+ device?: 'mobile' | 'desktop' | 'any';
27
+ featureFlags?: string[];
28
+ };
29
29
  }
30
30
 
31
31
  /**
32
32
  * Context value for overlay engine
33
33
  */
34
34
  export interface OverlayContextValue {
35
- /** Current overlays */
36
- overlays: OverlaySpec[];
37
- /** Apply overlays to a component */
38
- applyOverlay: <T extends Record<string, unknown>>(
39
- path: string,
40
- target: T
41
- ) => T;
42
- /** Check if a field should be hidden */
43
- isHidden: (path: string) => boolean;
44
- /** Get relabeled text */
45
- getLabel: (path: string, defaultLabel: string) => string;
46
- /** Get position for reordering */
47
- getPosition: (path: string, defaultPosition: number) => number;
48
- /** Get style modifications */
49
- getStyle: (path: string) => { className?: string; variant?: string };
50
- /** Get default value */
51
- getDefault: <T>(path: string, defaultValue: T) => T;
52
- /** Current user role */
53
- role: string;
54
- /** Current device type */
55
- device: 'mobile' | 'desktop';
35
+ /** Current overlays */
36
+ overlays: OverlaySpec[];
37
+ /** Apply overlays to a component */
38
+ applyOverlay: <T extends Record<string, unknown>>(
39
+ path: string,
40
+ target: T
41
+ ) => T;
42
+ /** Check if a field should be hidden */
43
+ isHidden: (path: string) => boolean;
44
+ /** Get relabeled text */
45
+ getLabel: (path: string, defaultLabel: string) => string;
46
+ /** Get position for reordering */
47
+ getPosition: (path: string, defaultPosition: number) => number;
48
+ /** Get style modifications */
49
+ getStyle: (path: string) => { className?: string; variant?: string };
50
+ /** Get default value */
51
+ getDefault: <T>(path: string, defaultValue: T) => T;
52
+ /** Current user role */
53
+ role: string;
54
+ /** Current device type */
55
+ device: 'mobile' | 'desktop';
56
56
  }
57
57
 
58
58
  const OverlayContext = React.createContext<OverlayContextValue | null>(null);
59
59
 
60
60
  export interface OverlayContextProviderProps extends React.PropsWithChildren {
61
- templateId: TemplateId;
62
- role?: string;
63
- device?: 'mobile' | 'desktop';
61
+ templateId: TemplateId;
62
+ role?: string;
63
+ device?: 'mobile' | 'desktop';
64
64
  }
65
65
 
66
66
  /**
@@ -68,274 +68,274 @@ export interface OverlayContextProviderProps extends React.PropsWithChildren {
68
68
  * Loads template-specific overlays and provides helper functions.
69
69
  */
70
70
  export function OverlayContextProvider({
71
- templateId,
72
- role = 'user',
73
- device = 'desktop',
74
- children,
71
+ templateId,
72
+ role = 'user',
73
+ device = 'desktop',
74
+ children,
75
75
  }: OverlayContextProviderProps) {
76
- // Load template-specific overlays
77
- const overlays = useMemo(
78
- () => getTemplateOverlays(templateId, role),
79
- [templateId, role]
80
- );
76
+ // Load template-specific overlays
77
+ const overlays = useMemo(
78
+ () => getTemplateOverlays(templateId, role),
79
+ [templateId, role]
80
+ );
81
81
 
82
- // Filter overlays based on current context
83
- const activeOverlays = useMemo(() => {
84
- return overlays.filter((overlay) => {
85
- const conditions = overlay.conditions;
86
- if (!conditions) return true;
82
+ // Filter overlays based on current context
83
+ const activeOverlays = useMemo(() => {
84
+ return overlays.filter((overlay) => {
85
+ const conditions = overlay.conditions;
86
+ if (!conditions) return true;
87
87
 
88
- if (conditions.role && !conditions.role.includes(role)) {
89
- return false;
90
- }
88
+ if (conditions.role && !conditions.role.includes(role)) {
89
+ return false;
90
+ }
91
91
 
92
- if (
93
- conditions.device &&
94
- conditions.device !== 'any' &&
95
- conditions.device !== device
96
- ) {
97
- return false;
98
- }
92
+ if (
93
+ conditions.device &&
94
+ conditions.device !== 'any' &&
95
+ conditions.device !== device
96
+ ) {
97
+ return false;
98
+ }
99
99
 
100
- return true;
101
- });
102
- }, [overlays, role, device]);
100
+ return true;
101
+ });
102
+ }, [overlays, role, device]);
103
103
 
104
- // Create overlay map for quick lookups
105
- const overlayMap = useMemo(() => {
106
- const map = new Map<string, OverlaySpec>();
107
- for (const overlay of activeOverlays) {
108
- map.set(overlay.target, overlay);
109
- }
110
- return map;
111
- }, [activeOverlays]);
104
+ // Create overlay map for quick lookups
105
+ const overlayMap = useMemo(() => {
106
+ const map = new Map<string, OverlaySpec>();
107
+ for (const overlay of activeOverlays) {
108
+ map.set(overlay.target, overlay);
109
+ }
110
+ return map;
111
+ }, [activeOverlays]);
112
112
 
113
- // Apply overlay to a target object
114
- const applyOverlay = useMemo(
115
- () =>
116
- <T extends Record<string, unknown>>(path: string, target: T): T => {
117
- const overlay = overlayMap.get(path);
118
- if (!overlay) return target;
113
+ // Apply overlay to a target object
114
+ const applyOverlay = useMemo(
115
+ () =>
116
+ <T extends Record<string, unknown>>(path: string, target: T): T => {
117
+ const overlay = overlayMap.get(path);
118
+ if (!overlay) return target;
119
119
 
120
- let result = { ...target };
121
- for (const mod of overlay.modifications) {
122
- switch (mod.op) {
123
- case 'hide':
124
- result = { ...result, hidden: true };
125
- break;
126
- case 'relabel':
127
- result = { ...result, label: mod.label };
128
- break;
129
- case 'reorder':
130
- result = { ...result, position: mod.position };
131
- break;
132
- case 'restyle':
133
- result = {
134
- ...result,
135
- className: mod.className,
136
- variant: mod.variant,
137
- };
138
- break;
139
- case 'set-default':
140
- result = { ...result, defaultValue: mod.value };
141
- break;
142
- }
143
- }
144
- return result;
145
- },
146
- [overlayMap]
147
- );
120
+ let result = { ...target };
121
+ for (const mod of overlay.modifications) {
122
+ switch (mod.op) {
123
+ case 'hide':
124
+ result = { ...result, hidden: true };
125
+ break;
126
+ case 'relabel':
127
+ result = { ...result, label: mod.label };
128
+ break;
129
+ case 'reorder':
130
+ result = { ...result, position: mod.position };
131
+ break;
132
+ case 'restyle':
133
+ result = {
134
+ ...result,
135
+ className: mod.className,
136
+ variant: mod.variant,
137
+ };
138
+ break;
139
+ case 'set-default':
140
+ result = { ...result, defaultValue: mod.value };
141
+ break;
142
+ }
143
+ }
144
+ return result;
145
+ },
146
+ [overlayMap]
147
+ );
148
148
 
149
- // Check if field is hidden
150
- const isHidden = useMemo(
151
- () =>
152
- (path: string): boolean => {
153
- const overlay = overlayMap.get(path);
154
- return overlay?.modifications.some((m) => m.op === 'hide') ?? false;
155
- },
156
- [overlayMap]
157
- );
149
+ // Check if field is hidden
150
+ const isHidden = useMemo(
151
+ () =>
152
+ (path: string): boolean => {
153
+ const overlay = overlayMap.get(path);
154
+ return overlay?.modifications.some((m) => m.op === 'hide') ?? false;
155
+ },
156
+ [overlayMap]
157
+ );
158
158
 
159
- // Get relabeled text
160
- const getLabel = useMemo(
161
- () =>
162
- (path: string, defaultLabel: string): string => {
163
- const overlay = overlayMap.get(path);
164
- const relabel = overlay?.modifications.find((m) => m.op === 'relabel');
165
- return relabel && relabel.op === 'relabel'
166
- ? relabel.label
167
- : defaultLabel;
168
- },
169
- [overlayMap]
170
- );
159
+ // Get relabeled text
160
+ const getLabel = useMemo(
161
+ () =>
162
+ (path: string, defaultLabel: string): string => {
163
+ const overlay = overlayMap.get(path);
164
+ const relabel = overlay?.modifications.find((m) => m.op === 'relabel');
165
+ return relabel && relabel.op === 'relabel'
166
+ ? relabel.label
167
+ : defaultLabel;
168
+ },
169
+ [overlayMap]
170
+ );
171
171
 
172
- // Get position for reordering
173
- const getPosition = useMemo(
174
- () =>
175
- (path: string, defaultPosition: number): number => {
176
- const overlay = overlayMap.get(path);
177
- const reorder = overlay?.modifications.find((m) => m.op === 'reorder');
178
- return reorder && reorder.op === 'reorder'
179
- ? reorder.position
180
- : defaultPosition;
181
- },
182
- [overlayMap]
183
- );
172
+ // Get position for reordering
173
+ const getPosition = useMemo(
174
+ () =>
175
+ (path: string, defaultPosition: number): number => {
176
+ const overlay = overlayMap.get(path);
177
+ const reorder = overlay?.modifications.find((m) => m.op === 'reorder');
178
+ return reorder && reorder.op === 'reorder'
179
+ ? reorder.position
180
+ : defaultPosition;
181
+ },
182
+ [overlayMap]
183
+ );
184
184
 
185
- // Get style modifications
186
- const getStyle = useMemo(
187
- () =>
188
- (path: string): { className?: string; variant?: string } => {
189
- const overlay = overlayMap.get(path);
190
- const restyle = overlay?.modifications.find((m) => m.op === 'restyle');
191
- if (restyle && restyle.op === 'restyle') {
192
- return {
193
- className: restyle.className,
194
- variant: restyle.variant,
195
- };
196
- }
197
- return {};
198
- },
199
- [overlayMap]
200
- );
185
+ // Get style modifications
186
+ const getStyle = useMemo(
187
+ () =>
188
+ (path: string): { className?: string; variant?: string } => {
189
+ const overlay = overlayMap.get(path);
190
+ const restyle = overlay?.modifications.find((m) => m.op === 'restyle');
191
+ if (restyle && restyle.op === 'restyle') {
192
+ return {
193
+ className: restyle.className,
194
+ variant: restyle.variant,
195
+ };
196
+ }
197
+ return {};
198
+ },
199
+ [overlayMap]
200
+ );
201
201
 
202
- // Get default value
203
- const getDefault = useMemo(
204
- () =>
205
- <T,>(path: string, defaultValue: T): T => {
206
- const overlay = overlayMap.get(path);
207
- const setDefault = overlay?.modifications.find(
208
- (m) => m.op === 'set-default'
209
- );
210
- return setDefault && setDefault.op === 'set-default'
211
- ? (setDefault.value as T)
212
- : defaultValue;
213
- },
214
- [overlayMap]
215
- );
202
+ // Get default value
203
+ const getDefault = useMemo(
204
+ () =>
205
+ <T,>(path: string, defaultValue: T): T => {
206
+ const overlay = overlayMap.get(path);
207
+ const setDefault = overlay?.modifications.find(
208
+ (m) => m.op === 'set-default'
209
+ );
210
+ return setDefault && setDefault.op === 'set-default'
211
+ ? (setDefault.value as T)
212
+ : defaultValue;
213
+ },
214
+ [overlayMap]
215
+ );
216
216
 
217
- const value = useMemo<OverlayContextValue>(
218
- () => ({
219
- overlays: activeOverlays,
220
- applyOverlay,
221
- isHidden,
222
- getLabel,
223
- getPosition,
224
- getStyle,
225
- getDefault,
226
- role,
227
- device,
228
- }),
229
- [
230
- activeOverlays,
231
- applyOverlay,
232
- isHidden,
233
- getLabel,
234
- getPosition,
235
- getStyle,
236
- getDefault,
237
- role,
238
- device,
239
- ]
240
- );
217
+ const value = useMemo<OverlayContextValue>(
218
+ () => ({
219
+ overlays: activeOverlays,
220
+ applyOverlay,
221
+ isHidden,
222
+ getLabel,
223
+ getPosition,
224
+ getStyle,
225
+ getDefault,
226
+ role,
227
+ device,
228
+ }),
229
+ [
230
+ activeOverlays,
231
+ applyOverlay,
232
+ isHidden,
233
+ getLabel,
234
+ getPosition,
235
+ getStyle,
236
+ getDefault,
237
+ role,
238
+ device,
239
+ ]
240
+ );
241
241
 
242
- return (
243
- <OverlayContext.Provider value={value}>{children}</OverlayContext.Provider>
244
- );
242
+ return (
243
+ <OverlayContext.Provider value={value}>{children}</OverlayContext.Provider>
244
+ );
245
245
  }
246
246
 
247
247
  /**
248
248
  * Hook to access overlay context
249
249
  */
250
250
  export function useOverlayContext(): OverlayContextValue {
251
- const context = useContext(OverlayContext);
252
- if (!context) {
253
- throw new Error(
254
- 'useOverlayContext must be used within an OverlayContextProvider'
255
- );
256
- }
257
- return context;
251
+ const context = useContext(OverlayContext);
252
+ if (!context) {
253
+ throw new Error(
254
+ 'useOverlayContext must be used within an OverlayContextProvider'
255
+ );
256
+ }
257
+ return context;
258
258
  }
259
259
 
260
260
  /**
261
261
  * Hook to check if within overlay context
262
262
  */
263
263
  export function useIsInOverlayContext(): boolean {
264
- return useContext(OverlayContext) !== null;
264
+ return useContext(OverlayContext) !== null;
265
265
  }
266
266
 
267
267
  /**
268
268
  * Get template-specific overlays
269
269
  */
270
270
  function getTemplateOverlays(
271
- templateId: TemplateId,
272
- _role: string
271
+ templateId: TemplateId,
272
+ _role: string
273
273
  ): OverlaySpec[] {
274
- // Demo overlays for each template
275
- const templateOverlays: Record<string, OverlaySpec[]> = {
276
- 'crm-pipeline': [
277
- {
278
- id: 'crm-hide-internal-fields',
279
- target: 'deal.internalNotes',
280
- modifications: [{ op: 'hide' }],
281
- conditions: { role: ['viewer', 'user'] },
282
- },
283
- {
284
- id: 'crm-relabel-value',
285
- target: 'deal.value',
286
- modifications: [{ op: 'relabel', label: 'Deal Amount' }],
287
- },
288
- ],
289
- 'saas-boilerplate': [
290
- {
291
- id: 'saas-hide-billing',
292
- target: 'settings.billing',
293
- modifications: [{ op: 'hide' }],
294
- conditions: { role: ['viewer'] },
295
- },
296
- {
297
- id: 'saas-restyle-plan',
298
- target: 'settings.plan',
299
- modifications: [{ op: 'restyle', variant: 'premium' }],
300
- conditions: { role: ['admin'] },
301
- },
302
- ],
303
- 'agent-console': [
304
- {
305
- id: 'agent-hide-cost',
306
- target: 'run.cost',
307
- modifications: [{ op: 'hide' }],
308
- conditions: { role: ['viewer'] },
309
- },
310
- {
311
- id: 'agent-relabel-tokens',
312
- target: 'run.tokens',
313
- modifications: [{ op: 'relabel', label: 'Token Usage' }],
314
- },
315
- ],
316
- 'todos-app': [
317
- {
318
- id: 'todos-hide-assignee',
319
- target: 'task.assignee',
320
- modifications: [{ op: 'hide' }],
321
- conditions: { device: 'mobile' },
322
- },
323
- ],
324
- 'messaging-app': [
325
- {
326
- id: 'messaging-reorder-timestamp',
327
- target: 'message.timestamp',
328
- modifications: [{ op: 'reorder', position: 0 }],
329
- },
330
- ],
331
- 'recipe-app-i18n': [
332
- {
333
- id: 'recipe-relabel-servings',
334
- target: 'recipe.servings',
335
- modifications: [{ op: 'relabel', label: 'Portions' }],
336
- },
337
- ],
338
- };
274
+ // Demo overlays for each template
275
+ const templateOverlays: Record<string, OverlaySpec[]> = {
276
+ 'crm-pipeline': [
277
+ {
278
+ id: 'crm-hide-internal-fields',
279
+ target: 'deal.internalNotes',
280
+ modifications: [{ op: 'hide' }],
281
+ conditions: { role: ['viewer', 'user'] },
282
+ },
283
+ {
284
+ id: 'crm-relabel-value',
285
+ target: 'deal.value',
286
+ modifications: [{ op: 'relabel', label: 'Deal Amount' }],
287
+ },
288
+ ],
289
+ 'saas-boilerplate': [
290
+ {
291
+ id: 'saas-hide-billing',
292
+ target: 'settings.billing',
293
+ modifications: [{ op: 'hide' }],
294
+ conditions: { role: ['viewer'] },
295
+ },
296
+ {
297
+ id: 'saas-restyle-plan',
298
+ target: 'settings.plan',
299
+ modifications: [{ op: 'restyle', variant: 'premium' }],
300
+ conditions: { role: ['admin'] },
301
+ },
302
+ ],
303
+ 'agent-console': [
304
+ {
305
+ id: 'agent-hide-cost',
306
+ target: 'run.cost',
307
+ modifications: [{ op: 'hide' }],
308
+ conditions: { role: ['viewer'] },
309
+ },
310
+ {
311
+ id: 'agent-relabel-tokens',
312
+ target: 'run.tokens',
313
+ modifications: [{ op: 'relabel', label: 'Token Usage' }],
314
+ },
315
+ ],
316
+ 'todos-app': [
317
+ {
318
+ id: 'todos-hide-assignee',
319
+ target: 'task.assignee',
320
+ modifications: [{ op: 'hide' }],
321
+ conditions: { device: 'mobile' },
322
+ },
323
+ ],
324
+ 'messaging-app': [
325
+ {
326
+ id: 'messaging-reorder-timestamp',
327
+ target: 'message.timestamp',
328
+ modifications: [{ op: 'reorder', position: 0 }],
329
+ },
330
+ ],
331
+ 'recipe-app-i18n': [
332
+ {
333
+ id: 'recipe-relabel-servings',
334
+ target: 'recipe.servings',
335
+ modifications: [{ op: 'relabel', label: 'Portions' }],
336
+ },
337
+ ],
338
+ };
339
339
 
340
- return templateOverlays[templateId] ?? [];
340
+ return templateOverlays[templateId] ?? [];
341
341
  }