@floegence/floe-webapp-core 0.86.32 → 0.86.33

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/chat.css CHANGED
@@ -1887,3 +1887,39 @@ pre.chat-md-code-block *::selection,
1887
1887
  @media (prefers-reduced-motion: reduce) { .chat-media-actions, .chat-media-action { transition: none; } }
1888
1888
  @media (max-width: 480px) { .chat-media[data-media-kind="html"] .chat-media-viewport, .chat-media-html { height: 18rem; } .chat-media-expanded[data-media-kind="html"] .chat-media-viewport { height: min(70vh, 48rem); } }
1889
1889
  }
1890
+
1891
+ /* HTML outputs retain one quiet, bounded canvas throughout their lifecycle. */
1892
+ @layer components {
1893
+ .chat-html-canvas { --canvas-inset: clamp(1rem, 5%, 3rem); position: relative; isolation: isolate; display: block; width: 100%; height: 28rem; overflow: hidden; border: 1px solid color-mix(in srgb, var(--border) 65%, transparent); border-radius: 1rem; background: color-mix(in srgb, var(--muted) 38%, var(--background)); }
1894
+ .chat-media-expanded .chat-html-canvas { height: min(78vh, 52rem); }
1895
+ .chat-html-dots, .chat-html-sheen { position: absolute; inset: 0; pointer-events: none; border-radius: inherit; }
1896
+ .chat-html-dots { background-image: radial-gradient(circle, color-mix(in srgb, var(--muted-foreground) 24%, transparent) .75px, transparent .9px); background-size: 20px 20px; background-position: 9px 9px; }
1897
+ .chat-html-sheen { opacity: 0; background: linear-gradient(115deg, transparent 25%, color-mix(in srgb, var(--foreground) 20%, transparent) 49%, transparent 73%); background-size: 250% 100%; mask-image: radial-gradient(circle, black .9px, transparent 1.1px); mask-size: 20px 20px; mask-position: 9px 9px; }
1898
+ .chat-html-canvas:is([data-state="generating"], [data-state="loading"]) .chat-html-sheen { opacity: 1; animation: chat-html-sweep 3.4s ease-in-out infinite; }
1899
+ .chat-html-sheet, .chat-html-source { position: absolute; inset: 3.5rem var(--canvas-inset) 4.25rem; display: flex; min-width: 0; min-height: 0; overflow: hidden; border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); border-radius: .625rem; box-shadow: 0 14px 40px rgb(0 0 0 / .06), 0 2px 6px rgb(0 0 0 / .03); background: var(--background); }
1900
+ .chat-html-sheet { transition: opacity 280ms ease; }
1901
+ .chat-html-sheet-hidden { opacity: 0; visibility: hidden; pointer-events: none; }
1902
+ .chat-html-sheet .chat-media-html { height: 100%; border-radius: 0; }
1903
+ .chat-html-source { display: block; margin: 0; padding: 1.25rem; overflow: auto; overscroll-behavior: contain; white-space: pre; font: .6875rem/1.85 var(--font-mono); color: var(--foreground); }
1904
+ .chat-html-source code { padding: 0; color: inherit; background: none; font: inherit; }
1905
+ .chat-html-loading { position: absolute; inset: 3.5rem var(--canvas-inset) 4.25rem; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 1rem; text-align: center; color: var(--muted-foreground); font-size: .75rem; }
1906
+ .chat-html-skeleton { display: flex; flex-direction: column; justify-content: center; gap: .875rem; width: min(72%, 19rem); height: 11rem; padding: 1.75rem; border: 1px solid color-mix(in srgb, var(--border) 60%, transparent); border-radius: .625rem; background: color-mix(in srgb, var(--background) 65%, transparent); }
1907
+ .chat-html-skeleton > span { height: .375rem; border-radius: 1rem; background: color-mix(in srgb, var(--muted-foreground) 12%, transparent); }
1908
+ .chat-html-skeleton > span:first-child { width: 35%; height: .5rem; margin-bottom: .75rem; }
1909
+ .chat-html-skeleton > span:nth-child(3) { width: 82%; }
1910
+ .chat-html-skeleton > span:last-child { width: 60%; }
1911
+ .chat-html-orb { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 2.125rem; height: 2.125rem; padding: 0; border: 1px solid color-mix(in srgb, var(--border) 80%, transparent); border-radius: 50%; color: var(--muted-foreground); background: color-mix(in srgb, var(--background) 88%, transparent); box-shadow: 0 2px 5px rgb(0 0 0 / .035); backdrop-filter: blur(12px); cursor: pointer; transition: color 140ms, background 140ms, transform 140ms; }
1912
+ .chat-html-orb:hover { color: var(--foreground); background: var(--background); transform: translateY(-1px); }
1913
+ .chat-html-orb[aria-pressed="true"] { color: var(--foreground); background: var(--muted); }
1914
+ .chat-html-orb:focus-visible, .chat-html-source:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }
1915
+ .chat-html-orb svg { width: .9375rem; height: .9375rem; }
1916
+ .chat-html-expand { position: absolute; top: .875rem; right: .875rem; }
1917
+ .chat-html-controls { position: absolute; bottom: .875rem; left: .875rem; display: flex; align-items: center; gap: .5rem; }
1918
+ .chat-html-feedback { position: absolute; right: 1rem; bottom: 1.125rem; max-width: calc(100% - 7.5rem); display: flex; align-items: center; gap: .4rem; font-size: .6875rem; line-height: 1.5; color: var(--muted-foreground); }
1919
+ .chat-html-feedback svg { width: .875rem; height: .875rem; flex-shrink: 0; }
1920
+ @keyframes chat-html-sweep { from { background-position: 120% 0; } to { background-position: -80% 0; } }
1921
+ @media (max-width: 480px) { .chat-html-canvas { height: 25rem; --canvas-inset: 1rem; } }
1922
+ @media (pointer: coarse) { .chat-html-orb { width: 2.625rem; height: 2.625rem; } .chat-html-controls { bottom: .75rem; left: .75rem; } }
1923
+ @media (prefers-reduced-motion: reduce) { .chat-html-sheen { animation: none !important; background-position: 50% 0; } .chat-html-orb, .chat-html-sheet { transition: none; } }
1924
+ @media (forced-colors: active) { .chat-html-orb, .chat-html-source, .chat-html-canvas { border-color: CanvasText; } .chat-html-sheen { display: none; } }
1925
+ }
@@ -0,0 +1,16 @@
1
+ import { type Component, type JSX } from 'solid-js';
2
+ import type { MarkdownMediaLabels, ResolvedMarkdownMedia } from './MarkdownMedia';
3
+ export interface HtmlPreviewCanvasProps {
4
+ state: 'loading' | 'generating' | 'incomplete' | 'ready' | 'error';
5
+ html: string;
6
+ labels: MarkdownMediaLabels;
7
+ expanded: boolean;
8
+ onExpand: () => void;
9
+ onRetry: () => void;
10
+ onPreview: () => void;
11
+ previewLabel: string;
12
+ resource?: ResolvedMarkdownMedia;
13
+ children: JSX.Element;
14
+ }
15
+ /** One bounded surface owns generation, source inspection, and the isolated preview. */
16
+ export declare const HtmlPreviewCanvas: Component<HtmlPreviewCanvasProps>;
@@ -0,0 +1,153 @@
1
+ import { createComponent as a, insert as l, effect as F, setAttribute as r, memo as N, template as u, use as O, delegateEvents as j } from "solid-js/web";
2
+ import { createSignal as U, onCleanup as B, createMemo as I, Show as d } from "solid-js";
3
+ import { Copy as J, Eye as z, Refresh as D, ExternalLink as K, FolderOpen as Q, Minus as W, Maximize as X, Code as Y, MoreHorizontal as Z, Check as p } from "../../icons/index.js";
4
+ import { Dropdown as ee } from "../../ui/Dropdown.js";
5
+ var te = /* @__PURE__ */ u("<pre class=chat-html-source tabindex=0><code>"), ae = /* @__PURE__ */ u("<span class=chat-html-skeleton aria-hidden=true><span></span><span></span><span></span><span>"), le = /* @__PURE__ */ u("<button type=button class=chat-media-retry>"), ne = /* @__PURE__ */ u("<span class=chat-html-loading role=status><span>"), re = /* @__PURE__ */ u("<span class=chat-html-feedback role=status>"), ie = /* @__PURE__ */ u('<span class=chat-html-canvas><span class=chat-html-dots aria-hidden=true></span><span class=chat-html-sheen aria-hidden=true></span><span class=chat-html-sheet></span><button type=button class="chat-html-orb chat-html-expand"></button><span class=chat-html-controls><button type=button class=chat-html-orb data-chat-media-action=source>');
6
+ const ue = (e) => {
7
+ const [c, y] = U(!1), [x, S] = U("");
8
+ let b, v = !1, f;
9
+ const k = (n) => {
10
+ clearTimeout(f), S(n), f = setTimeout(() => S(""), 2400);
11
+ };
12
+ B(() => {
13
+ v = !0, clearTimeout(f);
14
+ });
15
+ const V = async () => {
16
+ try {
17
+ if (await navigator.clipboard.writeText(e.html), v) return;
18
+ k(e.labels.copied ?? "Copied");
19
+ } catch {
20
+ if (v) return;
21
+ const n = e.labels.copyFailed ?? "Select and copy the source.";
22
+ y(!0), queueMicrotask(() => {
23
+ if (v) return;
24
+ b.focus({
25
+ preventScroll: !0
26
+ });
27
+ const g = document.createRange();
28
+ g.selectNodeContents(b);
29
+ const m = window.getSelection();
30
+ m?.removeAllRanges(), m?.addRange(g), k(n);
31
+ });
32
+ }
33
+ }, q = I(() => [{
34
+ id: "copy",
35
+ label: e.labels.copyHTML ?? "Copy HTML",
36
+ icon: () => a(J, {}),
37
+ disabled: !e.html
38
+ }, ...e.state === "ready" ? [{
39
+ id: "preview",
40
+ label: e.previewLabel,
41
+ icon: () => a(z, {})
42
+ }, {
43
+ id: "replay",
44
+ label: e.labels.replay ?? "Reload preview",
45
+ icon: () => a(D, {})
46
+ }] : [], ...e.resource?.openURL && !e.resource.reveal ? [{
47
+ id: "open",
48
+ label: e.labels.open,
49
+ icon: () => a(K, {})
50
+ }] : [], ...e.resource?.reveal ? [{
51
+ id: "reveal",
52
+ label: e.resource.reveal.label,
53
+ icon: () => a(Q, {})
54
+ }] : []]), G = () => e.state === "generating" ? e.labels.generating ?? e.labels.loading : e.state === "incomplete" ? e.labels.incomplete ?? "Generation interrupted. Source is available." : e.state === "error" ? e.labels.unavailable : e.labels.loading;
55
+ return (() => {
56
+ var n = ie(), g = n.firstChild, m = g.nextSibling, w = m.nextSibling, s = w.nextSibling, $ = s.nextSibling, h = $.firstChild;
57
+ return n.$$keydown = (t) => {
58
+ t.key === "Escape" && e.expanded && !t.defaultPrevented && (t.preventDefault(), e.onExpand());
59
+ }, l(w, () => e.children), l(n, a(d, {
60
+ get when() {
61
+ return c();
62
+ },
63
+ get children() {
64
+ var t = te(), o = t.firstChild, i = b;
65
+ return typeof i == "function" ? O(i, t) : b = t, l(o, () => e.html), F(() => r(t, "aria-label", e.labels.showSource ?? "HTML source")), t;
66
+ }
67
+ }), s), l(n, a(d, {
68
+ get when() {
69
+ return N(() => !c())() && e.state !== "ready";
70
+ },
71
+ get children() {
72
+ var t = ne(), o = t.firstChild;
73
+ return l(t, a(d, {
74
+ get when() {
75
+ return e.state === "loading" || e.state === "generating";
76
+ },
77
+ get children() {
78
+ return ae();
79
+ }
80
+ }), o), l(o, G), l(t, a(d, {
81
+ get when() {
82
+ return e.state === "error";
83
+ },
84
+ get children() {
85
+ var i = le();
86
+ return i.$$click = () => e.onRetry(), l(i, a(D, {}), null), l(i, () => e.labels.retry, null), i;
87
+ }
88
+ }), null), t;
89
+ }
90
+ }), s), s.$$click = () => e.onExpand(), l(s, a(d, {
91
+ get when() {
92
+ return e.expanded;
93
+ },
94
+ get fallback() {
95
+ return a(X, {});
96
+ },
97
+ get children() {
98
+ return a(W, {});
99
+ }
100
+ })), h.$$click = () => y((t) => !t), l(h, a(d, {
101
+ get when() {
102
+ return c();
103
+ },
104
+ get fallback() {
105
+ return a(Y, {});
106
+ },
107
+ get children() {
108
+ return a(z, {});
109
+ }
110
+ })), l($, a(ee, {
111
+ get trigger() {
112
+ return a(Z, {});
113
+ },
114
+ triggerClass: "chat-html-orb",
115
+ get triggerAriaLabel() {
116
+ return e.labels.more ?? "More actions";
117
+ },
118
+ items: q,
119
+ align: "start",
120
+ onSelect: (t) => {
121
+ t === "copy" ? V() : t === "preview" ? e.onPreview() : t === "replay" ? e.onRetry() : t === "reveal" ? e.resource?.reveal?.onSelect() : t === "open" && e.resource?.openURL && window.open(e.resource.openURL, "_blank", "noopener,noreferrer");
122
+ }
123
+ }), null), l(n, a(d, {
124
+ get when() {
125
+ return x();
126
+ },
127
+ get children() {
128
+ var t = re();
129
+ return l(t, a(p, {}), null), l(t, x, null), t;
130
+ }
131
+ }), null), F((t) => {
132
+ var o = e.state, i = c() ? "source" : "preview", C = e.state === "generating" || e.state === "loading", L = !!(c() || e.state !== "ready"), M = c() || e.state !== "ready", R = e.expanded ? "collapse" : "expand", T = e.expanded, _ = e.expanded ? e.labels.collapse : e.labels.expand, H = e.expanded ? e.labels.collapse : e.labels.expand, E = c(), P = c() ? e.labels.showPreview ?? "Show preview" : e.labels.showSource ?? "Show HTML source", A = c() ? e.labels.showPreview ?? "Show preview" : e.labels.showSource ?? "Show HTML source";
133
+ return o !== t.e && r(n, "data-state", t.e = o), i !== t.t && r(n, "data-view", t.t = i), C !== t.a && r(n, "aria-busy", t.a = C), L !== t.o && w.classList.toggle("chat-html-sheet-hidden", t.o = L), M !== t.i && (w.inert = t.i = M), R !== t.n && r(s, "data-chat-media-action", t.n = R), T !== t.s && r(s, "aria-expanded", t.s = T), _ !== t.h && r(s, "aria-label", t.h = _), H !== t.r && r(s, "title", t.r = H), E !== t.d && r(h, "aria-pressed", t.d = E), P !== t.l && r(h, "aria-label", t.l = P), A !== t.u && r(h, "title", t.u = A), t;
134
+ }, {
135
+ e: void 0,
136
+ t: void 0,
137
+ a: void 0,
138
+ o: void 0,
139
+ i: void 0,
140
+ n: void 0,
141
+ s: void 0,
142
+ h: void 0,
143
+ r: void 0,
144
+ d: void 0,
145
+ l: void 0,
146
+ u: void 0
147
+ }), n;
148
+ })();
149
+ };
150
+ j(["keydown", "click"]);
151
+ export {
152
+ ue as HtmlPreviewCanvas
153
+ };
@@ -1,6 +1,15 @@
1
1
  import { type Component } from 'solid-js';
2
2
  import { type MarkdownMediaSource } from '../markdown/media';
3
3
  export interface MarkdownMediaLabels {
4
+ generating?: string;
5
+ incomplete?: string;
6
+ showSource?: string;
7
+ showPreview?: string;
8
+ more?: string;
9
+ copyHTML?: string;
10
+ copied?: string;
11
+ copyFailed?: string;
12
+ replay?: string;
4
13
  image: string;
5
14
  /** Label for opening an image in its preview window. */
6
15
  previewImage?: string;
@@ -40,6 +49,8 @@ export interface ResolvedMarkdownMedia {
40
49
  }
41
50
  export interface MarkdownMediaProps {
42
51
  source: MarkdownMediaSource;
52
+ /** Partial HTML remains inspectable but never executes before a closed fence. */
53
+ htmlState?: 'generating' | 'incomplete';
43
54
  labels: MarkdownMediaLabels;
44
55
  /** Replace this callback when its authorization or resource context changes. Reads inside it do not trigger reloads. */
45
56
  resolve?: (source: MarkdownMediaSource, signal: AbortSignal) => Promise<ResolvedMarkdownMedia>;