@rslsp1/fa-app-tools 10.3.3 → 10.3.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.mjs CHANGED
@@ -22,7 +22,7 @@ import "./chunk-FUY2OCVC.mjs";
22
22
  import "./chunk-GKLCL2VQ.mjs";
23
23
 
24
24
  // package.json
25
- var version = "10.3.3";
25
+ var version = "10.3.4";
26
26
 
27
27
  // src/hooks/useOnClickOutside.ts
28
28
  import { useEffect } from "react";
@@ -5465,7 +5465,7 @@ import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs26 } from "react/jsx-r
5465
5465
  function FlowWorkspacePicker({
5466
5466
  tagBase,
5467
5467
  selectedUserId,
5468
- message,
5468
+ message: message2,
5469
5469
  onSelectUser,
5470
5470
  onSelectContext
5471
5471
  }) {
@@ -5489,7 +5489,7 @@ function FlowWorkspacePicker({
5489
5489
  /* @__PURE__ */ jsx28("strong", { children: "FineArt Flow" }),
5490
5490
  /* @__PURE__ */ jsx28("small", { children: step === "user" ? "Benutzer w\xE4hlen" : `Kontext f\xFCr ${selectedUser?.username ?? `User ${selectedUserId}`}` })
5491
5491
  ] }),
5492
- message && /* @__PURE__ */ jsx28("p", { className: "flow-workspace-picker__warning", children: message }),
5492
+ message2 && /* @__PURE__ */ jsx28("p", { className: "flow-workspace-picker__warning", children: message2 }),
5493
5493
  step === "user" ? /* @__PURE__ */ jsxs26("div", { className: "flow-workspace-picker__choices", "aria-label": "Benutzer w\xE4hlen", children: [
5494
5494
  tagBase.users.map((user) => /* @__PURE__ */ jsxs26("button", { type: "button", onClick: () => chooseUser(user.id), children: [
5495
5495
  /* @__PURE__ */ jsx28("span", { children: user.username }),
@@ -5606,7 +5606,8 @@ function createFlowBridgeClient({
5606
5606
  },
5607
5607
  async request(request) {
5608
5608
  const started = clock();
5609
- const reply = await exchange({ op: "request", target: request.target, method: request.method, path: request.path }, requestTimeoutMs);
5609
+ const tokenSuffix = "tokenSuffix" in request && request.tokenSuffix !== void 0 ? { tokenSuffix: request.tokenSuffix } : {};
5610
+ const reply = await exchange({ op: "request", target: request.target, method: request.method, path: request.path, ...tokenSuffix }, requestTimeoutMs);
5610
5611
  const roundTripMs = Math.round(clock() - started);
5611
5612
  if (!reply) {
5612
5613
  return { ok: false, transport: null, status: null, data: null, latencyMs: null, roundTripMs, error: noBridge(requestTimeoutMs) };
@@ -5625,13 +5626,130 @@ function createFlowBridgeClient({
5625
5626
  };
5626
5627
  }
5627
5628
 
5629
+ // src/lib/flowProbeLoader.ts
5630
+ var FLOW_PROBE_REQUEST = { target: "devMcp", method: "GET", path: "/flow-app/probe-bundle" };
5631
+ var HF_TOKEN_SUFFIX_CHARS = 8;
5632
+ var FLOW_PROBE_MARKER = "__faFlowProbe";
5633
+ var FLOW_PROBE_METHOD = "script-blob";
5634
+ var FLOW_PROBE_LOAD_TIMEOUT_MS = 5e3;
5635
+ var CSP_SETTLE_MS = 100;
5636
+ function isRecord2(value) {
5637
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5638
+ }
5639
+ function hfTokenSuffix(token) {
5640
+ const trimmed = (token ?? "").trim();
5641
+ return trimmed.length >= HF_TOKEN_SUFFIX_CHARS ? trimmed.slice(-HF_TOKEN_SUFFIX_CHARS) : null;
5642
+ }
5643
+ function readFlowProbeBundle(data) {
5644
+ if (!isRecord2(data)) return null;
5645
+ const { version: version2, sha256, source } = data;
5646
+ if (typeof version2 !== "string" || !/^probe-\d{3}$/.test(version2)) return null;
5647
+ if (typeof sha256 !== "string" || !/^[0-9a-f]{64}$/.test(sha256)) return null;
5648
+ if (typeof source !== "string" || source.length === 0) return null;
5649
+ return { version: version2, sha256, source };
5650
+ }
5651
+ function readMarker(value) {
5652
+ if (!isRecord2(value) || value.executed !== true || typeof value.version !== "string") return null;
5653
+ return { executed: true, version: value.version, executedAt: typeof value.executedAt === "string" ? value.executedAt : null };
5654
+ }
5655
+ async function sha256Hex(text) {
5656
+ if (!globalThis.crypto?.subtle) throw new Error("crypto.subtle ist in diesem Kontext nicht verfuegbar.");
5657
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
5658
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
5659
+ }
5660
+ var message = (error) => error instanceof Error ? error.message : String(error);
5661
+ var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5662
+ async function executeFlowProbeBundle(bundle, {
5663
+ win = window,
5664
+ doc = document,
5665
+ timeoutMs = FLOW_PROBE_LOAD_TIMEOUT_MS,
5666
+ digest = sha256Hex
5667
+ } = {}) {
5668
+ const markerHost = win;
5669
+ const result = {
5670
+ method: FLOW_PROBE_METHOD,
5671
+ pageOrigin: win.location.origin,
5672
+ hash: { expected: bundle.sha256, actual: null, matches: null, error: null },
5673
+ scriptEvent: "not-started",
5674
+ marker: null,
5675
+ executed: false,
5676
+ cspViolations: [],
5677
+ error: null
5678
+ };
5679
+ try {
5680
+ result.hash.actual = await digest(bundle.source);
5681
+ result.hash.matches = result.hash.actual === bundle.sha256;
5682
+ } catch (error) {
5683
+ result.hash.error = message(error);
5684
+ }
5685
+ if (result.hash.matches === false) {
5686
+ result.error = "SHA-256 des empfangenen Quelltexts weicht ab \u2014 Bundle nicht ausgefuehrt.";
5687
+ return result;
5688
+ }
5689
+ delete markerHost[FLOW_PROBE_MARKER];
5690
+ const onViolation = (event) => {
5691
+ const violation = event;
5692
+ result.cspViolations.push(`${violation.effectiveDirective || violation.violatedDirective}: ${violation.blockedURI || "unbekannt"}`);
5693
+ };
5694
+ doc.addEventListener("securitypolicyviolation", onViolation);
5695
+ let url = null;
5696
+ let script = null;
5697
+ try {
5698
+ url = URL.createObjectURL(new Blob([bundle.source], { type: "text/javascript" }));
5699
+ const element = doc.createElement("script");
5700
+ script = element;
5701
+ result.scriptEvent = await new Promise((resolve) => {
5702
+ const timer = setTimeout(() => resolve("timeout"), timeoutMs);
5703
+ element.addEventListener("load", () => {
5704
+ clearTimeout(timer);
5705
+ resolve("load");
5706
+ });
5707
+ element.addEventListener("error", () => {
5708
+ clearTimeout(timer);
5709
+ resolve("error");
5710
+ });
5711
+ element.src = url;
5712
+ (doc.head ?? doc.documentElement).appendChild(element);
5713
+ });
5714
+ } catch (error) {
5715
+ result.error = `Skript nicht einfuegbar: ${message(error)}`;
5716
+ }
5717
+ result.marker = readMarker(markerHost[FLOW_PROBE_MARKER]);
5718
+ result.executed = result.marker !== null && result.marker.version === bundle.version;
5719
+ if (!result.executed) await wait(CSP_SETTLE_MS);
5720
+ doc.removeEventListener("securitypolicyviolation", onViolation);
5721
+ script?.remove();
5722
+ if (url) URL.revokeObjectURL(url);
5723
+ if (!result.executed && !result.error) {
5724
+ if (result.marker) result.error = `Marker meldet Version ${result.marker.version}, erwartet ${bundle.version}.`;
5725
+ else if (result.scriptEvent === "load") result.error = "script.onload ohne Marker \u2014 Ausfuehrung nicht belegt.";
5726
+ else if (result.scriptEvent === "error") result.error = "script.onerror \u2014 Skript nicht geladen oder blockiert.";
5727
+ else result.error = `Kein Skript-Ereignis innerhalb von ${timeoutMs} ms.`;
5728
+ }
5729
+ return result;
5730
+ }
5731
+
5628
5732
  // src/components/FlowBridgePanel.tsx
5629
- import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
5733
+ import { Fragment as Fragment10, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
5630
5734
  var STATUS_REQUEST = { target: "dev", method: "GET", path: "/api/status" };
5631
- var FlowBridgePanel = ({ appVersion, client }) => {
5735
+ function hashText(execution) {
5736
+ if (execution.hash.matches === true) return "Hash gepr\xFCft";
5737
+ if (execution.hash.matches === false) return `Hash ABWEICHEND (berechnet ${execution.hash.actual?.slice(0, 12)}\u2026)`;
5738
+ return `Hash nicht pr\xFCfbar (${execution.hash.error})`;
5739
+ }
5740
+ function probeVerdict(run) {
5741
+ if (run.execution?.executed) return { ok: true, text: `ERGEBNIS: ausgef\xFChrt (executed: true, ${run.execution.marker?.version})` };
5742
+ if (!run.response.ok) {
5743
+ return { ok: false, text: `ERGEBNIS: nicht ausgef\xFChrt \u2014 ${run.response.error?.code === "no-hf-token" ? "kein HF-Token in FlowFA." : "Bundle nicht \xFCber die Bridge erhalten."}` };
5744
+ }
5745
+ if (!run.bundle) return { ok: false, text: "ERGEBNIS: nicht ausgef\xFChrt \u2014 Antwort ohne g\xFCltiges Bundle." };
5746
+ return { ok: false, text: `ERGEBNIS: nicht ausgef\xFChrt \u2014 ${run.execution?.error}` };
5747
+ }
5748
+ var FlowBridgePanel = ({ appVersion, client, hfToken }) => {
5632
5749
  const bridge = useMemo6(() => client ?? createFlowBridgeClient({ appVersion }), [client, appVersion]);
5633
5750
  const [status, setStatus] = useState25(null);
5634
5751
  const [response, setResponse] = useState25(null);
5752
+ const [probe, setProbe] = useState25(null);
5635
5753
  const [busy, setBusy] = useState25(null);
5636
5754
  const checkStatus = async () => {
5637
5755
  setBusy("status");
@@ -5659,6 +5777,35 @@ var FlowBridgePanel = ({ appVersion, client }) => {
5659
5777
  setBusy(null);
5660
5778
  }
5661
5779
  };
5780
+ const runProbe = async () => {
5781
+ setBusy("probe");
5782
+ setProbe(null);
5783
+ try {
5784
+ const tokenSuffix = hfTokenSuffix(hfToken);
5785
+ if (!tokenSuffix) {
5786
+ setProbe({
5787
+ response: {
5788
+ ok: false,
5789
+ transport: null,
5790
+ status: null,
5791
+ data: null,
5792
+ latencyMs: null,
5793
+ roundTripMs: 0,
5794
+ error: { code: "no-hf-token", message: "Kein HF-Token in FlowFA gesetzt \u2014 ohne ihn kein Zugang zum Dev-MCP-Host." }
5795
+ },
5796
+ bundle: null,
5797
+ execution: null
5798
+ });
5799
+ return;
5800
+ }
5801
+ const response2 = await bridge.request({ ...FLOW_PROBE_REQUEST, tokenSuffix });
5802
+ const bundle = response2.ok ? readFlowProbeBundle(response2.data) : null;
5803
+ const execution = bundle ? await executeFlowProbeBundle(bundle) : null;
5804
+ setProbe({ response: response2, bundle, execution });
5805
+ } finally {
5806
+ setBusy(null);
5807
+ }
5808
+ };
5662
5809
  const buttonStyle = {
5663
5810
  height: 36,
5664
5811
  border: "1px solid rgba(255,255,255,0.12)",
@@ -5672,6 +5819,7 @@ var FlowBridgePanel = ({ appVersion, client }) => {
5672
5819
  /* @__PURE__ */ jsx29("button", { type: "button", onClick: checkStatus, disabled: busy !== null, className: "rounded-lg text-[11px] font-bold disabled:opacity-40", style: buttonStyle, children: busy === "status" ? "Pr\xFCfe\u2026" : "Bridge pr\xFCfen" }),
5673
5820
  /* @__PURE__ */ jsx29("button", { type: "button", onClick: runRequest, disabled: busy !== null, className: "rounded-lg text-[11px] font-bold disabled:opacity-40", style: buttonStyle, children: busy === "request" ? "L\xE4uft\u2026" : "Dev /api/status" })
5674
5821
  ] }),
5822
+ /* @__PURE__ */ jsx29("button", { type: "button", onClick: runProbe, disabled: busy !== null, className: "rounded-lg text-[11px] font-bold disabled:opacity-40", style: buttonStyle, children: busy === "probe" ? "L\xE4uft\u2026" : "Mini-Bundle laden & ausf\xFChren" }),
5675
5823
  status && /* @__PURE__ */ jsx29("div", { role: "status", "data-testid": "flow-bridge-status", style: { color: status.available ? "#4ade80" : "#f87171" }, children: status.available ? `Bridge: verbunden \xB7 ${status.name} v${status.version} \xB7 Protokoll ${status.protocol} \xB7 ${status.roundTripMs} ms` : `Bridge: nicht verbunden \xB7 ${status.error.code}: ${status.error.message}` }),
5676
5824
  response && /* @__PURE__ */ jsxs27("div", { "data-testid": "flow-bridge-response", className: "flex flex-col gap-1", children: [
5677
5825
  /* @__PURE__ */ jsxs27("span", { style: { color: response.ok ? "#4ade80" : "#f87171" }, children: [
@@ -5681,6 +5829,24 @@ var FlowBridgePanel = ({ appVersion, client }) => {
5681
5829
  ] }),
5682
5830
  /* @__PURE__ */ jsx29("span", { className: "text-white/40", children: `Transport: ${response.transport ?? "keiner"} \xB7 Server ${response.latencyMs ?? "\u2014"} ms \xB7 gesamt ${response.roundTripMs} ms` }),
5683
5831
  response.ok && /* @__PURE__ */ jsx29("pre", { className: "text-white/60 whitespace-pre-wrap break-all bg-black/30 rounded-lg p-2", children: JSON.stringify(response.data, null, 2) })
5832
+ ] }),
5833
+ probe && /* @__PURE__ */ jsxs27("div", { "data-testid": "flow-bridge-probe", className: "flex flex-col gap-1", children: [
5834
+ /* @__PURE__ */ jsxs27("span", { style: { color: probe.response.ok ? "#4ade80" : "#f87171" }, children: [
5835
+ `${FLOW_PROBE_REQUEST.method} ${FLOW_PROBE_REQUEST.path} (${FLOW_PROBE_REQUEST.target}) \u2192 `,
5836
+ probe.response.status !== null ? `HTTP ${probe.response.status}` : "kein HTTP-Status",
5837
+ probe.response.ok ? "" : ` \xB7 ${probe.response.error?.code}: ${probe.response.error?.message}`
5838
+ ] }),
5839
+ /* @__PURE__ */ jsx29("span", { className: "text-white/40", children: `Transport: ${probe.response.transport ?? "keiner"} \xB7 Server ${probe.response.latencyMs ?? "\u2014"} ms \xB7 gesamt ${probe.response.roundTripMs} ms` }),
5840
+ probe.bundle && /* @__PURE__ */ jsxs27("span", { className: "text-white/60 break-all", children: [
5841
+ `Bundle ${probe.bundle.version} \xB7 ${probe.bundle.source.length} Zeichen \xB7 SHA-256 ${probe.bundle.sha256.slice(0, 12)}\u2026`,
5842
+ probe.execution ? ` \xB7 ${hashText(probe.execution)}` : ""
5843
+ ] }),
5844
+ probe.execution && /* @__PURE__ */ jsxs27(Fragment10, { children: [
5845
+ /* @__PURE__ */ jsx29("span", { className: "text-white/60 break-all", children: `Ausf\xFChrung: <script src=blob:> \xB7 Ereignis ${probe.execution.scriptEvent} \xB7 Seite ${probe.execution.pageOrigin}` }),
5846
+ /* @__PURE__ */ jsx29("span", { className: "text-white/60 break-all", children: `Marker window.${FLOW_PROBE_MARKER}: ${probe.execution.marker ? JSON.stringify(probe.execution.marker) : "fehlt"}` }),
5847
+ /* @__PURE__ */ jsx29("span", { className: "break-all", style: { color: probe.execution.cspViolations.length > 0 ? "#f87171" : "rgba(255,255,255,0.4)" }, children: probe.execution.cspViolations.length > 0 ? `CSP: ${probe.execution.cspViolations.join("; ")}` : "CSP: keine Verletzung gemeldet" })
5848
+ ] }),
5849
+ /* @__PURE__ */ jsx29("span", { "data-testid": "flow-bridge-probe-verdict", className: "font-bold", style: { color: probeVerdict(probe).ok ? "#4ade80" : "#f87171" }, children: probeVerdict(probe).text })
5684
5850
  ] })
5685
5851
  ] })
5686
5852
  ] });
@@ -5738,7 +5904,7 @@ function resolveBootstrapTarget(input) {
5738
5904
  }
5739
5905
 
5740
5906
  // src/components/AvatarArchitectApp.tsx
5741
- import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
5907
+ import { Fragment as Fragment11, jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
5742
5908
  function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl }) {
5743
5909
  useEffect11(() => {
5744
5910
  const id = "flow-styles";
@@ -5813,7 +5979,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5813
5979
  const [bootstrapLog, setBootstrapLog] = useState26([]);
5814
5980
  const [isBootstrapping, setIsBootstrapping] = useState26(false);
5815
5981
  const [hfMissingImages, setHfMissingImages] = useState26([]);
5816
- const syncTopSlot = /* @__PURE__ */ jsxs28(Fragment10, { children: [
5982
+ const syncTopSlot = /* @__PURE__ */ jsxs28(Fragment11, { children: [
5817
5983
  localOnlyCount > 0 && /* @__PURE__ */ jsxs28("div", { style: { background: "rgba(234,179,8,0.15)", border: "1px solid rgba(234,179,8,0.3)", padding: "4px 10px", fontSize: 11, color: "#fbbf24", borderRadius: 4, marginBottom: 4 }, children: [
5818
5984
  "\u26A0 ",
5819
5985
  localOnlyCount,
@@ -6550,7 +6716,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6550
6716
  setZoomScale(1);
6551
6717
  setZoomOffset({ x: 0, y: 0 });
6552
6718
  }, className: "absolute top-4 left-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
6553
- history.length > 1 && /* @__PURE__ */ jsxs28(Fragment10, { children: [
6719
+ history.length > 1 && /* @__PURE__ */ jsxs28(Fragment11, { children: [
6554
6720
  /* @__PURE__ */ jsx30("button", { onClick: () => {
6555
6721
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
6556
6722
  }, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
@@ -6668,7 +6834,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6668
6834
  layoutChoice === "mobile-desktop" && /* @__PURE__ */ jsx30("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
6669
6835
  layoutChoice === "tablet-landscape" && /* @__PURE__ */ jsx30("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
6670
6836
  ] }),
6671
- /* @__PURE__ */ jsx30(FlowBridgePanel, { appVersion: version })
6837
+ /* @__PURE__ */ jsx30(FlowBridgePanel, { appVersion: version, hfToken })
6672
6838
  ] });
6673
6839
  }
6674
6840
  if (requireWorkspaceSelection) {
@@ -6822,7 +6988,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6822
6988
  /* @__PURE__ */ jsx30("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
6823
6989
  ] }),
6824
6990
  currentResult?.status === "done" && /* @__PURE__ */ jsx30("button", { onClick: openFullscreen, className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center rounded-full bg-black/60 border border-white/10 z-10", children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
6825
- history.length > 1 && currentResult && /* @__PURE__ */ jsxs28(Fragment10, { children: [
6991
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs28(Fragment11, { children: [
6826
6992
  /* @__PURE__ */ jsx30("button", { onClick: goToPrev, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
6827
6993
  /* @__PURE__ */ jsx30("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
6828
6994
  /* @__PURE__ */ jsxs28("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
@@ -7114,7 +7280,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
7114
7280
  /* @__PURE__ */ jsx30("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
7115
7281
  ] }),
7116
7282
  currentResult?.status === "done" && /* @__PURE__ */ jsx30("button", { onClick: openFullscreen, style: { position: "absolute", top: 8, right: 8, width: 32, height: 32, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff" }, children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
7117
- history.length > 1 && currentResult && /* @__PURE__ */ jsxs28(Fragment10, { children: [
7283
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs28(Fragment11, { children: [
7118
7284
  /* @__PURE__ */ jsx30("button", { onClick: goToPrev, disabled: currentIndex <= 0, style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex <= 0 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
7119
7285
  /* @__PURE__ */ jsx30("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, style: { position: "absolute", right: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex >= history.length - 1 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx30("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
7120
7286
  /* @__PURE__ */ jsxs28("div", { style: { position: "absolute", bottom: 8, left: "50%", transform: "translateX(-50%)", background: "rgba(0,0,0,0.6)", borderRadius: 999, padding: "2px 12px", fontSize: 10, color: "rgba(255,255,255,0.4)", fontFamily: "monospace" }, children: [
@@ -7987,7 +8153,7 @@ function zugehoerigkeitenOf(item) {
7987
8153
  }
7988
8154
 
7989
8155
  // src/features/library/ZugehoerigkeitLinks.tsx
7990
- import { Fragment as Fragment11, jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
8156
+ import { Fragment as Fragment12, jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
7991
8157
  var CHIP_BASIS = "text-[9px] px-1.5 py-0.5 rounded-full font-bold border transition";
7992
8158
  var CHIP_LAUF = "bg-teal-900/60 text-teal-200 border-teal-800 hover:bg-teal-800 hover:text-white";
7993
8159
  var CHIP_SZENE = "bg-indigo-900/60 text-indigo-200 border-indigo-800 hover:bg-indigo-800 hover:text-white";
@@ -8013,7 +8179,7 @@ var ZugehoerigkeitLinks = ({
8013
8179
  };
8014
8180
  if (variante === "liste") {
8015
8181
  if (!hatNavigationsziele(zugehoerigkeit)) return null;
8016
- return /* @__PURE__ */ jsxs29(Fragment11, { children: [
8182
+ return /* @__PURE__ */ jsxs29(Fragment12, { children: [
8017
8183
  run && /* @__PURE__ */ jsx32(
8018
8184
  "button",
8019
8185
  {
@@ -8152,6 +8318,8 @@ export {
8152
8318
  CURRENT_TAG_EVENT_VERSION,
8153
8319
  CollapsibleCard,
8154
8320
  CompactDropdown,
8321
+ FLOW_PROBE_MARKER,
8322
+ FLOW_PROBE_REQUEST,
8155
8323
  FaApp,
8156
8324
  FaToolsBadge,
8157
8325
  FlowBridgePanel,
@@ -8209,6 +8377,7 @@ export {
8209
8377
  createTagsDefault,
8210
8378
  deriveTagBaseWorkspaceTags,
8211
8379
  deriveTagViewModel,
8380
+ executeFlowProbeBundle,
8212
8381
  formatTreeToMarkdown,
8213
8382
  frameToGeneration,
8214
8383
  getFormattedTimestamp,
@@ -8234,6 +8403,7 @@ export {
8234
8403
  parseLegacyRecentTags,
8235
8404
  parsePromptFile,
8236
8405
  parsePromptResponse,
8406
+ readFlowProbeBundle,
8237
8407
  runLegacyMigration,
8238
8408
  runLibraryLegacyImport,
8239
8409
  runTagsLegacyImport,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "10.3.3",
3
+ "version": "10.3.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",