@ai-matrx/chat 0.5.3 → 0.5.5
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/CHANGELOG.md +72 -0
- package/dist/agents/components/chat/ChatIncognitoHeaderButton.js +1 -1
- package/dist/agents/components/chat/SandboxPanel.js +1 -1
- package/dist/agents/components/context-items/registry.js +14 -3
- package/dist/agents/components/conversation-history/ConversationHistorySidebar.js +1 -1
- package/dist/agents/components/inputs/resources/SmartAgentResourceChips.js +4 -1
- package/dist/agents/components/inputs/resources/useAttachRenderedArtifact.d.ts +20 -0
- package/dist/agents/components/inputs/resources/useAttachRenderedArtifact.js +107 -0
- package/dist/agents/components/inputs/smart-input/RunControlsTabPanel.js +2 -1
- package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPill.js +135 -204
- package/dist/agents/components/inputs/smart-input/composer/already-answering.d.ts +1 -1
- package/dist/agents/components/inputs/smart-input/composer/already-answering.js +1 -1
- package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.d.ts +1 -1
- package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.js +1 -1
- package/dist/agents/components/inputs/variable-input-variations/AgentVariableCards.js +6 -5
- package/dist/agents/components/inputs/variable-input-variations/AgentVariableForm.js +6 -5
- package/dist/agents/components/inputs/variable-input-variations/AgentVariablesGuided.js +6 -5
- package/dist/agents/components/messages-display/user/UserActionBar.js +1 -1
- package/dist/agents/components/run/AgentRunHeader.js +1 -1
- package/dist/agents/components/run/run-sidebar/SidebarHeader.js +1 -1
- package/dist/agents/components/run-controls/RunModelPicker.d.ts +5 -1
- package/dist/agents/components/run-controls/RunModelPicker.js +23 -1
- package/dist/agents/components/shared/AgentConversationColumn.js +3 -1
- package/dist/agents/components/shared/AgentHeader.js +1 -1
- package/dist/agents/components/shared/AgentHeaderMobile.js +1 -1
- package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
- package/dist/agents/components/shared/assistant-message-layout.d.ts +21 -0
- package/dist/agents/components/shared/assistant-message-layout.js +7 -1
- package/dist/agents/components/working-document/WorkingDocumentVersionHistory.js +1 -1
- package/dist/agents/redux/execution-system/instance-resources/instance-resources.selectors.js +14 -0
- package/dist/agents/types/instance.types.d.ts +18 -3
- package/dist/agents/utils/renderedArtifactContext.d.ts +41 -0
- package/dist/agents/utils/renderedArtifactContext.js +41 -0
- package/dist/conversation/hooks/useDomCapturePrint.js +4 -0
- package/dist/conversation/utils/frame-print.d.ts +11 -0
- package/dist/conversation/utils/frame-print.js +46 -0
- package/dist/conversation/utils/markdown-print.d.ts +9 -1
- package/dist/conversation/utils/markdown-print.js +55 -26
- package/dist/cx-chat/components/ChatMobileHeaderBar.js +1 -1
- package/dist/cx-chat/components/ChatPanelBackButton.js +1 -1
- package/dist/cx-chat/components/ChatSidebarClient.js +1 -1
- package/dist/cx-chat/components/user-input/ConversationInput.js +2 -2
- package/dist/cx-chat/components/user-input/InputActionButtons.js +2 -2
- package/dist/host/frame-print.d.ts +17 -0
- package/dist/host/frame-print.js +13 -0
- package/dist/surfaces/components/chrome/SurfaceAgentsHeaderButton.js +1 -1
- package/dist/tool-call-visualization/registry/registry.js +29 -28
- package/dist/voice-agent/components/VoiceAgentSurface.js +2 -2
- package/package.json +29 -9
|
@@ -8,7 +8,7 @@ import { selectAgentIsDirty } from "../../redux/agent-definition/selectors.js";
|
|
|
8
8
|
import {
|
|
9
9
|
TapTargetButtonForGroup,
|
|
10
10
|
TapTargetButtonGroup
|
|
11
|
-
} from "@ai-matrx/tap-target";
|
|
11
|
+
} from "@ai-matrx/design-system/tap-target";
|
|
12
12
|
import { AgentListDropdown } from "@ai-matrx/agents/catalog/react";
|
|
13
13
|
import {
|
|
14
14
|
AlertDialog,
|
|
@@ -4,7 +4,7 @@ import { useCallback } from "react";
|
|
|
4
4
|
import { useRouter, usePathname, useSearchParams } from "../../../host/navigation.js";
|
|
5
5
|
import { useAppDispatch, useAppSelector } from "../../../store/hooks.js";
|
|
6
6
|
import { startNewConversation } from "../../redux/execution-system/thunks/create-instance.thunk.js";
|
|
7
|
-
import { PlusTapButton } from "@ai-matrx/tap-target/buttons";
|
|
7
|
+
import { PlusTapButton } from "@ai-matrx/design-system/tap-target/buttons";
|
|
8
8
|
import { selectFocusedConversation } from "../../redux/execution-system/conversation-focus/conversation-focus.selectors.js";
|
|
9
9
|
import { pushAddressWithoutNavigating } from "@ai-matrx/rich-content/utils/addressWithoutNavigating";
|
|
10
10
|
function AgentNewRunButton({ surfaceKey }) {
|
|
@@ -6,3 +6,24 @@
|
|
|
6
6
|
export declare const ASSISTANT_MESSAGE_COLUMN_CLASS = "mx-auto w-full max-w-3xl";
|
|
7
7
|
/** Chat's exact horizontal inset inside the canonical reading column. */
|
|
8
8
|
export declare const ASSISTANT_MESSAGE_COLUMN_INSET_CLASS = "px-2";
|
|
9
|
+
/**
|
|
10
|
+
* WIDE FIGURES — a figure (a rendered web page, a wide visual) is not prose.
|
|
11
|
+
* A transcript that opts in carries `WIDE_FIGURE_SCOPE_ATTRIBUTE` on its scroll
|
|
12
|
+
* area, which is a CSS size container; inside it an element with
|
|
13
|
+
* `WIDE_FIGURE_CLASS` breaks out of the reading column to the transcript's own
|
|
14
|
+
* width (`100cqi`), minus a 1rem gutter each side, centered on the column, and
|
|
15
|
+
* capped at `WIDE_FIGURE_MAX_WIDTH_PX`.
|
|
16
|
+
*
|
|
17
|
+
* 1100px: wide enough that the common 960–1080px layouts AI-written pages use
|
|
18
|
+
* (a 960px `main` plus its padding) render at full size; no wider, so a large
|
|
19
|
+
* monitor never stretches a figure into empty bands. `max(100%, …)` means a
|
|
20
|
+
* transcript no wider than the column (phones, split panes) leaves the figure
|
|
21
|
+
* exactly column-wide. Outside an opted-in transcript (notes, rich-document
|
|
22
|
+
* surfaces, a `width: "fill"` host, a start-aligned column) it does nothing.
|
|
23
|
+
*
|
|
24
|
+
* Classes are literal strings so the host's Tailwind scan of this package
|
|
25
|
+
* finds them; keep 1100px in sync with WIDE_FIGURE_MAX_WIDTH_PX.
|
|
26
|
+
*/
|
|
27
|
+
export declare const WIDE_FIGURE_MAX_WIDTH_PX = 1100;
|
|
28
|
+
export declare const WIDE_FIGURE_SCOPE_ATTRIBUTE = "data-wide-figures";
|
|
29
|
+
export declare const WIDE_FIGURE_CLASS = "[[data-wide-figures]_&]:w-[max(100%,min(1100px,100cqi_-_2rem))] [[data-wide-figures]_&]:ml-[calc((100%_-_max(100%,min(1100px,100cqi_-_2rem)))/2)]";
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const ASSISTANT_MESSAGE_COLUMN_CLASS = "mx-auto w-full max-w-3xl";
|
|
2
2
|
const ASSISTANT_MESSAGE_COLUMN_INSET_CLASS = "px-2";
|
|
3
|
+
const WIDE_FIGURE_MAX_WIDTH_PX = 1100;
|
|
4
|
+
const WIDE_FIGURE_SCOPE_ATTRIBUTE = "data-wide-figures";
|
|
5
|
+
const WIDE_FIGURE_CLASS = "[[data-wide-figures]_&]:w-[max(100%,min(1100px,100cqi_-_2rem))] [[data-wide-figures]_&]:ml-[calc((100%_-_max(100%,min(1100px,100cqi_-_2rem)))/2)]";
|
|
3
6
|
export {
|
|
4
7
|
ASSISTANT_MESSAGE_COLUMN_CLASS,
|
|
5
|
-
ASSISTANT_MESSAGE_COLUMN_INSET_CLASS
|
|
8
|
+
ASSISTANT_MESSAGE_COLUMN_INSET_CLASS,
|
|
9
|
+
WIDE_FIGURE_CLASS,
|
|
10
|
+
WIDE_FIGURE_MAX_WIDTH_PX,
|
|
11
|
+
WIDE_FIGURE_SCOPE_ATTRIBUTE
|
|
6
12
|
};
|
|
@@ -15,7 +15,7 @@ import { useWorkingDocumentVersions } from "./useWorkingDocumentVersions.js";
|
|
|
15
15
|
import { useWorkingDocument } from "../../hooks/useWorkingDocument.js";
|
|
16
16
|
import { setWorkingDocMainView } from "./workingDocumentViewStore.js";
|
|
17
17
|
import { ErrorAlchemyMenu } from "../../../host/ui-slots.js";
|
|
18
|
-
import { ChevronLeftTapButton, ChevronRightTapButton } from "@ai-matrx/tap-target/buttons";
|
|
18
|
+
import { ChevronLeftTapButton, ChevronRightTapButton } from "@ai-matrx/design-system/tap-target/buttons";
|
|
19
19
|
function formatWhen(iso) {
|
|
20
20
|
if (!iso) return "Now";
|
|
21
21
|
try {
|
package/dist/agents/redux/execution-system/instance-resources/instance-resources.selectors.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderedArtifactContextEntry } from "../../../utils/renderedArtifactContext.js";
|
|
1
2
|
import { createSelector } from "@reduxjs/toolkit";
|
|
2
3
|
import {
|
|
3
4
|
isMessagePart,
|
|
@@ -445,6 +446,7 @@ function buildResourcePayload(resource) {
|
|
|
445
446
|
};
|
|
446
447
|
}
|
|
447
448
|
case "processed_document":
|
|
449
|
+
case "rendered_artifact":
|
|
448
450
|
case "source_ref":
|
|
449
451
|
case "editor_error":
|
|
450
452
|
case "editor_code_snippet":
|
|
@@ -522,6 +524,18 @@ const selectResourceContextPayload = (conversationId) => createSelector(
|
|
|
522
524
|
if (!resources) return void 0;
|
|
523
525
|
const payload = {};
|
|
524
526
|
for (const resource of Object.values(resources)) {
|
|
527
|
+
if (resource.blockType === "rendered_artifact" && resource.status === "ready") {
|
|
528
|
+
const wire = renderedArtifactContextEntry(
|
|
529
|
+
resource.source,
|
|
530
|
+
resource.options.representation
|
|
531
|
+
);
|
|
532
|
+
if (!wire) {
|
|
533
|
+
if (resource.options.representation === "screenshot") continue;
|
|
534
|
+
invalidResource(resource, "rendered artifact reference is malformed");
|
|
535
|
+
}
|
|
536
|
+
payload[wire.key] = wire.reference;
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
525
539
|
if (resource.blockType !== "processed_document" || resource.status !== "ready") {
|
|
526
540
|
continue;
|
|
527
541
|
}
|
|
@@ -354,21 +354,36 @@ export type ResourceStatus = "pending" | "resolving" | "ready" | "error";
|
|
|
354
354
|
* code editor surfaces (errors, code snippets) where the persisted user
|
|
355
355
|
* message must round-trip through the DB and re-render as chips on load.
|
|
356
356
|
*/
|
|
357
|
-
export type ResourceBlockType = "text" | "image" | "audio" | "video" | "youtube_video" | "document" | "input_webpage" | "input_notes" | "input_task" | "input_table" | "input_list" | "input_data" | "input_agent" | "input_project" | "input_agent_app" | "input_transcript" | "input_transcript_session" | "input_workbook" | "input_document" | "processed_document" | "source_ref" | "editor_error" | "editor_code_snippet" | "input_remarks";
|
|
357
|
+
export type ResourceBlockType = "text" | "image" | "audio" | "video" | "youtube_video" | "document" | "input_webpage" | "input_notes" | "input_task" | "input_table" | "input_list" | "input_data" | "input_agent" | "input_project" | "input_agent_app" | "input_transcript" | "input_transcript_session" | "input_workbook" | "input_document" | "processed_document" | "source_ref" | "editor_error" | "editor_code_snippet" | "input_remarks" | "rendered_artifact";
|
|
358
358
|
/**
|
|
359
359
|
* Which representation of a processed document is the PRIMARY one placed in the
|
|
360
360
|
* agent's context. The agent reaches the other formats (raw text, per-page,
|
|
361
361
|
* knowledge assets, PDF page images) on demand via the `document_content` tool.
|
|
362
362
|
*/
|
|
363
363
|
export type DocumentRepresentation = "clean" | "raw" | "pdf";
|
|
364
|
+
/**
|
|
365
|
+
* Representations of a rendered artifact (HTML page / canvas item). `code` is the
|
|
366
|
+
* document, `text` what a reader sees, `screenshot` the image as rendered (an
|
|
367
|
+
* explicit image part produced at attach time).
|
|
368
|
+
*/
|
|
369
|
+
export type RenderedArtifactRepresentation = "code" | "screenshot" | "text";
|
|
370
|
+
/** The representation choices, per resource type — one union, keyed by type. */
|
|
371
|
+
export interface RepresentationByResourceType {
|
|
372
|
+
processed_document: DocumentRepresentation;
|
|
373
|
+
rendered_artifact: RenderedArtifactRepresentation;
|
|
374
|
+
}
|
|
375
|
+
export type ResourceRepresentation = RepresentationByResourceType[keyof RepresentationByResourceType];
|
|
364
376
|
export interface ResourceOptions {
|
|
365
377
|
keepFresh: boolean;
|
|
366
378
|
editable: boolean;
|
|
367
379
|
convertToText: boolean;
|
|
368
380
|
optionalContext: boolean;
|
|
369
381
|
template?: "full" | "compact" | "minimal";
|
|
370
|
-
/**
|
|
371
|
-
|
|
382
|
+
/**
|
|
383
|
+
* The chosen primary representation — its legal values depend on the
|
|
384
|
+
* resource type (`RepresentationByResourceType`).
|
|
385
|
+
*/
|
|
386
|
+
representation?: ResourceRepresentation;
|
|
372
387
|
/** Per-reference file-family promotion/suppression policy. */
|
|
373
388
|
resourcePolicy?: VariableResourceContextConfig;
|
|
374
389
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendered-artifact context items (rendered-output standard, guarantee 3).
|
|
3
|
+
*
|
|
4
|
+
* An HTML page or HTML canvas item is attachable to a chat turn three ways:
|
|
5
|
+
* - `code` — the document, as a resource reference (canvas_item preferred),
|
|
6
|
+
* - `text` — what a reader sees, the server resolver's `text` representation,
|
|
7
|
+
* - `screenshot` — the page as rendered, captured at ATTACH time by the host's
|
|
8
|
+
* capture port and attached as an explicit image part
|
|
9
|
+
* (`input_image`, the paste-image path). Never captured at send.
|
|
10
|
+
*
|
|
11
|
+
* `code` / `text` ride `request.context` as a resource reference
|
|
12
|
+
* (`selectResourceContextPayload`); they never become `user_input`.
|
|
13
|
+
*/
|
|
14
|
+
import { createResourceReference } from "../agent-context/resource-reference.js";
|
|
15
|
+
import type { RenderedArtifactRepresentation, ResourceRepresentation } from "../types/instance.types.js";
|
|
16
|
+
export declare const RENDERED_ARTIFACT_BLOCK_TYPE = "rendered_artifact";
|
|
17
|
+
export type RenderedArtifactRecordType = "canvas_item" | "html_page";
|
|
18
|
+
/** The `source` of a `rendered_artifact` resource. */
|
|
19
|
+
export interface RenderedArtifactSource {
|
|
20
|
+
kind: "rendered_artifact";
|
|
21
|
+
record_type: RenderedArtifactRecordType;
|
|
22
|
+
record_id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
}
|
|
25
|
+
export interface RenderedArtifactRepresentationOption {
|
|
26
|
+
value: RenderedArtifactRepresentation;
|
|
27
|
+
label: string;
|
|
28
|
+
hint: string;
|
|
29
|
+
}
|
|
30
|
+
/** The options a rendered artifact offers, data-driven so a new one is one entry. */
|
|
31
|
+
export declare function availableRenderedArtifactRepresentations(): RenderedArtifactRepresentationOption[];
|
|
32
|
+
export declare function isRenderedArtifactSource(source: unknown): source is RenderedArtifactSource;
|
|
33
|
+
export declare function isRenderedArtifactRepresentation(value: unknown): value is RenderedArtifactRepresentation;
|
|
34
|
+
/**
|
|
35
|
+
* The `request.context` entry for a code/text attachment, or null when the
|
|
36
|
+
* source is malformed or the choice is `screenshot` (an image part, not context).
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderedArtifactContextEntry(source: unknown, representation: ResourceRepresentation | undefined): {
|
|
39
|
+
key: string;
|
|
40
|
+
reference: ReturnType<typeof createResourceReference>;
|
|
41
|
+
} | null;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createResourceReference } from "../agent-context/resource-reference.js";
|
|
2
|
+
const RENDERED_ARTIFACT_BLOCK_TYPE = "rendered_artifact";
|
|
3
|
+
function availableRenderedArtifactRepresentations() {
|
|
4
|
+
return [
|
|
5
|
+
{ value: "screenshot", label: "Screenshot", hint: "The page as you see it" },
|
|
6
|
+
{ value: "code", label: "Code", hint: "The full HTML document" },
|
|
7
|
+
{ value: "text", label: "Text", hint: "The words on the page" }
|
|
8
|
+
];
|
|
9
|
+
}
|
|
10
|
+
function isRenderedArtifactSource(source) {
|
|
11
|
+
if (!source || typeof source !== "object") return false;
|
|
12
|
+
const s = source;
|
|
13
|
+
return s.kind === "rendered_artifact" && (s.record_type === "canvas_item" || s.record_type === "html_page") && typeof s.record_id === "string" && s.record_id.length > 0;
|
|
14
|
+
}
|
|
15
|
+
function isRenderedArtifactRepresentation(value) {
|
|
16
|
+
return value === "code" || value === "screenshot" || value === "text";
|
|
17
|
+
}
|
|
18
|
+
function serverRepresentation(recordType, representation) {
|
|
19
|
+
if (representation === "text") return "text";
|
|
20
|
+
return recordType === "html_page" ? "html" : "content";
|
|
21
|
+
}
|
|
22
|
+
function renderedArtifactContextEntry(source, representation) {
|
|
23
|
+
if (!isRenderedArtifactSource(source)) return null;
|
|
24
|
+
const rep = isRenderedArtifactRepresentation(
|
|
25
|
+
representation
|
|
26
|
+
) ? representation : "code";
|
|
27
|
+
if (rep === "screenshot") return null;
|
|
28
|
+
return {
|
|
29
|
+
key: `resource_${source.record_type}_${source.record_id}_${rep}`,
|
|
30
|
+
reference: createResourceReference(source.record_type, source.record_id, {
|
|
31
|
+
representation: serverRepresentation(source.record_type, rep)
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
RENDERED_ARTIFACT_BLOCK_TYPE,
|
|
37
|
+
availableRenderedArtifactRepresentations,
|
|
38
|
+
isRenderedArtifactRepresentation,
|
|
39
|
+
isRenderedArtifactSource,
|
|
40
|
+
renderedArtifactContextEntry
|
|
41
|
+
};
|
|
@@ -15,9 +15,12 @@ function useDomCapturePrint() {
|
|
|
15
15
|
setProgress(0);
|
|
16
16
|
setError(null);
|
|
17
17
|
let releaseRenderAll = null;
|
|
18
|
+
let restoreFrames = null;
|
|
18
19
|
try {
|
|
19
20
|
const { renderAllDiagrams } = await import("@ai-matrx/rich-content/mermaid/lazy-draw");
|
|
20
21
|
releaseRenderAll = (await renderAllDiagrams()).release;
|
|
22
|
+
const { framesToPictures } = await import("../utils/frame-print.js");
|
|
23
|
+
restoreFrames = (await framesToPictures(el)).restore;
|
|
21
24
|
const [{ captureDocumentPdf }, { downloadFile }] = await Promise.all([
|
|
22
25
|
import("@ai-matrx/alchemy/operate/capture"),
|
|
23
26
|
import("@ai-matrx/kit/download")
|
|
@@ -36,6 +39,7 @@ function useDomCapturePrint() {
|
|
|
36
39
|
const msg = err instanceof Error ? err.message : "PDF export failed";
|
|
37
40
|
setError(msg);
|
|
38
41
|
} finally {
|
|
42
|
+
restoreFrames?.();
|
|
39
43
|
releaseRenderAll?.();
|
|
40
44
|
setIsCapturing(false);
|
|
41
45
|
setProgress(0);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full Print's frame pre-pass (the `renderAllDiagrams` of live pages): every
|
|
3
|
+
* `<iframe>` inside the captured message is shown as its picture — from the
|
|
4
|
+
* host's frame capture port — or as an honest stand-in, for exactly as long
|
|
5
|
+
* as the capture runs. The frames are hidden, never removed (removing one
|
|
6
|
+
* reloads its page), and come back untouched when `restore` runs.
|
|
7
|
+
*/
|
|
8
|
+
export declare function framesToPictures(root: HTMLElement): Promise<{
|
|
9
|
+
restore: () => void;
|
|
10
|
+
frames: number;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { FRAME_PRINT_STAND_IN, getFramePrintCapture } from "../../host/frame-print.js";
|
|
2
|
+
async function framesToPictures(root) {
|
|
3
|
+
const frames = [...root.querySelectorAll("iframe")];
|
|
4
|
+
if (frames.length === 0) return { restore: () => void 0, frames: 0 };
|
|
5
|
+
const capture = getFramePrintCapture();
|
|
6
|
+
const undo = [];
|
|
7
|
+
await Promise.all(
|
|
8
|
+
frames.map(async (frame) => {
|
|
9
|
+
let src = null;
|
|
10
|
+
if (capture) {
|
|
11
|
+
try {
|
|
12
|
+
src = await capture(frame);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error("[print] a page could not be captured for print", error);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const width = frame.clientWidth || frame.offsetWidth;
|
|
18
|
+
const height = frame.clientHeight || frame.offsetHeight;
|
|
19
|
+
let stand;
|
|
20
|
+
if (src) {
|
|
21
|
+
const img = document.createElement("img");
|
|
22
|
+
img.src = src;
|
|
23
|
+
img.alt = frame.title || "Web page";
|
|
24
|
+
img.style.cssText = `display:block;width:${width ? `${width}px` : "100%"};max-width:100%;height:auto`;
|
|
25
|
+
await img.decode().catch(() => void 0);
|
|
26
|
+
stand = img;
|
|
27
|
+
} else {
|
|
28
|
+
stand = document.createElement("div");
|
|
29
|
+
stand.textContent = FRAME_PRINT_STAND_IN;
|
|
30
|
+
stand.style.cssText = `display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:${width ? `${width}px` : "100%"};max-width:100%;min-height:${Math.min(Math.max(height, 96), 240)}px;padding:16px;border:1px dashed #94a3b8;border-radius:8px;color:#475569;font:italic 13px/1.4 system-ui,sans-serif;text-align:center;background:#f8fafc`;
|
|
31
|
+
}
|
|
32
|
+
stand.setAttribute("data-matrx-frame-print", "");
|
|
33
|
+
const display = frame.style.display;
|
|
34
|
+
frame.style.display = "none";
|
|
35
|
+
frame.after(stand);
|
|
36
|
+
undo.push(() => {
|
|
37
|
+
stand.remove();
|
|
38
|
+
frame.style.display = display;
|
|
39
|
+
});
|
|
40
|
+
})
|
|
41
|
+
);
|
|
42
|
+
return { restore: () => undo.forEach((fn) => fn()), frames: frames.length };
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
framesToPictures
|
|
46
|
+
};
|
|
@@ -12,5 +12,13 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Fix conversion or styling in the package (SAME-SESSION LAW), never here.
|
|
14
14
|
*/
|
|
15
|
-
import type
|
|
15
|
+
import { type PrintOutcome } from "@ai-matrx/print/core";
|
|
16
|
+
/**
|
|
17
|
+
* Prints a message: its prose through the article skin, and every embedded
|
|
18
|
+
* block through the SAME adapter its own print uses (`@ai-matrx/print`'s one
|
|
19
|
+
* block-printer registry, keyed by artifact type / `__kind`) — a quiz as its
|
|
20
|
+
* questions, a deck as its study sheet, a diagram as its drawing, an HTML page
|
|
21
|
+
* as its picture (a print window runs no author scripts; the page's own Print
|
|
22
|
+
* is the sharp one, and the person is told so).
|
|
23
|
+
*/
|
|
16
24
|
export declare function printMarkdownContent(markdown: string, title?: string): PrintOutcome | Promise<PrintOutcome>;
|
|
@@ -1,43 +1,72 @@
|
|
|
1
|
-
import { printMarkdown, printMarkdownWhenReady } from "@ai-matrx/print/markdown";
|
|
1
|
+
import { listPrintBlocks, printMarkdown, printMarkdownWhenReady } from "@ai-matrx/print/markdown";
|
|
2
|
+
import { getBlockPrinter } from "@ai-matrx/print/core";
|
|
2
3
|
import { notifyPrintOutcome } from "../../host/ui-slots.js";
|
|
3
|
-
const
|
|
4
|
+
const FRAME_BLOCK_TYPES = /* @__PURE__ */ new Set(["html", "react", "iframe"]);
|
|
5
|
+
function blockData(block) {
|
|
6
|
+
const text = block.body.trim();
|
|
7
|
+
if (text.startsWith("{") || text.startsWith("[")) {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(text);
|
|
10
|
+
} catch {
|
|
11
|
+
return block.body;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return block.body;
|
|
15
|
+
}
|
|
16
|
+
function adapterFor(block) {
|
|
17
|
+
const printer = getBlockPrinter(block.type);
|
|
18
|
+
return printer?.toPrintHtml ? printer : null;
|
|
19
|
+
}
|
|
4
20
|
function printMarkdownContent(markdown, title = "AI Response") {
|
|
5
|
-
|
|
21
|
+
const blocks = listPrintBlocks(markdown, "article");
|
|
22
|
+
const hasMermaid = blocks.some((block) => block.source === "fence" && block.type === "mermaid");
|
|
23
|
+
const adapted = blocks.filter((block) => adapterFor(block) !== null);
|
|
24
|
+
if (!hasMermaid && adapted.length === 0) {
|
|
6
25
|
const outcome = printMarkdown(markdown, { skin: "article", title });
|
|
7
26
|
notifyPrintOutcome(outcome);
|
|
8
27
|
return outcome;
|
|
9
28
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
const { drawMermaidForPrint } = await import("@ai-matrx/rich-content/mermaid/print-render");
|
|
17
|
-
drawn = await drawMermaidForPrint(markdown);
|
|
18
|
-
} catch (error) {
|
|
19
|
-
console.error("[print] diagrams could not be drawn", error);
|
|
20
|
-
const { toast } = await import("../../host/notify.js");
|
|
21
|
-
toast.warning("Diagrams could not be drawn, so they print as source.");
|
|
22
|
-
return {};
|
|
23
|
-
}
|
|
24
|
-
const { pictures, failed } = drawn;
|
|
29
|
+
let drawing = null;
|
|
30
|
+
const drawAll = () => drawing ??= (async () => {
|
|
31
|
+
try {
|
|
32
|
+
const { drawMermaidForPrint } = await import("@ai-matrx/rich-content/mermaid/print-render");
|
|
33
|
+
const { pictures, failed } = await drawMermaidForPrint(markdown);
|
|
25
34
|
if (failed > 0) {
|
|
26
35
|
const { toast } = await import("../../host/notify.js");
|
|
27
36
|
toast.warning(
|
|
28
37
|
`${failed} diagram${failed === 1 ? "" : "s"} could not be drawn and print${failed === 1 ? "s" : ""} as source.`
|
|
29
38
|
);
|
|
30
39
|
}
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
40
|
+
return pictures;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error("[print] diagrams could not be drawn", error);
|
|
43
|
+
const { toast } = await import("../../host/notify.js");
|
|
44
|
+
toast.warning("Diagrams could not be drawn, so they print as source.");
|
|
45
|
+
return null;
|
|
38
46
|
}
|
|
39
|
-
})
|
|
47
|
+
})();
|
|
48
|
+
return printMarkdownWhenReady(markdown, {
|
|
49
|
+
skin: "article",
|
|
50
|
+
title,
|
|
51
|
+
renderBlock: async (block) => {
|
|
52
|
+
if (block.source === "fence" && block.type === "mermaid") {
|
|
53
|
+
const svg = (await drawAll())?.get(block.body);
|
|
54
|
+
return svg ? { svg, alt: "Diagram" } : null;
|
|
55
|
+
}
|
|
56
|
+
const printer = adapterFor(block);
|
|
57
|
+
if (!printer?.toPrintHtml) return null;
|
|
58
|
+
return printer.toPrintHtml(blockData(block), {
|
|
59
|
+
type: block.type,
|
|
60
|
+
raw: block.body,
|
|
61
|
+
...block.title ? { title: block.title } : {}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}).then(async (outcome) => {
|
|
40
65
|
notifyPrintOutcome(outcome);
|
|
66
|
+
if (outcome === "opened" && adapted.some((block) => FRAME_BLOCK_TYPES.has(block.type))) {
|
|
67
|
+
const { toast } = await import("../../host/notify.js");
|
|
68
|
+
toast.info("Web pages print as a picture inside a message. For sharp text, print the page from its card.");
|
|
69
|
+
}
|
|
41
70
|
return outcome;
|
|
42
71
|
});
|
|
43
72
|
}
|
|
@@ -3,7 +3,7 @@ import { AppLink } from "../../host/ui-slots.js";
|
|
|
3
3
|
import {
|
|
4
4
|
MenuTapButton,
|
|
5
5
|
SquarePenTapButton
|
|
6
|
-
} from "@ai-matrx/tap-target/buttons";
|
|
6
|
+
} from "@ai-matrx/design-system/tap-target/buttons";
|
|
7
7
|
import ChatMobileAgentName from "./ChatMobileAgentName.js";
|
|
8
8
|
import ChatMobileAdminToggles from "./ChatMobileAdminToggles.js";
|
|
9
9
|
function ChatMobileHeaderBar() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { ChevronLeftTapButton } from "@ai-matrx/tap-target/buttons";
|
|
3
|
+
import { ChevronLeftTapButton } from "@ai-matrx/design-system/tap-target/buttons";
|
|
4
4
|
function ChatPanelBackButton() {
|
|
5
5
|
function handleBack() {
|
|
6
6
|
const panelCheckbox = document.getElementById(
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ChevronLeftTapButton,
|
|
13
13
|
PanelLeftTapButton,
|
|
14
14
|
PlusTapButton
|
|
15
|
-
} from "@ai-matrx/tap-target/buttons";
|
|
15
|
+
} from "@ai-matrx/design-system/tap-target/buttons";
|
|
16
16
|
import { useAppDispatch, useAppSelector } from "../../store/hooks.js";
|
|
17
17
|
import { selectAgentById } from "../../agents/redux/agent-definition/selectors.js";
|
|
18
18
|
import { pushAppHref } from "../../host/ui-slots.js";
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
Layers
|
|
19
19
|
} from "lucide-react";
|
|
20
20
|
import { ResponseModeButtons, BackToStartButton } from "./InpuControlButtons.js";
|
|
21
|
-
import { TapTargetButtonTransparent } from "@ai-matrx/tap-target";
|
|
22
|
-
import { PlusTapButton } from "@ai-matrx/tap-target/buttons";
|
|
21
|
+
import { TapTargetButtonTransparent } from "@ai-matrx/design-system/tap-target";
|
|
22
|
+
import { PlusTapButton } from "@ai-matrx/design-system/tap-target/buttons";
|
|
23
23
|
import { InputActionButtons } from "./InputActionButtons.js";
|
|
24
24
|
import { useAppSelector, useAppDispatch } from "../../../store/hooks.js";
|
|
25
25
|
import { selectIsSuperAdminDebugger } from "../../../host/prefs.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Loader2, Bug } from "lucide-react";
|
|
4
|
-
import { TapTargetButtonSolid } from "@ai-matrx/tap-target";
|
|
5
|
-
import { ArrowUpTapButton } from "@ai-matrx/tap-target/buttons";
|
|
4
|
+
import { TapTargetButtonSolid } from "@ai-matrx/design-system/tap-target";
|
|
5
|
+
import { ArrowUpTapButton } from "@ai-matrx/design-system/tap-target/buttons";
|
|
6
6
|
import { MicrophoneIconButton } from "../../../host/ui-slots.js";
|
|
7
7
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
8
8
|
function InputActionButtons({
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame-capture port for message prints.
|
|
3
|
+
*
|
|
4
|
+
* A live page inside a message (an HTML page's `<iframe>`) cannot be copied
|
|
5
|
+
* by a DOM capture — html2canvas draws a cross-origin frame blank. Full Print
|
|
6
|
+
* asks this port for a picture of each frame before it rasterizes the
|
|
7
|
+
* message; the host plugs its capture engine in (the server's real browser
|
|
8
|
+
* for a published page). Without one, the frame prints an honest stand-in,
|
|
9
|
+
* never blank and never its code.
|
|
10
|
+
*/
|
|
11
|
+
/** A picture (data URL or image URL) of exactly what the frame shows, or null when none can be made. */
|
|
12
|
+
export type FramePrintCapture = (frame: HTMLIFrameElement) => Promise<string | null>;
|
|
13
|
+
/** What prints in a frame's place when no picture can be made. */
|
|
14
|
+
export declare const FRAME_PRINT_STAND_IN = "Page image unavailable \u2014 print the page from its card.";
|
|
15
|
+
/** Plugs (or, with null, removes) the host's frame capture engine. */
|
|
16
|
+
export declare function setFramePrintCapture(capture: FramePrintCapture | null): void;
|
|
17
|
+
export declare function getFramePrintCapture(): FramePrintCapture | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const FRAME_PRINT_STAND_IN = "Page image unavailable \u2014 print the page from its card.";
|
|
2
|
+
const PORT = /* @__PURE__ */ Symbol.for("ai-matrx.chat.frame-print-capture");
|
|
3
|
+
function setFramePrintCapture(capture) {
|
|
4
|
+
globalThis[PORT] = capture;
|
|
5
|
+
}
|
|
6
|
+
function getFramePrintCapture() {
|
|
7
|
+
return globalThis[PORT] ?? null;
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
FRAME_PRINT_STAND_IN,
|
|
11
|
+
getFramePrintCapture,
|
|
12
|
+
setFramePrintCapture
|
|
13
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useRef, useState } from "react";
|
|
4
4
|
import { SurfaceAgentsPanelImpl } from "../../../ui/lazy/SurfaceAgentsPanelImpl.js";
|
|
5
|
-
import { TapTargetButtonTransparent } from "@ai-matrx/tap-target";
|
|
5
|
+
import { TapTargetButtonTransparent } from "@ai-matrx/design-system/tap-target";
|
|
6
6
|
import { INTELLIGENCE_ICON } from "@ai-matrx/icons/domain";
|
|
7
7
|
import {
|
|
8
8
|
Popover,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { formatCount } from "@ai-matrx/kit/format";
|
|
4
|
+
import { clientLazy } from "@ai-matrx/kit/client-lazy";
|
|
4
5
|
import {
|
|
5
6
|
CheckCircle,
|
|
6
7
|
FileSearch,
|
|
@@ -37,39 +38,39 @@ import { withSurfaceWriteDiff } from "../surface-write/withSurfaceWriteDiff.js";
|
|
|
37
38
|
import { DIFF_START_OPEN_KNOB, readSurfaceWrite } from "../surface-write/readSurfaceWrite.js";
|
|
38
39
|
import { getSessionKnob } from "../../host/prefs.js";
|
|
39
40
|
import { reportUnregisteredHostSlot } from "../../host/diagnostics.js";
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
const SearchInline = clientLazy(() => import("../renderers/search/SearchInline.js").then((m) => ({ default: m.SearchInline })));
|
|
42
|
+
const SearchOverlay = clientLazy(() => import("../renderers/search/SearchOverlay.js").then((m) => ({ default: m.SearchOverlay })));
|
|
43
|
+
const ScrapeInline = clientLazy(() => import("../renderers/scrape/ScrapeInline.js").then((m) => ({ default: m.ScrapeInline })));
|
|
44
|
+
const ScrapeOverlay = clientLazy(() => import("../renderers/scrape/ScrapeOverlay.js").then((m) => ({ default: m.ScrapeOverlay })));
|
|
44
45
|
import { parseScrape } from "../renderers/scrape/parseScrape.js";
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
const WebInline = clientLazy(() => import("../renderers/web/WebInline.js").then((m) => ({ default: m.WebInline })));
|
|
47
|
+
const WebOverlay = clientLazy(() => import("../renderers/web/WebOverlay.js").then((m) => ({ default: m.WebOverlay })));
|
|
47
48
|
import { resolveWebActionKind } from "../renderers/web/webAction.js";
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
const NewsInline = clientLazy(() => import("../renderers/news-api/NewsInline.js").then((m) => ({ default: m.NewsInline })));
|
|
50
|
+
const NewsOverlay = clientLazy(() => import("../renderers/news-api/NewsOverlay.js").then((m) => ({ default: m.NewsOverlay })));
|
|
51
|
+
const WorkbookInline = clientLazy(() => import("../renderers/workbook/WorkbookInline.js").then((m) => ({ default: m.WorkbookInline })));
|
|
52
|
+
const WorkbookOverlay = clientLazy(() => import("../renderers/workbook/WorkbookOverlay.js").then((m) => ({ default: m.WorkbookOverlay })));
|
|
53
|
+
const DictionaryInline = clientLazy(() => import("../renderers/dictionary/DictionaryInline.js").then((m) => ({ default: m.DictionaryInline })));
|
|
54
|
+
const DictionaryOverlay = clientLazy(() => import("../renderers/dictionary/DictionaryOverlay.js").then((m) => ({ default: m.DictionaryOverlay })));
|
|
55
|
+
const ResearchInline = clientLazy(() => import("../renderers/research/ResearchInline.js").then((m) => ({ default: m.ResearchInline })));
|
|
55
56
|
import { researchOverlayTabs } from "../renderers/research/ResearchOverlay.js";
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
const RandomWheelInline = clientLazy(() => import("../renderers/random-wheel/RandomWheelInline.js").then((m) => ({ default: m.RandomWheelInline })));
|
|
58
|
+
const RecordsInline = clientLazy(() => import("../renderers/records/RecordsInline.js").then((m) => ({ default: m.RecordsInline })));
|
|
59
|
+
const CtxGetInline = clientLazy(() => import("../renderers/ctx/CtxGetInline.js").then((m) => ({ default: m.CtxGetInline })));
|
|
60
|
+
const CtxBatchInline = clientLazy(() => import("../renderers/ctx/CtxBatchInline.js").then((m) => ({ default: m.CtxBatchInline })));
|
|
61
|
+
const CtxPatchInline = clientLazy(() => import("../renderers/ctx/CtxPatchInline.js").then((m) => ({ default: m.CtxPatchInline })));
|
|
62
|
+
const ContextActionInline = clientLazy(() => import("../renderers/ctx/ContextActionInline.js").then((m) => ({ default: m.ContextActionInline })));
|
|
63
|
+
const SqlInline = clientLazy(() => import("../renderers/sql/SqlInline.js").then((m) => ({ default: m.SqlInline })));
|
|
64
|
+
const FsInline = clientLazy(() => import("../renderers/fs/FsInline.js").then((m) => ({ default: m.FsInline })));
|
|
65
|
+
const ShellInline = clientLazy(() => import("../renderers/shell/ShellInline.js").then((m) => ({ default: m.ShellInline })));
|
|
66
|
+
const SkillInline = clientLazy(() => import("../renderers/skill/SkillInline.js").then((m) => ({ default: m.SkillInline })));
|
|
67
|
+
const AskInline = clientLazy(() => import("../renderers/ask/AskInline.js").then((m) => ({ default: m.AskInline })));
|
|
68
|
+
const AskPersonInline = clientLazy(() => import("../renderers/ask-person/AskPersonInline.js").then((m) => ({ default: m.AskPersonInline })));
|
|
69
|
+
const CloudBrowserInline = clientLazy(() => import("../renderers/cloud-browser/CloudBrowserRunCard.js").then((m) => ({ default: m.CloudBrowserInline })));
|
|
69
70
|
import { cloudBrowserAction } from "../renderers/cloud-browser/cloudBrowserRun.js";
|
|
70
|
-
|
|
71
|
+
const DbSchemaInline = clientLazy(() => import("../renderers/sql/DbSchemaInline.js").then((m) => ({ default: m.DbSchemaInline })));
|
|
71
72
|
import { summarizeSql } from "../renderers/sql/summarizeSql.js";
|
|
72
|
-
|
|
73
|
+
const AgentCallInline = clientLazy(() => import("../renderers/agent-call/AgentCallInline.js").then((m) => ({ default: m.AgentCallInline })));
|
|
73
74
|
import { isImageGenerationAgentCall } from "../renderers/agent-call/agentCallKind.js";
|
|
74
75
|
import { isCollaborationAgentCall } from "../renderers/agent-call/collab.js";
|
|
75
76
|
import { collabHeaderSubtitle } from "../renderers/agent-call/collab-subtitle.js";
|
|
@@ -3,8 +3,8 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { useRouter } from "../../host/navigation.js";
|
|
5
5
|
import { Music2, Radio, Settings2 } from "lucide-react";
|
|
6
|
-
import { TapTargetButton } from "@ai-matrx/tap-target";
|
|
7
|
-
import { ChevronLeftTapButton } from "@ai-matrx/tap-target/buttons";
|
|
6
|
+
import { TapTargetButton } from "@ai-matrx/design-system/tap-target";
|
|
7
|
+
import { ChevronLeftTapButton } from "@ai-matrx/design-system/tap-target/buttons";
|
|
8
8
|
import { RouteHeader } from "../../host/chrome.js";
|
|
9
9
|
import { toast } from "../../host/notify.js";
|
|
10
10
|
import { useAppDispatch, useAppSelector } from "../../store/hooks.js";
|