@rslsp1/fa-app-tools 0.1.2 → 0.1.4
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 +92 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +303 -0
- package/dist/index.mjs +298 -0
- package/package.json +9 -5
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
|
-
|
|
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
|
-
|
|
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, { 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, { 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.
|
|
3
|
+
"version": "0.1.4",
|
|
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": [
|
|
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"
|