@weirdfingers/baseboards 0.4.1 → 0.5.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.
@@ -1,3 +1,4 @@
1
+ import { useGeneratorSelection } from "@weirdfingers/boards";
1
2
  import { ArtifactPreview } from "./ArtifactPreview";
2
3
 
3
4
  interface Generation {
@@ -19,6 +20,8 @@ export function GenerationGrid({
19
20
  generations,
20
21
  onGenerationClick,
21
22
  }: GenerationGridProps) {
23
+ const { canArtifactBeAdded, addArtifactToSlot } = useGeneratorSelection();
24
+
22
25
  if (generations.length === 0) {
23
26
  return (
24
27
  <div className="flex items-center justify-center py-12 text-gray-500">
@@ -27,19 +30,56 @@ export function GenerationGrid({
27
30
  );
28
31
  }
29
32
 
33
+ const handleDownload = (generation: Generation) => {
34
+ if (generation.storageUrl) {
35
+ window.open(generation.storageUrl, "_blank");
36
+ }
37
+ };
38
+
39
+ const handlePreview = (generation: Generation) => {
40
+ // For now, use the same handler as onClick
41
+ onGenerationClick?.(generation);
42
+ };
43
+
44
+ const handleAddToSlot = (generation: Generation) => {
45
+ const success = addArtifactToSlot({
46
+ id: generation.id,
47
+ artifactType: generation.artifactType,
48
+ storageUrl: generation.storageUrl,
49
+ thumbnailUrl: generation.thumbnailUrl,
50
+ });
51
+
52
+ if (success) {
53
+ // Scroll to the generation input to show the user where the artifact was added
54
+ const generationInput = document.getElementById('generation-input');
55
+ if (generationInput) {
56
+ generationInput.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
57
+ }
58
+ }
59
+ };
60
+
30
61
  return (
31
62
  <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
32
- {generations.map((generation) => (
33
- <ArtifactPreview
34
- key={generation.id}
35
- artifactType={generation.artifactType}
36
- storageUrl={generation.storageUrl}
37
- thumbnailUrl={generation.thumbnailUrl}
38
- status={generation.status}
39
- errorMessage={generation.errorMessage}
40
- onClick={() => onGenerationClick?.(generation)}
41
- />
42
- ))}
63
+ {generations.map((generation) => {
64
+ const canAdd = generation.status === "COMPLETED" && canArtifactBeAdded(generation.artifactType);
65
+
66
+ return (
67
+ <ArtifactPreview
68
+ key={generation.id}
69
+ artifactId={generation.id}
70
+ artifactType={generation.artifactType}
71
+ storageUrl={generation.storageUrl}
72
+ thumbnailUrl={generation.thumbnailUrl}
73
+ status={generation.status}
74
+ errorMessage={generation.errorMessage}
75
+ onClick={() => onGenerationClick?.(generation)}
76
+ onAddToSlot={() => handleAddToSlot(generation)}
77
+ canAddToSlot={canAdd}
78
+ onDownload={() => handleDownload(generation)}
79
+ onPreview={() => handlePreview(generation)}
80
+ />
81
+ );
82
+ })}
43
83
  </div>
44
84
  );
45
85
  }
@@ -4,8 +4,7 @@ import { useState, useMemo, useEffect } from "react";
4
4
  import { Settings, ArrowUp, X } from "lucide-react";
5
5
  import Image from "next/image";
6
6
  import {
7
- ParsedGeneratorSchema,
8
- parseGeneratorSchema,
7
+ useGeneratorSelection,
9
8
  } from "@weirdfingers/boards";
10
9
  import { GeneratorSelector, GeneratorInfo } from "./GeneratorSelector";
11
10
  import { ArtifactInputSlots } from "./ArtifactInputSlots";
@@ -35,44 +34,49 @@ export function GenerationInput({
35
34
  onSubmit,
36
35
  isGenerating = false,
37
36
  }: GenerationInputProps) {
38
- const [selectedGenerator, setSelectedGenerator] =
39
- useState<GeneratorInfo | null>(generators[0] || null);
37
+ const {
38
+ selectedGenerator,
39
+ setSelectedGenerator,
40
+ parsedSchema,
41
+ selectedArtifacts,
42
+ setSelectedArtifacts
43
+ } = useGeneratorSelection();
44
+
40
45
  const [prompt, setPrompt] = useState("");
41
- const [selectedArtifacts, setSelectedArtifacts] = useState<
42
- Map<string, Generation>
43
- >(new Map());
44
46
  const [attachedImage, setAttachedImage] = useState<Generation | null>(null);
45
47
  const [showSettings, setShowSettings] = useState(false);
46
48
  const [settings, setSettings] = useState<Record<string, unknown>>({});
47
49
 
48
- // Parse input schema using the toolkit's schema parser
49
- const parsedSchema = useMemo((): ParsedGeneratorSchema => {
50
- if (!selectedGenerator) {
51
- return { artifactSlots: [], promptField: null, settingsFields: [] };
50
+ // Initialize selected generator if not set
51
+ useEffect(() => {
52
+ if (!selectedGenerator && generators.length > 0) {
53
+ setSelectedGenerator(generators[0]);
52
54
  }
53
- return parseGeneratorSchema(selectedGenerator.inputSchema);
54
- }, [selectedGenerator]);
55
+ }, [generators, selectedGenerator, setSelectedGenerator]);
55
56
 
56
57
  const artifactSlots = useMemo(() => {
58
+ if (!parsedSchema) return [];
57
59
  return parsedSchema.artifactSlots.map((slot) => ({
58
60
  name: slot.fieldName,
59
61
  type: slot.artifactType,
60
62
  required: slot.required,
61
63
  }));
62
- }, [parsedSchema.artifactSlots]);
64
+ }, [parsedSchema]);
63
65
 
64
66
  const needsArtifactInputs = artifactSlots.length > 0;
65
67
 
66
68
  // Initialize settings with defaults when generator changes
67
69
  const defaultSettings = useMemo(() => {
68
70
  const defaults: Record<string, unknown> = {};
69
- parsedSchema.settingsFields.forEach((field) => {
70
- if (field.default !== undefined) {
71
- defaults[field.fieldName] = field.default;
72
- }
73
- });
71
+ if (parsedSchema) {
72
+ parsedSchema.settingsFields.forEach((field) => {
73
+ if (field.default !== undefined) {
74
+ defaults[field.fieldName] = field.default;
75
+ }
76
+ });
77
+ }
74
78
  return defaults;
75
- }, [parsedSchema.settingsFields]);
79
+ }, [parsedSchema]);
76
80
 
77
81
  // Reset settings when generator changes or defaultSettings change
78
82
  useEffect(() => {
@@ -89,9 +93,8 @@ export function GenerationInput({
89
93
  settings,
90
94
  });
91
95
 
92
- // Reset form
96
+ // Reset form (but keep selected artifacts and generator)
93
97
  setPrompt("");
94
- setSelectedArtifacts(new Map());
95
98
  setAttachedImage(null);
96
99
  setSettings(defaultSettings);
97
100
  };
@@ -229,7 +232,7 @@ export function GenerationInput({
229
232
  {/* Settings panel (collapsed by default) */}
230
233
  {showSettings && (
231
234
  <div className="px-4 py-4 border-t border-gray-200 bg-gray-50">
232
- {parsedSchema.settingsFields.length === 0 ? (
235
+ {!parsedSchema || parsedSchema.settingsFields.length === 0 ? (
233
236
  <p className="text-sm text-gray-600">
234
237
  No additional settings available for this generator
235
238
  </p>
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { Zap, Check } from "lucide-react";
4
4
  import type { JSONSchema7 } from "@weirdfingers/boards";
5
+ import { useGeneratorSelection } from "@weirdfingers/boards";
5
6
  import {
6
7
  DropdownMenu,
7
8
  DropdownMenuContent,
@@ -27,11 +28,19 @@ export function GeneratorSelector({
27
28
  selectedGenerator,
28
29
  onSelect,
29
30
  }: GeneratorSelectorProps) {
31
+ const { setSelectedGenerator } = useGeneratorSelection();
32
+
30
33
  const getGeneratorIcon = (name: string) => {
31
34
  // You can customize icons per generator here
32
35
  return <Zap className="w-4 h-4" />;
33
36
  };
34
37
 
38
+ const handleSelect = (generator: GeneratorInfo) => {
39
+ // Update both local state and context
40
+ setSelectedGenerator(generator);
41
+ onSelect(generator);
42
+ };
43
+
35
44
  return (
36
45
  <DropdownMenu>
37
46
  <DropdownMenuTrigger asChild>
@@ -57,7 +66,7 @@ export function GeneratorSelector({
57
66
  {generators.map((generator) => (
58
67
  <DropdownMenuItem
59
68
  key={generator.name}
60
- onClick={() => onSelect(generator)}
69
+ onClick={() => handleSelect(generator)}
61
70
  className="px-4 py-3 flex items-start gap-3 cursor-pointer"
62
71
  >
63
72
  <div className="flex-shrink-0 mt-0.5">