@rslsp1/fa-app-tools 0.1.2 → 0.1.3

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.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import React, { RefObject } from 'react';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { Node, Edge } from '@xyflow/react';
3
4
 
4
5
  declare function useOnClickOutside(ref: RefObject<HTMLElement | null>, handler: (e: MouseEvent | TouchEvent) => void): void;
5
6
 
@@ -32,4 +33,94 @@ interface CompactDropdownProps {
32
33
  }
33
34
  declare const CompactDropdown: React.FC<CompactDropdownProps>;
34
35
 
35
- export { CompactDropdown, FaToolsBadge, PillButton, SectionLabel, useOnClickOutside };
36
+ interface WorkspaceTags {
37
+ by_category: Record<string, string[]>;
38
+ all: Array<{
39
+ label: string;
40
+ value: string;
41
+ category: string;
42
+ }>;
43
+ }
44
+ interface Generation {
45
+ id: string;
46
+ nodeId: string;
47
+ base64?: string;
48
+ mediaId?: string;
49
+ prompt?: string;
50
+ hierarchy?: string;
51
+ seed?: number;
52
+ model?: string;
53
+ timestamp: number;
54
+ status: 'processing' | 'done' | 'error';
55
+ step?: string;
56
+ tags: string[];
57
+ type?: 'generation' | 'import';
58
+ error?: {
59
+ message: string;
60
+ details?: string;
61
+ };
62
+ selectedForExport?: boolean;
63
+ }
64
+ interface ProjectSettings {
65
+ aspectRatio: string;
66
+ selectedModel: string;
67
+ seed: number;
68
+ seedMode: 'random' | 'fixed';
69
+ }
70
+
71
+ interface HistoryPanelProps {
72
+ history: Generation[];
73
+ currentResultId: string | null;
74
+ onSelect: (gen: Generation) => void;
75
+ onDelete: (id: string) => void;
76
+ }
77
+ declare const HistoryPanel: React.FC<HistoryPanelProps>;
78
+
79
+ interface InspectPanelProps {
80
+ currentResult: Generation | null;
81
+ history: Generation[];
82
+ onSelect: (gen: Generation) => void;
83
+ workspaceTags: WorkspaceTags | null;
84
+ onTagToggle?: (tagName: string) => void;
85
+ }
86
+ declare const InspectPanel: React.FC<InspectPanelProps>;
87
+
88
+ interface SetupPanelProps {
89
+ onWorkspaceImport: (e: React.ChangeEvent<HTMLInputElement>) => void;
90
+ onProjectExport: () => void;
91
+ onProjectImport: (e: React.ChangeEvent<HTMLInputElement>) => void;
92
+ projectActionState: 'idle' | 'working' | 'done' | 'error';
93
+ }
94
+ declare const SetupPanel: React.FC<SetupPanelProps>;
95
+
96
+ interface MediaLibraryProps {
97
+ items: Generation[];
98
+ onImport: () => void;
99
+ onDelete: (id: string) => void;
100
+ onSelect: (item: Generation) => void;
101
+ onToggleSelection: (id: string) => void;
102
+ onBatchDownload: () => void;
103
+ }
104
+ declare const MediaLibrary: React.FC<MediaLibraryProps>;
105
+
106
+ interface ListViewProps {
107
+ nodes: Node[];
108
+ edges: Edge[];
109
+ onNodeChange: (id: string, label: string) => void;
110
+ onAddChild: (parentId: string) => void;
111
+ onDeleteNode: (id: string) => void;
112
+ onMoveNode: (id: string, direction: 'up' | 'down') => void;
113
+ onIndentNode: (id: string) => void;
114
+ onOutdentNode: (id: string) => void;
115
+ onAddSibling: (id: string) => void;
116
+ focusedNodeId: string | null;
117
+ onFocus: (id: string | null) => void;
118
+ activePath: Set<string>;
119
+ onGenerate: (id: string) => void;
120
+ onGenerateBranch: (id: string) => void;
121
+ onGenerateSubtree: (id: string) => void;
122
+ isGeneratingNodeId: (id: string) => boolean;
123
+ }
124
+ declare function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }: ListViewProps): react_jsx_runtime.JSX.Element;
125
+
126
+ export { CompactDropdown, FaToolsBadge, type Generation, HistoryPanel, InspectPanel, ListView, MediaLibrary, PillButton, type ProjectSettings, SectionLabel, SetupPanel, type WorkspaceTags, useOnClickOutside };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import React, { RefObject } from 'react';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { Node, Edge } from '@xyflow/react';
3
4
 
4
5
  declare function useOnClickOutside(ref: RefObject<HTMLElement | null>, handler: (e: MouseEvent | TouchEvent) => void): void;
5
6
 
@@ -32,4 +33,94 @@ interface CompactDropdownProps {
32
33
  }
33
34
  declare const CompactDropdown: React.FC<CompactDropdownProps>;
34
35
 
35
- export { CompactDropdown, FaToolsBadge, PillButton, SectionLabel, useOnClickOutside };
36
+ interface WorkspaceTags {
37
+ by_category: Record<string, string[]>;
38
+ all: Array<{
39
+ label: string;
40
+ value: string;
41
+ category: string;
42
+ }>;
43
+ }
44
+ interface Generation {
45
+ id: string;
46
+ nodeId: string;
47
+ base64?: string;
48
+ mediaId?: string;
49
+ prompt?: string;
50
+ hierarchy?: string;
51
+ seed?: number;
52
+ model?: string;
53
+ timestamp: number;
54
+ status: 'processing' | 'done' | 'error';
55
+ step?: string;
56
+ tags: string[];
57
+ type?: 'generation' | 'import';
58
+ error?: {
59
+ message: string;
60
+ details?: string;
61
+ };
62
+ selectedForExport?: boolean;
63
+ }
64
+ interface ProjectSettings {
65
+ aspectRatio: string;
66
+ selectedModel: string;
67
+ seed: number;
68
+ seedMode: 'random' | 'fixed';
69
+ }
70
+
71
+ interface HistoryPanelProps {
72
+ history: Generation[];
73
+ currentResultId: string | null;
74
+ onSelect: (gen: Generation) => void;
75
+ onDelete: (id: string) => void;
76
+ }
77
+ declare const HistoryPanel: React.FC<HistoryPanelProps>;
78
+
79
+ interface InspectPanelProps {
80
+ currentResult: Generation | null;
81
+ history: Generation[];
82
+ onSelect: (gen: Generation) => void;
83
+ workspaceTags: WorkspaceTags | null;
84
+ onTagToggle?: (tagName: string) => void;
85
+ }
86
+ declare const InspectPanel: React.FC<InspectPanelProps>;
87
+
88
+ interface SetupPanelProps {
89
+ onWorkspaceImport: (e: React.ChangeEvent<HTMLInputElement>) => void;
90
+ onProjectExport: () => void;
91
+ onProjectImport: (e: React.ChangeEvent<HTMLInputElement>) => void;
92
+ projectActionState: 'idle' | 'working' | 'done' | 'error';
93
+ }
94
+ declare const SetupPanel: React.FC<SetupPanelProps>;
95
+
96
+ interface MediaLibraryProps {
97
+ items: Generation[];
98
+ onImport: () => void;
99
+ onDelete: (id: string) => void;
100
+ onSelect: (item: Generation) => void;
101
+ onToggleSelection: (id: string) => void;
102
+ onBatchDownload: () => void;
103
+ }
104
+ declare const MediaLibrary: React.FC<MediaLibraryProps>;
105
+
106
+ interface ListViewProps {
107
+ nodes: Node[];
108
+ edges: Edge[];
109
+ onNodeChange: (id: string, label: string) => void;
110
+ onAddChild: (parentId: string) => void;
111
+ onDeleteNode: (id: string) => void;
112
+ onMoveNode: (id: string, direction: 'up' | 'down') => void;
113
+ onIndentNode: (id: string) => void;
114
+ onOutdentNode: (id: string) => void;
115
+ onAddSibling: (id: string) => void;
116
+ focusedNodeId: string | null;
117
+ onFocus: (id: string | null) => void;
118
+ activePath: Set<string>;
119
+ onGenerate: (id: string) => void;
120
+ onGenerateBranch: (id: string) => void;
121
+ onGenerateSubtree: (id: string) => void;
122
+ isGeneratingNodeId: (id: string) => boolean;
123
+ }
124
+ declare function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }: ListViewProps): react_jsx_runtime.JSX.Element;
125
+
126
+ export { CompactDropdown, FaToolsBadge, type Generation, HistoryPanel, InspectPanel, ListView, MediaLibrary, PillButton, type ProjectSettings, SectionLabel, SetupPanel, type WorkspaceTags, useOnClickOutside };
package/dist/index.js CHANGED
@@ -22,8 +22,13 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CompactDropdown: () => CompactDropdown,
24
24
  FaToolsBadge: () => FaToolsBadge,
25
+ HistoryPanel: () => HistoryPanel,
26
+ InspectPanel: () => InspectPanel,
27
+ ListView: () => ListView,
28
+ MediaLibrary: () => MediaLibrary,
25
29
  PillButton: () => PillButton,
26
30
  SectionLabel: () => SectionLabel,
31
+ SetupPanel: () => SetupPanel,
27
32
  useOnClickOutside: () => useOnClickOutside
28
33
  });
29
34
  module.exports = __toCommonJS(index_exports);
@@ -142,11 +147,309 @@ var CompactDropdown = ({
142
147
  )) }) })
143
148
  ] });
144
149
  };
150
+
151
+ // src/components/HistoryPanel.tsx
152
+ var import_react3 = require("motion/react");
153
+ var import_jsx_runtime5 = require("react/jsx-runtime");
154
+ var formatFriendlyTimestamp = (timestamp) => {
155
+ const date = new Date(timestamp);
156
+ const now = /* @__PURE__ */ new Date();
157
+ const isToday = date.toDateString() === now.toDateString();
158
+ const timeStr = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
159
+ if (isToday) return `Heute, ${timeStr}`;
160
+ const yesterday = /* @__PURE__ */ new Date();
161
+ yesterday.setDate(now.getDate() - 1);
162
+ if (date.toDateString() === yesterday.toDateString()) return `Gestern, ${timeStr}`;
163
+ return `${date.toLocaleDateString([], { day: "2-digit", month: "2-digit" })}, ${timeStr}`;
164
+ };
165
+ var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
166
+ if (history.length === 0) {
167
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
168
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "history" }),
169
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[10px] font-bold uppercase tracking-widest", children: "Keine Historie" })
170
+ ] });
171
+ }
172
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react3.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-4 flex flex-col gap-3 overflow-y-auto dark-scrollbar", children: history.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
173
+ "div",
174
+ {
175
+ className: `flex gap-3 p-2 bg-white/5 border rounded-2xl cursor-pointer group transition-all relative ${currentResultId === gen.id ? "border-white/40 bg-white/10 shadow-lg" : "border-white/5 hover:border-white/20"}`,
176
+ onClick: () => onSelect(gen),
177
+ children: [
178
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: gen.base64?.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}`, className: "w-full h-full object-cover", alt: "Thumbnail" }) }),
179
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
180
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(gen.timestamp) }),
182
+ gen.model && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[7px] font-bold text-white/40 bg-white/5 px-1 rounded uppercase truncate max-w-[60px]", children: gen.model })
183
+ ] }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
185
+ '"',
186
+ gen.prompt,
187
+ '"'
188
+ ] })
189
+ ] }),
190
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: (e) => {
191
+ e.stopPropagation();
192
+ onDelete(gen.id);
193
+ }, className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 hover:text-white rounded-md transition-all", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "delete" }) })
194
+ ]
195
+ },
196
+ gen.id
197
+ )) });
198
+ };
199
+
200
+ // src/components/InspectPanel.tsx
201
+ var import_react4 = require("motion/react");
202
+ var import_jsx_runtime6 = require("react/jsx-runtime");
203
+ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagToggle }) => {
204
+ if (!currentResult) {
205
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
206
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "info" }),
207
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[10px] font-bold uppercase tracking-widest", children: "Kein Asset ausgew\xE4hlt" })
208
+ ] });
209
+ }
210
+ const fullDateStr = new Date(currentResult.timestamp).toLocaleString([], { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" });
211
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react4.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 flex flex-col overflow-hidden", children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-shrink-0 border-b border-white/5 bg-black/20 py-3 px-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex gap-2 overflow-x-auto no-scrollbar pb-1", children: history.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => onSelect(gen), className: `w-10 h-10 rounded-lg overflow-hidden border shrink-0 transition-all ${currentResult.id === gen.id ? "border-white scale-105 shadow-lg" : "border-white/5 opacity-40 hover:opacity-100"}`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: gen.base64?.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}`, className: "w-full h-full object-cover", alt: "Thumbnail" }) }, gen.id)) }) }),
213
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 overflow-y-auto p-4 flex flex-col gap-6 dark-scrollbar pb-10", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-4", children: [
214
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: "Vorschau" }),
215
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "aspect-square w-full rounded-2xl bg-black overflow-hidden border border-white/10 shadow-2xl", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: currentResult.base64?.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`, className: "w-full h-full object-cover", alt: "Preview" }) }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-4 bg-white/5 rounded-2xl border border-white/5", children: [
217
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[8px] font-bold text-white/20 uppercase mb-2 tracking-widest", children: "Prompt Details" }),
218
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-[10px] text-white/70 italic leading-relaxed font-medium", children: [
219
+ '"',
220
+ currentResult.prompt,
221
+ '"'
222
+ ] })
223
+ ] }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
225
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-3 bg-white/5 rounded-xl border border-white/5", children: [
226
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[7px] font-bold text-white/20 uppercase mb-1", children: "Modell" }),
227
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[9px] text-white/80 font-bold", children: currentResult.model || "Standard" })
228
+ ] }),
229
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-3 bg-white/5 rounded-xl border border-white/5", children: [
230
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[7px] font-bold text-white/20 uppercase mb-1", children: "Seed" }),
231
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[9px] text-white/80 font-mono", children: currentResult.seed || "0" })
232
+ ] })
233
+ ] }),
234
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-3 bg-white/5 rounded-xl border border-white/5", children: [
235
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[7px] font-bold text-white/20 uppercase mb-1", children: "Zeitstempel" }),
236
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[9px] text-white/80 font-mono", children: fullDateStr })
237
+ ] }),
238
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-4", children: [
239
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: "Workspace Tags" }),
240
+ Object.entries(workspaceTags.by_category).map(([cat, tags]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-2", children: [
241
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[8px] font-bold text-white/30 uppercase tracking-widest px-1", children: cat }),
242
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-1.5", children: tags.map((t) => {
243
+ const isActive = (currentResult.tags || []).includes(t);
244
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => onTagToggle?.(t), className: `px-2.5 py-1 rounded-lg text-[8px] font-bold uppercase transition-all border ${isActive ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_10px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/20 hover:text-white/50 hover:bg-white/10"}`, children: t }, t);
245
+ }) })
246
+ ] }, cat))
247
+ ] })
248
+ ] }) })
249
+ ] });
250
+ };
251
+
252
+ // src/components/SetupPanel.tsx
253
+ var import_react5 = require("react");
254
+ var import_react6 = require("motion/react");
255
+ var import_jsx_runtime7 = require("react/jsx-runtime");
256
+ var SetupPanel = ({ onWorkspaceImport, onProjectExport, onProjectImport, projectActionState }) => {
257
+ const workspaceInputRef = (0, import_react5.useRef)(null);
258
+ const projectInputRef = (0, import_react5.useRef)(null);
259
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react6.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10", children: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4", children: [
261
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
262
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "Workspace Management" }),
263
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Importiere Kategorien und Tags als JSON-Struktur." })
264
+ ] }),
265
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillButton, { variant: "outline", icon: "upload_file", onClick: () => workspaceInputRef.current?.click(), children: "Tags laden (.json)" }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref: workspaceInputRef, type: "file", accept: ".json", className: "hidden", onChange: onWorkspaceImport })
267
+ ] }),
268
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4", children: [
269
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
270
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "Projekt Management" }),
271
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Speichere den gesamten Baum und alle Assets." })
272
+ ] }),
273
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-2", children: [
274
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillButton, { variant: "filled", icon: "download", onClick: onProjectExport, loading: projectActionState === "working", children: "Exportieren (.zip)" }),
275
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "Projekt laden (.zip)" }),
276
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref: projectInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
277
+ onProjectImport(e);
278
+ if (e.target) e.target.value = "";
279
+ } })
280
+ ] })
281
+ ] }),
282
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mt-auto p-4 bg-white/5 rounded-2xl border border-white/5 flex flex-col gap-2 opacity-50", children: [
283
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[8px] font-bold text-white/20 uppercase tracking-widest", children: "System Info" }),
284
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
285
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Version" }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: "1.2.0" })
287
+ ] }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
289
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Engine" }),
290
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: "Avatar Architect" })
291
+ ] })
292
+ ] })
293
+ ] });
294
+ };
295
+
296
+ // src/components/MediaLibrary.tsx
297
+ var import_react7 = require("motion/react");
298
+ var import_jsx_runtime8 = require("react/jsx-runtime");
299
+ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload }) => {
300
+ const selectedCount = items.filter((i) => i.selectedForExport).length;
301
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col h-full overflow-hidden", children: [
302
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col p-4 border-b border-white/5 gap-3 bg-black/20", children: [
303
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
304
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col", children: [
305
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] font-bold text-white uppercase tracking-widest", children: "Projekt Galerie" }),
306
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[9px] text-white/30 uppercase tracking-tighter", children: [
307
+ items.length,
308
+ " Assets geladen"
309
+ ] })
310
+ ] }),
311
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { onClick: onImport, className: "flex items-center gap-1.5 px-3 py-1.5 bg-blue-500 hover:bg-blue-400 text-white rounded-lg text-[10px] font-bold uppercase transition-all shadow-[0_0_15px_rgba(59,130,246,0.3)]", children: [
312
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "add_photo_alternate" }),
313
+ "Laden"
314
+ ] })
315
+ ] }),
316
+ selectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react7.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, className: "flex items-center justify-between bg-blue-500/10 border border-blue-500/20 p-2 rounded-xl", children: [
317
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[9px] font-bold uppercase text-blue-400 ml-1", children: [
318
+ selectedCount,
319
+ " ausgew\xE4hlt"
320
+ ] }),
321
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { onClick: onBatchDownload, className: "text-[9px] font-bold uppercase bg-blue-500 text-white px-3 py-1 rounded-lg hover:bg-blue-400 transition-all flex items-center gap-1", children: [
322
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "archive" }),
323
+ " ZIP Download"
324
+ ] })
325
+ ] })
326
+ ] }),
327
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1 overflow-y-auto p-4 dark-scrollbar", children: items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
328
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "photo_library" }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col gap-1", children: [
330
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[12px] font-bold uppercase tracking-widest", children: "Keine Medien" }),
331
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[10px] italic", children: "Importiere Assets aus deinem Projekt." })
332
+ ] })
333
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-2 gap-3 pb-10", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react7.motion.div, { layout: true, initial: { opacity: 0, scale: 0.9 }, animate: { opacity: 1, scale: 1 }, className: `relative aspect-square group/item rounded-xl overflow-hidden border transition-all bg-white/5 cursor-pointer shadow-lg ${item.selectedForExport ? "border-blue-500 shadow-[0_0_15px_rgba(59,130,246,0.2)]" : "border-white/10 opacity-70 hover:opacity-100"}`, onClick: () => onSelect(item), children: [
334
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: item.base64?.startsWith("data:") ? item.base64 : `data:image/png;base64,${item.base64}`, className: "w-full h-full object-cover transition-transform duration-500 group-hover/item:scale-110", alt: item.prompt }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: `absolute top-2 left-2 w-5 h-5 rounded-md border flex items-center justify-center transition-all z-20 ${item.selectedForExport ? "bg-blue-500 border-blue-400" : "bg-black/60 border-white/20"}`, onClick: (e) => {
336
+ e.stopPropagation();
337
+ onToggleSelection(item.id);
338
+ }, children: item.selectedForExport && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "material-symbols-outlined text-[14px] text-white", children: "check" }) }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover/item:opacity-100 transition-opacity flex flex-col justify-end p-2 gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
340
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[8px] font-bold text-white/80 truncate max-w-[80px] uppercase tracking-tighter", children: item.prompt || "Importiert" }),
341
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: (e) => {
342
+ e.stopPropagation();
343
+ onDelete(item.id);
344
+ }, className: "w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 rounded-md transition-all hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "delete" }) })
345
+ ] }) }),
346
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute top-1 right-1 px-1.5 py-0.5 bg-black/60 backdrop-blur-md rounded text-[7px] font-bold text-white/60 uppercase tracking-tight", children: item.type === "import" ? "Library" : "Gen" })
347
+ ] }, item.id)) }) })
348
+ ] });
349
+ };
350
+
351
+ // src/components/ListView.tsx
352
+ var import_react8 = require("react");
353
+ var import_jsx_runtime9 = require("react/jsx-runtime");
354
+ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive, isInPath, onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating, isCollapsed, toggleCollapse, renderNode, children }) => {
355
+ const inputRef = (0, import_react8.useRef)(null);
356
+ const hasChildren = children && children.length > 0;
357
+ const handleKeyDown = (e) => {
358
+ if (e.key === "Tab") {
359
+ e.preventDefault();
360
+ if (e.shiftKey) onOutdentNode(node.id);
361
+ else onIndentNode(node.id);
362
+ return;
363
+ }
364
+ if (e.key === "Enter") {
365
+ e.preventDefault();
366
+ if (e.ctrlKey) {
367
+ if (e.shiftKey) onGenerateSubtree(node.id);
368
+ else onGenerateBranch(node.id);
369
+ } else {
370
+ onAddSibling(node.id);
371
+ }
372
+ return;
373
+ }
374
+ if (e.shiftKey && e.key === "ArrowUp") {
375
+ e.preventDefault();
376
+ onMoveNode(node.id, "up");
377
+ }
378
+ if (e.shiftKey && e.key === "ArrowDown") {
379
+ e.preventDefault();
380
+ onMoveNode(node.id, "down");
381
+ }
382
+ };
383
+ (0, import_react8.useEffect)(() => {
384
+ if (isActive && inputRef.current) inputRef.current.focus();
385
+ }, [isActive]);
386
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col w-full", children: [
387
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `flex items-center gap-1 group relative py-0.5 ${depth === 0 ? "mt-6 mb-2" : ""}`, children: [
388
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-4 h-4 flex items-center justify-center shrink-0 z-10", children: hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => toggleCollapse(node.id), className: `material-symbols-outlined text-[14px] transition-colors cursor-pointer ${isInPath ? "text-white" : "text-white/20 hover:text-white"}`, children: isCollapsed ? "chevron_right" : "expand_more" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `rounded-full transition-all ${depth === 0 ? "w-1.5 h-1.5 bg-white/40" : "w-1 h-1 bg-white/10"}` }) }),
389
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-1 flex items-center min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
390
+ "input",
391
+ {
392
+ ref: inputRef,
393
+ value: node.data.label,
394
+ onChange: (e) => onNodeChange(node.id, e.target.value),
395
+ onFocus: () => onFocus(node.id),
396
+ onKeyDown: handleKeyDown,
397
+ className: `bg-transparent outline-none flex-1 py-1 px-1 rounded-md hover:bg-white/5 transition-all truncate ${depth === 0 ? "text-[11px] font-bold uppercase tracking-tight" : "text-[11px]"} ${isActive ? "text-white bg-white/5 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.1)]" : isInPath ? "text-white/80" : "text-white/30"}`,
398
+ placeholder: node.data.placeholder || "..."
399
+ }
400
+ ) }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center opacity-0 group-hover:opacity-100 transition-opacity pr-1 gap-px", children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center bg-white/5 rounded-md px-1 mr-1", children: [
403
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => onMoveNode(node.id, "up"), title: "Shift+Up", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "keyboard_arrow_up" }),
404
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => onMoveNode(node.id, "down"), title: "Shift+Down", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "keyboard_arrow_down" }),
405
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => onOutdentNode(node.id), title: "Shift+Tab", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "format_indent_decrease" }),
406
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => onIndentNode(node.id), title: "Tab", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "format_indent_increase" })
407
+ ] }),
408
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => onGenerate(node.id), disabled: isGenerating, className: `material-symbols-outlined text-[16px] p-1 rounded-md transition-all ${isGenerating ? "animate-spin text-blue-400" : "text-white/20 hover:text-white hover:bg-white/10"}`, children: isGenerating ? "autorenew" : "auto_fix_high" }),
409
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => onAddChild(node.id), className: "material-symbols-outlined text-[16px] text-white/10 hover:text-white p-1 hover:bg-white/10 rounded-md", children: "add" }),
410
+ depth > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => onDeleteNode(node.id), className: "material-symbols-outlined text-[16px] text-white/5 hover:text-red-400 p-1 hover:bg-red-500/10 rounded-md", children: "close" })
411
+ ] })
412
+ ] }),
413
+ !isCollapsed && hasChildren && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-col w-full ml-1.5 pl-3 border-l border-white/5", children: children.map((child) => renderNode(child, depth + 1)) })
414
+ ] });
415
+ };
416
+ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }) {
417
+ const [collapsed, setCollapsed] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
418
+ const toggleCollapse = (id) => {
419
+ setCollapsed((prev) => {
420
+ const next = new Set(prev);
421
+ if (next.has(id)) next.delete(id);
422
+ else next.add(id);
423
+ return next;
424
+ });
425
+ };
426
+ const nodeMap = /* @__PURE__ */ new Map();
427
+ nodes.forEach((n) => nodeMap.set(n.id, { ...n, children: [] }));
428
+ edges.forEach((e) => {
429
+ const parent = nodeMap.get(e.source);
430
+ const child = nodeMap.get(e.target);
431
+ if (parent && child) parent.children.push(child);
432
+ });
433
+ const targetIds = new Set(edges.map((e) => e.target));
434
+ const roots = nodes.filter((n) => !targetIds.has(n.id)).map((n) => nodeMap.get(n.id)).filter(Boolean);
435
+ const renderNode = (node, depth = 0) => {
436
+ if (!node) return null;
437
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ListNode, { node, depth, children: node.children || [], onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive: focusedNodeId === node.id, isInPath: activePath.has(node.id), onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating: isGeneratingNodeId(node.id), isCollapsed: collapsed.has(node.id), toggleCollapse, renderNode }, node.id);
438
+ };
439
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-full h-full overflow-y-auto px-4 py-4 dark-scrollbar scroll-smooth", onClick: (e) => {
440
+ if (e.target === e.currentTarget) onFocus(null);
441
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-full flex flex-col", children: roots.map((root) => renderNode(root, 0)) }) });
442
+ }
145
443
  // Annotate the CommonJS export names for ESM import in node:
146
444
  0 && (module.exports = {
147
445
  CompactDropdown,
148
446
  FaToolsBadge,
447
+ HistoryPanel,
448
+ InspectPanel,
449
+ ListView,
450
+ MediaLibrary,
149
451
  PillButton,
150
452
  SectionLabel,
453
+ SetupPanel,
151
454
  useOnClickOutside
152
455
  });
package/dist/index.mjs CHANGED
@@ -112,10 +112,308 @@ var CompactDropdown = ({
112
112
  )) }) })
113
113
  ] });
114
114
  };
115
+
116
+ // src/components/HistoryPanel.tsx
117
+ import { motion } from "motion/react";
118
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
119
+ var formatFriendlyTimestamp = (timestamp) => {
120
+ const date = new Date(timestamp);
121
+ const now = /* @__PURE__ */ new Date();
122
+ const isToday = date.toDateString() === now.toDateString();
123
+ const timeStr = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
124
+ if (isToday) return `Heute, ${timeStr}`;
125
+ const yesterday = /* @__PURE__ */ new Date();
126
+ yesterday.setDate(now.getDate() - 1);
127
+ if (date.toDateString() === yesterday.toDateString()) return `Gestern, ${timeStr}`;
128
+ return `${date.toLocaleDateString([], { day: "2-digit", month: "2-digit" })}, ${timeStr}`;
129
+ };
130
+ var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
131
+ if (history.length === 0) {
132
+ return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
133
+ /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[64px]", children: "history" }),
134
+ /* @__PURE__ */ jsx5("p", { className: "text-[10px] font-bold uppercase tracking-widest", children: "Keine Historie" })
135
+ ] });
136
+ }
137
+ return /* @__PURE__ */ jsx5(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-4 flex flex-col gap-3 overflow-y-auto dark-scrollbar", children: history.map((gen) => /* @__PURE__ */ jsxs3(
138
+ "div",
139
+ {
140
+ className: `flex gap-3 p-2 bg-white/5 border rounded-2xl cursor-pointer group transition-all relative ${currentResultId === gen.id ? "border-white/40 bg-white/10 shadow-lg" : "border-white/5 hover:border-white/20"}`,
141
+ onClick: () => onSelect(gen),
142
+ children: [
143
+ /* @__PURE__ */ jsx5("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5", children: /* @__PURE__ */ jsx5("img", { src: gen.base64?.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}`, className: "w-full h-full object-cover", alt: "Thumbnail" }) }),
144
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
145
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1", children: [
146
+ /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(gen.timestamp) }),
147
+ gen.model && /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/40 bg-white/5 px-1 rounded uppercase truncate max-w-[60px]", children: gen.model })
148
+ ] }),
149
+ /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
150
+ '"',
151
+ gen.prompt,
152
+ '"'
153
+ ] })
154
+ ] }),
155
+ /* @__PURE__ */ jsx5("button", { onClick: (e) => {
156
+ e.stopPropagation();
157
+ onDelete(gen.id);
158
+ }, className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 hover:text-white rounded-md transition-all", children: /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[14px]", children: "delete" }) })
159
+ ]
160
+ },
161
+ gen.id
162
+ )) });
163
+ };
164
+
165
+ // src/components/InspectPanel.tsx
166
+ import { motion as motion2 } from "motion/react";
167
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
168
+ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagToggle }) => {
169
+ if (!currentResult) {
170
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
171
+ /* @__PURE__ */ jsx6("span", { className: "material-symbols-outlined text-[64px]", children: "info" }),
172
+ /* @__PURE__ */ jsx6("p", { className: "text-[10px] font-bold uppercase tracking-widest", children: "Kein Asset ausgew\xE4hlt" })
173
+ ] });
174
+ }
175
+ const fullDateStr = new Date(currentResult.timestamp).toLocaleString([], { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" });
176
+ return /* @__PURE__ */ jsxs4(motion2.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 flex flex-col overflow-hidden", children: [
177
+ /* @__PURE__ */ jsx6("div", { className: "flex-shrink-0 border-b border-white/5 bg-black/20 py-3 px-4", children: /* @__PURE__ */ jsx6("div", { className: "flex gap-2 overflow-x-auto no-scrollbar pb-1", children: history.map((gen) => /* @__PURE__ */ jsx6("button", { onClick: () => onSelect(gen), className: `w-10 h-10 rounded-lg overflow-hidden border shrink-0 transition-all ${currentResult.id === gen.id ? "border-white scale-105 shadow-lg" : "border-white/5 opacity-40 hover:opacity-100"}`, children: /* @__PURE__ */ jsx6("img", { src: gen.base64?.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}`, className: "w-full h-full object-cover", alt: "Thumbnail" }) }, gen.id)) }) }),
178
+ /* @__PURE__ */ jsx6("div", { className: "flex-1 overflow-y-auto p-4 flex flex-col gap-6 dark-scrollbar pb-10", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4", children: [
179
+ /* @__PURE__ */ jsx6(SectionLabel, { children: "Vorschau" }),
180
+ /* @__PURE__ */ jsx6("div", { className: "aspect-square w-full rounded-2xl bg-black overflow-hidden border border-white/10 shadow-2xl", children: /* @__PURE__ */ jsx6("img", { src: currentResult.base64?.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`, className: "w-full h-full object-cover", alt: "Preview" }) }),
181
+ /* @__PURE__ */ jsxs4("div", { className: "p-4 bg-white/5 rounded-2xl border border-white/5", children: [
182
+ /* @__PURE__ */ jsx6("p", { className: "text-[8px] font-bold text-white/20 uppercase mb-2 tracking-widest", children: "Prompt Details" }),
183
+ /* @__PURE__ */ jsxs4("p", { className: "text-[10px] text-white/70 italic leading-relaxed font-medium", children: [
184
+ '"',
185
+ currentResult.prompt,
186
+ '"'
187
+ ] })
188
+ ] }),
189
+ /* @__PURE__ */ jsxs4("div", { className: "grid grid-cols-2 gap-2", children: [
190
+ /* @__PURE__ */ jsxs4("div", { className: "p-3 bg-white/5 rounded-xl border border-white/5", children: [
191
+ /* @__PURE__ */ jsx6("p", { className: "text-[7px] font-bold text-white/20 uppercase mb-1", children: "Modell" }),
192
+ /* @__PURE__ */ jsx6("p", { className: "text-[9px] text-white/80 font-bold", children: currentResult.model || "Standard" })
193
+ ] }),
194
+ /* @__PURE__ */ jsxs4("div", { className: "p-3 bg-white/5 rounded-xl border border-white/5", children: [
195
+ /* @__PURE__ */ jsx6("p", { className: "text-[7px] font-bold text-white/20 uppercase mb-1", children: "Seed" }),
196
+ /* @__PURE__ */ jsx6("p", { className: "text-[9px] text-white/80 font-mono", children: currentResult.seed || "0" })
197
+ ] })
198
+ ] }),
199
+ /* @__PURE__ */ jsxs4("div", { className: "p-3 bg-white/5 rounded-xl border border-white/5", children: [
200
+ /* @__PURE__ */ jsx6("p", { className: "text-[7px] font-bold text-white/20 uppercase mb-1", children: "Zeitstempel" }),
201
+ /* @__PURE__ */ jsx6("p", { className: "text-[9px] text-white/80 font-mono", children: fullDateStr })
202
+ ] }),
203
+ workspaceTags && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4", children: [
204
+ /* @__PURE__ */ jsx6(SectionLabel, { children: "Workspace Tags" }),
205
+ Object.entries(workspaceTags.by_category).map(([cat, tags]) => /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
206
+ /* @__PURE__ */ jsx6("span", { className: "text-[8px] font-bold text-white/30 uppercase tracking-widest px-1", children: cat }),
207
+ /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap gap-1.5", children: tags.map((t) => {
208
+ const isActive = (currentResult.tags || []).includes(t);
209
+ return /* @__PURE__ */ jsx6("button", { onClick: () => onTagToggle?.(t), className: `px-2.5 py-1 rounded-lg text-[8px] font-bold uppercase transition-all border ${isActive ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_10px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/20 hover:text-white/50 hover:bg-white/10"}`, children: t }, t);
210
+ }) })
211
+ ] }, cat))
212
+ ] })
213
+ ] }) })
214
+ ] });
215
+ };
216
+
217
+ // src/components/SetupPanel.tsx
218
+ import { useRef as useRef2 } from "react";
219
+ import { motion as motion3 } from "motion/react";
220
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
221
+ var SetupPanel = ({ onWorkspaceImport, onProjectExport, onProjectImport, projectActionState }) => {
222
+ const workspaceInputRef = useRef2(null);
223
+ const projectInputRef = useRef2(null);
224
+ return /* @__PURE__ */ jsxs5(motion3.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10", children: [
225
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4", children: [
226
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
227
+ /* @__PURE__ */ jsx7(SectionLabel, { children: "Workspace Management" }),
228
+ /* @__PURE__ */ jsx7("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Importiere Kategorien und Tags als JSON-Struktur." })
229
+ ] }),
230
+ /* @__PURE__ */ jsx7(PillButton, { variant: "outline", icon: "upload_file", onClick: () => workspaceInputRef.current?.click(), children: "Tags laden (.json)" }),
231
+ /* @__PURE__ */ jsx7("input", { ref: workspaceInputRef, type: "file", accept: ".json", className: "hidden", onChange: onWorkspaceImport })
232
+ ] }),
233
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4", children: [
234
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
235
+ /* @__PURE__ */ jsx7(SectionLabel, { children: "Projekt Management" }),
236
+ /* @__PURE__ */ jsx7("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Speichere den gesamten Baum und alle Assets." })
237
+ ] }),
238
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
239
+ /* @__PURE__ */ jsx7(PillButton, { variant: "filled", icon: "download", onClick: onProjectExport, loading: projectActionState === "working", children: "Exportieren (.zip)" }),
240
+ /* @__PURE__ */ jsx7(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "Projekt laden (.zip)" }),
241
+ /* @__PURE__ */ jsx7("input", { ref: projectInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
242
+ onProjectImport(e);
243
+ if (e.target) e.target.value = "";
244
+ } })
245
+ ] })
246
+ ] }),
247
+ /* @__PURE__ */ jsxs5("div", { className: "mt-auto p-4 bg-white/5 rounded-2xl border border-white/5 flex flex-col gap-2 opacity-50", children: [
248
+ /* @__PURE__ */ jsx7("span", { className: "text-[8px] font-bold text-white/20 uppercase tracking-widest", children: "System Info" }),
249
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
250
+ /* @__PURE__ */ jsx7("span", { children: "Version" }),
251
+ /* @__PURE__ */ jsx7("span", { className: "font-mono", children: "1.2.0" })
252
+ ] }),
253
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
254
+ /* @__PURE__ */ jsx7("span", { children: "Engine" }),
255
+ /* @__PURE__ */ jsx7("span", { className: "font-mono", children: "Avatar Architect" })
256
+ ] })
257
+ ] })
258
+ ] });
259
+ };
260
+
261
+ // src/components/MediaLibrary.tsx
262
+ import { motion as motion4 } from "motion/react";
263
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
264
+ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload }) => {
265
+ const selectedCount = items.filter((i) => i.selectedForExport).length;
266
+ return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col h-full overflow-hidden", children: [
267
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-col p-4 border-b border-white/5 gap-3 bg-black/20", children: [
268
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
269
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-col", children: [
270
+ /* @__PURE__ */ jsx8("span", { className: "text-[10px] font-bold text-white uppercase tracking-widest", children: "Projekt Galerie" }),
271
+ /* @__PURE__ */ jsxs6("span", { className: "text-[9px] text-white/30 uppercase tracking-tighter", children: [
272
+ items.length,
273
+ " Assets geladen"
274
+ ] })
275
+ ] }),
276
+ /* @__PURE__ */ jsxs6("button", { onClick: onImport, className: "flex items-center gap-1.5 px-3 py-1.5 bg-blue-500 hover:bg-blue-400 text-white rounded-lg text-[10px] font-bold uppercase transition-all shadow-[0_0_15px_rgba(59,130,246,0.3)]", children: [
277
+ /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[16px]", children: "add_photo_alternate" }),
278
+ "Laden"
279
+ ] })
280
+ ] }),
281
+ selectedCount > 0 && /* @__PURE__ */ jsxs6(motion4.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, className: "flex items-center justify-between bg-blue-500/10 border border-blue-500/20 p-2 rounded-xl", children: [
282
+ /* @__PURE__ */ jsxs6("span", { className: "text-[9px] font-bold uppercase text-blue-400 ml-1", children: [
283
+ selectedCount,
284
+ " ausgew\xE4hlt"
285
+ ] }),
286
+ /* @__PURE__ */ jsxs6("button", { onClick: onBatchDownload, className: "text-[9px] font-bold uppercase bg-blue-500 text-white px-3 py-1 rounded-lg hover:bg-blue-400 transition-all flex items-center gap-1", children: [
287
+ /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px]", children: "archive" }),
288
+ " ZIP Download"
289
+ ] })
290
+ ] })
291
+ ] }),
292
+ /* @__PURE__ */ jsx8("div", { className: "flex-1 overflow-y-auto p-4 dark-scrollbar", children: items.length === 0 ? /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
293
+ /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[64px]", children: "photo_library" }),
294
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", children: [
295
+ /* @__PURE__ */ jsx8("p", { className: "text-[12px] font-bold uppercase tracking-widest", children: "Keine Medien" }),
296
+ /* @__PURE__ */ jsx8("p", { className: "text-[10px] italic", children: "Importiere Assets aus deinem Projekt." })
297
+ ] })
298
+ ] }) : /* @__PURE__ */ jsx8("div", { className: "grid grid-cols-2 gap-3 pb-10", children: items.map((item) => /* @__PURE__ */ jsxs6(motion4.div, { layout: true, initial: { opacity: 0, scale: 0.9 }, animate: { opacity: 1, scale: 1 }, className: `relative aspect-square group/item rounded-xl overflow-hidden border transition-all bg-white/5 cursor-pointer shadow-lg ${item.selectedForExport ? "border-blue-500 shadow-[0_0_15px_rgba(59,130,246,0.2)]" : "border-white/10 opacity-70 hover:opacity-100"}`, onClick: () => onSelect(item), children: [
299
+ /* @__PURE__ */ jsx8("img", { src: item.base64?.startsWith("data:") ? item.base64 : `data:image/png;base64,${item.base64}`, className: "w-full h-full object-cover transition-transform duration-500 group-hover/item:scale-110", alt: item.prompt }),
300
+ /* @__PURE__ */ jsx8("div", { className: `absolute top-2 left-2 w-5 h-5 rounded-md border flex items-center justify-center transition-all z-20 ${item.selectedForExport ? "bg-blue-500 border-blue-400" : "bg-black/60 border-white/20"}`, onClick: (e) => {
301
+ e.stopPropagation();
302
+ onToggleSelection(item.id);
303
+ }, children: item.selectedForExport && /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px] text-white", children: "check" }) }),
304
+ /* @__PURE__ */ jsx8("div", { className: "absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover/item:opacity-100 transition-opacity flex flex-col justify-end p-2 gap-2", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
305
+ /* @__PURE__ */ jsx8("span", { className: "text-[8px] font-bold text-white/80 truncate max-w-[80px] uppercase tracking-tighter", children: item.prompt || "Importiert" }),
306
+ /* @__PURE__ */ jsx8("button", { onClick: (e) => {
307
+ e.stopPropagation();
308
+ onDelete(item.id);
309
+ }, className: "w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 rounded-md transition-all hover:text-white", children: /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px]", children: "delete" }) })
310
+ ] }) }),
311
+ /* @__PURE__ */ jsx8("div", { className: "absolute top-1 right-1 px-1.5 py-0.5 bg-black/60 backdrop-blur-md rounded text-[7px] font-bold text-white/60 uppercase tracking-tight", children: item.type === "import" ? "Library" : "Gen" })
312
+ ] }, item.id)) }) })
313
+ ] });
314
+ };
315
+
316
+ // src/components/ListView.tsx
317
+ import { useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
318
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
319
+ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive, isInPath, onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating, isCollapsed, toggleCollapse, renderNode, children }) => {
320
+ const inputRef = useRef3(null);
321
+ const hasChildren = children && children.length > 0;
322
+ const handleKeyDown = (e) => {
323
+ if (e.key === "Tab") {
324
+ e.preventDefault();
325
+ if (e.shiftKey) onOutdentNode(node.id);
326
+ else onIndentNode(node.id);
327
+ return;
328
+ }
329
+ if (e.key === "Enter") {
330
+ e.preventDefault();
331
+ if (e.ctrlKey) {
332
+ if (e.shiftKey) onGenerateSubtree(node.id);
333
+ else onGenerateBranch(node.id);
334
+ } else {
335
+ onAddSibling(node.id);
336
+ }
337
+ return;
338
+ }
339
+ if (e.shiftKey && e.key === "ArrowUp") {
340
+ e.preventDefault();
341
+ onMoveNode(node.id, "up");
342
+ }
343
+ if (e.shiftKey && e.key === "ArrowDown") {
344
+ e.preventDefault();
345
+ onMoveNode(node.id, "down");
346
+ }
347
+ };
348
+ useEffect2(() => {
349
+ if (isActive && inputRef.current) inputRef.current.focus();
350
+ }, [isActive]);
351
+ return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col w-full", children: [
352
+ /* @__PURE__ */ jsxs7("div", { className: `flex items-center gap-1 group relative py-0.5 ${depth === 0 ? "mt-6 mb-2" : ""}`, children: [
353
+ /* @__PURE__ */ jsx9("div", { className: "w-4 h-4 flex items-center justify-center shrink-0 z-10", children: hasChildren ? /* @__PURE__ */ jsx9("button", { onClick: () => toggleCollapse(node.id), className: `material-symbols-outlined text-[14px] transition-colors cursor-pointer ${isInPath ? "text-white" : "text-white/20 hover:text-white"}`, children: isCollapsed ? "chevron_right" : "expand_more" }) : /* @__PURE__ */ jsx9("div", { className: `rounded-full transition-all ${depth === 0 ? "w-1.5 h-1.5 bg-white/40" : "w-1 h-1 bg-white/10"}` }) }),
354
+ /* @__PURE__ */ jsx9("div", { className: "flex-1 flex items-center min-w-0", children: /* @__PURE__ */ jsx9(
355
+ "input",
356
+ {
357
+ ref: inputRef,
358
+ value: node.data.label,
359
+ onChange: (e) => onNodeChange(node.id, e.target.value),
360
+ onFocus: () => onFocus(node.id),
361
+ onKeyDown: handleKeyDown,
362
+ className: `bg-transparent outline-none flex-1 py-1 px-1 rounded-md hover:bg-white/5 transition-all truncate ${depth === 0 ? "text-[11px] font-bold uppercase tracking-tight" : "text-[11px]"} ${isActive ? "text-white bg-white/5 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.1)]" : isInPath ? "text-white/80" : "text-white/30"}`,
363
+ placeholder: node.data.placeholder || "..."
364
+ }
365
+ ) }),
366
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center opacity-0 group-hover:opacity-100 transition-opacity pr-1 gap-px", children: [
367
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center bg-white/5 rounded-md px-1 mr-1", children: [
368
+ /* @__PURE__ */ jsx9("button", { onClick: () => onMoveNode(node.id, "up"), title: "Shift+Up", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "keyboard_arrow_up" }),
369
+ /* @__PURE__ */ jsx9("button", { onClick: () => onMoveNode(node.id, "down"), title: "Shift+Down", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "keyboard_arrow_down" }),
370
+ /* @__PURE__ */ jsx9("button", { onClick: () => onOutdentNode(node.id), title: "Shift+Tab", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "format_indent_decrease" }),
371
+ /* @__PURE__ */ jsx9("button", { onClick: () => onIndentNode(node.id), title: "Tab", className: "material-symbols-outlined text-[14px] text-white/20 hover:text-white p-0.5", children: "format_indent_increase" })
372
+ ] }),
373
+ /* @__PURE__ */ jsx9("button", { onClick: () => onGenerate(node.id), disabled: isGenerating, className: `material-symbols-outlined text-[16px] p-1 rounded-md transition-all ${isGenerating ? "animate-spin text-blue-400" : "text-white/20 hover:text-white hover:bg-white/10"}`, children: isGenerating ? "autorenew" : "auto_fix_high" }),
374
+ /* @__PURE__ */ jsx9("button", { onClick: () => onAddChild(node.id), className: "material-symbols-outlined text-[16px] text-white/10 hover:text-white p-1 hover:bg-white/10 rounded-md", children: "add" }),
375
+ depth > 0 && /* @__PURE__ */ jsx9("button", { onClick: () => onDeleteNode(node.id), className: "material-symbols-outlined text-[16px] text-white/5 hover:text-red-400 p-1 hover:bg-red-500/10 rounded-md", children: "close" })
376
+ ] })
377
+ ] }),
378
+ !isCollapsed && hasChildren && /* @__PURE__ */ jsx9("div", { className: "flex flex-col w-full ml-1.5 pl-3 border-l border-white/5", children: children.map((child) => renderNode(child, depth + 1)) })
379
+ ] });
380
+ };
381
+ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }) {
382
+ const [collapsed, setCollapsed] = useState2(/* @__PURE__ */ new Set());
383
+ const toggleCollapse = (id) => {
384
+ setCollapsed((prev) => {
385
+ const next = new Set(prev);
386
+ if (next.has(id)) next.delete(id);
387
+ else next.add(id);
388
+ return next;
389
+ });
390
+ };
391
+ const nodeMap = /* @__PURE__ */ new Map();
392
+ nodes.forEach((n) => nodeMap.set(n.id, { ...n, children: [] }));
393
+ edges.forEach((e) => {
394
+ const parent = nodeMap.get(e.source);
395
+ const child = nodeMap.get(e.target);
396
+ if (parent && child) parent.children.push(child);
397
+ });
398
+ const targetIds = new Set(edges.map((e) => e.target));
399
+ const roots = nodes.filter((n) => !targetIds.has(n.id)).map((n) => nodeMap.get(n.id)).filter(Boolean);
400
+ const renderNode = (node, depth = 0) => {
401
+ if (!node) return null;
402
+ return /* @__PURE__ */ jsx9(ListNode, { node, depth, children: node.children || [], onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive: focusedNodeId === node.id, isInPath: activePath.has(node.id), onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating: isGeneratingNodeId(node.id), isCollapsed: collapsed.has(node.id), toggleCollapse, renderNode }, node.id);
403
+ };
404
+ return /* @__PURE__ */ jsx9("div", { className: "w-full h-full overflow-y-auto px-4 py-4 dark-scrollbar scroll-smooth", onClick: (e) => {
405
+ if (e.target === e.currentTarget) onFocus(null);
406
+ }, children: /* @__PURE__ */ jsx9("div", { className: "w-full flex flex-col", children: roots.map((root) => renderNode(root, 0)) }) });
407
+ }
115
408
  export {
116
409
  CompactDropdown,
117
410
  FaToolsBadge,
411
+ HistoryPanel,
412
+ InspectPanel,
413
+ ListView,
414
+ MediaLibrary,
118
415
  PillButton,
119
416
  SectionLabel,
417
+ SetupPanel,
120
418
  useOnClickOutside
121
419
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -12,18 +12,22 @@
12
12
  "require": "./dist/index.js"
13
13
  }
14
14
  },
15
- "files": ["dist"],
15
+ "files": [
16
+ "dist"
17
+ ],
16
18
  "scripts": {
17
- "build": "tsup src/index.ts --format esm,cjs --dts --clean",
19
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean --external react --external @xyflow/react --external motion/react",
18
20
  "prepublishOnly": "npm run build"
19
21
  },
20
22
  "peerDependencies": {
21
23
  "react": ">=18"
22
24
  },
23
25
  "devDependencies": {
26
+ "@types/react": "^18.0.0",
27
+ "@xyflow/react": "^12.10.2",
28
+ "motion": "^12.40.0",
24
29
  "tsup": "^8.0.0",
25
- "typescript": "^5.0.0",
26
- "@types/react": "^18.0.0"
30
+ "typescript": "^5.0.0"
27
31
  },
28
32
  "publishConfig": {
29
33
  "access": "public"