@alpaca-editor/core 1.0.3902 → 1.0.3904

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 (98) hide show
  1. package/dist/components/ui/CardConnector.d.ts +2 -1
  2. package/dist/components/ui/CardConnector.js +3 -3
  3. package/dist/components/ui/CardConnector.js.map +1 -1
  4. package/dist/components/ui/button.js +1 -1
  5. package/dist/components/ui/button.js.map +1 -1
  6. package/dist/components/ui/card.d.ts +7 -1
  7. package/dist/components/ui/card.js +71 -3
  8. package/dist/components/ui/card.js.map +1 -1
  9. package/dist/config/config.js +4 -2
  10. package/dist/config/config.js.map +1 -1
  11. package/dist/config/types.d.ts +1 -0
  12. package/dist/editor/MobileLayout.js +1 -1
  13. package/dist/editor/MobileLayout.js.map +1 -1
  14. package/dist/editor/PictureEditor.js +13 -5
  15. package/dist/editor/PictureEditor.js.map +1 -1
  16. package/dist/editor/client/EditorClient.js +2 -2
  17. package/dist/editor/client/EditorClient.js.map +1 -1
  18. package/dist/editor/client/editContext.d.ts +14 -1
  19. package/dist/editor/client/editContext.js.map +1 -1
  20. package/dist/editor/client/operations.js +1 -1
  21. package/dist/editor/client/operations.js.map +1 -1
  22. package/dist/editor/page-editor-chrome/CommentHighlighting.js +4 -1
  23. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  24. package/dist/editor/ui/Splitter.js +3 -1
  25. package/dist/editor/ui/Splitter.js.map +1 -1
  26. package/dist/page-wizard/WizardBoxConnector.d.ts +2 -1
  27. package/dist/page-wizard/WizardBoxConnector.js +3 -3
  28. package/dist/page-wizard/WizardBoxConnector.js.map +1 -1
  29. package/dist/page-wizard/WizardSteps.js +63 -17
  30. package/dist/page-wizard/WizardSteps.js.map +1 -1
  31. package/dist/page-wizard/service.d.ts +1 -1
  32. package/dist/page-wizard/service.js +1 -1
  33. package/dist/page-wizard/service.js.map +1 -1
  34. package/dist/page-wizard/steps/CollectStep.js +11 -17
  35. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  36. package/dist/page-wizard/steps/ComponentTypesSelector.d.ts +1 -0
  37. package/dist/page-wizard/steps/ComponentTypesSelector.js +53 -78
  38. package/dist/page-wizard/steps/ComponentTypesSelector.js.map +1 -1
  39. package/dist/page-wizard/steps/ContentStep.d.ts +2 -0
  40. package/dist/page-wizard/steps/ContentStep.js +403 -0
  41. package/dist/page-wizard/steps/ContentStep.js.map +1 -0
  42. package/dist/page-wizard/steps/Generate.js +1 -1
  43. package/dist/page-wizard/steps/Generate.js.map +1 -1
  44. package/dist/page-wizard/steps/ImagesStep.js +16 -13
  45. package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
  46. package/dist/page-wizard/steps/SelectStep.js +1 -1
  47. package/dist/page-wizard/steps/SelectStep.js.map +1 -1
  48. package/dist/page-wizard/steps/SetupPageStep.d.ts +2 -0
  49. package/dist/page-wizard/steps/SetupPageStep.js +152 -0
  50. package/dist/page-wizard/steps/SetupPageStep.js.map +1 -0
  51. package/dist/page-wizard/steps/usePageCreator.js +4 -4
  52. package/dist/page-wizard/steps/usePageCreator.js.map +1 -1
  53. package/dist/page-wizard/usePageWizard.d.ts +17 -3
  54. package/dist/page-wizard/usePageWizard.js +62 -2
  55. package/dist/page-wizard/usePageWizard.js.map +1 -1
  56. package/dist/revision.d.ts +2 -2
  57. package/dist/revision.js +2 -2
  58. package/dist/splash-screen/NewPage.js +10 -10
  59. package/dist/splash-screen/NewPage.js.map +1 -1
  60. package/dist/splash-screen/SplashScreen.js +3 -3
  61. package/dist/splash-screen/SplashScreen.js.map +1 -1
  62. package/dist/styles.css +184 -68
  63. package/package.json +1 -1
  64. package/src/components/ui/CardConnector.tsx +50 -15
  65. package/src/components/ui/button.tsx +1 -1
  66. package/src/components/ui/card.tsx +331 -15
  67. package/src/config/config.tsx +4 -2
  68. package/src/config/types.ts +3 -0
  69. package/src/editor/MobileLayout.tsx +7 -9
  70. package/src/editor/PictureEditor.tsx +16 -10
  71. package/src/editor/client/EditorClient.tsx +3 -5
  72. package/src/editor/client/editContext.ts +23 -1
  73. package/src/editor/client/operations.ts +1 -1
  74. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +6 -1
  75. package/src/editor/ui/Splitter.tsx +10 -1
  76. package/src/page-wizard/WizardBoxConnector.tsx +50 -15
  77. package/src/page-wizard/WizardSteps.tsx +163 -34
  78. package/src/page-wizard/service.ts +2 -2
  79. package/src/page-wizard/steps/CollectStep.tsx +95 -141
  80. package/src/page-wizard/steps/ComponentTypesSelector.tsx +225 -245
  81. package/src/page-wizard/steps/ContentStep.tsx +648 -0
  82. package/src/page-wizard/steps/Generate.tsx +3 -3
  83. package/src/page-wizard/steps/ImagesStep.tsx +20 -15
  84. package/src/page-wizard/steps/SelectStep.tsx +4 -4
  85. package/src/page-wizard/steps/SetupPageStep.tsx +329 -0
  86. package/src/page-wizard/steps/usePageCreator.ts +4 -4
  87. package/src/page-wizard/usePageWizard.ts +69 -4
  88. package/src/revision.ts +2 -2
  89. package/src/splash-screen/NewPage.tsx +22 -16
  90. package/src/splash-screen/SplashScreen.tsx +3 -1
  91. package/dist/page-wizard/steps/CreatePage.d.ts +0 -12
  92. package/dist/page-wizard/steps/CreatePage.js +0 -149
  93. package/dist/page-wizard/steps/CreatePage.js.map +0 -1
  94. package/dist/page-wizard/steps/CreatePageAndLayoutStep.d.ts +0 -2
  95. package/dist/page-wizard/steps/CreatePageAndLayoutStep.js +0 -235
  96. package/dist/page-wizard/steps/CreatePageAndLayoutStep.js.map +0 -1
  97. package/src/page-wizard/steps/CreatePage.tsx +0 -329
  98. package/src/page-wizard/steps/CreatePageAndLayoutStep.tsx +0 -430
@@ -26,11 +26,8 @@ export function ComponentTypeSelector({
26
26
  const [availableComponentTypes, setAvailableComponentTypes] = useState<
27
27
  string[]
28
28
  >([]);
29
- const [isSettingsPanelCollapsed, setIsSettingsPanelCollapsed] =
30
- useState(true);
31
29
  const [preselectedTypes, setPreselectedTypes] = useState<string[]>([]);
32
30
  const [searchFilter, setSearchFilter] = useState<string>("");
33
- const contentRef = useRef<HTMLDivElement>(null);
34
31
 
35
32
  // Ensure selectedComponentTypes is always an array
36
33
  useEffect(() => {
@@ -175,7 +172,7 @@ export function ComponentTypeSelector({
175
172
  uniqueTypes: string[],
176
173
  stepPreselectedTypes: string[],
177
174
  ) => {
178
- // Initialize selected types
175
+ // Initialize selected types from data (parent component should have already initialized this)
179
176
  if (
180
177
  data.selectedComponentTypes &&
181
178
  Array.isArray(data.selectedComponentTypes)
@@ -184,29 +181,16 @@ export function ComponentTypeSelector({
184
181
  const validSelectedTypes = data.selectedComponentTypes.filter((type) =>
185
182
  uniqueTypes.includes(type),
186
183
  );
187
- setSelectedComponentTypes(
188
- validSelectedTypes.length > 0
189
- ? validSelectedTypes
190
- : stepPreselectedTypes.length > 0
191
- ? stepPreselectedTypes
192
- : uniqueTypes,
193
- );
194
- } else if (stepPreselectedTypes.length > 0) {
195
- // Use preselected types from step
196
- setSelectedComponentTypes(stepPreselectedTypes);
197
- // Update data for persistence
198
- setData((prev: WizardData) => ({
199
- ...prev,
200
- selectedComponentTypes: stepPreselectedTypes,
201
- }));
184
+ setSelectedComponentTypes(validSelectedTypes);
202
185
  } else {
203
- // Default to selecting all component types
204
- setSelectedComponentTypes(uniqueTypes);
205
- // Update data for persistence
206
- setData((prev: WizardData) => ({
207
- ...prev,
208
- selectedComponentTypes: uniqueTypes,
209
- }));
186
+ // Fallback initialization if parent didn't handle it
187
+ const initialSelection =
188
+ stepPreselectedTypes.length > 0 ? stepPreselectedTypes : uniqueTypes;
189
+ setSelectedComponentTypes(initialSelection);
190
+ setData({
191
+ ...data,
192
+ selectedComponentTypes: initialSelection,
193
+ });
210
194
  }
211
195
  };
212
196
 
@@ -254,242 +238,238 @@ export function ComponentTypeSelector({
254
238
  }, [schema, step]);
255
239
 
256
240
  return (
257
- <div className="mt-3 pb-3">
258
- <div className="mb-2 flex items-center justify-between">
259
- <div>
260
- <h3 className="text-sm font-medium">Layout Generation Settings</h3>
261
- <div className="text-xs text-gray-500">
262
- {selectedComponentTypes &&
263
- Array.isArray(selectedComponentTypes) &&
264
- selectedComponentTypes.length > 0 &&
265
- `${selectedComponentTypes.length} component types`}
266
- {(!selectedComponentTypes ||
267
- !Array.isArray(selectedComponentTypes) ||
268
- selectedComponentTypes.length === 0) &&
269
- "Configure what the AI will use to generate the layout"}
270
- </div>
271
- </div>
272
- <button
273
- onClick={() => setIsSettingsPanelCollapsed(!isSettingsPanelCollapsed)}
274
- className="flex h-6 w-6 items-center justify-center rounded bg-gray-200 p-1 text-gray-700 transition-colors duration-200 hover:bg-gray-300"
275
- title={
276
- isSettingsPanelCollapsed ? "Expand settings" : "Collapse settings"
277
- }
278
- aria-expanded={!isSettingsPanelCollapsed}
279
- >
280
- <svg
281
- className={`h-4 w-4 transition-transform duration-300 ease-in-out ${
282
- isSettingsPanelCollapsed ? "rotate-0" : "rotate-180"
283
- }`}
284
- fill="none"
285
- stroke="currentColor"
286
- viewBox="0 0 24 24"
287
- >
288
- <path
289
- strokeLinecap="round"
290
- strokeLinejoin="round"
291
- strokeWidth={2}
292
- d="M19 9l-7 7-7-7"
293
- />
294
- </svg>
295
- </button>
241
+ <div className="space-y-4">
242
+ <div className="mb-2">
243
+ <input
244
+ type="text"
245
+ value={searchFilter}
246
+ onChange={(e) => setSearchFilter(e.target.value)}
247
+ placeholder="Search components..."
248
+ className="focus:ring-theme-secondary w-full rounded border border-gray-300 px-3 py-1 text-sm focus:ring-1 focus:outline-none"
249
+ />
296
250
  </div>
297
251
 
298
- <div
299
- ref={contentRef}
300
- className={`overflow-hidden transition-all duration-300 ease-in-out ${
301
- isSettingsPanelCollapsed
302
- ? "max-h-0 opacity-0"
303
- : "max-h-[1000px] opacity-100"
304
- }`}
305
- style={{
306
- transitionProperty: "max-height, opacity",
307
- }}
308
- >
309
- <div className="pt-2">
310
- <div className="mb-2">
311
- <input
312
- type="text"
313
- value={searchFilter}
314
- onChange={(e) => setSearchFilter(e.target.value)}
315
- placeholder="Search components and placeholders..."
316
- className="w-full rounded border border-gray-300 px-3 py-1 text-sm focus:ring-1 focus:ring-blue-500 focus:outline-none"
317
- />
252
+ <div className="mb-3 flex flex-col gap-3">
253
+ {/* Component Types Section */}
254
+ <div className="min-w-0 flex-1 rounded border border-gray-200 bg-white p-2">
255
+ <div className="mb-2 flex items-center justify-between">
256
+ <h4 className="text-sm font-bold">Component Types</h4>
257
+ <span className="rounded-full bg-gray-200 px-2 py-1 text-xs text-gray-500">
258
+ {selectedComponentTypes && Array.isArray(selectedComponentTypes)
259
+ ? selectedComponentTypes.length
260
+ : 0}{" "}
261
+ of {availableComponentTypes.length} selected
262
+ </span>
318
263
  </div>
319
264
 
320
- <div className="mb-3 flex flex-col gap-3">
321
- {/* Component Types Section */}
322
- <div className="min-w-0 flex-1 rounded border border-gray-200 bg-white p-2">
323
- <div className="mb-2 flex items-center justify-between">
324
- <h4 className="text-sm font-bold">Component Types</h4>
325
- <span className="rounded-full bg-gray-200 px-2 py-1 text-xs text-gray-500">
326
- {selectedComponentTypes &&
327
- Array.isArray(selectedComponentTypes)
328
- ? selectedComponentTypes.length
329
- : 0}{" "}
330
- of {availableComponentTypes.length} selected
331
- </span>
332
- </div>
333
-
334
- {/* Selected component tags */}
335
- <div className="mb-2">
336
- <div className="mb-2 flex flex-wrap gap-1">
337
- {selectedComponentTypes &&
265
+ {/* Selected component tags */}
266
+ {/* <div className="mb-2">
267
+ <div className="mb-2 flex flex-wrap gap-1">
268
+ {selectedComponentTypes &&
269
+ Array.isArray(selectedComponentTypes) &&
270
+ selectedComponentTypes.length > 0 ? (
271
+ selectedComponentTypes.map((type) => (
272
+ <div
273
+ key={type}
274
+ className={`flex items-center rounded-full px-2 py-1 text-xs ${
275
+ preselectedTypes.includes(type)
276
+ ? "bg-green-100 text-green-800"
277
+ : "bg-blue-100 text-blue-800"
278
+ }`}
279
+ >
280
+ {type}
281
+ <button
282
+ onClick={() => toggleComponentType(type)}
283
+ className={`ml-1 ${
284
+ preselectedTypes.includes(type)
285
+ ? "text-green-600 hover:text-green-800"
286
+ : "text-blue-600 hover:text-blue-800"
287
+ }`}
288
+ aria-label={`Remove ${type}`}
289
+ >
290
+ ×
291
+ </button>
292
+ </div>
293
+ ))
294
+ ) : (
295
+ <div className="text-xs text-gray-500 italic">
296
+ No component types selected
297
+ </div>
298
+ )}
299
+ </div>
300
+ </div> */}
301
+
302
+ {/* Component list */}
303
+ <div
304
+ className={`mb-2 max-h-96 space-y-2 overflow-y-auto ${
305
+ filteredComponentTypes.length === 0
306
+ ? "flex items-center justify-center"
307
+ : ""
308
+ }`}
309
+ >
310
+ {filteredComponentTypes.length > 0 ? (
311
+ filteredComponentTypes.map((type) => {
312
+ const isSelected =
313
+ selectedComponentTypes &&
338
314
  Array.isArray(selectedComponentTypes) &&
339
- selectedComponentTypes.length > 0 ? (
340
- selectedComponentTypes.map((type) => (
341
- <div
342
- key={type}
343
- className={`flex items-center rounded-full px-2 py-1 text-xs ${
344
- preselectedTypes.includes(type)
345
- ? "bg-green-100 text-green-800"
346
- : "bg-blue-100 text-blue-800"
347
- }`}
348
- >
349
- {type}
350
- <button
351
- onClick={() => toggleComponentType(type)}
352
- className={`ml-1 ${
353
- preselectedTypes.includes(type)
354
- ? "text-green-600 hover:text-green-800"
355
- : "text-blue-600 hover:text-blue-800"
356
- }`}
357
- aria-label={`Remove ${type}`}
358
- >
359
- ×
360
- </button>
315
+ selectedComponentTypes.includes(type);
316
+ const isPreselected = preselectedTypes.includes(type);
317
+
318
+ // Generate a description for each component type
319
+ const getComponentDescription = (componentType: string) => {
320
+ return `${componentType} component for your page layout`;
321
+ };
322
+
323
+ return (
324
+ <div
325
+ key={type}
326
+ className={`relative cursor-pointer rounded-lg border p-4 transition-all ${
327
+ isSelected
328
+ ? "border-theme-secondary bg-theme-secondary-50"
329
+ : "border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50"
330
+ }`}
331
+ onClick={() => toggleComponentType(type)}
332
+ >
333
+ <div className="flex items-start justify-between">
334
+ <div className="min-w-0 flex-1">
335
+ <div className="mb-1 flex items-center gap-2">
336
+ <div className="flex items-center gap-2">
337
+ <svg
338
+ className="h-5 w-5 text-gray-400"
339
+ fill="none"
340
+ stroke="currentColor"
341
+ viewBox="0 0 24 24"
342
+ >
343
+ <path
344
+ strokeLinecap="round"
345
+ strokeLinejoin="round"
346
+ strokeWidth={2}
347
+ d="M4 6h16M4 10h16M4 14h16M4 18h16"
348
+ />
349
+ </svg>
350
+ <h3 className="font-medium text-gray-900">
351
+ {type}
352
+ </h3>
353
+ {isPreselected && (
354
+ <span
355
+ className="text-theme-secondary text-sm"
356
+ title="Recommended by template"
357
+ >
358
+
359
+ </span>
360
+ )}
361
+ </div>
362
+ </div>
363
+ {/* <p className="text-sm leading-relaxed text-gray-600">
364
+ {getComponentDescription(type)}
365
+ </p> */}
361
366
  </div>
362
- ))
363
- ) : (
364
- <div className="text-xs text-gray-500 italic">
365
- No component types selected
366
- </div>
367
- )}
368
- </div>
369
- </div>
370
-
371
- {/* Component list */}
372
- <div
373
- className={`mb-2 max-h-28 overflow-y-auto rounded border border-gray-200 bg-white p-1 ${
374
- filteredComponentTypes.length === 0
375
- ? "flex items-center justify-center"
376
- : ""
377
- }`}
378
- >
379
- {filteredComponentTypes.length > 0 ? (
380
- <div className="grid grid-cols-3 gap-1">
381
- {filteredComponentTypes.map((type) => {
382
- const isSelected =
383
- selectedComponentTypes &&
384
- Array.isArray(selectedComponentTypes) &&
385
- selectedComponentTypes.includes(type);
386
- const isPreselected = preselectedTypes.includes(type);
387
- return (
388
- <label
389
- key={type}
390
- className={`flex cursor-pointer items-center rounded-md border p-1 text-sm ${
367
+ <div className="ml-4 flex-shrink-0">
368
+ <div
369
+ className={`flex h-6 w-6 items-center justify-center rounded-full border-2 ${
391
370
  isSelected
392
- ? isPreselected
393
- ? "border-green-200 bg-green-50 text-green-700"
394
- : "border-blue-200 bg-blue-50 text-blue-700"
395
- : isPreselected
396
- ? "border-green-200 bg-green-50/30 text-green-700/70 hover:bg-green-50/50"
397
- : "border-transparent hover:bg-gray-100"
371
+ ? "border-theme-secondary bg-theme-secondary"
372
+ : "border-gray-300 bg-white"
398
373
  }`}
399
374
  >
400
- <input
401
- type="checkbox"
402
- checked={isSelected}
403
- onChange={() => toggleComponentType(type)}
404
- className="mr-2"
405
- />
406
- {type}
407
- {isPreselected && (
408
- <span
409
- className="ml-1 text-xs text-green-600"
410
- title="Recommended by template"
375
+ {isSelected && (
376
+ <svg
377
+ className="h-3 w-3 text-white"
378
+ fill="currentColor"
379
+ viewBox="0 0 20 20"
411
380
  >
412
-
413
- </span>
381
+ <path
382
+ fillRule="evenodd"
383
+ d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
384
+ clipRule="evenodd"
385
+ />
386
+ </svg>
414
387
  )}
415
- </label>
416
- );
417
- })}
418
- </div>
419
- ) : (
420
- <div className="p-4 text-sm text-gray-500">
421
- {searchFilter
422
- ? "No matching component types found"
423
- : "No component types available"}
388
+ </div>
389
+ </div>
390
+ </div>
424
391
  </div>
425
- )}
392
+ );
393
+ })
394
+ ) : (
395
+ <div className="p-8 text-center text-gray-500">
396
+ {searchFilter
397
+ ? "No matching component types found"
398
+ : "No component types available"}
426
399
  </div>
400
+ )}
401
+ </div>
427
402
 
428
- {/* Component type action buttons */}
429
- <div className="flex gap-1">
430
- <button
431
- onClick={handleSelectAllComponents}
432
- disabled={
433
- selectedComponentTypes &&
434
- Array.isArray(selectedComponentTypes) &&
435
- selectedComponentTypes.length ===
436
- availableComponentTypes.length
437
- }
438
- className="flex-1 rounded bg-gray-200 px-2 py-1 text-xs text-gray-700 hover:bg-gray-300 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400"
439
- >
440
- Select All
441
- </button>
442
- <button
443
- onClick={handleClearAllComponents}
444
- disabled={
445
- !selectedComponentTypes ||
446
- !Array.isArray(selectedComponentTypes) ||
447
- selectedComponentTypes.length === 0
448
- }
449
- className="flex-1 rounded bg-gray-200 px-2 py-1 text-xs text-gray-700 hover:bg-gray-300 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400"
450
- >
451
- Clear All
452
- </button>
453
- </div>
454
- {/* Generate button and selection summary - always visible */}
455
- <div className="flex w-full gap-2">
456
- {/* Use recommended buttons - only show if not collapsed or if none are selected */}
457
- {!isSettingsPanelCollapsed ||
458
- (selectedComponentTypes &&
459
- Array.isArray(selectedComponentTypes) &&
460
- selectedComponentTypes.length === 0 &&
461
- preselectedTypes.length > 0) || (
462
- <div className="flex gap-1">
463
- {preselectedTypes.length > 0 && (
464
- <button
465
- onClick={() => {
466
- setSelectedComponentTypes(preselectedTypes);
467
- setData({
468
- ...data,
469
- selectedComponentTypes: preselectedTypes,
470
- });
471
- }}
472
- disabled={
473
- selectedComponentTypes &&
474
- Array.isArray(selectedComponentTypes) &&
475
- preselectedTypes.length ===
476
- selectedComponentTypes.length &&
477
- preselectedTypes.every((type) =>
478
- selectedComponentTypes.includes(type),
479
- )
480
- }
481
- className="flex-1 rounded bg-green-500 px-2 py-1 text-xs text-white hover:bg-green-600 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400"
482
- >
483
- Use Recommended Components
484
- </button>
485
- )}
486
- </div>
487
- )}
488
- </div>
489
- </div>
403
+ {/* Component type action buttons */}
404
+ <div className="flex gap-1">
405
+ <button
406
+ onClick={handleSelectAllComponents}
407
+ disabled={
408
+ selectedComponentTypes &&
409
+ Array.isArray(selectedComponentTypes) &&
410
+ selectedComponentTypes.length === availableComponentTypes.length
411
+ }
412
+ className="flex-1 rounded bg-gray-200 px-2 py-1 text-xs text-gray-700 hover:bg-gray-300 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400"
413
+ >
414
+ Select All
415
+ </button>
416
+ <button
417
+ onClick={handleClearAllComponents}
418
+ disabled={
419
+ !selectedComponentTypes ||
420
+ !Array.isArray(selectedComponentTypes) ||
421
+ selectedComponentTypes.length === 0
422
+ }
423
+ className="flex-1 rounded bg-gray-200 px-2 py-1 text-xs text-gray-700 hover:bg-gray-300 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400"
424
+ >
425
+ Clear All
426
+ </button>
490
427
  </div>
428
+
429
+ {/* Use recommended components button */}
430
+ {preselectedTypes.length > 0 && (
431
+ <div className="mt-2">
432
+ <button
433
+ onClick={() => {
434
+ setSelectedComponentTypes(preselectedTypes);
435
+ setData({
436
+ ...data,
437
+ selectedComponentTypes: preselectedTypes,
438
+ });
439
+ }}
440
+ disabled={
441
+ selectedComponentTypes &&
442
+ Array.isArray(selectedComponentTypes) &&
443
+ preselectedTypes.length === selectedComponentTypes.length &&
444
+ preselectedTypes.every((type) =>
445
+ selectedComponentTypes.includes(type),
446
+ )
447
+ }
448
+ className="w-full rounded bg-green-500 px-2 py-1 text-xs text-white hover:bg-green-600 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400"
449
+ >
450
+ Use Recommended Components
451
+ </button>
452
+ </div>
453
+ )}
491
454
  </div>
492
455
  </div>
493
456
  </div>
494
457
  );
495
458
  }
459
+
460
+ // Helper function to get summary for parent card
461
+ export function getComponentTypeSelectorSummary(
462
+ selectedComponentTypes?: string[],
463
+ availableComponentTypes?: string[],
464
+ ): string {
465
+ if (!selectedComponentTypes || !Array.isArray(selectedComponentTypes)) {
466
+ return "Configure what the AI will use to generate the layout";
467
+ }
468
+
469
+ if (selectedComponentTypes.length === 0) {
470
+ return "No component types selected";
471
+ }
472
+
473
+ const total = availableComponentTypes?.length || 0;
474
+ return `${selectedComponentTypes.length} of ${total} component types selected`;
475
+ }