@rslsp1/fa-app-tools 2.0.46 → 2.0.48

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
@@ -641,6 +641,6 @@ declare function findTips(dag: Dag): number[];
641
641
  declare function findForks(dag: Dag): DagFork[];
642
642
  declare function topoSort(events: HFEvent[]): HFEvent[];
643
643
 
644
- declare const LIB_VERSION = "2.0.46";
644
+ declare const LIB_VERSION = "2.0.48";
645
645
 
646
646
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type DagFork, type ExtractedCharacter, FaApp, type FaAppProps, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, type HFEvent, type HFEventVersion, type HFFileInfo$1 as HFFileInfo, type HFMetadataEntry, type HFStateMeta, type HFStateResult, type HFStateSnapshot, HistoryPanel, type ImageAddedPayload, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabFrameExtractor, type LabFrameExtractorProps, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, type MetadataUpdatedPayload, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, ServerTab, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type TagUpsertedPayload, type WorkspaceTags, applyEvent, applyEvents, autoLabel, buildBlendInstruction, buildCompareInstruction, buildDag, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, findForks, findTips, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, getHFToken, getSessionClientId, groupGenerationsToLabItems, hfBatchArchive, hfBootstrapFromLegacy, hfDeleteProject, hfDownloadProject, hfListDir, hfListProjects, hfLoadImageAsBase64, hfUploadImage, hfUploadProjectForm, hfUploadSmallFile, importProjectFromZip, injectXMPMetadata, interpretSdkError, loadHFState, loadPendingEvents, parsePromptFile, parsePromptResponse, setHFToken, topoSort, tsFromEventPath, useHFState, useKeyboardNavigation, useOnClickOutside, writeHFEvent };
package/dist/index.d.ts CHANGED
@@ -641,6 +641,6 @@ declare function findTips(dag: Dag): number[];
641
641
  declare function findForks(dag: Dag): DagFork[];
642
642
  declare function topoSort(events: HFEvent[]): HFEvent[];
643
643
 
644
- declare const LIB_VERSION = "2.0.46";
644
+ declare const LIB_VERSION = "2.0.48";
645
645
 
646
646
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type DagFork, type ExtractedCharacter, FaApp, type FaAppProps, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, type HFEvent, type HFEventVersion, type HFFileInfo$1 as HFFileInfo, type HFMetadataEntry, type HFStateMeta, type HFStateResult, type HFStateSnapshot, HistoryPanel, type ImageAddedPayload, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabFrameExtractor, type LabFrameExtractorProps, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, type MetadataUpdatedPayload, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, ServerTab, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type TagUpsertedPayload, type WorkspaceTags, applyEvent, applyEvents, autoLabel, buildBlendInstruction, buildCompareInstruction, buildDag, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, findForks, findTips, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, getHFToken, getSessionClientId, groupGenerationsToLabItems, hfBatchArchive, hfBootstrapFromLegacy, hfDeleteProject, hfDownloadProject, hfListDir, hfListProjects, hfLoadImageAsBase64, hfUploadImage, hfUploadProjectForm, hfUploadSmallFile, importProjectFromZip, injectXMPMetadata, interpretSdkError, loadHFState, loadPendingEvents, parsePromptFile, parsePromptResponse, setHFToken, topoSort, tsFromEventPath, useHFState, useKeyboardNavigation, useOnClickOutside, writeHFEvent };
package/dist/index.js CHANGED
@@ -1030,29 +1030,6 @@ var CompactDropdown = ({
1030
1030
  // src/components/HistoryPanel.tsx
1031
1031
  var import_react4 = require("react");
1032
1032
  var import_react5 = require("motion/react");
1033
-
1034
- // src/lib/grouping.ts
1035
- function groupByPrompt(items) {
1036
- const map = /* @__PURE__ */ new Map();
1037
- for (const item of items) {
1038
- const key = item.prompt ?? "";
1039
- if (!map.has(key)) map.set(key, []);
1040
- map.get(key).push(item);
1041
- }
1042
- const groups = [];
1043
- for (const [prompt, groupItems] of map) {
1044
- const titled = groupItems.filter((i) => i.titleTs);
1045
- const representative = titled.length > 0 ? titled.reduce((a, b) => a.titleTs > b.titleTs ? a : b) : groupItems.reduce((a, b) => a.timestamp > b.timestamp ? a : b);
1046
- groups.push({ prompt, items: groupItems, representative });
1047
- }
1048
- return groups.sort((a, b) => {
1049
- const aMax = Math.max(...a.items.map((i) => i.timestamp));
1050
- const bMax = Math.max(...b.items.map((i) => i.timestamp));
1051
- return bMax - aMax;
1052
- });
1053
- }
1054
-
1055
- // src/components/HistoryPanel.tsx
1056
1033
  var import_jsx_runtime5 = require("react/jsx-runtime");
1057
1034
  var PAGE_SIZE = 20;
1058
1035
  var formatFriendlyTimestamp = (timestamp) => {
@@ -1068,10 +1045,7 @@ var formatFriendlyTimestamp = (timestamp) => {
1068
1045
  };
1069
1046
  var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
1070
1047
  const [visibleCount, setVisibleCount] = (0, import_react4.useState)(PAGE_SIZE);
1071
- const doneItems = history.filter((g) => g.status === "done");
1072
- const processingItems = history.filter((g) => g.status !== "done");
1073
- const groups = groupByPrompt(doneItems);
1074
- const visibleGroups = groups.slice(0, visibleCount);
1048
+ const visibleHistory = history.slice(0, visibleCount);
1075
1049
  if (history.length === 0) {
1076
1050
  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: [
1077
1051
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "history" }),
@@ -1079,60 +1053,43 @@ var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
1079
1053
  ] });
1080
1054
  }
1081
1055
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react5.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: [
1082
- processingItems.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1056
+ visibleHistory.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1083
1057
  "div",
1084
1058
  {
1085
- className: "flex gap-3 p-2 bg-white/5 border border-white/5 rounded-2xl cursor-pointer group transition-all relative",
1059
+ 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"}`,
1086
1060
  onClick: () => onSelect(gen),
1087
1061
  children: [
1088
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-5 h-5 border-t-2 border-white rounded-full animate-spin" }) }),
1062
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 flex items-center justify-center", children: gen.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-5 h-5 border-t-2 border-white/40 rounded-full animate-spin" }) : gen.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "material-symbols-outlined text-red-400 text-[20px]", children: "warning" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: gen.base64 ? gen.base64.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }) }),
1089
1063
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
1090
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[7px] font-bold text-white/20 uppercase", children: "Generiere..." }),
1091
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-[9px] text-white/40 line-clamp-2 leading-tight mt-1", children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(gen.timestamp) }),
1066
+ 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 })
1067
+ ] }),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
1092
1069
  '"',
1093
1070
  gen.prompt,
1094
1071
  '"'
1095
- ] })
1096
- ] })
1072
+ ] }),
1073
+ gen.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[8px] text-white/30 mt-0.5", children: "Generiert\u2026" }),
1074
+ gen.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[8px] text-red-400/70 mt-0.5 truncate", children: gen.error?.message })
1075
+ ] }),
1076
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1077
+ "button",
1078
+ {
1079
+ onClick: (e) => {
1080
+ e.stopPropagation();
1081
+ onDelete(gen.id);
1082
+ },
1083
+ 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",
1084
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "delete" })
1085
+ }
1086
+ )
1097
1087
  ]
1098
1088
  },
1099
1089
  gen.id
1100
1090
  )),
1101
- visibleGroups.map((group) => {
1102
- const rep = group.representative;
1103
- const isActive = group.items.some((i) => i.id === currentResultId);
1104
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1105
- "div",
1106
- {
1107
- className: `flex gap-3 p-2 bg-white/5 border rounded-2xl cursor-pointer group transition-all relative ${isActive ? "border-white/40 bg-white/10 shadow-lg" : "border-white/5 hover:border-white/20"}`,
1108
- onClick: () => onSelect(rep),
1109
- children: [
1110
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 relative", children: [
1111
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: rep.base64 ? rep.base64.startsWith("data:") ? rep.base64 : `data:image/png;base64,${rep.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }),
1112
- group.items.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute bottom-0.5 right-0.5 px-1 bg-black/80 rounded text-[7px] font-bold text-white/70", children: group.items.length })
1113
- ] }),
1114
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
1115
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
1116
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(rep.timestamp) }),
1117
- rep.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: rep.model })
1118
- ] }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
1120
- '"',
1121
- rep.prompt,
1122
- '"'
1123
- ] })
1124
- ] }),
1125
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: (e) => {
1126
- e.stopPropagation();
1127
- onDelete(rep.id);
1128
- }, 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" }) })
1129
- ]
1130
- },
1131
- rep.id
1132
- );
1133
- }),
1134
- visibleCount < groups.length && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", onClick: () => setVisibleCount((c) => c + PAGE_SIZE), className: "w-full py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-[10px] font-bold uppercase text-white/60 hover:text-white transition-all", children: [
1135
- groups.length - visibleCount,
1091
+ visibleCount < history.length && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", onClick: () => setVisibleCount((c) => c + PAGE_SIZE), className: "w-full py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-[10px] font-bold uppercase text-white/60 hover:text-white transition-all", children: [
1092
+ history.length - visibleCount,
1136
1093
  " weitere laden"
1137
1094
  ] })
1138
1095
  ] });
@@ -1383,6 +1340,29 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
1383
1340
  // src/components/MediaLibrary.tsx
1384
1341
  var import_react10 = require("react");
1385
1342
  var import_react11 = require("motion/react");
1343
+
1344
+ // src/lib/grouping.ts
1345
+ function groupByPrompt(items) {
1346
+ const map = /* @__PURE__ */ new Map();
1347
+ for (const item of items) {
1348
+ const key = item.prompt ?? "";
1349
+ if (!map.has(key)) map.set(key, []);
1350
+ map.get(key).push(item);
1351
+ }
1352
+ const groups = [];
1353
+ for (const [prompt, groupItems] of map) {
1354
+ const titled = groupItems.filter((i) => i.titleTs);
1355
+ const representative = titled.length > 0 ? titled.reduce((a, b) => a.titleTs > b.titleTs ? a : b) : groupItems.reduce((a, b) => a.timestamp > b.timestamp ? a : b);
1356
+ groups.push({ prompt, items: groupItems, representative });
1357
+ }
1358
+ return groups.sort((a, b) => {
1359
+ const aMax = Math.max(...a.items.map((i) => i.timestamp));
1360
+ const bMax = Math.max(...b.items.map((i) => i.timestamp));
1361
+ return bMax - aMax;
1362
+ });
1363
+ }
1364
+
1365
+ // src/components/MediaLibrary.tsx
1386
1366
  var import_jsx_runtime8 = require("react/jsx-runtime");
1387
1367
  var PAGE_SIZE2 = 20;
1388
1368
  var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload, onGenerateReference }) => {
@@ -4978,7 +4958,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4978
4958
  try {
4979
4959
  const stored = localStorage.getItem("aa-hf-namespace");
4980
4960
  if (stored !== null) return stored;
4981
- return allowDevNamespace ? "app.art-by-rolands.de/" : "";
4961
+ return "app.art-by-rolands.de/";
4982
4962
  } catch {
4983
4963
  return "";
4984
4964
  }
@@ -6729,7 +6709,7 @@ function FaApp({
6729
6709
  // src/index.ts
6730
6710
  init_hfStateService();
6731
6711
  init_hfStateService();
6732
- var LIB_VERSION = "2.0.46";
6712
+ var LIB_VERSION = "2.0.48";
6733
6713
  // Annotate the CommonJS export names for ESM import in node:
6734
6714
  0 && (module.exports = {
6735
6715
  AvatarArchitectApp,
package/dist/index.mjs CHANGED
@@ -355,29 +355,6 @@ var CompactDropdown = ({
355
355
  // src/components/HistoryPanel.tsx
356
356
  import { useState as useState2 } from "react";
357
357
  import { motion } from "motion/react";
358
-
359
- // src/lib/grouping.ts
360
- function groupByPrompt(items) {
361
- const map = /* @__PURE__ */ new Map();
362
- for (const item of items) {
363
- const key = item.prompt ?? "";
364
- if (!map.has(key)) map.set(key, []);
365
- map.get(key).push(item);
366
- }
367
- const groups = [];
368
- for (const [prompt, groupItems] of map) {
369
- const titled = groupItems.filter((i) => i.titleTs);
370
- const representative = titled.length > 0 ? titled.reduce((a, b) => a.titleTs > b.titleTs ? a : b) : groupItems.reduce((a, b) => a.timestamp > b.timestamp ? a : b);
371
- groups.push({ prompt, items: groupItems, representative });
372
- }
373
- return groups.sort((a, b) => {
374
- const aMax = Math.max(...a.items.map((i) => i.timestamp));
375
- const bMax = Math.max(...b.items.map((i) => i.timestamp));
376
- return bMax - aMax;
377
- });
378
- }
379
-
380
- // src/components/HistoryPanel.tsx
381
358
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
382
359
  var PAGE_SIZE = 20;
383
360
  var formatFriendlyTimestamp = (timestamp) => {
@@ -393,10 +370,7 @@ var formatFriendlyTimestamp = (timestamp) => {
393
370
  };
394
371
  var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
395
372
  const [visibleCount, setVisibleCount] = useState2(PAGE_SIZE);
396
- const doneItems = history.filter((g) => g.status === "done");
397
- const processingItems = history.filter((g) => g.status !== "done");
398
- const groups = groupByPrompt(doneItems);
399
- const visibleGroups = groups.slice(0, visibleCount);
373
+ const visibleHistory = history.slice(0, visibleCount);
400
374
  if (history.length === 0) {
401
375
  return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
402
376
  /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[64px]", children: "history" }),
@@ -404,60 +378,43 @@ var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
404
378
  ] });
405
379
  }
406
380
  return /* @__PURE__ */ jsxs3(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: [
407
- processingItems.map((gen) => /* @__PURE__ */ jsxs3(
381
+ visibleHistory.map((gen) => /* @__PURE__ */ jsxs3(
408
382
  "div",
409
383
  {
410
- className: "flex gap-3 p-2 bg-white/5 border border-white/5 rounded-2xl cursor-pointer group transition-all relative",
384
+ 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"}`,
411
385
  onClick: () => onSelect(gen),
412
386
  children: [
413
- /* @__PURE__ */ jsx5("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 flex items-center justify-center", children: /* @__PURE__ */ jsx5("div", { className: "w-5 h-5 border-t-2 border-white rounded-full animate-spin" }) }),
387
+ /* @__PURE__ */ jsx5("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 flex items-center justify-center", children: gen.status === "processing" ? /* @__PURE__ */ jsx5("div", { className: "w-5 h-5 border-t-2 border-white/40 rounded-full animate-spin" }) : gen.status === "error" ? /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-red-400 text-[20px]", children: "warning" }) : /* @__PURE__ */ jsx5("img", { src: gen.base64 ? gen.base64.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }) }),
414
388
  /* @__PURE__ */ jsxs3("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
415
- /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase", children: "Generiere..." }),
416
- /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/40 line-clamp-2 leading-tight mt-1", children: [
389
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1", children: [
390
+ /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(gen.timestamp) }),
391
+ 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 })
392
+ ] }),
393
+ /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
417
394
  '"',
418
395
  gen.prompt,
419
396
  '"'
420
- ] })
421
- ] })
397
+ ] }),
398
+ gen.status === "processing" && /* @__PURE__ */ jsx5("p", { className: "text-[8px] text-white/30 mt-0.5", children: "Generiert\u2026" }),
399
+ gen.status === "error" && /* @__PURE__ */ jsx5("p", { className: "text-[8px] text-red-400/70 mt-0.5 truncate", children: gen.error?.message })
400
+ ] }),
401
+ /* @__PURE__ */ jsx5(
402
+ "button",
403
+ {
404
+ onClick: (e) => {
405
+ e.stopPropagation();
406
+ onDelete(gen.id);
407
+ },
408
+ 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",
409
+ children: /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[14px]", children: "delete" })
410
+ }
411
+ )
422
412
  ]
423
413
  },
424
414
  gen.id
425
415
  )),
426
- visibleGroups.map((group) => {
427
- const rep = group.representative;
428
- const isActive = group.items.some((i) => i.id === currentResultId);
429
- return /* @__PURE__ */ jsxs3(
430
- "div",
431
- {
432
- className: `flex gap-3 p-2 bg-white/5 border rounded-2xl cursor-pointer group transition-all relative ${isActive ? "border-white/40 bg-white/10 shadow-lg" : "border-white/5 hover:border-white/20"}`,
433
- onClick: () => onSelect(rep),
434
- children: [
435
- /* @__PURE__ */ jsxs3("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 relative", children: [
436
- /* @__PURE__ */ jsx5("img", { src: rep.base64 ? rep.base64.startsWith("data:") ? rep.base64 : `data:image/png;base64,${rep.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }),
437
- group.items.length > 1 && /* @__PURE__ */ jsx5("div", { className: "absolute bottom-0.5 right-0.5 px-1 bg-black/80 rounded text-[7px] font-bold text-white/70", children: group.items.length })
438
- ] }),
439
- /* @__PURE__ */ jsxs3("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
440
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1", children: [
441
- /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(rep.timestamp) }),
442
- rep.model && /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/40 bg-white/5 px-1 rounded uppercase truncate max-w-[60px]", children: rep.model })
443
- ] }),
444
- /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
445
- '"',
446
- rep.prompt,
447
- '"'
448
- ] })
449
- ] }),
450
- /* @__PURE__ */ jsx5("button", { onClick: (e) => {
451
- e.stopPropagation();
452
- onDelete(rep.id);
453
- }, 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" }) })
454
- ]
455
- },
456
- rep.id
457
- );
458
- }),
459
- visibleCount < groups.length && /* @__PURE__ */ jsxs3("button", { type: "button", onClick: () => setVisibleCount((c) => c + PAGE_SIZE), className: "w-full py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-[10px] font-bold uppercase text-white/60 hover:text-white transition-all", children: [
460
- groups.length - visibleCount,
416
+ visibleCount < history.length && /* @__PURE__ */ jsxs3("button", { type: "button", onClick: () => setVisibleCount((c) => c + PAGE_SIZE), className: "w-full py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-[10px] font-bold uppercase text-white/60 hover:text-white transition-all", children: [
417
+ history.length - visibleCount,
461
418
  " weitere laden"
462
419
  ] })
463
420
  ] });
@@ -708,6 +665,29 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
708
665
  // src/components/MediaLibrary.tsx
709
666
  import { useState as useState4 } from "react";
710
667
  import { motion as motion4 } from "motion/react";
668
+
669
+ // src/lib/grouping.ts
670
+ function groupByPrompt(items) {
671
+ const map = /* @__PURE__ */ new Map();
672
+ for (const item of items) {
673
+ const key = item.prompt ?? "";
674
+ if (!map.has(key)) map.set(key, []);
675
+ map.get(key).push(item);
676
+ }
677
+ const groups = [];
678
+ for (const [prompt, groupItems] of map) {
679
+ const titled = groupItems.filter((i) => i.titleTs);
680
+ const representative = titled.length > 0 ? titled.reduce((a, b) => a.titleTs > b.titleTs ? a : b) : groupItems.reduce((a, b) => a.timestamp > b.timestamp ? a : b);
681
+ groups.push({ prompt, items: groupItems, representative });
682
+ }
683
+ return groups.sort((a, b) => {
684
+ const aMax = Math.max(...a.items.map((i) => i.timestamp));
685
+ const bMax = Math.max(...b.items.map((i) => i.timestamp));
686
+ return bMax - aMax;
687
+ });
688
+ }
689
+
690
+ // src/components/MediaLibrary.tsx
711
691
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
712
692
  var PAGE_SIZE2 = 20;
713
693
  var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload, onGenerateReference }) => {
@@ -4294,7 +4274,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4294
4274
  try {
4295
4275
  const stored = localStorage.getItem("aa-hf-namespace");
4296
4276
  if (stored !== null) return stored;
4297
- return allowDevNamespace ? "app.art-by-rolands.de/" : "";
4277
+ return "app.art-by-rolands.de/";
4298
4278
  } catch {
4299
4279
  return "";
4300
4280
  }
@@ -6043,7 +6023,7 @@ function FaApp({
6043
6023
  }
6044
6024
 
6045
6025
  // src/index.ts
6046
- var LIB_VERSION = "2.0.46";
6026
+ var LIB_VERSION = "2.0.48";
6047
6027
  export {
6048
6028
  AvatarArchitectApp,
6049
6029
  CollapsibleCard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "2.0.46",
3
+ "version": "2.0.48",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",