@rslsp1/fa-app-tools 1.2.2 → 1.2.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 CHANGED
@@ -445,6 +445,6 @@ declare function buildLoopInstruction(rounds: Array<{
445
445
  declare function autoLabel(index: number): string;
446
446
  declare function buildReferenceImageMediaIds(images: SelectedLabImage[]): string[];
447
447
 
448
- declare const LIB_VERSION = "1.2.2";
448
+ declare const LIB_VERSION = "1.2.4";
449
449
 
450
450
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type WorkspaceTags, autoLabel, buildBlendInstruction, buildCompareInstruction, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, groupGenerationsToLabItems, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
package/dist/index.d.ts CHANGED
@@ -445,6 +445,6 @@ declare function buildLoopInstruction(rounds: Array<{
445
445
  declare function autoLabel(index: number): string;
446
446
  declare function buildReferenceImageMediaIds(images: SelectedLabImage[]): string[];
447
447
 
448
- declare const LIB_VERSION = "1.2.2";
448
+ declare const LIB_VERSION = "1.2.4";
449
449
 
450
450
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type WorkspaceTags, autoLabel, buildBlendInstruction, buildCompareInstruction, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, groupGenerationsToLabItems, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
package/dist/index.js CHANGED
@@ -738,34 +738,55 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
738
738
  var import_react7 = require("react");
739
739
  var import_react8 = require("motion/react");
740
740
  var import_jsx_runtime7 = require("react/jsx-runtime");
741
+ var PRESET_URLS = [
742
+ "https://jsonplaceholder.typicode.com/todos/1",
743
+ "https://huggingface.co/api/whoami-v2",
744
+ "https://esm.sh/@rslsp1/fa-app-tools@latest"
745
+ ];
741
746
  var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
742
747
  const workspaceInputRef = (0, import_react7.useRef)(null);
743
- const blobTestInputRef = (0, import_react7.useRef)(null);
744
- const [blobStatus, setBlobStatus] = (0, import_react7.useState)("idle");
745
- const [blobThumb, setBlobThumb] = (0, import_react7.useState)(null);
746
- const [blobError, setBlobError] = (0, import_react7.useState)(null);
747
- const handleBlobTest = async (file) => {
748
- setBlobStatus("loading");
749
- setBlobThumb(null);
750
- setBlobError(null);
751
- const blobUrl = URL.createObjectURL(file);
748
+ const [urlInput, setUrlInput] = (0, import_react7.useState)("");
749
+ const [tokenInput, setTokenInput] = (0, import_react7.useState)("");
750
+ const [testStatus, setTestStatus] = (0, import_react7.useState)("idle");
751
+ const [result, setResult] = (0, import_react7.useState)(null);
752
+ const [fetchError, setFetchError] = (0, import_react7.useState)(null);
753
+ const runTest = async (url) => {
754
+ if (!url.trim()) return;
755
+ setTestStatus("loading");
756
+ setResult(null);
757
+ setFetchError(null);
758
+ const t0 = Date.now();
752
759
  try {
753
- const res = await fetch(blobUrl);
754
- if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
755
- const blob = await res.blob();
756
- const dataUrl = await new Promise((resolve, reject) => {
757
- const reader = new FileReader();
758
- reader.onload = () => resolve(reader.result);
759
- reader.onerror = () => reject(new Error("FileReader failed"));
760
- reader.readAsDataURL(blob);
760
+ const reqHeaders = {};
761
+ if (tokenInput.trim()) reqHeaders["Authorization"] = `Bearer ${tokenInput.trim()}`;
762
+ const res = await fetch(url.trim(), { headers: reqHeaders });
763
+ const durationMs = Date.now() - t0;
764
+ const contentType = res.headers.get("content-type") || "";
765
+ const headers = {};
766
+ res.headers.forEach((v, k) => {
767
+ headers[k] = v;
761
768
  });
762
- setBlobThumb(dataUrl);
763
- setBlobStatus("ok");
769
+ const blob = await res.blob();
770
+ const bodySize = blob.size;
771
+ const isImage = contentType.startsWith("image/");
772
+ let bodyPreview = "";
773
+ let imageDataUrl;
774
+ if (isImage) {
775
+ imageDataUrl = await new Promise((resolve, reject) => {
776
+ const reader = new FileReader();
777
+ reader.onload = () => resolve(reader.result);
778
+ reader.onerror = reject;
779
+ reader.readAsDataURL(blob);
780
+ });
781
+ } else {
782
+ const text = await blob.text();
783
+ bodyPreview = text.slice(0, 500);
784
+ }
785
+ setResult({ status: res.status, statusText: res.statusText, contentType, headers, bodyPreview, isImage, imageDataUrl, bodySize, durationMs });
786
+ setTestStatus("ok");
764
787
  } catch (e) {
765
- setBlobError(e.message || String(e));
766
- setBlobStatus("error");
767
- } finally {
768
- URL.revokeObjectURL(blobUrl);
788
+ setFetchError(e.message || String(e));
789
+ setTestStatus("error");
769
790
  }
770
791
  };
771
792
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react8.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10 overflow-y-auto", children: [
@@ -783,42 +804,97 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
783
804
  ] }),
784
805
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3", children: [
785
806
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
786
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "blob: URL Test" }),
787
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Testet ob fetch() auf blob: URLs funktioniert \u2014 lokale Datei \u2192 createObjectURL \u2192 fetch \u2192 base64." })
807
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "URL Fetch Diagnose" }),
808
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Testet ob fetch() auf externe URLs funktioniert." })
788
809
  ] }),
789
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
790
- PillButton,
810
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex flex-col gap-1", children: PRESET_URLS.map((u) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
811
+ "button",
791
812
  {
792
- variant: "outline",
793
- icon: blobStatus === "loading" ? "sync" : "image_search",
794
- onClick: () => blobTestInputRef.current?.click(),
795
- children: "Bild w\xE4hlen & testen"
796
- }
797
- ),
813
+ onClick: () => {
814
+ setUrlInput(u);
815
+ runTest(u);
816
+ },
817
+ className: "text-left px-2 py-1 rounded bg-white/3 border border-white/5 text-[9px] text-white/40 hover:text-white/70 hover:bg-white/6 font-mono truncate transition",
818
+ children: u
819
+ },
820
+ u
821
+ )) }),
798
822
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
799
823
  "input",
800
824
  {
801
- ref: blobTestInputRef,
802
- type: "file",
803
- accept: "image/*",
804
- className: "hidden",
805
- onChange: (e) => {
806
- const f = e.target.files?.[0];
807
- if (f) handleBlobTest(f);
808
- e.target.value = "";
809
- }
825
+ value: tokenInput,
826
+ onChange: (e) => setTokenInput(e.target.value),
827
+ placeholder: "Bearer Token (optional)",
828
+ type: "password",
829
+ className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2 text-[11px] text-white/60 outline-none focus:border-white/20 placeholder:text-white/20 font-mono"
810
830
  }
811
831
  ),
812
- blobStatus === "ok" && blobThumb && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-2", children: [
813
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: blobThumb, alt: "blob test", className: "w-full max-h-48 object-contain rounded-xl border border-white/10 bg-black/40" }),
814
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-[10px] text-green-400 font-mono flex items-center gap-1", children: [
815
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "check_circle" }),
816
- "blob: fetch \u2713 \u2014 base64 erfolgreich"
832
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-2", children: [
833
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
834
+ "input",
835
+ {
836
+ value: urlInput,
837
+ onChange: (e) => setUrlInput(e.target.value),
838
+ onKeyDown: (e) => e.key === "Enter" && runTest(urlInput),
839
+ placeholder: "https://...",
840
+ className: "flex-1 bg-white/5 border border-white/10 rounded-xl px-3 py-2 text-[11px] text-white/80 outline-none focus:border-white/20 placeholder:text-white/20 font-mono"
841
+ }
842
+ ),
843
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
844
+ "button",
845
+ {
846
+ onClick: () => runTest(urlInput),
847
+ disabled: testStatus === "loading" || !urlInput.trim(),
848
+ className: "px-3 py-2 bg-white/10 hover:bg-white/15 border border-white/10 rounded-xl text-white/60 transition disabled:opacity-40 shrink-0",
849
+ children: testStatus === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "material-symbols-outlined text-[16px] animate-spin", children: "sync" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "send" })
850
+ }
851
+ )
852
+ ] }),
853
+ testStatus === "ok" && result && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-2 bg-white/3 border border-white/8 rounded-xl p-3 text-[10px] font-mono", children: [
854
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: `font-bold ${result.status < 400 ? "text-green-400" : "text-red-400"}`, children: [
856
+ result.status,
857
+ " ",
858
+ result.statusText
859
+ ] }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-white/30", children: "\xB7" }),
861
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-white/50", children: [
862
+ result.durationMs,
863
+ "ms"
864
+ ] }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-white/30", children: "\xB7" }),
866
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-white/50", children: [
867
+ (result.bodySize / 1024).toFixed(1),
868
+ " KB"
869
+ ] })
870
+ ] }),
871
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "text-white/40", children: [
872
+ "Content-Type: ",
873
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-white/70", children: result.contentType })
874
+ ] }),
875
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("details", { className: "cursor-pointer", children: [
876
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("summary", { className: "text-white/30 hover:text-white/60 transition", children: [
877
+ "Headers (",
878
+ Object.keys(result.headers).length,
879
+ ")"
880
+ ] }),
881
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-1 space-y-0.5 pl-2 border-l border-white/10", children: Object.entries(result.headers).map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "text-white/30", children: [
882
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-white/50", children: [
883
+ k,
884
+ ":"
885
+ ] }),
886
+ " ",
887
+ v
888
+ ] }, k)) })
889
+ ] }),
890
+ result.isImage && result.imageDataUrl ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: result.imageDataUrl, alt: "response", className: "w-full max-h-40 object-contain rounded-lg border border-white/10 bg-black/40 mt-1" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("pre", { className: "text-white/60 whitespace-pre-wrap break-all bg-black/30 rounded-lg p-2 max-h-40 overflow-y-auto text-[9px]", children: [
891
+ result.bodyPreview,
892
+ result.bodySize > 500 ? "\n\u2026" : ""
817
893
  ] })
818
894
  ] }),
819
- blobStatus === "error" && blobError && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-[10px] text-red-400 font-mono flex items-start gap-1", children: [
820
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "material-symbols-outlined text-[14px] shrink-0", children: "error" }),
821
- blobError
895
+ testStatus === "error" && fetchError && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "bg-red-900/20 border border-red-700/30 rounded-xl p-3 text-[10px] font-mono text-red-400 flex items-start gap-2", children: [
896
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "material-symbols-outlined text-[14px] shrink-0 mt-0.5", children: "error" }),
897
+ fetchError
822
898
  ] })
823
899
  ] }),
824
900
  /* @__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: [
@@ -4180,7 +4256,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4180
4256
  }
4181
4257
 
4182
4258
  // src/index.ts
4183
- var LIB_VERSION = "1.2.2";
4259
+ var LIB_VERSION = "1.2.4";
4184
4260
  // Annotate the CommonJS export names for ESM import in node:
4185
4261
  0 && (module.exports = {
4186
4262
  AvatarArchitectApp,
package/dist/index.mjs CHANGED
@@ -656,34 +656,55 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
656
656
  import { useRef as useRef2, useState as useState2 } from "react";
657
657
  import { motion as motion3 } from "motion/react";
658
658
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
659
+ var PRESET_URLS = [
660
+ "https://jsonplaceholder.typicode.com/todos/1",
661
+ "https://huggingface.co/api/whoami-v2",
662
+ "https://esm.sh/@rslsp1/fa-app-tools@latest"
663
+ ];
659
664
  var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
660
665
  const workspaceInputRef = useRef2(null);
661
- const blobTestInputRef = useRef2(null);
662
- const [blobStatus, setBlobStatus] = useState2("idle");
663
- const [blobThumb, setBlobThumb] = useState2(null);
664
- const [blobError, setBlobError] = useState2(null);
665
- const handleBlobTest = async (file) => {
666
- setBlobStatus("loading");
667
- setBlobThumb(null);
668
- setBlobError(null);
669
- const blobUrl = URL.createObjectURL(file);
666
+ const [urlInput, setUrlInput] = useState2("");
667
+ const [tokenInput, setTokenInput] = useState2("");
668
+ const [testStatus, setTestStatus] = useState2("idle");
669
+ const [result, setResult] = useState2(null);
670
+ const [fetchError, setFetchError] = useState2(null);
671
+ const runTest = async (url) => {
672
+ if (!url.trim()) return;
673
+ setTestStatus("loading");
674
+ setResult(null);
675
+ setFetchError(null);
676
+ const t0 = Date.now();
670
677
  try {
671
- const res = await fetch(blobUrl);
672
- if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
673
- const blob = await res.blob();
674
- const dataUrl = await new Promise((resolve, reject) => {
675
- const reader = new FileReader();
676
- reader.onload = () => resolve(reader.result);
677
- reader.onerror = () => reject(new Error("FileReader failed"));
678
- reader.readAsDataURL(blob);
678
+ const reqHeaders = {};
679
+ if (tokenInput.trim()) reqHeaders["Authorization"] = `Bearer ${tokenInput.trim()}`;
680
+ const res = await fetch(url.trim(), { headers: reqHeaders });
681
+ const durationMs = Date.now() - t0;
682
+ const contentType = res.headers.get("content-type") || "";
683
+ const headers = {};
684
+ res.headers.forEach((v, k) => {
685
+ headers[k] = v;
679
686
  });
680
- setBlobThumb(dataUrl);
681
- setBlobStatus("ok");
687
+ const blob = await res.blob();
688
+ const bodySize = blob.size;
689
+ const isImage = contentType.startsWith("image/");
690
+ let bodyPreview = "";
691
+ let imageDataUrl;
692
+ if (isImage) {
693
+ imageDataUrl = await new Promise((resolve, reject) => {
694
+ const reader = new FileReader();
695
+ reader.onload = () => resolve(reader.result);
696
+ reader.onerror = reject;
697
+ reader.readAsDataURL(blob);
698
+ });
699
+ } else {
700
+ const text = await blob.text();
701
+ bodyPreview = text.slice(0, 500);
702
+ }
703
+ setResult({ status: res.status, statusText: res.statusText, contentType, headers, bodyPreview, isImage, imageDataUrl, bodySize, durationMs });
704
+ setTestStatus("ok");
682
705
  } catch (e) {
683
- setBlobError(e.message || String(e));
684
- setBlobStatus("error");
685
- } finally {
686
- URL.revokeObjectURL(blobUrl);
706
+ setFetchError(e.message || String(e));
707
+ setTestStatus("error");
687
708
  }
688
709
  };
689
710
  return /* @__PURE__ */ jsxs5(motion3.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10 overflow-y-auto", children: [
@@ -701,42 +722,97 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
701
722
  ] }),
702
723
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-3", children: [
703
724
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
704
- /* @__PURE__ */ jsx7(SectionLabel, { children: "blob: URL Test" }),
705
- /* @__PURE__ */ jsx7("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Testet ob fetch() auf blob: URLs funktioniert \u2014 lokale Datei \u2192 createObjectURL \u2192 fetch \u2192 base64." })
725
+ /* @__PURE__ */ jsx7(SectionLabel, { children: "URL Fetch Diagnose" }),
726
+ /* @__PURE__ */ jsx7("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Testet ob fetch() auf externe URLs funktioniert." })
706
727
  ] }),
707
- /* @__PURE__ */ jsx7(
708
- PillButton,
728
+ /* @__PURE__ */ jsx7("div", { className: "flex flex-col gap-1", children: PRESET_URLS.map((u) => /* @__PURE__ */ jsx7(
729
+ "button",
709
730
  {
710
- variant: "outline",
711
- icon: blobStatus === "loading" ? "sync" : "image_search",
712
- onClick: () => blobTestInputRef.current?.click(),
713
- children: "Bild w\xE4hlen & testen"
714
- }
715
- ),
731
+ onClick: () => {
732
+ setUrlInput(u);
733
+ runTest(u);
734
+ },
735
+ className: "text-left px-2 py-1 rounded bg-white/3 border border-white/5 text-[9px] text-white/40 hover:text-white/70 hover:bg-white/6 font-mono truncate transition",
736
+ children: u
737
+ },
738
+ u
739
+ )) }),
716
740
  /* @__PURE__ */ jsx7(
717
741
  "input",
718
742
  {
719
- ref: blobTestInputRef,
720
- type: "file",
721
- accept: "image/*",
722
- className: "hidden",
723
- onChange: (e) => {
724
- const f = e.target.files?.[0];
725
- if (f) handleBlobTest(f);
726
- e.target.value = "";
727
- }
743
+ value: tokenInput,
744
+ onChange: (e) => setTokenInput(e.target.value),
745
+ placeholder: "Bearer Token (optional)",
746
+ type: "password",
747
+ className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2 text-[11px] text-white/60 outline-none focus:border-white/20 placeholder:text-white/20 font-mono"
728
748
  }
729
749
  ),
730
- blobStatus === "ok" && blobThumb && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
731
- /* @__PURE__ */ jsx7("img", { src: blobThumb, alt: "blob test", className: "w-full max-h-48 object-contain rounded-xl border border-white/10 bg-black/40" }),
732
- /* @__PURE__ */ jsxs5("p", { className: "text-[10px] text-green-400 font-mono flex items-center gap-1", children: [
733
- /* @__PURE__ */ jsx7("span", { className: "material-symbols-outlined text-[14px]", children: "check_circle" }),
734
- "blob: fetch \u2713 \u2014 base64 erfolgreich"
750
+ /* @__PURE__ */ jsxs5("div", { className: "flex gap-2", children: [
751
+ /* @__PURE__ */ jsx7(
752
+ "input",
753
+ {
754
+ value: urlInput,
755
+ onChange: (e) => setUrlInput(e.target.value),
756
+ onKeyDown: (e) => e.key === "Enter" && runTest(urlInput),
757
+ placeholder: "https://...",
758
+ className: "flex-1 bg-white/5 border border-white/10 rounded-xl px-3 py-2 text-[11px] text-white/80 outline-none focus:border-white/20 placeholder:text-white/20 font-mono"
759
+ }
760
+ ),
761
+ /* @__PURE__ */ jsx7(
762
+ "button",
763
+ {
764
+ onClick: () => runTest(urlInput),
765
+ disabled: testStatus === "loading" || !urlInput.trim(),
766
+ className: "px-3 py-2 bg-white/10 hover:bg-white/15 border border-white/10 rounded-xl text-white/60 transition disabled:opacity-40 shrink-0",
767
+ children: testStatus === "loading" ? /* @__PURE__ */ jsx7("span", { className: "material-symbols-outlined text-[16px] animate-spin", children: "sync" }) : /* @__PURE__ */ jsx7("span", { className: "material-symbols-outlined text-[16px]", children: "send" })
768
+ }
769
+ )
770
+ ] }),
771
+ testStatus === "ok" && result && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2 bg-white/3 border border-white/8 rounded-xl p-3 text-[10px] font-mono", children: [
772
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
773
+ /* @__PURE__ */ jsxs5("span", { className: `font-bold ${result.status < 400 ? "text-green-400" : "text-red-400"}`, children: [
774
+ result.status,
775
+ " ",
776
+ result.statusText
777
+ ] }),
778
+ /* @__PURE__ */ jsx7("span", { className: "text-white/30", children: "\xB7" }),
779
+ /* @__PURE__ */ jsxs5("span", { className: "text-white/50", children: [
780
+ result.durationMs,
781
+ "ms"
782
+ ] }),
783
+ /* @__PURE__ */ jsx7("span", { className: "text-white/30", children: "\xB7" }),
784
+ /* @__PURE__ */ jsxs5("span", { className: "text-white/50", children: [
785
+ (result.bodySize / 1024).toFixed(1),
786
+ " KB"
787
+ ] })
788
+ ] }),
789
+ /* @__PURE__ */ jsxs5("div", { className: "text-white/40", children: [
790
+ "Content-Type: ",
791
+ /* @__PURE__ */ jsx7("span", { className: "text-white/70", children: result.contentType })
792
+ ] }),
793
+ /* @__PURE__ */ jsxs5("details", { className: "cursor-pointer", children: [
794
+ /* @__PURE__ */ jsxs5("summary", { className: "text-white/30 hover:text-white/60 transition", children: [
795
+ "Headers (",
796
+ Object.keys(result.headers).length,
797
+ ")"
798
+ ] }),
799
+ /* @__PURE__ */ jsx7("div", { className: "mt-1 space-y-0.5 pl-2 border-l border-white/10", children: Object.entries(result.headers).map(([k, v]) => /* @__PURE__ */ jsxs5("div", { className: "text-white/30", children: [
800
+ /* @__PURE__ */ jsxs5("span", { className: "text-white/50", children: [
801
+ k,
802
+ ":"
803
+ ] }),
804
+ " ",
805
+ v
806
+ ] }, k)) })
807
+ ] }),
808
+ result.isImage && result.imageDataUrl ? /* @__PURE__ */ jsx7("img", { src: result.imageDataUrl, alt: "response", className: "w-full max-h-40 object-contain rounded-lg border border-white/10 bg-black/40 mt-1" }) : /* @__PURE__ */ jsxs5("pre", { className: "text-white/60 whitespace-pre-wrap break-all bg-black/30 rounded-lg p-2 max-h-40 overflow-y-auto text-[9px]", children: [
809
+ result.bodyPreview,
810
+ result.bodySize > 500 ? "\n\u2026" : ""
735
811
  ] })
736
812
  ] }),
737
- blobStatus === "error" && blobError && /* @__PURE__ */ jsxs5("p", { className: "text-[10px] text-red-400 font-mono flex items-start gap-1", children: [
738
- /* @__PURE__ */ jsx7("span", { className: "material-symbols-outlined text-[14px] shrink-0", children: "error" }),
739
- blobError
813
+ testStatus === "error" && fetchError && /* @__PURE__ */ jsxs5("div", { className: "bg-red-900/20 border border-red-700/30 rounded-xl p-3 text-[10px] font-mono text-red-400 flex items-start gap-2", children: [
814
+ /* @__PURE__ */ jsx7("span", { className: "material-symbols-outlined text-[14px] shrink-0 mt-0.5", children: "error" }),
815
+ fetchError
740
816
  ] })
741
817
  ] }),
742
818
  /* @__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: [
@@ -4098,7 +4174,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4098
4174
  }
4099
4175
 
4100
4176
  // src/index.ts
4101
- var LIB_VERSION = "1.2.2";
4177
+ var LIB_VERSION = "1.2.4";
4102
4178
  export {
4103
4179
  AvatarArchitectApp,
4104
4180
  CollapsibleCard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "1.2.2",
3
+ "version": "1.2.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",