@getdom/studio 0.1.0

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 (213) hide show
  1. package/LICENSE.md +109 -0
  2. package/README.md +44 -0
  3. package/SKILL.md +83 -0
  4. package/package.json +73 -0
  5. package/public/llms.txt +57 -0
  6. package/public/skills.sh +11 -0
  7. package/src/pages/_layout/componentCatalog.generated.js +4514 -0
  8. package/src/pages/_layout/componentInspector.js +60 -0
  9. package/src/pages/_layout/componentManager.js +192 -0
  10. package/src/pages/_layout/componentRuntimeRecords.js +49 -0
  11. package/src/pages/_layout/inspector/componentRegistry.js +639 -0
  12. package/src/pages/_layout/inspector/serialize.js +134 -0
  13. package/src/pages/_layout/inspector/useInspector.js +293 -0
  14. package/src/pages/components/accordion/DomAccordion.vue +39 -0
  15. package/src/pages/components/breadcrumbs/DomBreadcrumbs.vue +347 -0
  16. package/src/pages/components/button/DomButton.vue +132 -0
  17. package/src/pages/components/command-palette/DomCommandPalette.vue +482 -0
  18. package/src/pages/components/context-menu/DomContextMenu.vue +178 -0
  19. package/src/pages/components/dialog/DomDialog.vue +271 -0
  20. package/src/pages/components/dialog/DomDialogSchemaForm.vue +72 -0
  21. package/src/pages/components/dialog/DomDialogStack.vue +289 -0
  22. package/src/pages/components/dialog/useDialogs.js +253 -0
  23. package/src/pages/components/drawer/DomDrawer.vue +159 -0
  24. package/src/pages/components/dropdown/DomDropdown.vue +261 -0
  25. package/src/pages/components/icon-button/DomIconButton.vue +155 -0
  26. package/src/pages/components/lightbox/DomLightbox.vue +1561 -0
  27. package/src/pages/components/menu/DomMenu.vue +359 -0
  28. package/src/pages/components/menu/DomMenuItem.vue +233 -0
  29. package/src/pages/components/menu/DomMenuLabel.vue +19 -0
  30. package/src/pages/components/menu/DomMenuSeparator.vue +14 -0
  31. package/src/pages/components/menu/menuContext.js +11 -0
  32. package/src/pages/components/menu/menuPlacement.js +26 -0
  33. package/src/pages/components/menu/submenu.js +58 -0
  34. package/src/pages/components/pagination/DomPagination.vue +265 -0
  35. package/src/pages/components/playground/DomRenderer.vue +48 -0
  36. package/src/pages/components/popover/DomPopover.vue +244 -0
  37. package/src/pages/components/profile-menu/DomProfileMenu.vue +408 -0
  38. package/src/pages/components/profile-menu/DomProfileMenuItemContent.vue +45 -0
  39. package/src/pages/components/scroll-menu/DomScrollMenu.vue +430 -0
  40. package/src/pages/components/scroll-menu/DomScrollMenuItem.vue +212 -0
  41. package/src/pages/components/scroll-menu/scrollMenuContext.js +22 -0
  42. package/src/pages/components/splitter-panel/DomSplitterPanel.vue +283 -0
  43. package/src/pages/components/stepper/DomStepper.vue +386 -0
  44. package/src/pages/components/tabs/DomTabs.vue +149 -0
  45. package/src/pages/components/theme-switcher/DomThemeSwitcher.vue +226 -0
  46. package/src/pages/components/toast/DomToastItem.vue +52 -0
  47. package/src/pages/components/toast/DomToastStack.vue +768 -0
  48. package/src/pages/components/toast/useToasts.js +237 -0
  49. package/src/pages/components/toolbar/DomToolbar.vue +158 -0
  50. package/src/pages/components/tooltip/DomTooltip.vue +69 -0
  51. package/src/pages/components/tooltip/vDomTooltip.js +106 -0
  52. package/src/pages/components/tree-view/DomTreeView.vue +961 -0
  53. package/src/pages/components/tree-view/treeSchemas.js +129 -0
  54. package/src/pages/components/tree-view/treeUtils.js +228 -0
  55. package/src/pages/data/_shared/calculateVirtualWindow.js +101 -0
  56. package/src/pages/data/_shared/resourceRecord.js +29 -0
  57. package/src/pages/data/activity-timeline/DomActivityTimeline.vue +417 -0
  58. package/src/pages/data/agent-chat/DomAgentChatShell.vue +565 -0
  59. package/src/pages/data/agent-chat/DomAgentConversation.vue +737 -0
  60. package/src/pages/data/agent-chat/DomAgentConversationList.vue +319 -0
  61. package/src/pages/data/agent-chat/examples/agentChatMockApi.js +620 -0
  62. package/src/pages/data/agent-chat/examples/useAgentChatDemoStore.js +730 -0
  63. package/src/pages/data/data-grid/DataGridVirtualRows.vue +220 -0
  64. package/src/pages/data/data-grid/DomDataGrid.vue +1643 -0
  65. package/src/pages/data/document-editor/DomDocumentEditor.vue +3030 -0
  66. package/src/pages/data/document-editor/documentEditorCodeBlock.js +282 -0
  67. package/src/pages/data/document-editor/examples/documentEditorMockApi.js +272 -0
  68. package/src/pages/data/document-editor/examples/useDocumentEditorDemoStore.js +284 -0
  69. package/src/pages/data/filter-bar/DomFilterBar.vue +533 -0
  70. package/src/pages/data/kanban/DomKanban.vue +1703 -0
  71. package/src/pages/data/kanban/kanbanApi.js +80 -0
  72. package/src/pages/data/media-browser/DomMediaBrowser.vue +2344 -0
  73. package/src/pages/data/media-browser/examples/mediaBrowserMockApi.js +653 -0
  74. package/src/pages/data/media-browser/examples/useMediaBrowserDemoStore.js +1157 -0
  75. package/src/pages/data/month-calendar/DomMonthCalendar.vue +1718 -0
  76. package/src/pages/data/week-planner/DomWeekPlanner.vue +2308 -0
  77. package/src/pages/dev/code-block/DomCodeBlock.vue +263 -0
  78. package/src/pages/dev/json-viewer/DomJsonViewer.vue +962 -0
  79. package/src/pages/forms/_shared/DomFieldLoadingSpinner.vue +12 -0
  80. package/src/pages/forms/_shared/options.js +61 -0
  81. package/src/pages/forms/_shared/useOptionMenu.js +59 -0
  82. package/src/pages/forms/autocomplete/DomAutocomplete.vue +180 -0
  83. package/src/pages/forms/calendar/DomCalendar.vue +442 -0
  84. package/src/pages/forms/checkbox/DomCheckbox.vue +69 -0
  85. package/src/pages/forms/class-toggle-input/DomClassToggleInput.vue +316 -0
  86. package/src/pages/forms/code-input/DomCodeInput.vue +202 -0
  87. package/src/pages/forms/color-input/DomColorInput.vue +118 -0
  88. package/src/pages/forms/combobox/DomCombobox.vue +237 -0
  89. package/src/pages/forms/confirmation-code-input/DomConfirmationCodeInput.vue +345 -0
  90. package/src/pages/forms/date-picker/DomDatePicker.vue +299 -0
  91. package/src/pages/forms/date-time-picker/DomDateTimePicker.vue +630 -0
  92. package/src/pages/forms/email-input/DomEmailInput.vue +55 -0
  93. package/src/pages/forms/field/DomField.vue +267 -0
  94. package/src/pages/forms/field/fieldLayout.js +1 -0
  95. package/src/pages/forms/field/fieldProps.js +114 -0
  96. package/src/pages/forms/field/layouts/DomGridFieldLayout.vue +38 -0
  97. package/src/pages/forms/field/useField.js +343 -0
  98. package/src/pages/forms/field/validators.js +364 -0
  99. package/src/pages/forms/file-upload/DomFileUpload.vue +361 -0
  100. package/src/pages/forms/form/DomForm.vue +985 -0
  101. package/src/pages/forms/form/adapters/fieldBuilder.js +45 -0
  102. package/src/pages/forms/form/adapters/jsonSchema.js +264 -0
  103. package/src/pages/forms/form/adapters/shared.js +53 -0
  104. package/src/pages/forms/form/adapters/zod.js +235 -0
  105. package/src/pages/forms/form/formApi.js +38 -0
  106. package/src/pages/forms/form/formDefinition.js +392 -0
  107. package/src/pages/forms/form/schemaAdapter.js +54 -0
  108. package/src/pages/forms/icon-selector/DomIconSelector.vue +460 -0
  109. package/src/pages/forms/icon-selector/iconSelectorItems.js +490 -0
  110. package/src/pages/forms/input-group/DomInputGroup.vue +232 -0
  111. package/src/pages/forms/json-input/DomJsonInput.vue +227 -0
  112. package/src/pages/forms/json-list-input/DomJsonListInput.vue +206 -0
  113. package/src/pages/forms/listbox/DomListbox.vue +105 -0
  114. package/src/pages/forms/money-input/DomMoneyInput.vue +147 -0
  115. package/src/pages/forms/native-select/DomNativeSelect.vue +67 -0
  116. package/src/pages/forms/number-input/DomNumberInput.vue +47 -0
  117. package/src/pages/forms/password-input/DomPasswordInput.vue +218 -0
  118. package/src/pages/forms/password-input/pwnedPasswords.js +47 -0
  119. package/src/pages/forms/password-strength/DomPasswordStrength.vue +128 -0
  120. package/src/pages/forms/password-strength/passwordStrength.js +92 -0
  121. package/src/pages/forms/position-input/DomPositionInput.vue +83 -0
  122. package/src/pages/forms/radio-group/DomRadioGroup.vue +102 -0
  123. package/src/pages/forms/range-input/DomRangeInput.vue +296 -0
  124. package/src/pages/forms/select/DomSelect.vue +479 -0
  125. package/src/pages/forms/select-input/DomSelectInput.vue +155 -0
  126. package/src/pages/forms/tag-combobox/DomTagCombobox.vue +358 -0
  127. package/src/pages/forms/text-input/DomTextInput.vue +48 -0
  128. package/src/pages/forms/textarea-composer/DomTextareaComposer.vue +234 -0
  129. package/src/pages/forms/textarea-input/DomTextareaInput.vue +86 -0
  130. package/src/pages/forms/textarea-input/useTextareaAutosize.js +147 -0
  131. package/src/pages/forms/time-selector/DomTimeSelector.vue +389 -0
  132. package/src/pages/forms/time-selector/timeUtils.js +194 -0
  133. package/src/pages/forms/toggle/DomToggle.vue +158 -0
  134. package/src/pages/forms/toggle-button/DomToggleButton.vue +88 -0
  135. package/src/pages/forms/toggle-button-group/DomToggleButtonGroup.vue +340 -0
  136. package/src/pages/forms/url-input/DomUrlInput.vue +55 -0
  137. package/src/pages/forms/wysiwyg/DomWysiwyg.vue +204 -0
  138. package/src/pages/lib/css/color.js +245 -0
  139. package/src/pages/lib/headless/accordion.d.ts +7 -0
  140. package/src/pages/lib/headless/accordion.js +246 -0
  141. package/src/pages/lib/headless/autocomplete.d.ts +4 -0
  142. package/src/pages/lib/headless/autocomplete.js +33 -0
  143. package/src/pages/lib/headless/base.d.ts +25 -0
  144. package/src/pages/lib/headless/base.js +127 -0
  145. package/src/pages/lib/headless/checkbox.d.ts +19 -0
  146. package/src/pages/lib/headless/checkbox.js +99 -0
  147. package/src/pages/lib/headless/combobox.d.ts +7 -0
  148. package/src/pages/lib/headless/combobox.js +488 -0
  149. package/src/pages/lib/headless/dialog.d.ts +23 -0
  150. package/src/pages/lib/headless/dialog.js +384 -0
  151. package/src/pages/lib/headless/drawer.d.ts +21 -0
  152. package/src/pages/lib/headless/drawer.js +682 -0
  153. package/src/pages/lib/headless/dropdown.d.ts +19 -0
  154. package/src/pages/lib/headless/dropdown.js +412 -0
  155. package/src/pages/lib/headless/floating-effect.js +118 -0
  156. package/src/pages/lib/headless/floating.d.ts +34 -0
  157. package/src/pages/lib/headless/floating.js +382 -0
  158. package/src/pages/lib/headless/index.d.ts +19 -0
  159. package/src/pages/lib/headless/index.js +19 -0
  160. package/src/pages/lib/headless/listbox.d.ts +19 -0
  161. package/src/pages/lib/headless/listbox.js +126 -0
  162. package/src/pages/lib/headless/menu.d.ts +18 -0
  163. package/src/pages/lib/headless/menu.js +117 -0
  164. package/src/pages/lib/headless/popover-panel.d.ts +18 -0
  165. package/src/pages/lib/headless/popover-panel.js +309 -0
  166. package/src/pages/lib/headless/popover.d.ts +19 -0
  167. package/src/pages/lib/headless/popover.js +391 -0
  168. package/src/pages/lib/headless/radio-group.d.ts +19 -0
  169. package/src/pages/lib/headless/radio-group.js +100 -0
  170. package/src/pages/lib/headless/tabs.d.ts +18 -0
  171. package/src/pages/lib/headless/tabs.js +119 -0
  172. package/src/pages/lib/headless/toast.d.ts +13 -0
  173. package/src/pages/lib/headless/toast.js +65 -0
  174. package/src/pages/lib/headless/toggle.d.ts +19 -0
  175. package/src/pages/lib/headless/toggle.js +162 -0
  176. package/src/pages/lib/headless/tooltip-controller.d.ts +22 -0
  177. package/src/pages/lib/headless/tooltip-controller.js +549 -0
  178. package/src/pages/lib/headless/tooltip.d.ts +19 -0
  179. package/src/pages/lib/headless/tooltip.js +153 -0
  180. package/src/pages/lib/headless/transitions.d.ts +5 -0
  181. package/src/pages/lib/headless/transitions.js +89 -0
  182. package/src/pages/lib/vue/_internal/DomLiquidGlassSurface.vue +310 -0
  183. package/src/pages/lib/vue/_internal/liquidGlass.js +112 -0
  184. package/src/pages/lib/vue/_internal/liquidGlassConfig.js +10 -0
  185. package/src/pages/lib/vue/codeHighlighter.js +132 -0
  186. package/src/pages/lib/vue/floatingTeleportTarget.js +23 -0
  187. package/src/pages/lib/vue/index.d.ts +6806 -0
  188. package/src/pages/lib/vue/index.js +218 -0
  189. package/src/pages/lib/vue/useTheme.js +116 -0
  190. package/src/pages/mobile/action-sheet/DomActionSheet.vue +186 -0
  191. package/src/pages/mobile/bottom-nav/DomAppBottomNav.vue +94 -0
  192. package/src/pages/mobile/list-item/DomAppListItem.vue +90 -0
  193. package/src/pages/mobile/safe-area/DomSafeArea.vue +39 -0
  194. package/src/pages/mobile/shell/DomAppShell.vue +51 -0
  195. package/src/pages/mobile/stack/AppStackScreen.vue +63 -0
  196. package/src/pages/mobile/stack/DomAppStack.vue +711 -0
  197. package/src/pages/mobile/top-bar/DomAppTopBar.vue +67 -0
  198. package/src/pages/visual/alert/DomAlert.vue +363 -0
  199. package/src/pages/visual/avatar/DomAvatar.vue +110 -0
  200. package/src/pages/visual/avatar-stack/DomAvatarStack.vue +89 -0
  201. package/src/pages/visual/badge/DomBadge.vue +105 -0
  202. package/src/pages/visual/card/DomCard.vue +65 -0
  203. package/src/pages/visual/card-rail/DomCardRail.vue +99 -0
  204. package/src/pages/visual/empty-state/DomEmptyState.vue +88 -0
  205. package/src/pages/visual/progress/DomProgress.vue +212 -0
  206. package/src/pages/visual/progress/DomRadialProgress.vue +186 -0
  207. package/src/pages/visual/progress/DomSegmentedProgress.vue +350 -0
  208. package/src/pages/visual/skeleton/DomSkeleton.vue +174 -0
  209. package/src/pages/visual/spinner/DomSpinner.vue +122 -0
  210. package/src/pages/visual/status-pill/DomStatusPill.vue +75 -0
  211. package/src/pages/visual/text-diff/DomTextDiff.vue +577 -0
  212. package/src/pages/visual/text-diff/textDiff.js +300 -0
  213. package/src/style.css +446 -0
@@ -0,0 +1,392 @@
1
+ export const FORM_NODE_RESERVED_KEYS = new Set([
2
+ 'id',
3
+ 'type',
4
+ 'component',
5
+ 'children',
6
+ 'properties',
7
+ 'items',
8
+ 'props',
9
+ 'slot',
10
+ 'text',
11
+ 'schema',
12
+ ]);
13
+
14
+ export const defaultComponentByType = {
15
+ string: 'DomTextInput',
16
+ text: 'DomTextInput',
17
+ email: 'DomEmailInput',
18
+ url: 'DomUrlInput',
19
+ number: 'DomNumberInput',
20
+ integer: 'DomNumberInput',
21
+ decimal: 'DomMoneyInput',
22
+ currency: 'DomMoneyInput',
23
+ money: 'DomMoneyInput',
24
+ boolean: 'DomToggle',
25
+ date: 'DomDatePicker',
26
+ time: 'DomTimeSelector',
27
+ datetime: 'DomDateTimePicker',
28
+ 'date-time': 'DomDateTimePicker',
29
+ file: 'DomFileUpload',
30
+ array: 'DomJsonListInput',
31
+ object: 'DomForm',
32
+ json: 'DomJsonInput',
33
+ };
34
+
35
+ export const typeByComponent = {
36
+ DomAutocomplete: 'string',
37
+ DomCalendar: 'date',
38
+ DomCheckbox: 'boolean',
39
+ DomCodeInput: 'string',
40
+ DomCombobox: 'string',
41
+ DomDateTimePicker: 'datetime',
42
+ DomDatePicker: 'date',
43
+ DomEmailInput: 'email',
44
+ DomForm: 'object',
45
+ DomJsonInput: 'json',
46
+ DomJsonListInput: 'array',
47
+ DomFileUpload: 'file',
48
+ DomMoneyInput: 'decimal',
49
+ DomNativeSelect: 'string',
50
+ DomNumberInput: 'number',
51
+ DomPasswordInput: 'string',
52
+ DomRadioGroup: 'string',
53
+ DomSelect: 'string',
54
+ DomSelectInput: 'string',
55
+ DomTagCombobox: 'array',
56
+ DomTextInput: 'string',
57
+ DomTextareaComposer: 'string',
58
+ DomTextareaInput: 'string',
59
+ DomTimeSelector: 'time',
60
+ DomToggle: 'boolean',
61
+ DomUrlInput: 'url',
62
+ };
63
+
64
+ const formTypes = new Map();
65
+
66
+ export function registerFormType(name, definition) {
67
+ if (!name) return null;
68
+ formTypes.set(name, definition);
69
+ return definition;
70
+ }
71
+
72
+ export function getFormType(name) {
73
+ return formTypes.get(name) || null;
74
+ }
75
+
76
+ export function isKnownComponent(name) {
77
+ return Boolean(typeByComponent[name]);
78
+ }
79
+
80
+ export function isKnownDataType(name) {
81
+ return Boolean(defaultComponentByType[name]);
82
+ }
83
+
84
+ function isComponentLike(name) {
85
+ return typeof name === 'string' && /^[A-Z]/.test(name);
86
+ }
87
+
88
+ function clone(value) {
89
+ if (value == null || typeof value !== 'object') return value;
90
+ if (Array.isArray(value)) return value.map(clone);
91
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, clone(item)]));
92
+ }
93
+
94
+ function formatInferredLabel(key = '') {
95
+ return String(key)
96
+ .replace(/[-_]+/g, ' ')
97
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
98
+ .replace(/^./, (char) => char.toUpperCase());
99
+ }
100
+
101
+ function definedValues(values) {
102
+ return values.filter((value) => value !== undefined && value !== null);
103
+ }
104
+
105
+ function inferStringType(value, key = '') {
106
+ const name = String(key).toLowerCase();
107
+ const text = String(value || '');
108
+ if (name.includes('email') || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(text)) return 'email';
109
+ if (
110
+ name === 'url'
111
+ || name === 'href'
112
+ || name.endsWith('url')
113
+ || name.includes('avatar')
114
+ || name.includes('image')
115
+ || /^https?:\/\//.test(text)
116
+ ) return 'url';
117
+ if (name.includes('datetime') || name.includes('date_time') || name.includes('date-time') || /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test(text)) return 'datetime';
118
+ if (name.includes('time') || /^\d{2}:\d{2}(?::\d{2})?$/.test(text)) return 'time';
119
+ if (name.includes('date') || /^\d{4}-\d{2}-\d{2}$/.test(text)) return 'date';
120
+ if (text.length > 160 || text.includes('\n')) return 'text';
121
+ return 'string';
122
+ }
123
+
124
+ function inferNodeForValues(values, context = {}) {
125
+ const candidates = definedValues(values);
126
+ const props = context.key ? { label: formatInferredLabel(context.key) } : {};
127
+
128
+ if (!candidates.length) return { type: 'json', ...props };
129
+ if (candidates.every(Array.isArray)) {
130
+ return {
131
+ type: 'array',
132
+ ...props,
133
+ items: inferNodeForValues(candidates.flat(), context),
134
+ };
135
+ }
136
+ if (candidates.every((value) => value && typeof value === 'object' && !Array.isArray(value))) {
137
+ return inferObjectFormNode(candidates, props);
138
+ }
139
+ if (candidates.some((value) => value && typeof value === 'object')) return { type: 'json', ...props };
140
+ if (candidates.every((value) => typeof value === 'boolean')) return { type: 'boolean', ...props };
141
+ if (candidates.every((value) => typeof value === 'number')) {
142
+ return { type: candidates.every(Number.isInteger) ? 'integer' : 'number', ...props };
143
+ }
144
+ if (candidates.every((value) => typeof value === 'string')) {
145
+ return { type: inferStringType(candidates[0], context.key), ...props };
146
+ }
147
+ return { type: 'json', ...props };
148
+ }
149
+
150
+ function inferObjectFormNode(objects, props = {}) {
151
+ const keys = [...new Set(objects.flatMap((object) => Object.keys(object)))];
152
+ return {
153
+ type: 'DomForm',
154
+ ...props,
155
+ properties: Object.fromEntries(keys.map((key) => [
156
+ key,
157
+ inferNodeForValues(objects.map((object) => object[key]), { key }),
158
+ ])),
159
+ };
160
+ }
161
+
162
+ export function inferFormNodeFromValue(value) {
163
+ if (Array.isArray(value)) {
164
+ return {
165
+ type: 'array',
166
+ items: inferNodeForValues(value),
167
+ };
168
+ }
169
+ if (value && typeof value === 'object') {
170
+ return inferObjectFormNode([value]);
171
+ }
172
+ return null;
173
+ }
174
+
175
+ function shorthandProps(node) {
176
+ return Object.fromEntries(
177
+ Object.entries(node)
178
+ .filter(([key]) => !FORM_NODE_RESERVED_KEYS.has(key))
179
+ .map(([key, value]) => [key, clone(value)]),
180
+ );
181
+ }
182
+
183
+ function normalizeChildren(children) {
184
+ if (!children) return [];
185
+ if (Array.isArray(children)) return children.map((child) => normalizeFormNode(child));
186
+ if (typeof children === 'object') {
187
+ return Object.entries(children).map(([name, child]) => normalizeFormNode(child, { name }));
188
+ }
189
+ return [];
190
+ }
191
+
192
+ function resolvedTypeDefinition(type) {
193
+ const definition = getFormType(type);
194
+ if (!definition) return null;
195
+ return normalizeFormNode(definition);
196
+ }
197
+
198
+ function resolveTypeAndComponent(node, typeDefinition) {
199
+ const rawType = node.type;
200
+ let type = null;
201
+ let component = node.component || null;
202
+
203
+ if (typeDefinition) {
204
+ type = typeDefinition.type;
205
+ component ||= typeDefinition.component;
206
+ }
207
+
208
+ if (rawType && isKnownComponent(rawType)) {
209
+ component ||= rawType;
210
+ type ||= typeByComponent[rawType];
211
+ } else if (rawType && isKnownDataType(rawType)) {
212
+ type ||= rawType;
213
+ component ||= defaultComponentByType[rawType];
214
+ } else if (rawType && isComponentLike(rawType)) {
215
+ component ||= rawType;
216
+ type ||= typeByComponent[rawType] || (node.children || node.properties ? 'object' : rawType);
217
+ } else if (rawType) {
218
+ type ||= rawType;
219
+ component ||= defaultComponentByType[rawType] || null;
220
+ }
221
+
222
+ if (component && !type) type = typeByComponent[component] || null;
223
+ if (!type && (node.children || node.properties)) type = 'object';
224
+ if (!component && type) component = defaultComponentByType[type] || null;
225
+ if (!type) type = 'string';
226
+ if (!component) component = defaultComponentByType[type] || type;
227
+
228
+ return { type, component };
229
+ }
230
+
231
+ export function normalizeFormNode(node, context = {}) {
232
+ if (node == null) return null;
233
+ if (typeof node === 'string' || typeof node === 'number') {
234
+ return { type: 'text', text: String(node) };
235
+ }
236
+ if (typeof node !== 'object') return null;
237
+ if (node.component == null && node.text != null) {
238
+ return {
239
+ ...(node.id ? { id: node.id } : {}),
240
+ ...(node.slot ? { slot: node.slot } : {}),
241
+ type: 'text',
242
+ text: String(node.text || ''),
243
+ };
244
+ }
245
+
246
+ const typeDefinition = resolvedTypeDefinition(node.type);
247
+ const mergedNode = typeDefinition
248
+ ? {
249
+ ...typeDefinition,
250
+ ...node,
251
+ props: {
252
+ ...(typeDefinition.props || {}),
253
+ ...(node.props || {}),
254
+ },
255
+ children: node.children ?? node.properties ?? typeDefinition.children,
256
+ items: node.items ?? typeDefinition.items,
257
+ }
258
+ : node;
259
+ const { type, component } = resolveTypeAndComponent(mergedNode, typeDefinition);
260
+ const children = normalizeChildren(mergedNode.children ?? mergedNode.properties);
261
+ const topLevelProps = shorthandProps(mergedNode);
262
+ const inferredName = context.name && topLevelProps.name == null && mergedNode.props?.name == null
263
+ ? { name: context.name }
264
+ : {};
265
+ const props = {
266
+ ...topLevelProps,
267
+ ...inferredName,
268
+ ...(mergedNode.props ? clone(mergedNode.props) : {}),
269
+ };
270
+ const normalized = {
271
+ ...(mergedNode.id ? { id: mergedNode.id } : {}),
272
+ ...(mergedNode.slot ? { slot: mergedNode.slot } : {}),
273
+ type,
274
+ component,
275
+ props,
276
+ };
277
+
278
+ if (children.length) normalized.children = children;
279
+ if (mergedNode.items) normalized.items = normalizeFormNode(mergedNode.items);
280
+ return normalized;
281
+ }
282
+
283
+ export function normalizeFormChildren(children) {
284
+ return normalizeChildren(children).filter(Boolean);
285
+ }
286
+
287
+ function resolveDefaultValue(value, context = {}) {
288
+ return typeof value === 'function' ? value(context.index ?? 0, context) : clone(value);
289
+ }
290
+
291
+ export function formNodeDefaultValue(node, context = {}) {
292
+ const normalized = normalizeFormNode(node);
293
+ if (!normalized) return null;
294
+ const props = normalized.props || {};
295
+ if (props.default !== undefined) return resolveDefaultValue(props.default, context);
296
+ if (props.options?.length) {
297
+ const first = props.options[0];
298
+ return first && typeof first === 'object' && 'value' in first ? clone(first.value) : clone(first);
299
+ }
300
+ if (normalized.type === 'object') {
301
+ return Object.fromEntries(
302
+ (normalized.children || [])
303
+ .map((child) => [child.props?.name, formNodeDefaultValue(child, context)])
304
+ .filter(([name]) => name),
305
+ );
306
+ }
307
+ if (normalized.type === 'array') return [];
308
+ if (normalized.type === 'file') return [];
309
+ if (normalized.type === 'boolean') return false;
310
+ if (normalized.type === 'number' || normalized.type === 'integer') return 0;
311
+ if (normalized.type === 'json') return null;
312
+ if (props.name === 'id' || props.name === 'value') return `item-${(context.index ?? 0) + 1}`;
313
+ if (props.name === 'label' || props.name === 'name') return `Item ${(context.index ?? 0) + 1}`;
314
+ return '';
315
+ }
316
+
317
+ function optionValues(options) {
318
+ if (!Array.isArray(options)) return null;
319
+ return options.map((option) => (
320
+ option && typeof option === 'object' && 'value' in option ? option.value : option
321
+ ));
322
+ }
323
+
324
+ function jsonSchemaTypeFor(node) {
325
+ if (node.type === 'email' || node.type === 'url' || node.type === 'date' || node.type === 'text') return 'string';
326
+ if (node.type === 'decimal' || node.type === 'currency' || node.type === 'money') return 'number';
327
+ if (node.type === 'file') return 'array';
328
+ if (node.type === 'json') return 'object';
329
+ if (isKnownDataType(node.type)) return node.type === 'integer' ? 'integer' : node.type;
330
+ if (node.children?.length) return 'object';
331
+ return 'string';
332
+ }
333
+
334
+ export function formDefinitionToJsonSchema(definition) {
335
+ const node = Array.isArray(definition) || (definition && !definition.type && !definition.component)
336
+ ? normalizeFormNode({ type: 'object', children: definition })
337
+ : normalizeFormNode(definition);
338
+ return formNodeToJsonSchema(node);
339
+ }
340
+
341
+ export function formNodeToJsonSchema(node) {
342
+ const normalized = normalizeFormNode(node);
343
+ if (!normalized) return {};
344
+ const props = normalized.props || {};
345
+ const type = jsonSchemaTypeFor(normalized);
346
+ const schema = { type };
347
+
348
+ if (props.label) schema.title = props.label;
349
+ if (props.description) schema.description = props.description;
350
+ if (props.default !== undefined) schema.default = clone(props.default);
351
+ if (props.placeholder) schema.examples = [props.placeholder];
352
+
353
+ if (normalized.type === 'email') schema.format = 'email';
354
+ if (normalized.type === 'url') schema.format = 'uri';
355
+ if (normalized.type === 'date') schema.format = 'date';
356
+ if (props.format) schema.format = props.format;
357
+
358
+ const enumValues = props.enum || optionValues(props.options);
359
+ if (enumValues?.length) schema.enum = enumValues;
360
+
361
+ for (const [propName, schemaName] of [
362
+ ['minLength', 'minLength'],
363
+ ['maxLength', 'maxLength'],
364
+ ['minimum', 'minimum'],
365
+ ['maximum', 'maximum'],
366
+ ['min', 'minimum'],
367
+ ['max', 'maximum'],
368
+ ['pattern', 'pattern'],
369
+ ]) {
370
+ if (props[propName] !== undefined && schema[schemaName] === undefined) {
371
+ schema[schemaName] = clone(props[propName]);
372
+ }
373
+ }
374
+
375
+ if (type === 'object') {
376
+ const required = [];
377
+ schema.properties = {};
378
+ for (const child of normalized.children || []) {
379
+ const name = child.props?.name;
380
+ if (!name) continue;
381
+ schema.properties[name] = formNodeToJsonSchema(child);
382
+ if (child.props?.required) required.push(name);
383
+ }
384
+ if (required.length) schema.required = required;
385
+ }
386
+
387
+ if (type === 'array') {
388
+ schema.items = normalized.items ? formNodeToJsonSchema(normalized.items) : {};
389
+ }
390
+
391
+ return schema;
392
+ }
@@ -0,0 +1,54 @@
1
+ import { isJsonSchema, jsonSchemaAdapter, jsonSchemaToChildren } from './adapters/jsonSchema.js';
2
+ import { isZodLikeSchema, zodSchemaAdapter, zodSchemaToChildren } from './adapters/zod.js';
3
+ import { normalizeFormChildren, normalizeFormNode } from './formDefinition.js';
4
+
5
+ export {
6
+ isJsonSchema,
7
+ jsonSchemaAdapter,
8
+ jsonSchemaToChildren,
9
+ isZodLikeSchema,
10
+ zodSchemaAdapter,
11
+ zodSchemaToChildren,
12
+ };
13
+
14
+ export const builtInSchemaAdapters = [
15
+ jsonSchemaAdapter,
16
+ zodSchemaAdapter,
17
+ ];
18
+
19
+ function isObject(value) {
20
+ return value && typeof value === 'object';
21
+ }
22
+
23
+ function hasOwn(value, key) {
24
+ return Object.prototype.hasOwnProperty.call(value || {}, key);
25
+ }
26
+
27
+ function isKeyedChildren(value) {
28
+ return isObject(value)
29
+ && !Array.isArray(value)
30
+ && !hasOwn(value, '$schema')
31
+ && !hasOwn(value, 'type')
32
+ && !hasOwn(value, 'component')
33
+ && !hasOwn(value, 'children')
34
+ && !hasOwn(value, 'properties')
35
+ && !hasOwn(value, 'items');
36
+ }
37
+
38
+ export function nativeSchemaToChildren(schema) {
39
+ if (!schema) return [];
40
+ if (Array.isArray(schema) || isKeyedChildren(schema)) return normalizeFormChildren(schema);
41
+ if (!isObject(schema)) return [];
42
+
43
+ const node = normalizeFormNode(schema);
44
+ if (!node) return [];
45
+ if (node.component === 'DomForm' || node.type === 'object') return node.children || [];
46
+ return [node];
47
+ }
48
+
49
+ export function schemaToChildren(schema, options = {}, adapters = builtInSchemaAdapters) {
50
+ if (!schema) return [];
51
+ const adapter = adapters.find((candidate) => candidate?.matches?.(schema, options));
52
+ const children = adapter?.toChildren?.(schema, options) || [];
53
+ return children.length ? children : nativeSchemaToChildren(schema);
54
+ }