@ai-matrx/chat 0.4.24 → 0.4.26
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 +32 -0
- package/dist/agent-copy/RichCopySplit.d.ts +11 -1
- package/dist/agent-copy/RichCopySplit.js +9 -3
- package/dist/agents/components/chat/ChatConversationRoom.js +6 -4
- package/dist/agents/components/chat/ChatNewClient.js +7 -4
- package/dist/agents/components/chat/ChatNewHeader.js +3 -2
- package/dist/agents/components/chat/default-chat-mandate.d.ts +7 -0
- package/dist/agents/components/chat/default-chat-mandate.js +9 -0
- package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +29 -20
- package/dist/agents/redux/execution-system/utils/required-organization.js +2 -2
- package/dist/agents/ui-first-tools/redux/build-ambient-context.js +8 -7
- package/dist/host/contract.d.ts +7 -1
- package/dist/host/defaults/memory-navigation.d.ts +12 -0
- package/dist/host/defaults/memory-navigation.js +102 -0
- package/dist/host/index.d.ts +1 -0
- package/dist/host/index.js +4 -0
- package/dist/host/ui-slots.js +25 -11
- package/dist/ui/aspect-ratio-options.d.ts +4 -26
- package/dist/ui/aspect-ratio-options.js +7 -75
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.4.26
|
|
4
|
+
|
|
5
|
+
- `RichCopySplit` renders the content action set (`ContentActions` from rich-content 0.2.52): Copy, Plain (with the new `viewKey` prop), Export, Print and Transform (the host's Alchemy palette) each one click. New `exportTitle` prop names exported files.
|
|
6
|
+
- A chat answer honors its bar's Plain switch: the exact source in place of the render (the render stays mounted).
|
|
7
|
+
|
|
8
|
+
Consumer action: pass `viewKey` and wrap the host's content in `ContentView` to get Plain.
|
|
9
|
+
|
|
10
|
+
## 0.4.25 — nine more host slots draw the real component in any host
|
|
11
|
+
|
|
12
|
+
- `ReadFailure`, `UntrustedCount`, `OptionCombobox`, `AspectRatioSelect`, `FloatingSheet`,
|
|
13
|
+
`ConfigurationTable`, `ConfigurationTableRow`, `FieldHelp` and `TextInputDialog` default to
|
|
14
|
+
`@ai-matrx/design-system` 0.82.0's components (moved there whole from matrx-frontend); a slot whose
|
|
15
|
+
default is the package component no longer reports itself as unregistered.
|
|
16
|
+
- `./ui/aspect-ratio-options` re-exports design-system's ratio helpers (one copy).
|
|
17
|
+
- Guard: `slots-draw-their-package-default.test.tsx` (the list only grows).
|
|
18
|
+
|
|
19
|
+
Consumer action: none — a host's registration still wins; matrx-frontend may drop its registrations of
|
|
20
|
+
these nine slots (they are the same components).
|
|
21
|
+
|
|
22
|
+
Also in 0.4.25 — `createMemoryNavigation` (from `@ai-matrx/chat/host`): a navigation port that keeps the
|
|
23
|
+
chat's address in memory for hosts whose own address must not move (an extension side panel; Matrx
|
|
24
|
+
Local, whose router already owns `/chat`). Options `initial`, `home`, `persist(address)`,
|
|
25
|
+
`openExternal(href)`; back/forward, link clicks, one search-params object per address. Guard:
|
|
26
|
+
`memory-navigation-keeps-the-page-still.test.tsx` (red 2/5 on a planted break → green 5/5). Consumer
|
|
27
|
+
action: none; matrx-extend may drop its own `memory-navigation.tsx`.
|
|
28
|
+
|
|
29
|
+
Also in 0.4.25 — a host names the mandate that answers its new-chat screen: `app.defaultChatMandateKey`
|
|
30
|
+
(Matrx Local `local.cloud_chat`, Matrx Extend `extend.browser_chat`); absent = `chat.default_new_chat`.
|
|
31
|
+
`ChatNewClient`, `ChatConversationRoom`, `ChatNewHeader` read it through one hook
|
|
32
|
+
(`useDefaultChatMandateKey`). Guard: `a-host-names-its-default-chat-mandate.test.tsx` (red 1/3 on a
|
|
33
|
+
planted break → green 3/3). Consumer action: none.
|
|
34
|
+
|
|
3
35
|
## 0.4.24
|
|
4
36
|
|
|
5
37
|
Republish of 0.4.23 (its publish failed in finish-build on a member call read as an import; fixed in 4008c0cbbc). No consumer action required.
|
|
@@ -2,9 +2,19 @@ import * as React from "react";
|
|
|
2
2
|
import type { MatrxCopyMenuProps } from "@ai-matrx/alchemy/react/workspace";
|
|
3
3
|
export type RichCopySplitProps = MatrxCopyMenuProps & {
|
|
4
4
|
human: NonNullable<MatrxCopyMenuProps["human"]>;
|
|
5
|
+
/**
|
|
6
|
+
* The item's Plain / Formatted key: the host renders its content through
|
|
7
|
+
* <ContentView viewKey={…} text={…}> (@ai-matrx/rich-content/copy/ContentActions).
|
|
8
|
+
* Guard: matrx-frontend content-actions.census — every display host passes it.
|
|
9
|
+
*/
|
|
10
|
+
viewKey?: string;
|
|
11
|
+
/** File name / print title for Export and Print. */
|
|
12
|
+
exportTitle?: string;
|
|
5
13
|
};
|
|
6
14
|
/**
|
|
7
|
-
* THE
|
|
15
|
+
* THE content action set of a markdown host (ContentActions, Arman 2026-10-08: copy, Plain,
|
|
16
|
+
* Export, Print and Transform each one click away; Transform opens the Alchemy palette below).
|
|
17
|
+
* Formerly the split Copy of a markdown host (Arman, 2026-10-04: "one click to get either the markdown
|
|
8
18
|
* version or the no-markup version"). One click copies the person's flavor (`copy.default_flavor`);
|
|
9
19
|
* the chevron offers exactly Copy markdown and Copy plain text (2026-10-07). The Alchemy palette
|
|
10
20
|
* (formatted, JSON, download, AI, the host's record copies) is "Export…" — ONE row under a divider
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { ContentActions } from "@ai-matrx/rich-content/copy/ContentActions";
|
|
5
5
|
import { copyRichContent } from "@ai-matrx/rich-content/copy/copy-commands";
|
|
6
6
|
import { ExportPaletteAnchor, openExportPalette, useExportPaletteKey } from "@ai-matrx/rich-content/copy/ExportPalette";
|
|
7
7
|
import { cn } from "@ai-matrx/design-system";
|
|
@@ -17,12 +17,18 @@ function RichCopySplit(props) {
|
|
|
17
17
|
const exportKey = useExportPaletteKey();
|
|
18
18
|
return /* @__PURE__ */ jsxs("span", { className: cn("inline-flex shrink-0 items-center", className), children: [
|
|
19
19
|
/* @__PURE__ */ jsx(
|
|
20
|
-
|
|
20
|
+
ContentActions,
|
|
21
21
|
{
|
|
22
|
+
content: () => {
|
|
23
|
+
const value = typeof human === "function" ? human() : human;
|
|
24
|
+
return typeof value === "string" ? value : String(value ?? "");
|
|
25
|
+
},
|
|
22
26
|
copy,
|
|
23
27
|
size: splitSize,
|
|
24
28
|
label: `Copy ${props.label ?? ""}`.trim(),
|
|
25
|
-
|
|
29
|
+
title: props.exportTitle ?? props.label,
|
|
30
|
+
viewKey: props.viewKey,
|
|
31
|
+
onTransform: () => void openExportPalette(exportKey)
|
|
26
32
|
}
|
|
27
33
|
),
|
|
28
34
|
/* @__PURE__ */ jsx(ExportPaletteAnchor, { exportKey, controllerRef: palette }),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { ChatRoomClient } from "./ChatRoomClient.js";
|
|
4
4
|
import { ChatMandateUnavailable, ChatNewLandingSkeleton } from "./ChatNewClient.js";
|
|
5
|
-
import {
|
|
5
|
+
import { useDefaultChatMandateKey } from "./default-chat-mandate.js";
|
|
6
6
|
import { useMandate } from "../../../mandates/useMandate.js";
|
|
7
7
|
import { WorkspaceGate } from "../../../host/ui-slots.js";
|
|
8
8
|
function ChatConversationRoom({
|
|
@@ -13,6 +13,7 @@ function ChatConversationRoom({
|
|
|
13
13
|
composer,
|
|
14
14
|
organizationId = null
|
|
15
15
|
}) {
|
|
16
|
+
const defaultMandateKey = useDefaultChatMandateKey();
|
|
16
17
|
if (agentId && !ownedByMandate) {
|
|
17
18
|
return /* @__PURE__ */ jsx(
|
|
18
19
|
ChatRoomClient,
|
|
@@ -30,7 +31,7 @@ function ChatConversationRoom({
|
|
|
30
31
|
{
|
|
31
32
|
agentId,
|
|
32
33
|
conversationId,
|
|
33
|
-
mandateKey:
|
|
34
|
+
mandateKey: defaultMandateKey,
|
|
34
35
|
sandboxBinding,
|
|
35
36
|
composer
|
|
36
37
|
}
|
|
@@ -52,8 +53,9 @@ function ChatConversationRoomResolved({
|
|
|
52
53
|
sandboxBinding,
|
|
53
54
|
composer
|
|
54
55
|
}) {
|
|
56
|
+
const defaultMandateKey = useDefaultChatMandateKey();
|
|
55
57
|
const { mandate, loading, error, organizationPending } = useMandate(
|
|
56
|
-
|
|
58
|
+
defaultMandateKey,
|
|
57
59
|
organizationId ? { organizationId } : {}
|
|
58
60
|
);
|
|
59
61
|
if (loading) return /* @__PURE__ */ jsx(ChatNewLandingSkeleton, {});
|
|
@@ -66,7 +68,7 @@ function ChatConversationRoomResolved({
|
|
|
66
68
|
{
|
|
67
69
|
agentId: mandate.agentId,
|
|
68
70
|
conversationId,
|
|
69
|
-
mandateKey:
|
|
71
|
+
mandateKey: defaultMandateKey,
|
|
70
72
|
sandboxBinding,
|
|
71
73
|
composer
|
|
72
74
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ComposerGreeting,
|
|
12
12
|
ComposerQuickActionsSkeleton
|
|
13
13
|
} from "../inputs/smart-input/composer/ComposerSplash.js";
|
|
14
|
-
import {
|
|
14
|
+
import { useDefaultChatMandateKey } from "./default-chat-mandate.js";
|
|
15
15
|
import { useMandate } from "../../../mandates/useMandate.js";
|
|
16
16
|
import { ErrorAlchemyMenu } from "../../../host/ui-slots.js";
|
|
17
17
|
import { asClause } from "@ai-matrx/kit/text";
|
|
@@ -21,7 +21,8 @@ function ChatNewClient({
|
|
|
21
21
|
pinned = false,
|
|
22
22
|
composer
|
|
23
23
|
}) {
|
|
24
|
-
const
|
|
24
|
+
const defaultMandateKey = useDefaultChatMandateKey();
|
|
25
|
+
const { mandate } = useMandate(defaultMandateKey, { optional: true });
|
|
25
26
|
if (pinned && agentId) {
|
|
26
27
|
return /* @__PURE__ */ jsx(ChatNewBody, { agentId, composer, pinned: true });
|
|
27
28
|
}
|
|
@@ -31,7 +32,8 @@ function ChatNewClient({
|
|
|
31
32
|
function ChatNewClientResolved({
|
|
32
33
|
composer
|
|
33
34
|
}) {
|
|
34
|
-
const
|
|
35
|
+
const defaultMandateKey = useDefaultChatMandateKey();
|
|
36
|
+
const { mandate, loading, error, organizationPending } = useMandate(defaultMandateKey);
|
|
35
37
|
if (loading) return /* @__PURE__ */ jsx(ChatNewLandingSkeleton, {});
|
|
36
38
|
if (organizationPending) {
|
|
37
39
|
return /* @__PURE__ */ jsx(WorkspaceGate, { blocked: true, sentence: "Chat needs a workspace to open.", children: /* @__PURE__ */ jsx(ChatNewLandingSkeleton, {}) });
|
|
@@ -71,12 +73,13 @@ function ChatNewBody({
|
|
|
71
73
|
composer,
|
|
72
74
|
pinned = false
|
|
73
75
|
}) {
|
|
76
|
+
const defaultMandateKey = useDefaultChatMandateKey();
|
|
74
77
|
const surfaceKey = chatRouteSurfaceKey(agentId);
|
|
75
78
|
return /* @__PURE__ */ jsx(
|
|
76
79
|
ChatRoomClient,
|
|
77
80
|
{
|
|
78
81
|
agentId,
|
|
79
|
-
mandateKey: pinned ? void 0 :
|
|
82
|
+
mandateKey: pinned ? void 0 : defaultMandateKey,
|
|
80
83
|
composer,
|
|
81
84
|
landingContent: (conversationId, composerPresentation) => composerPresentation ? /* @__PURE__ */ jsx(
|
|
82
85
|
NewChatGreeting,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { ChatRunHeader } from "./ChatRunHeader.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useDefaultChatMandateKey } from "./default-chat-mandate.js";
|
|
5
5
|
import { useMandate } from "../../../mandates/useMandate.js";
|
|
6
6
|
function ChatNewHeader({
|
|
7
7
|
agentId,
|
|
@@ -20,7 +20,8 @@ function ChatNewHeader({
|
|
|
20
20
|
function ChatNewHeaderResolved({
|
|
21
21
|
composerMode
|
|
22
22
|
}) {
|
|
23
|
-
const
|
|
23
|
+
const defaultMandateKey = useDefaultChatMandateKey();
|
|
24
|
+
const { mandate } = useMandate(defaultMandateKey);
|
|
24
25
|
return /* @__PURE__ */ jsx(ChatRunHeader, { activeAgentId: mandate?.agentId, composerMode });
|
|
25
26
|
}
|
|
26
27
|
export {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mandate that answers the new-chat screen and agentless conversations in THIS host: the host's
|
|
3
|
+
* `app.defaultChatMandateKey` (Matrx Local names `local.cloud_chat`), else the web's
|
|
4
|
+
* `chat.default_new_chat`. One reader, so every screen that resolves the default agrees.
|
|
5
|
+
*/
|
|
6
|
+
import type { AnyMandateKey } from "@ai-matrx/agents/mandates";
|
|
7
|
+
export declare function useDefaultChatMandateKey(): AnyMandateKey;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMaybeChatHost } from "../../../host/react.js";
|
|
3
|
+
import { DEFAULT_NEW_CHAT_MANDATE_KEY } from "./chat-quick-actions.config.js";
|
|
4
|
+
function useDefaultChatMandateKey() {
|
|
5
|
+
return useMaybeChatHost()?.app.defaultChatMandateKey ?? DEFAULT_NEW_CHAT_MANDATE_KEY;
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
useDefaultChatMandateKey
|
|
9
|
+
};
|
|
@@ -74,6 +74,9 @@ import {
|
|
|
74
74
|
import { retryConversationTurn } from "../../../redux/execution-system/message-crud/retry-turn.thunk.js";
|
|
75
75
|
import { commitInlineContentEdit } from "../../../redux/execution-system/message-crud/commit-inline-edit.thunk.js";
|
|
76
76
|
import { InPlaceAnswerEditor } from "./InPlaceAnswerEditor.js";
|
|
77
|
+
import { PlainTextView } from "@ai-matrx/rich-content/copy/ContentActions";
|
|
78
|
+
import { usePlainView } from "@ai-matrx/rich-content/copy/content-view-store";
|
|
79
|
+
import { richDocumentViewKey } from "@ai-matrx/rich-content/rich-document/runtime/useActionSurfaceProvider";
|
|
77
80
|
import { updateMessageRecord } from "../../../redux/execution-system/messages/messages.slice.js";
|
|
78
81
|
import { handInPlaceCaret } from "@ai-matrx/rich-editor/in-place/caret-handoff";
|
|
79
82
|
import { toast } from "../../../../host/notify.js";
|
|
@@ -197,6 +200,9 @@ function AgentAssistantMessage({
|
|
|
197
200
|
() => extractFlatText(record, { withCitationMarkers: hasCitations, keepInlineThinking: true }),
|
|
198
201
|
[record, hasCitations]
|
|
199
202
|
);
|
|
203
|
+
const plainView = usePlainView(
|
|
204
|
+
messageId ? richDocumentViewKey({ type: "chat-message", conversationId, messageId }, "") : null
|
|
205
|
+
);
|
|
200
206
|
const attachmentParts = extractContentBlocks(record).filter(
|
|
201
207
|
(b) => isAttachmentMessagePart(b) && !isInlineAssistantMedia(b)
|
|
202
208
|
);
|
|
@@ -427,26 +433,29 @@ function AgentAssistantMessage({
|
|
|
427
433
|
) : (
|
|
428
434
|
// THE READING SET on a saved chat answer (highlight, comment, suggest, link +
|
|
429
435
|
// the Notes & comments dock) — the same mount every note and saved document uses.
|
|
430
|
-
/* @__PURE__ */ jsx(RecordAnnotations, { record: annotationRecord, children: /* @__PURE__ */
|
|
431
|
-
|
|
432
|
-
{
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
436
|
+
/* @__PURE__ */ jsx(RecordAnnotations, { record: annotationRecord, children: /* @__PURE__ */ jsxs("div", { "data-message-content": true, ...answerReadProps, children: [
|
|
437
|
+
plainView ? /* @__PURE__ */ jsx(PlainTextView, { text: renderedText ?? "" }) : null,
|
|
438
|
+
/* @__PURE__ */ jsx("div", { className: plainView ? "hidden" : "contents", children: /* @__PURE__ */ jsx(
|
|
439
|
+
MarkdownStream,
|
|
440
|
+
{
|
|
441
|
+
imagePolicy: "ai",
|
|
442
|
+
requestId: effectiveRequestId,
|
|
443
|
+
recordMessageIds,
|
|
444
|
+
streamSlotStart: streamSlotStart ?? record?._streamSlotStart,
|
|
445
|
+
streamSlotEnd: streamSlotEnd ?? record?._streamSlotEnd,
|
|
446
|
+
turnId: messageId,
|
|
447
|
+
conversationId,
|
|
448
|
+
messageId: messageId ?? void 0,
|
|
449
|
+
content: renderedText,
|
|
450
|
+
isStreamActive: isStreamActive && !failed,
|
|
451
|
+
hideCopyButton: true,
|
|
452
|
+
allowFullScreenEditor: false,
|
|
453
|
+
serverProcessedBlocks,
|
|
454
|
+
onContentChange: handleInlineContentChange,
|
|
455
|
+
applyLocalEdits: false
|
|
456
|
+
}
|
|
457
|
+
) })
|
|
458
|
+
] }) })
|
|
450
459
|
),
|
|
451
460
|
!isClosedStreamSegment && displaySources.length > 0 && /* @__PURE__ */ jsx(MessageSourcesRow, { sources: displaySources, className: "mt-2" }),
|
|
452
461
|
!isStreamActive && /* @__PURE__ */ jsx(PrefillNote, { metadata: record?.metadata }),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { adminLaneOrganizationId } from "../../../../host/server/admin-lane.js";
|
|
2
2
|
import { selectOrganizationId } from "../../../../host/org.js";
|
|
3
|
+
import { selectAccessToken, selectFingerprintId } from "../../../../host/identity.js";
|
|
3
4
|
function isFingerprintGuestExecution(state) {
|
|
4
|
-
|
|
5
|
-
return !partial.userAuth?.accessToken && Boolean(partial.userProfile?.fingerprintId);
|
|
5
|
+
return !selectAccessToken(state) && Boolean(selectFingerprintId(state));
|
|
6
6
|
}
|
|
7
7
|
function executionOrganizationForRequest(state, conversationId) {
|
|
8
8
|
if (isFingerprintGuestExecution(state)) return void 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { selectProjectId, selectProjectName, selectTaskId, selectTaskName, selectScopeSelectionsContext } from "../../../context/sources/scopes.js";
|
|
2
2
|
import { selectOrganizationId, selectOrganizationName } from "../../../host/org.js";
|
|
3
|
+
import { selectChatIdentity } from "../../../host/identity.js";
|
|
3
4
|
function snapshotRoute(engineered) {
|
|
4
5
|
if (typeof window === "undefined") {
|
|
5
6
|
return { url: null, title: null, route_kind: "server" };
|
|
@@ -18,22 +19,22 @@ function snapshotRoute(engineered) {
|
|
|
18
19
|
return { url, title, route_kind };
|
|
19
20
|
}
|
|
20
21
|
function buildAmbientContext(state, conversationId) {
|
|
21
|
-
const auth = state
|
|
22
|
-
if (!auth
|
|
23
|
-
const
|
|
24
|
-
const name = profile?.fullName || profile?.name || profile?.preferredUsername || auth.email || "user";
|
|
22
|
+
const auth = selectChatIdentity(state);
|
|
23
|
+
if (!auth.userId) return null;
|
|
24
|
+
const name = auth.displayName || auth.name || auth.preferredUsername || auth.email || "user";
|
|
25
25
|
const now = /* @__PURE__ */ new Date();
|
|
26
26
|
const timezone = typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC";
|
|
27
27
|
const locale = typeof navigator !== "undefined" && navigator.language ? navigator.language : "en-US";
|
|
28
28
|
const snapshot = {
|
|
29
29
|
user: {
|
|
30
|
-
id: auth.
|
|
30
|
+
id: auth.userId,
|
|
31
31
|
name,
|
|
32
32
|
email: auth.email ?? null,
|
|
33
33
|
// ADMIN POWER, not identity: an agent on a user page must see an admin
|
|
34
34
|
// exactly as it sees anyone else (utils/supabase/adminLane.ts).
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// The identity port's adminLevel is already what the host grants on this page.
|
|
36
|
+
is_admin: auth.adminLevel !== null,
|
|
37
|
+
admin_level: auth.adminLevel
|
|
37
38
|
},
|
|
38
39
|
client: {
|
|
39
40
|
surface: "nextjs",
|
package/dist/host/contract.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
19
19
|
import type { AnchorHTMLAttributes, ComponentType, ReactNode, Ref } from "react";
|
|
20
20
|
import type { AgentCatalog } from "@ai-matrx/agents/catalog";
|
|
21
|
+
import type { AnyMandateKey } from "@ai-matrx/agents/mandates";
|
|
21
22
|
import type { ChatDatabase } from "./db-types.js";
|
|
22
23
|
import type { ChatWindowId } from "./windows.js";
|
|
23
24
|
import type { CanvasJson } from "@ai-matrx/canvas";
|
|
@@ -730,7 +731,12 @@ export interface ChatAppIdentity {
|
|
|
730
731
|
/** `chat.conversation.source_app` — a producer registered with the server. */
|
|
731
732
|
sourceApp: string;
|
|
732
733
|
/** `source_feature` for every conversation this app starts; absent keeps each screen's own. */
|
|
733
|
-
sourceFeature?: string;
|
|
734
|
+
sourceFeature?: string; /**
|
|
735
|
+
* The mandate that answers this app's new-chat screen and agentless conversations (Matrx Local:
|
|
736
|
+
* `local.cloud_chat`; Matrx Extend: `extend.browser_chat`). Which agent runs is the database's
|
|
737
|
+
* answer for that key, never the host's. Absent: `chat.default_new_chat`, as on the web.
|
|
738
|
+
*/
|
|
739
|
+
defaultChatMandateKey?: AnyMandateKey;
|
|
734
740
|
}
|
|
735
741
|
/**
|
|
736
742
|
* Where each conversation's text may go (lanes/agents-chat-proposal.md W0(a)). The package enforces
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChatNavigationPort } from "../contract.js";
|
|
2
|
+
export interface MemoryNavigationOptions {
|
|
3
|
+
initial?: string | null;
|
|
4
|
+
home?: string;
|
|
5
|
+
persist?: (address: string) => void;
|
|
6
|
+
openExternal?: (href: string) => void;
|
|
7
|
+
}
|
|
8
|
+
/** A memory port plus the address it holds, for hosts that read or restore it. */
|
|
9
|
+
export interface MemoryNavigationPort extends ChatNavigationPort {
|
|
10
|
+
address(): string;
|
|
11
|
+
}
|
|
12
|
+
export declare function createMemoryNavigation(options?: MemoryNavigationOptions): MemoryNavigationPort;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
|
+
const LOCAL_ORIGIN = "https://chat.memory.local";
|
|
5
|
+
function defaultOpenExternal(href) {
|
|
6
|
+
if (typeof window !== "undefined") window.open(href, "_blank", "noopener,noreferrer");
|
|
7
|
+
}
|
|
8
|
+
function createMemoryNavigation(options = {}) {
|
|
9
|
+
const home = options.home ?? "/chat";
|
|
10
|
+
const openExternal = options.openExternal ?? defaultOpenExternal;
|
|
11
|
+
let address = options.initial && options.initial.startsWith("/") ? options.initial : home;
|
|
12
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
13
|
+
const history = [];
|
|
14
|
+
const ahead = [];
|
|
15
|
+
const set = (next) => {
|
|
16
|
+
if (next === address) return;
|
|
17
|
+
address = next;
|
|
18
|
+
for (const listener of listeners) listener();
|
|
19
|
+
options.persist?.(next);
|
|
20
|
+
};
|
|
21
|
+
const toAddress = (href) => {
|
|
22
|
+
try {
|
|
23
|
+
const url = new URL(href, LOCAL_ORIGIN + address);
|
|
24
|
+
return url.origin === LOCAL_ORIGIN ? url.pathname + url.search : null;
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const go = (href, mode) => {
|
|
30
|
+
const next = toAddress(href);
|
|
31
|
+
if (next === null) {
|
|
32
|
+
openExternal(href);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (mode === "push" && next !== address) {
|
|
36
|
+
history.push(address);
|
|
37
|
+
ahead.length = 0;
|
|
38
|
+
}
|
|
39
|
+
set(next);
|
|
40
|
+
};
|
|
41
|
+
const router = {
|
|
42
|
+
push: (href) => go(href, "push"),
|
|
43
|
+
replace: (href) => go(href, "replace"),
|
|
44
|
+
back: () => {
|
|
45
|
+
ahead.push(address);
|
|
46
|
+
set(history.pop() ?? home);
|
|
47
|
+
},
|
|
48
|
+
forward: () => {
|
|
49
|
+
const next = ahead.pop();
|
|
50
|
+
if (next === void 0) return;
|
|
51
|
+
history.push(address);
|
|
52
|
+
set(next);
|
|
53
|
+
},
|
|
54
|
+
refresh: () => void 0,
|
|
55
|
+
prefetch: () => void 0
|
|
56
|
+
};
|
|
57
|
+
const subscribe = (listener) => {
|
|
58
|
+
listeners.add(listener);
|
|
59
|
+
return () => {
|
|
60
|
+
listeners.delete(listener);
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const useAddress = () => useSyncExternalStore(subscribe, () => address, () => address);
|
|
64
|
+
let cached = null;
|
|
65
|
+
const paramsFor = (current) => {
|
|
66
|
+
if (cached?.address !== current) {
|
|
67
|
+
cached = { address: current, params: new URLSearchParams(current.split("?")[1] ?? "") };
|
|
68
|
+
}
|
|
69
|
+
return cached.params;
|
|
70
|
+
};
|
|
71
|
+
function MemoryLink({ href, children, onClick, prefetch, replace, scroll, ...anchor }) {
|
|
72
|
+
void prefetch;
|
|
73
|
+
void scroll;
|
|
74
|
+
return /* @__PURE__ */ jsx(
|
|
75
|
+
"a",
|
|
76
|
+
{
|
|
77
|
+
...anchor,
|
|
78
|
+
href,
|
|
79
|
+
onClick: (event) => {
|
|
80
|
+
onClick?.(event);
|
|
81
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey) return;
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
go(href, replace ? "replace" : "push");
|
|
84
|
+
},
|
|
85
|
+
children
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
address: () => address,
|
|
91
|
+
push: router.push,
|
|
92
|
+
replace: router.replace,
|
|
93
|
+
back: router.back,
|
|
94
|
+
useRouter: () => router,
|
|
95
|
+
usePathname: () => useAddress().split("?")[0] || home,
|
|
96
|
+
useSearchParams: () => paramsFor(useAddress()),
|
|
97
|
+
Link: MemoryLink
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
createMemoryNavigation
|
|
102
|
+
};
|
package/dist/host/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { createDefaultServer, DEFAULT_CHAT_SERVER_URL, } from "./defaults/server
|
|
|
12
12
|
export { createDomNotifier } from "./defaults/notify.js";
|
|
13
13
|
export { createWebPrefs, createHostKeptPrefs, CHAT_PREFS_PREFIX, type HostKeptPrefs } from "./defaults/prefs.js";
|
|
14
14
|
export { createWindowNavigation } from "./defaults/navigation.js";
|
|
15
|
+
export { createMemoryNavigation, type MemoryNavigationOptions, type MemoryNavigationPort, } from "./defaults/memory-navigation.js";
|
|
15
16
|
export { createUnhostedWindows } from "./defaults/windows.js";
|
|
16
17
|
export { CHAT_WINDOWS, CHAT_WINDOW_IDS, DEFAULT_WINDOW_INSTANCE_ID, isChatWindowId, type ChatWindowId, } from "./windows.js";
|
|
17
18
|
export type { ChatWindowOpeners } from "./window-openers.js";
|
package/dist/host/index.js
CHANGED
|
@@ -29,6 +29,9 @@ import {
|
|
|
29
29
|
import { createDomNotifier } from "./defaults/notify.js";
|
|
30
30
|
import { createWebPrefs, createHostKeptPrefs, CHAT_PREFS_PREFIX } from "./defaults/prefs.js";
|
|
31
31
|
import { createWindowNavigation } from "./defaults/navigation.js";
|
|
32
|
+
import {
|
|
33
|
+
createMemoryNavigation
|
|
34
|
+
} from "./defaults/memory-navigation.js";
|
|
32
35
|
import { createUnhostedWindows } from "./defaults/windows.js";
|
|
33
36
|
import {
|
|
34
37
|
CHAT_WINDOWS,
|
|
@@ -89,6 +92,7 @@ export {
|
|
|
89
92
|
createDomNotifier,
|
|
90
93
|
createHostKeptPrefs,
|
|
91
94
|
createLogClientErrorDiagnostics,
|
|
95
|
+
createMemoryNavigation,
|
|
92
96
|
createNoDatabaseStandIn,
|
|
93
97
|
createNoPickerOrg,
|
|
94
98
|
createStoredOrg,
|
package/dist/host/ui-slots.js
CHANGED
|
@@ -11,10 +11,21 @@ import { DefaultFullScreenOverlay, DefaultWindowPanel } from "./defaults/window-
|
|
|
11
11
|
import { DefaultSettingControlInput } from "./defaults/setting-control-input.js";
|
|
12
12
|
import { DefaultResourcePickerMenu } from "./defaults/resource-picker-menu.js";
|
|
13
13
|
import { captureReactRenderError } from "./diagnostics.js";
|
|
14
|
-
import { ConfirmDialog as PackageConfirmDialog } from "@ai-matrx/design-system";
|
|
15
14
|
import {
|
|
15
|
+
ConfirmDialog as PackageConfirmDialog,
|
|
16
|
+
FloatingSheet as PackageFloatingSheet,
|
|
17
|
+
ReadFailure as PackageReadFailure,
|
|
18
|
+
TextInputDialog as PackageTextInputDialog,
|
|
19
|
+
UntrustedCount as PackageUntrustedCount
|
|
20
|
+
} from "@ai-matrx/design-system";
|
|
21
|
+
import {
|
|
22
|
+
AspectRatioSelect as PackageAspectRatioSelect,
|
|
16
23
|
ClampedNumberInput as PackageClampedNumberInput,
|
|
24
|
+
ConfigurationTable as PackageConfigurationTable,
|
|
25
|
+
ConfigurationTableRow as PackageConfigurationTableRow,
|
|
26
|
+
FieldHelp as PackageFieldHelp,
|
|
17
27
|
LightSwitchToggle as PackageLightSwitchToggle,
|
|
28
|
+
OptionCombobox as PackageOptionCombobox,
|
|
18
29
|
NumberStepper as PackageNumberStepper
|
|
19
30
|
} from "@ai-matrx/design-system/controls";
|
|
20
31
|
const slots = {};
|
|
@@ -46,7 +57,9 @@ function slotComponent(name, Fallback) {
|
|
|
46
57
|
const Impl = slots[name];
|
|
47
58
|
if (!Impl) {
|
|
48
59
|
const absent = !Fallback || Fallback.optionalAbsent;
|
|
49
|
-
|
|
60
|
+
if (!Fallback?.isPackageDefault) {
|
|
61
|
+
reportUnregistered(name, absent ? "it renders nothing here" : "a plain stand-in is drawn");
|
|
62
|
+
}
|
|
50
63
|
return Fallback ? createElement(SlotBoundary, { name }, createElement(Fallback, props)) : null;
|
|
51
64
|
}
|
|
52
65
|
return createElement(SlotBoundary, { name }, createElement(Impl, props));
|
|
@@ -57,6 +70,7 @@ function slotComponent(name, Fallback) {
|
|
|
57
70
|
function withPackageDefault(Component2) {
|
|
58
71
|
const PackageDefault = (props) => createElement(Component2, props);
|
|
59
72
|
PackageDefault.displayName = `PackageDefault(${Component2.displayName ?? Component2.name ?? "component"})`;
|
|
73
|
+
PackageDefault.isPackageDefault = true;
|
|
60
74
|
return PackageDefault;
|
|
61
75
|
}
|
|
62
76
|
function unregisteredWidget(name) {
|
|
@@ -317,9 +331,9 @@ const useRecordAndTranscribe = slotFn("useRecordAndTranscribe");
|
|
|
317
331
|
const requestScribeAudioSeek = slotFn("requestScribeAudioSeek", () => void 0);
|
|
318
332
|
const resolvePdfSurfaceIds = slotFn("resolvePdfSurfaceIds");
|
|
319
333
|
const readListRpc = slotFn("readListRpc");
|
|
320
|
-
const ReadFailure = slotComponent("ReadFailure",
|
|
334
|
+
const ReadFailure = slotComponent("ReadFailure", withPackageDefault(PackageReadFailure));
|
|
321
335
|
const ItemRow = slotComponent("ItemRow", unregisteredWidget("ItemRow"));
|
|
322
|
-
const UntrustedCount = slotComponent("UntrustedCount",
|
|
336
|
+
const UntrustedCount = slotComponent("UntrustedCount", withPackageDefault(PackageUntrustedCount));
|
|
323
337
|
function PlainStaleDataNotice(props) {
|
|
324
338
|
const what = props.what ?? "this";
|
|
325
339
|
const sentence = props.hasData ? `Showing what was last read; ${what} could not be refreshed.` : `Could not read ${what}.`;
|
|
@@ -347,12 +361,12 @@ const StructuredValueView = slotComponent("StructuredValueView", unregisteredWid
|
|
|
347
361
|
const KindValueFrontDoor = slotComponent("KindValueFrontDoor", unregisteredWidget("KindValueFrontDoor"));
|
|
348
362
|
const KindDataGate = slotComponent("KindDataGate", unregisteredWidget("KindDataGate"));
|
|
349
363
|
const ServerNotes = slotComponent("ServerNotes", unregisteredWidget("ServerNotes"));
|
|
350
|
-
const OptionCombobox = slotComponent("OptionCombobox",
|
|
364
|
+
const OptionCombobox = slotComponent("OptionCombobox", withPackageDefault(PackageOptionCombobox));
|
|
351
365
|
const NumberStepper = slotComponent("NumberStepper", withPackageDefault(PackageNumberStepper));
|
|
352
366
|
const MatrxFloatingFrame = slotComponent("MatrxFloatingFrame", unregisteredWidget("MatrxFloatingFrame"));
|
|
353
367
|
const ItemMenu = slotComponent("ItemMenu", unregisteredWidget("ItemMenu"));
|
|
354
368
|
const ClampedNumberInput = slotComponent("ClampedNumberInput", withPackageDefault(PackageClampedNumberInput));
|
|
355
|
-
const AspectRatioSelect = slotComponent("AspectRatioSelect",
|
|
369
|
+
const AspectRatioSelect = slotComponent("AspectRatioSelect", withPackageDefault(PackageAspectRatioSelect));
|
|
356
370
|
const AnswerTextPreview = slotComponent("AnswerTextPreview", unregisteredWidget("AnswerTextPreview"));
|
|
357
371
|
const AccessGate = slotComponent("AccessGate", unregisteredWidget("AccessGate"));
|
|
358
372
|
const ReferenceCopyMenuItem = slotComponent("ReferenceCopyMenuItem", unregisteredWidget("ReferenceCopyMenuItem"));
|
|
@@ -369,16 +383,16 @@ const VoiceTextarea = slotComponent("VoiceTextarea", (props) => {
|
|
|
369
383
|
const { onChange, value, className, placeholder, rows, id, ref, autoFocus, disabled } = props;
|
|
370
384
|
return createElement("textarea", { ref, id, value, onChange, className, placeholder, rows, autoFocus, disabled, "data-chat-slot-fallback": "textarea" });
|
|
371
385
|
});
|
|
372
|
-
const FloatingSheet = slotComponent("FloatingSheet",
|
|
386
|
+
const FloatingSheet = slotComponent("FloatingSheet", withPackageDefault(PackageFloatingSheet));
|
|
373
387
|
const AppLink = slotComponent("AppLink", ({ href, children, className, ...rest }) => createElement("a", { href, className, ...rest }, children));
|
|
374
388
|
const IconButton = slotComponent("IconButton", unregisteredWidget("IconButton"));
|
|
375
389
|
const LightSwitchToggle = slotComponent("LightSwitchToggle", withPackageDefault(PackageLightSwitchToggle));
|
|
376
390
|
const CitationChip = slotComponent("CitationChip", unregisteredWidget("CitationChip"));
|
|
377
391
|
const MatrxEnvelopeBlock = slotComponent("MatrxEnvelopeBlock", unregisteredWidget("MatrxEnvelopeBlock"));
|
|
378
392
|
const ErrorBoundaryWithCapture = slotComponent("ErrorBoundaryWithCapture", ({ children }) => children ?? null);
|
|
379
|
-
const ConfigurationTable = slotComponent("ConfigurationTable",
|
|
380
|
-
const ConfigurationTableRow = slotComponent("ConfigurationTableRow",
|
|
381
|
-
const FieldHelp = slotComponent("FieldHelp",
|
|
393
|
+
const ConfigurationTable = slotComponent("ConfigurationTable", withPackageDefault(PackageConfigurationTable));
|
|
394
|
+
const ConfigurationTableRow = slotComponent("ConfigurationTableRow", withPackageDefault(PackageConfigurationTableRow));
|
|
395
|
+
const FieldHelp = slotComponent("FieldHelp", withPackageDefault(PackageFieldHelp));
|
|
382
396
|
const HtmlPreviewFullScreenEditor = slotComponent("HtmlPreviewFullScreenEditor", unregisteredWidget("HtmlPreviewFullScreenEditor"));
|
|
383
397
|
const AgentEditAccessBadge = slotComponent("AgentEditAccessBadge", unregisteredWidget("AgentEditAccessBadge"));
|
|
384
398
|
const DataRefPreviewContent = slotComponent("DataRefPreviewContent", unregisteredWidget("DataRefPreviewContent"));
|
|
@@ -499,7 +513,7 @@ const CustomDataBindingSummary = slotComponent("CustomDataBindingSummary", unreg
|
|
|
499
513
|
const CustomDataBindingPreview = slotComponent("CustomDataBindingPreview", unregisteredWidget("CustomDataBindingPreview"));
|
|
500
514
|
const AiModelRef = slotComponent("AiModelRef", unregisteredWidget("AiModelRef"));
|
|
501
515
|
const AiToolRef = slotComponent("AiToolRef", unregisteredWidget("AiToolRef"));
|
|
502
|
-
const TextInputDialog = slotComponent("TextInputDialog",
|
|
516
|
+
const TextInputDialog = slotComponent("TextInputDialog", withPackageDefault(PackageTextInputDialog));
|
|
503
517
|
const ProInput = slotComponent("ProInput", (props) => createElement("input", { ...props, "data-chat-slot-fallback": "input" }));
|
|
504
518
|
const MatrxDynamicPanelHost = slotComponent("MatrxDynamicPanelHost", unregisteredWidget("MatrxDynamicPanelHost"));
|
|
505
519
|
const useClippedContentGuard = slotFn("useClippedContentGuard");
|
|
@@ -1,28 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Aspect-ratio option sets —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* "9:19.5", Runway's pixel pairs "1280:720", plus words like "auto" or
|
|
6
|
-
* "match_input_image"). Any option list that is mostly ratios renders through
|
|
7
|
-
* `AspectRatioSelect` — wherever it came from — so the rule lives on the data,
|
|
8
|
-
* not on one control key.
|
|
2
|
+
* Aspect-ratio option sets — the one copy lives in `@ai-matrx/design-system/controls`
|
|
3
|
+
* (moved there with `AspectRatioSelect`, chat host-slot batch 2, 2026-10-08).
|
|
4
|
+
* This subpath keeps `@ai-matrx/chat/ui/aspect-ratio-options` importers working.
|
|
9
5
|
*/
|
|
10
|
-
export
|
|
11
|
-
w: number;
|
|
12
|
-
h: number;
|
|
13
|
-
}
|
|
14
|
-
export declare function parseRatio(value: string): ParsedRatio | null;
|
|
15
|
-
/** True when at least two options and every option but the words is a ratio. */
|
|
16
|
-
export declare function isAspectRatioOptionSet(options: readonly string[]): boolean;
|
|
17
|
-
/** "Square", "Landscape", … — a plain word for the shape. */
|
|
18
|
-
export declare function ratioShapeName(value: string): string | null;
|
|
19
|
-
/** Words that are not ratios ("auto", "match_input_image") read as words. */
|
|
20
|
-
export declare function humanizeRatioWord(value: string): string;
|
|
21
|
-
export interface RatioGroups {
|
|
22
|
-
/** Non-ratio words first (Auto, Match input image). */
|
|
23
|
-
words: string[];
|
|
24
|
-
common: string[];
|
|
25
|
-
more: string[];
|
|
26
|
-
}
|
|
27
|
-
/** Split options into words, common ratios (fixed order) and the rest (by shape). */
|
|
28
|
-
export declare function groupRatioOptions(options: readonly string[]): RatioGroups;
|
|
6
|
+
export { parseRatio, isAspectRatioOptionSet, ratioShapeName, humanizeRatioWord, groupRatioOptions, type ParsedRatio, type RatioGroups, } from "@ai-matrx/design-system/controls";
|
|
@@ -1,78 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (!(w > 0 && h > 0)) return null;
|
|
9
|
-
return { w, h };
|
|
10
|
-
}
|
|
11
|
-
function isAspectRatioOptionSet(options) {
|
|
12
|
-
const ratios = options.filter((o) => parseRatio(o) !== null).length;
|
|
13
|
-
return ratios >= 2 && ratios >= options.length - 2;
|
|
14
|
-
}
|
|
15
|
-
const COMMON_ORDER = [
|
|
16
|
-
"1:1",
|
|
17
|
-
"16:9",
|
|
18
|
-
"9:16",
|
|
19
|
-
"4:3",
|
|
20
|
-
"3:4",
|
|
21
|
-
"3:2",
|
|
22
|
-
"2:3",
|
|
23
|
-
"4:5",
|
|
24
|
-
"5:4",
|
|
25
|
-
"21:9"
|
|
26
|
-
];
|
|
27
|
-
function reduced(r) {
|
|
28
|
-
const scale = 1e3;
|
|
29
|
-
let a = Math.round(r.w * scale);
|
|
30
|
-
let b = Math.round(r.h * scale);
|
|
31
|
-
const gcd = (x, y) => y === 0 ? x : gcd(y, x % y);
|
|
32
|
-
const g = gcd(a, b);
|
|
33
|
-
a /= g;
|
|
34
|
-
b /= g;
|
|
35
|
-
return `${a}:${b}`;
|
|
36
|
-
}
|
|
37
|
-
function ratioShapeName(value) {
|
|
38
|
-
const r = parseRatio(value);
|
|
39
|
-
if (!r) return null;
|
|
40
|
-
const k = r.w / r.h;
|
|
41
|
-
if (Math.abs(k - 1) < 0.02) return "Square";
|
|
42
|
-
if (k >= 2.2) return "Ultrawide";
|
|
43
|
-
if (k <= 1 / 2.2) return "Tall";
|
|
44
|
-
if (k > 1.6) return "Widescreen";
|
|
45
|
-
if (k < 1 / 1.6) return "Story";
|
|
46
|
-
return k > 1 ? "Landscape" : "Portrait";
|
|
47
|
-
}
|
|
48
|
-
function humanizeRatioWord(value) {
|
|
49
|
-
return humanizeIdentifier(value) || value;
|
|
50
|
-
}
|
|
51
|
-
function groupRatioOptions(options) {
|
|
52
|
-
const words = [];
|
|
53
|
-
const ratioOpts = [];
|
|
54
|
-
for (const o of options) (parseRatio(o) ? ratioOpts : words).push(o);
|
|
55
|
-
const byReduced = /* @__PURE__ */ new Map();
|
|
56
|
-
for (const o of ratioOpts) {
|
|
57
|
-
const key = reduced(parseRatio(o));
|
|
58
|
-
if (!byReduced.has(key)) byReduced.set(key, o);
|
|
59
|
-
}
|
|
60
|
-
const common = [];
|
|
61
|
-
for (const c of COMMON_ORDER) {
|
|
62
|
-
const hit = byReduced.get(reduced(parseRatio(c)));
|
|
63
|
-
if (hit) common.push(hit);
|
|
64
|
-
}
|
|
65
|
-
const commonSet = new Set(common);
|
|
66
|
-
const more = ratioOpts.filter((o) => !commonSet.has(o)).sort((a, b) => {
|
|
67
|
-
const ra = parseRatio(a);
|
|
68
|
-
const rb = parseRatio(b);
|
|
69
|
-
return rb.w / rb.h - ra.w / ra.h;
|
|
70
|
-
});
|
|
71
|
-
if (ratioOpts.length <= 6) {
|
|
72
|
-
return { words, common: [...common, ...more], more: [] };
|
|
73
|
-
}
|
|
74
|
-
return { words, common, more };
|
|
75
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
parseRatio,
|
|
3
|
+
isAspectRatioOptionSet,
|
|
4
|
+
ratioShapeName,
|
|
5
|
+
humanizeRatioWord,
|
|
6
|
+
groupRatioOptions
|
|
7
|
+
} from "@ai-matrx/design-system/controls";
|
|
76
8
|
export {
|
|
77
9
|
groupRatioOptions,
|
|
78
10
|
humanizeRatioWord,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.26",
|
|
4
4
|
"description": "THE one AI Matrx person-to-agent chat system: the full chat (running agents, conversations, tool calls, streaming, context, surfaces, voice) as per-module subpaths @ai-matrx/chat/<domain>/<module> over a host contract (/host, /host/react) whose only required value is a db; plus the kernel compatibility entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) until P28.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -234,18 +234,18 @@
|
|
|
234
234
|
"@ai-matrx/content-ir": "latest",
|
|
235
235
|
"@ai-matrx/content-ir-react": "latest",
|
|
236
236
|
"@ai-matrx/data": "latest",
|
|
237
|
+
"@ai-matrx/design-system": "latest",
|
|
237
238
|
"@ai-matrx/diff": "latest",
|
|
238
239
|
"@ai-matrx/icons": "latest",
|
|
239
|
-
"@ai-matrx/design-system": "latest",
|
|
240
|
-
"@ai-matrx/kit": "latest",
|
|
241
240
|
"@ai-matrx/media": "latest",
|
|
242
241
|
"@ai-matrx/print": "latest",
|
|
242
|
+
"@ai-matrx/kit": "latest",
|
|
243
243
|
"@ai-matrx/realtime": "latest",
|
|
244
244
|
"@ai-matrx/records": "latest",
|
|
245
245
|
"@ai-matrx/records-ui": "latest",
|
|
246
|
+
"@ai-matrx/rich-content": "latest",
|
|
246
247
|
"@ai-matrx/rich-editor": "latest",
|
|
247
|
-
"@ai-matrx/tap-target": "latest"
|
|
248
|
-
"@ai-matrx/rich-content": "latest"
|
|
248
|
+
"@ai-matrx/tap-target": "latest"
|
|
249
249
|
},
|
|
250
250
|
"devDependencies": {
|
|
251
251
|
"@arethetypeswrong/cli": "^0.18.5",
|