@burdenoff/fe-libs 2026.904.7 → 2026.904.9
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/node_modules/react-markdown/lib/index.js +1 -1
- package/dist/shared/assistant/attachmentSources.d.ts +58 -0
- package/dist/shared/assistant/attachmentSources.d.ts.map +1 -0
- package/dist/shared/assistant/attachmentSources.js +41 -0
- package/dist/shared/assistant/clipboard.d.ts +12 -0
- package/dist/shared/assistant/clipboard.d.ts.map +1 -0
- package/dist/shared/assistant/clipboard.js +10 -0
- package/dist/shared/assistant/context.d.ts +22 -0
- package/dist/shared/assistant/context.d.ts.map +1 -0
- package/dist/shared/assistant/context.js +36 -0
- package/dist/shared/assistant/index.d.ts +26 -0
- package/dist/shared/assistant/index.d.ts.map +1 -1
- package/dist/shared/assistant/sandboxReadiness.d.ts +45 -0
- package/dist/shared/assistant/sandboxReadiness.d.ts.map +1 -0
- package/dist/shared/assistant/sandboxReadiness.js +22 -0
- package/dist/shared/assistant/ui/AssistantAutoSpeakToggle.d.ts +20 -0
- package/dist/shared/assistant/ui/AssistantAutoSpeakToggle.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantAutoSpeakToggle.js +25 -0
- package/dist/shared/assistant/ui/AssistantMarkdownImage.d.ts +38 -0
- package/dist/shared/assistant/ui/AssistantMarkdownImage.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantMarkdownImage.js +147 -0
- package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts +10 -0
- package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantMarkdownRenderer.js +198 -0
- package/dist/shared/assistant/ui/AssistantMessageAttachments.d.ts +10 -0
- package/dist/shared/assistant/ui/AssistantMessageAttachments.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantMessageAttachments.js +80 -0
- package/dist/shared/assistant/ui/AssistantPendingWriteCard.d.ts +32 -0
- package/dist/shared/assistant/ui/AssistantPendingWriteCard.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantPendingWriteCard.js +53 -0
- package/dist/shared/assistant/ui/AssistantReplyLanguageSelector.d.ts +22 -0
- package/dist/shared/assistant/ui/AssistantReplyLanguageSelector.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantReplyLanguageSelector.js +58 -0
- package/dist/shared/assistant/ui/AssistantSpeakButton.d.ts +33 -0
- package/dist/shared/assistant/ui/AssistantSpeakButton.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantSpeakButton.js +21 -0
- package/dist/shared/assistant/ui/AssistantSpokenTurnsNotice.d.ts +31 -0
- package/dist/shared/assistant/ui/AssistantSpokenTurnsNotice.d.ts.map +1 -0
- package/dist/shared/assistant/ui/AssistantSpokenTurnsNotice.js +31 -0
- package/dist/shared/assistant/ui/composer/AssistantAttachmentButton.d.ts +51 -0
- package/dist/shared/assistant/ui/composer/AssistantAttachmentButton.d.ts.map +1 -0
- package/dist/shared/assistant/ui/composer/AssistantAttachmentButton.js +86 -0
- package/dist/shared/assistant/ui/composer/AssistantAttachmentChips.d.ts +29 -0
- package/dist/shared/assistant/ui/composer/AssistantAttachmentChips.d.ts.map +1 -0
- package/dist/shared/assistant/ui/composer/AssistantAttachmentChips.js +70 -0
- package/dist/shared/assistant/ui/composer/AssistantLanguageSelector.d.ts +39 -0
- package/dist/shared/assistant/ui/composer/AssistantLanguageSelector.d.ts.map +1 -0
- package/dist/shared/assistant/ui/composer/AssistantLanguageSelector.js +42 -0
- package/dist/shared/assistant/ui/composer/AssistantModeSelector.d.ts +44 -0
- package/dist/shared/assistant/ui/composer/AssistantModeSelector.d.ts.map +1 -0
- package/dist/shared/assistant/ui/composer/AssistantModeSelector.js +81 -0
- package/dist/shared/assistant/ui/composer/AssistantVoiceButton.d.ts +20 -0
- package/dist/shared/assistant/ui/composer/AssistantVoiceButton.d.ts.map +1 -0
- package/dist/shared/assistant/ui/composer/AssistantVoiceButton.js +24 -0
- package/dist/shared/assistant/ui/composer/AssistantVoiceSelector.d.ts +25 -0
- package/dist/shared/assistant/ui/composer/AssistantVoiceSelector.d.ts.map +1 -0
- package/dist/shared/assistant/ui/composer/AssistantVoiceSelector.js +37 -0
- package/dist/shared/assistant/ui/voice/AssistantVoiceOrb.d.ts +33 -0
- package/dist/shared/assistant/ui/voice/AssistantVoiceOrb.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/AssistantVoiceOrb.js +24 -0
- package/dist/shared/assistant/ui/voice/AssistantVoiceSessionButton.d.ts +23 -0
- package/dist/shared/assistant/ui/voice/AssistantVoiceSessionButton.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/AssistantVoiceSessionButton.js +19 -0
- package/dist/shared/assistant/ui/voice/VoiceCaption.d.ts +59 -0
- package/dist/shared/assistant/ui/voice/VoiceCaption.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/VoiceCaption.js +25 -0
- package/dist/shared/assistant/ui/voice/VoiceConversationView.d.ts +38 -0
- package/dist/shared/assistant/ui/voice/VoiceConversationView.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/VoiceConversationView.js +45 -0
- package/dist/shared/assistant/ui/voice/VoiceMiniController.d.ts +32 -0
- package/dist/shared/assistant/ui/voice/VoiceMiniController.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/VoiceMiniController.js +47 -0
- package/dist/shared/assistant/ui/voice/VoiceStage.d.ts +42 -0
- package/dist/shared/assistant/ui/voice/VoiceStage.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/VoiceStage.js +58 -0
- package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.d.ts +39 -0
- package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.js +61 -0
- package/dist/shared/assistant/ui/voice/VoiceWaveform.d.ts +33 -0
- package/dist/shared/assistant/ui/voice/VoiceWaveform.d.ts.map +1 -0
- package/dist/shared/assistant/ui/voice/VoiceWaveform.js +45 -0
- package/dist/shared-assistant.js +27 -1
- package/package.json +5 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an attachment can come from.
|
|
3
|
+
*
|
|
4
|
+
* `+` used to open the OS picker directly, which is one source pretending to be
|
|
5
|
+
* all of them: on a phone it lands in Files, so photographing a diseased leaf —
|
|
6
|
+
* the single most likely thing a farmer wants to show the assistant — took a
|
|
7
|
+
* detour through the camera app and back.
|
|
8
|
+
*
|
|
9
|
+
* ★ Every source's `accept` is DERIVED, never typed out. `ATTACHMENT_ACCEPT` is
|
|
10
|
+
* itself built from the classifier and extractor tables precisely so the picker
|
|
11
|
+
* cannot offer a file the model then admits it cannot read — a hand-written list
|
|
12
|
+
* once advertised .pdf a year before anything could open one. Narrowing a source
|
|
13
|
+
* therefore filters that derived list rather than restating it, so a new
|
|
14
|
+
* document format is offered here the moment the extractor learns it.
|
|
15
|
+
*
|
|
16
|
+
* ★ There is deliberately NO generic "all files" source, though the spec asks
|
|
17
|
+
* for one. It would restore exactly the failure the derived list was written to
|
|
18
|
+
* prevent. Every format the assistant can actually read is already reachable
|
|
19
|
+
* through Documents or Files here; anything else is a file it would have to
|
|
20
|
+
* refuse after the user had already chosen it.
|
|
21
|
+
*/
|
|
22
|
+
export type AttachmentSourceId = "camera" | "gallery" | "documents" | "files";
|
|
23
|
+
export interface AttachmentSource {
|
|
24
|
+
id: AttachmentSourceId;
|
|
25
|
+
/** i18n key and the English fallback, resolved by the caller. */
|
|
26
|
+
labelKey: string;
|
|
27
|
+
labelFallback: string;
|
|
28
|
+
/** `accept` for this source's hidden input. */
|
|
29
|
+
accept: string;
|
|
30
|
+
/**
|
|
31
|
+
* Ask for the camera rather than the gallery. Honoured on mobile browsers,
|
|
32
|
+
* where the OS camera provides capture → preview → retake → use for free;
|
|
33
|
+
* ignored on desktop, which falls back to the file picker rather than
|
|
34
|
+
* breaking.
|
|
35
|
+
*/
|
|
36
|
+
capture?: "environment" | "user";
|
|
37
|
+
/** Multi-select. A camera capture is one shot at a time. */
|
|
38
|
+
multiple: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Only offer where it can work. Camera and gallery are pointless on a desktop
|
|
41
|
+
* without one, and a control that does nothing is worse than an absent one.
|
|
42
|
+
*/
|
|
43
|
+
mobileOnly?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export declare const ATTACHMENT_SOURCES: readonly AttachmentSource[];
|
|
46
|
+
/**
|
|
47
|
+
* Can this device plausibly take a photo?
|
|
48
|
+
*
|
|
49
|
+
* Coarse on purpose. `mediaDevices` is the honest capability check but it needs
|
|
50
|
+
* a permission prompt to be sure, and asking for the camera to decide whether to
|
|
51
|
+
* DRAW a camera button is backwards. A pointer query separates phones and
|
|
52
|
+
* tablets from desktops well enough for a menu entry, and the worst case is a
|
|
53
|
+
* touchscreen laptop seeing one extra option that still opens a working picker.
|
|
54
|
+
*/
|
|
55
|
+
export declare function deviceHasCamera(): boolean;
|
|
56
|
+
/** The sources worth showing on this device, in menu order. */
|
|
57
|
+
export declare function availableAttachmentSources(hasCamera: boolean): AttachmentSource[];
|
|
58
|
+
//# sourceMappingURL=attachmentSources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachmentSources.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/attachmentSources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,kBAAkB,CAAC;IACvB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAcD,eAAO,MAAM,kBAAkB,EAAE,SAAS,gBAAgB,EA+BhD,CAAC;AAEX;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAIzC;AAED,+DAA+D;AAC/D,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,OAAO,GACjB,gBAAgB,EAAE,CAEpB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ATTACHMENT_ACCEPT as e } from "./attachments.js";
|
|
2
|
+
var t = [
|
|
3
|
+
{
|
|
4
|
+
id: "camera",
|
|
5
|
+
labelKey: "composer.sourceCamera",
|
|
6
|
+
labelFallback: "Camera",
|
|
7
|
+
accept: "image/*",
|
|
8
|
+
capture: "environment",
|
|
9
|
+
multiple: !1,
|
|
10
|
+
mobileOnly: !0
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "gallery",
|
|
14
|
+
labelKey: "composer.sourceGallery",
|
|
15
|
+
labelFallback: "Photos",
|
|
16
|
+
accept: "image/*",
|
|
17
|
+
multiple: !0
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "documents",
|
|
21
|
+
labelKey: "composer.sourceDocuments",
|
|
22
|
+
labelFallback: "Documents",
|
|
23
|
+
accept: e.split(",").filter((e) => !e.startsWith("image/")).join(","),
|
|
24
|
+
multiple: !0
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "files",
|
|
28
|
+
labelKey: "composer.sourceFiles",
|
|
29
|
+
labelFallback: "Files",
|
|
30
|
+
accept: e,
|
|
31
|
+
multiple: !0
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
function n() {
|
|
35
|
+
return typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia("(pointer: coarse)").matches;
|
|
36
|
+
}
|
|
37
|
+
function r(e) {
|
|
38
|
+
return t.filter((t) => !t.mobileOnly || e);
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { t as ATTACHMENT_SOURCES, r as availableAttachmentSources, n as deviceHasCamera };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy text to the clipboard, reporting whether it actually happened.
|
|
3
|
+
*
|
|
4
|
+
* `navigator.clipboard` is undefined outside a secure context (plain http on a
|
|
5
|
+
* LAN address — how an on-farm tablet often reaches an internal host), and
|
|
6
|
+
* `writeText` can also reject when the document is not focused or permission is
|
|
7
|
+
* denied. Fire-and-forgetting it means the UI claims "Copied" while the
|
|
8
|
+
* clipboard still holds whatever was there before, which is worse than no
|
|
9
|
+
* feedback: the user pastes stale content and does not know why.
|
|
10
|
+
*/
|
|
11
|
+
export declare function copyToClipboard(text: string): Promise<boolean>;
|
|
12
|
+
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/clipboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQpE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active workspace / organization resolution for the assistant.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from AssistantChatArea so the composer's attachment uploads resolve
|
|
5
|
+
* the SAME workspace the turn is sent to — a second, subtly different copy of
|
|
6
|
+
* this lookup is exactly how files end up in the wrong tenant.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getWorkspaceId(): string;
|
|
9
|
+
export declare function getOrganizationId(): string;
|
|
10
|
+
/**
|
|
11
|
+
* The current user's actor id, read from the access token's `sub` claim.
|
|
12
|
+
*
|
|
13
|
+
* ★ Verified against prod: `sub` is the profile id the RBAC service expects as
|
|
14
|
+
* `actorId`. There is no other source in this app — HealthyBowl has never
|
|
15
|
+
* needed an actor id before, because every authz decision it makes is taken
|
|
16
|
+
* server-side from the token itself.
|
|
17
|
+
*
|
|
18
|
+
* Returns null rather than throwing on anything unexpected: the one caller
|
|
19
|
+
* treats null as "cannot tell", which is deliberately not "denied".
|
|
20
|
+
*/
|
|
21
|
+
export declare function getActorId(): string | null;
|
|
22
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmCH,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAqB1C"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/shared/assistant/context.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
try {
|
|
4
|
+
let t = e === "workspaceId" ? "burdenoff-active-context-workspace" : "burdenoff-active-context-organization", n = sessionStorage.getItem(t);
|
|
5
|
+
if (n) return n;
|
|
6
|
+
let r = localStorage.getItem(t);
|
|
7
|
+
if (r) return r;
|
|
8
|
+
let i = sessionStorage.getItem("bf-active-profile");
|
|
9
|
+
if (!i) return "";
|
|
10
|
+
let a = localStorage.getItem(`bf-p-${i}-context`);
|
|
11
|
+
return a ? JSON.parse(a)[e] ?? "" : "";
|
|
12
|
+
} catch {
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function t() {
|
|
17
|
+
return new URLSearchParams(window.location.search).get("workspace") ?? e("workspaceId");
|
|
18
|
+
}
|
|
19
|
+
function n() {
|
|
20
|
+
return new URLSearchParams(window.location.search).get("org") ?? e("organizationId");
|
|
21
|
+
}
|
|
22
|
+
function r() {
|
|
23
|
+
if (typeof window > "u") return null;
|
|
24
|
+
try {
|
|
25
|
+
let e = window.sessionStorage.getItem("bf-active-profile");
|
|
26
|
+
if (!e) return null;
|
|
27
|
+
let t = window.sessionStorage.getItem(`bf-p-${e}-tokens`) ?? window.localStorage.getItem(`bf-p-${e}-tokens`);
|
|
28
|
+
if (!t) return null;
|
|
29
|
+
let n = JSON.parse(t).accessToken?.split(".")[1];
|
|
30
|
+
return n ? JSON.parse(atob(n.replace(/-/g, "+").replace(/_/g, "/"))).sub ?? null : null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { r as getActorId, n as getOrganizationId, t as getWorkspaceId };
|
|
@@ -72,4 +72,30 @@ export * from './documentText';
|
|
|
72
72
|
export * from './ui/AssistantToolActivity';
|
|
73
73
|
export * from './ui/AssistantSpeechProvider';
|
|
74
74
|
export * from './ui/useVoiceSession';
|
|
75
|
+
export * from './clipboard';
|
|
76
|
+
export * from './context';
|
|
77
|
+
export * from './attachmentSources';
|
|
78
|
+
export * from './sandboxReadiness';
|
|
79
|
+
export * from './ui/AssistantAutoSpeakToggle';
|
|
80
|
+
export * from './ui/AssistantMarkdownImage';
|
|
81
|
+
export * from './ui/AssistantMarkdownRenderer';
|
|
82
|
+
export * from './ui/AssistantMessageAttachments';
|
|
83
|
+
export * from './ui/AssistantPendingWriteCard';
|
|
84
|
+
export * from './ui/AssistantReplyLanguageSelector';
|
|
85
|
+
export * from './ui/AssistantSpeakButton';
|
|
86
|
+
export * from './ui/AssistantSpokenTurnsNotice';
|
|
87
|
+
export * from './ui/composer/AssistantAttachmentButton';
|
|
88
|
+
export * from './ui/composer/AssistantAttachmentChips';
|
|
89
|
+
export * from './ui/composer/AssistantLanguageSelector';
|
|
90
|
+
export * from './ui/composer/AssistantModeSelector';
|
|
91
|
+
export * from './ui/composer/AssistantVoiceButton';
|
|
92
|
+
export * from './ui/composer/AssistantVoiceSelector';
|
|
93
|
+
export * from './ui/voice/AssistantVoiceOrb';
|
|
94
|
+
export * from './ui/voice/AssistantVoiceSessionButton';
|
|
95
|
+
export * from './ui/voice/VoiceCaption';
|
|
96
|
+
export * from './ui/voice/VoiceConversationView';
|
|
97
|
+
export * from './ui/voice/VoiceMiniController';
|
|
98
|
+
export * from './ui/voice/VoiceStage';
|
|
99
|
+
export * from './ui/voice/VoiceTranscriptDrawer';
|
|
100
|
+
export * from './ui/voice/VoiceWaveform';
|
|
75
101
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AAGxB,cAAc,wBAAwB,CAAC;AAKvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAK/B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAG3C,cAAc,QAAQ,CAAC;AAEvB,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AAGxB,cAAc,wBAAwB,CAAC;AAKvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAK/B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAG3C,cAAc,QAAQ,CAAC;AAEvB,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AAGrC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AACpD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC;AACvD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,oCAAoC,CAAC;AACnD,cAAc,sCAAsC,CAAC;AACrD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wCAAwC,CAAC;AACvD,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Was this sandbox proven reachable recently enough to skip re-proving it?
|
|
3
|
+
*
|
|
4
|
+
* Every turn re-ran two network waits before the model saw the prompt — a
|
|
5
|
+
* `WaitForSandboxRunning` long-poll and a `/health` GET through the cf-proxy —
|
|
6
|
+
* even when the previous turn had finished seconds earlier. That is a constant
|
|
7
|
+
* tax on the hot path, paid most visibly in voice, where it sits between the
|
|
8
|
+
* user finishing a sentence and hearing anything at all.
|
|
9
|
+
*
|
|
10
|
+
* The probe is not pointless: a sandbox can expire on its TTL or have its pod
|
|
11
|
+
* reclaimed while still briefly reporting RUNNING, and proxying to a dead pod
|
|
12
|
+
* fails in ways that are worse to surface than a re-provision. So this does not
|
|
13
|
+
* remove the check — it stops repeating a check that just passed.
|
|
14
|
+
*
|
|
15
|
+
* ★ The TTL is deliberately far shorter than the sandbox's own lifetime. Each
|
|
16
|
+
* turn extends the sandbox by 600s, so a 60s readiness window can never outlive
|
|
17
|
+
* the thing it describes; the worst case is that a sandbox dies inside the
|
|
18
|
+
* window and one turn fails, which is exactly what happens today when it dies
|
|
19
|
+
* mid-turn — and the caller already handles that by dropping it and
|
|
20
|
+
* provisioning a fresh one.
|
|
21
|
+
*
|
|
22
|
+
* Correctness rests on the caller invalidating on failure. A cache that only
|
|
23
|
+
* ever gets written on success, and never cleared, would pin a dead sandbox and
|
|
24
|
+
* turn a recoverable error into a loop.
|
|
25
|
+
*/
|
|
26
|
+
/** How long a successful reachability proof stays good for. */
|
|
27
|
+
export declare const SANDBOX_READY_TTL_MS = 60000;
|
|
28
|
+
/**
|
|
29
|
+
* `now` is injected rather than read so the rule is testable and a clock skew
|
|
30
|
+
* cannot make a passing check fail mysteriously.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isRecentlyVerified(lastVerifiedAt: number | null | undefined, now: number, ttlMs?: number): boolean;
|
|
33
|
+
export declare function createReadinessTracker(clock?: () => number): {
|
|
34
|
+
/** True when this sandbox was proven reachable inside the TTL. */
|
|
35
|
+
isFresh(sandboxId: string): boolean;
|
|
36
|
+
/** Record a successful proof. */
|
|
37
|
+
markVerified(sandboxId: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Drop a proof. MUST be called whenever a turn fails against this sandbox,
|
|
40
|
+
* so a dead one is re-probed rather than trusted until the TTL lapses.
|
|
41
|
+
*/
|
|
42
|
+
invalidate(sandboxId: string | null | undefined): void;
|
|
43
|
+
};
|
|
44
|
+
export type ReadinessTracker = ReturnType<typeof createReadinessTracker>;
|
|
45
|
+
//# sourceMappingURL=sandboxReadiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxReadiness.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/sandboxReadiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACzC,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,MAA6B,GACnC,OAAO,CAKT;AAWD,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,MAAM,MAAiB;IAIjE,kEAAkE;uBAC/C,MAAM,GAAG,OAAO;IAGnC,iCAAiC;4BACT,MAAM,GAAG,IAAI;IAKrC;;;OAGG;0BACmB,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI;EAIzD;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/shared/assistant/sandboxReadiness.ts
|
|
2
|
+
var e = 6e4;
|
|
3
|
+
function t(t, n, r = e) {
|
|
4
|
+
return t == null || t > n ? !1 : n - t < r;
|
|
5
|
+
}
|
|
6
|
+
var n = 32;
|
|
7
|
+
function r(e = Date.now) {
|
|
8
|
+
let r = /* @__PURE__ */ new Map();
|
|
9
|
+
return {
|
|
10
|
+
isFresh(n) {
|
|
11
|
+
return t(r.get(n), e());
|
|
12
|
+
},
|
|
13
|
+
markVerified(t) {
|
|
14
|
+
r.size >= n && !r.has(t) && r.clear(), r.set(t, e());
|
|
15
|
+
},
|
|
16
|
+
invalidate(e) {
|
|
17
|
+
e && r.delete(e);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { e as SANDBOX_READY_TTL_MS, r as createReadinessTracker, t as isRecentlyVerified };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Header toggle for "read every reply aloud" (BOFF-6290).
|
|
4
|
+
*
|
|
5
|
+
* Opt-in, persisted per profile, and **off by default** — the assistant must
|
|
6
|
+
* never start talking on its own until someone has said they want that. Turning
|
|
7
|
+
* it on affects the NEXT reply only; it does not start reading the transcript.
|
|
8
|
+
*
|
|
9
|
+
* Renders nothing when the platform has no speech output, so browsers without
|
|
10
|
+
* `speechSynthesis` see no dead switch.
|
|
11
|
+
*
|
|
12
|
+
* Availability is checked here for the same reason the per-message button
|
|
13
|
+
* checks it: with no installed voice for the app language, switching this on
|
|
14
|
+
* would hand every reply to whatever voice the engine defaults to — a Tamil
|
|
15
|
+
* answer read in English. So it is disabled, with the reason as its tooltip,
|
|
16
|
+
* exactly like the Listen button. It stays operable while it is ON, because a
|
|
17
|
+
* switch you cannot turn off is worse than one you cannot turn on.
|
|
18
|
+
*/
|
|
19
|
+
export declare const AssistantAutoSpeakToggle: FC;
|
|
20
|
+
//# sourceMappingURL=AssistantAutoSpeakToggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantAutoSpeakToggle.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantAutoSpeakToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAKhC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,wBAAwB,EAAE,EA4CtC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useAssistantTr as e } from "../i18n.js";
|
|
2
|
+
import { useAssistantSpeech as t } from "./AssistantSpeechProvider.js";
|
|
3
|
+
import { Volume2 as n, VolumeX as r } from "lucide-react";
|
|
4
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
5
|
+
//#region src/shared/assistant/ui/AssistantAutoSpeakToggle.tsx
|
|
6
|
+
var a = () => {
|
|
7
|
+
let a = e(), { isSupported: o, isBlocked: s, disabledReason: c, autoSpeakEnabled: l, setAutoSpeak: u, stop: d } = t();
|
|
8
|
+
if (!o) return null;
|
|
9
|
+
let f = s && !l, p = l ? a("header.autoSpeakOff", "Stop reading replies aloud") : a("header.autoSpeakOn", "Read replies aloud automatically");
|
|
10
|
+
return /* @__PURE__ */ i("button", {
|
|
11
|
+
type: "button",
|
|
12
|
+
onClick: () => {
|
|
13
|
+
l && d(), u(!l);
|
|
14
|
+
},
|
|
15
|
+
disabled: f,
|
|
16
|
+
className: `rounded-lg p-1.5 transition-colors disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent ${l ? "bg-action-primary-bg/15 text-action-primary-bg" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
|
|
17
|
+
title: f ? c ?? p : p,
|
|
18
|
+
"aria-label": p,
|
|
19
|
+
"aria-pressed": l,
|
|
20
|
+
"data-testid": "assistant-auto-speak",
|
|
21
|
+
children: i(l ? n : r, { className: "size-4" })
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { a as AssistantAutoSpeakToggle };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Would rendering this `src` immediately hit a server we do not control?
|
|
4
|
+
*
|
|
5
|
+
* An `<img>` is a GET the browser performs the moment it paints, and the markup
|
|
6
|
+
* here is written by a model that has read tool output, fetched web pages and
|
|
7
|
+
* user-uploaded files. So `` is a working exfiltration beacon that also
|
|
9
|
+
* discloses that a HealthyBowl user opened the reply, when, and from which IP —
|
|
10
|
+
* with no click required. `referrerPolicy` does not help: the payload is the URL
|
|
11
|
+
* itself, not the Referer header.
|
|
12
|
+
*
|
|
13
|
+
* `data:` URLs carry their own bytes and reach no network, so those still render
|
|
14
|
+
* straight away — and that is the shape the transport actually emits for inline
|
|
15
|
+
* images (fe-libs `getAssistantMedia`). Anything else is held behind an explicit
|
|
16
|
+
* click, the same bargain email clients have offered for remote images for
|
|
17
|
+
* twenty years. Unparseable input fails CLOSED.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isRemoteImageSrc(src: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Host to name in the "show this?" prompt, so the choice is an informed one.
|
|
22
|
+
*
|
|
23
|
+
* Parsed with NO base URL on purpose. Resolving against `location.href` would
|
|
24
|
+
* make a relative or malformed src inherit OUR hostname, and the prompt would
|
|
25
|
+
* then read "Show image from app.gethealthybowl.com" for something this app does
|
|
26
|
+
* not serve — a prompt that lies is worse than an unnamed one. Absolute http(s)
|
|
27
|
+
* only; everything else falls back to the generic wording.
|
|
28
|
+
*/
|
|
29
|
+
export declare function imageHostLabel(src: string): string | null;
|
|
30
|
+
interface Props {
|
|
31
|
+
/** Resolved `src` — already scheme-checked by the renderer's urlTransform. */
|
|
32
|
+
src?: string;
|
|
33
|
+
alt?: string;
|
|
34
|
+
title?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const AssistantMarkdownImage: FC<Props>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=AssistantMarkdownImage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantMarkdownImage.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMarkdownImage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAKL,KAAK,EAAE,EAER,MAAM,OAAO,CAAC;AAKf;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASzD;AAED,UAAU,KAAK;IACb,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD,eAAO,MAAM,sBAAsB,EAAE,EAAE,CAAC,KAAK,CAkR5C,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useAssistantTr as e } from "../i18n.js";
|
|
2
|
+
import { useCallback as t, useEffect as n, useRef as r, useState as i } from "react";
|
|
3
|
+
import { Eye as a, ImageOff as o, X as s } from "lucide-react";
|
|
4
|
+
import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
5
|
+
import { createPortal as d } from "react-dom";
|
|
6
|
+
//#region src/shared/assistant/ui/AssistantMarkdownImage.tsx
|
|
7
|
+
function f(e) {
|
|
8
|
+
return !/^data:/i.test(e.trim());
|
|
9
|
+
}
|
|
10
|
+
function p(e) {
|
|
11
|
+
try {
|
|
12
|
+
let t = new URL(e.trim());
|
|
13
|
+
return t.protocol === "http:" || t.protocol === "https:" ? t.hostname : null;
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
var m = [], h = ({ src: h, alt: g, title: _ }) => {
|
|
19
|
+
let v = e(), [y, b] = i(!1), [x, S] = i(!1), [C, w] = i(!1), T = r(null), E = r(null), D = r(null), O = r(null), k = g?.trim() ?? "", A = !!h && f(h) && !C, j = x && !A && !y && !!h;
|
|
20
|
+
n(() => {
|
|
21
|
+
if (!j) return;
|
|
22
|
+
let e = D.current, t = document.body.style.overflow, n = T.current;
|
|
23
|
+
document.body.style.overflow = "hidden", e && m.push(e);
|
|
24
|
+
let r = () => m[m.length - 1] === D.current, i = () => Array.from(D.current?.querySelectorAll("button:not([disabled]), [href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex=\"-1\"])") ?? []), a = () => {
|
|
25
|
+
let e = D.current;
|
|
26
|
+
e && (i()[0] ?? e).focus();
|
|
27
|
+
};
|
|
28
|
+
a();
|
|
29
|
+
let o = (e) => {
|
|
30
|
+
if (!(!r() || e.isComposing)) {
|
|
31
|
+
if (e.key === "Escape") {
|
|
32
|
+
e.stopPropagation(), S(!1);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (e.key === "Tab") {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
let t = i();
|
|
38
|
+
if (t.length === 0) {
|
|
39
|
+
D.current?.focus();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
let n = t.indexOf(document.activeElement), r = e.shiftKey ? -1 : 1;
|
|
43
|
+
t[n < 0 ? 0 : (n + r + t.length) % t.length]?.focus();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}, s = (e) => {
|
|
47
|
+
if (!r()) return;
|
|
48
|
+
let t = D.current;
|
|
49
|
+
t && !t.contains(e.target) && a();
|
|
50
|
+
};
|
|
51
|
+
return document.addEventListener("keydown", o, !0), document.addEventListener("focusin", s), () => {
|
|
52
|
+
document.removeEventListener("keydown", o, !0), document.removeEventListener("focusin", s);
|
|
53
|
+
let r = e ? m.indexOf(e) : -1;
|
|
54
|
+
r >= 0 && m.splice(r, 1), document.body.style.overflow = t, n?.isConnected ? n.focus() : O.current?.focus();
|
|
55
|
+
};
|
|
56
|
+
}, [j]);
|
|
57
|
+
let M = t(() => S(!1), []), N = (e) => {
|
|
58
|
+
e.target === e.currentTarget && M();
|
|
59
|
+
};
|
|
60
|
+
if (h && A && !y) {
|
|
61
|
+
let e = p(h);
|
|
62
|
+
return /* @__PURE__ */ u("button", {
|
|
63
|
+
type: "button",
|
|
64
|
+
onClick: () => w(!0),
|
|
65
|
+
className: "my-2 flex max-w-full items-center gap-2 rounded-lg border border-dashed border-border-default bg-bg-sunken px-3 py-2 text-left text-[0.9em] text-text-secondary transition-colors hover:border-border-strong hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
66
|
+
"data-testid": "assistant-markdown-image-gated",
|
|
67
|
+
children: [/* @__PURE__ */ l(a, {
|
|
68
|
+
className: "size-4 flex-shrink-0",
|
|
69
|
+
"aria-hidden": "true"
|
|
70
|
+
}), /* @__PURE__ */ u("span", {
|
|
71
|
+
className: "min-w-0",
|
|
72
|
+
children: [/* @__PURE__ */ l("span", {
|
|
73
|
+
className: "block break-words",
|
|
74
|
+
children: e ? v("markdown.showRemoteImageNamed", "Show image from {{host}}", { host: e }) : v("markdown.showRemoteImage", "Show remote image")
|
|
75
|
+
}), k && /* @__PURE__ */ l("span", {
|
|
76
|
+
className: "block truncate text-[0.85em] text-text-muted",
|
|
77
|
+
children: k
|
|
78
|
+
})]
|
|
79
|
+
})]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return !h || y ? /* @__PURE__ */ u("span", {
|
|
83
|
+
ref: O,
|
|
84
|
+
tabIndex: -1,
|
|
85
|
+
className: "my-2 inline-flex max-w-full items-center gap-2 rounded-lg border border-dashed border-border-default bg-bg-sunken px-3 py-2 text-[0.9em] text-text-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
86
|
+
"data-testid": "assistant-markdown-image-fallback",
|
|
87
|
+
children: [/* @__PURE__ */ l(o, {
|
|
88
|
+
className: "size-4 flex-shrink-0",
|
|
89
|
+
"aria-hidden": "true"
|
|
90
|
+
}), /* @__PURE__ */ l("span", {
|
|
91
|
+
className: "min-w-0 break-words",
|
|
92
|
+
children: k || v("markdown.imageUnavailable", "Image unavailable")
|
|
93
|
+
})]
|
|
94
|
+
}) : /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l("button", {
|
|
95
|
+
ref: T,
|
|
96
|
+
type: "button",
|
|
97
|
+
onClick: () => S(!0),
|
|
98
|
+
"aria-label": k ? v("markdown.enlargeImageNamed", "Enlarge image: {{alt}}", { alt: k }) : v("markdown.enlargeImage", "Enlarge image"),
|
|
99
|
+
className: "my-2 block max-w-full cursor-zoom-in overflow-hidden rounded-lg border border-border-subtle bg-bg-sunken focus:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
100
|
+
"data-testid": "assistant-markdown-image",
|
|
101
|
+
children: /* @__PURE__ */ l("img", {
|
|
102
|
+
src: h,
|
|
103
|
+
alt: k,
|
|
104
|
+
title: _,
|
|
105
|
+
loading: "lazy",
|
|
106
|
+
decoding: "async",
|
|
107
|
+
referrerPolicy: "no-referrer",
|
|
108
|
+
onError: () => b(!0),
|
|
109
|
+
className: "max-h-64 w-auto max-w-full object-contain"
|
|
110
|
+
})
|
|
111
|
+
}), j && typeof document < "u" && d(/* @__PURE__ */ u("div", {
|
|
112
|
+
ref: D,
|
|
113
|
+
role: "dialog",
|
|
114
|
+
"aria-modal": "true",
|
|
115
|
+
tabIndex: -1,
|
|
116
|
+
"aria-label": k ? v("markdown.imagePreviewNamed", "Image preview: {{alt}}", { alt: k }) : v("markdown.imagePreview", "Image preview"),
|
|
117
|
+
onClick: N,
|
|
118
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-canvas/95 p-6 backdrop-blur-sm focus:outline-none motion-safe:transition-opacity",
|
|
119
|
+
"data-testid": "assistant-markdown-image-lightbox",
|
|
120
|
+
children: [/* @__PURE__ */ l("button", {
|
|
121
|
+
ref: E,
|
|
122
|
+
type: "button",
|
|
123
|
+
onClick: M,
|
|
124
|
+
"aria-label": v("markdown.closePreview", "Close image preview"),
|
|
125
|
+
className: "absolute right-4 top-4 flex size-9 items-center justify-center rounded-full border border-border-subtle bg-bg-surface text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
126
|
+
"data-testid": "assistant-markdown-image-close",
|
|
127
|
+
children: /* @__PURE__ */ l(s, { className: "size-4" })
|
|
128
|
+
}), /* @__PURE__ */ u("figure", {
|
|
129
|
+
className: "flex max-h-full min-w-0 flex-col items-center gap-2",
|
|
130
|
+
children: [/* @__PURE__ */ l("img", {
|
|
131
|
+
src: h,
|
|
132
|
+
alt: k,
|
|
133
|
+
referrerPolicy: "no-referrer",
|
|
134
|
+
onError: () => {
|
|
135
|
+
b(!0), M();
|
|
136
|
+
},
|
|
137
|
+
className: "max-h-[80vh] max-w-full rounded-lg object-contain",
|
|
138
|
+
"data-testid": "assistant-markdown-image-full"
|
|
139
|
+
}), k && /* @__PURE__ */ l("figcaption", {
|
|
140
|
+
className: "max-w-xl text-center text-xs text-text-secondary",
|
|
141
|
+
children: k
|
|
142
|
+
})]
|
|
143
|
+
})]
|
|
144
|
+
}), document.body)] });
|
|
145
|
+
};
|
|
146
|
+
//#endregion
|
|
147
|
+
export { h as AssistantMarkdownImage, p as imageHostLabel, f as isRemoteImageSrc };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { Options } from 'react-markdown';
|
|
3
|
+
interface Props {
|
|
4
|
+
content: string;
|
|
5
|
+
}
|
|
6
|
+
/** @internal Exported for unit tests; not part of the module's public surface. */
|
|
7
|
+
export declare const assistantUrlTransform: Options["urlTransform"];
|
|
8
|
+
export declare const AssistantMarkdownRenderer: FC<Props>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=AssistantMarkdownRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantMarkdownRenderer.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMarkdownRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAY,KAAK,EAAE,EAAkB,MAAM,OAAO,CAAC;AAC1D,OAAiB,EAIf,KAAK,OAAO,EACb,MAAM,gBAAgB,CAAC;AAQxB,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAqDD,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,EAAE,OAAO,CAAC,cAAc,CAWzD,CAAC;AAkQF,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,KAAK,CAiB/C,CAAC"}
|