@khorsheed/dsh-client-message-tools 0.1.0
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 +11 -0
- package/LICENSE +21 -0
- package/README.en.md +82 -0
- package/README.i18n.yaml +6 -0
- package/README.md +82 -0
- package/cordis.patch.yml +6 -0
- package/lib/client.js +5824 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +644 -0
- package/lib/invariant.js +27 -0
- package/lib/tsconfig.client.tsbuildinfo +1 -0
- package/lib/tsconfig.host.tsbuildinfo +1 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +250 -0
- package/lib/typert.remote-client.d.ts +26 -0
- package/lib/typert.remote-client.d.ts.map +1 -0
- package/lib/typert.remote-client.js +154 -0
- package/lib/types/client/ModelChip.d.ts +29 -0
- package/lib/types/client/ModelChip.d.ts.map +1 -0
- package/lib/types/client/ModelChip.js +86 -0
- package/lib/types/client/RestoredMessageView.d.ts +17 -0
- package/lib/types/client/RestoredMessageView.d.ts.map +1 -0
- package/lib/types/client/RestoredMessageView.js +9 -0
- package/lib/types/client/UserMessageView.d.ts +16 -0
- package/lib/types/client/UserMessageView.d.ts.map +1 -0
- package/lib/types/client/UserMessageView.js +172 -0
- package/lib/types/client/WithdrawnDividerView.d.ts +17 -0
- package/lib/types/client/WithdrawnDividerView.d.ts.map +1 -0
- package/lib/types/client/WithdrawnDividerView.js +59 -0
- package/lib/types/client/backfill.d.ts +19 -0
- package/lib/types/client/backfill.d.ts.map +1 -0
- package/lib/types/client/backfill.js +20 -0
- package/lib/types/client/dom-hider.d.ts +61 -0
- package/lib/types/client/dom-hider.d.ts.map +1 -0
- package/lib/types/client/dom-hider.js +195 -0
- package/lib/types/client/edit-in-place.d.ts +107 -0
- package/lib/types/client/edit-in-place.d.ts.map +1 -0
- package/lib/types/client/edit-in-place.js +97 -0
- package/lib/types/client/icons.d.ts +23 -0
- package/lib/types/client/icons.d.ts.map +1 -0
- package/lib/types/client/icons.js +11 -0
- package/lib/types/client/index.d.ts +39 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +187 -0
- package/lib/types/client/locales.d.ts +74 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/locales.js +65 -0
- package/lib/types/client/slots.d.ts +44 -0
- package/lib/types/client/slots.d.ts.map +1 -0
- package/lib/types/client/slots.js +1 -0
- package/lib/types/client/withdraw-backfill.d.ts +25 -0
- package/lib/types/client/withdraw-backfill.d.ts.map +1 -0
- package/lib/types/client/withdraw-backfill.js +21 -0
- package/lib/types/client/withdrawn-node.d.ts +163 -0
- package/lib/types/client/withdrawn-node.d.ts.map +1 -0
- package/lib/types/client/withdrawn-node.js +297 -0
- package/lib/types/index.d.ts +69 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +175 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +25 -0
- package/lib/types/marker.d.ts +94 -0
- package/lib/types/marker.d.ts.map +1 -0
- package/lib/types/marker.js +131 -0
- package/lib/types/types.d.ts +116 -0
- package/lib/types/types.d.ts.map +1 -0
- package/lib/types/types.js +1 -0
- package/lib/types/withdraw.d.ts +112 -0
- package/lib/types/withdraw.d.ts.map +1 -0
- package/lib/types/withdraw.js +279 -0
- package/package.json +108 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot-facing types of the message-tools client half: the injected action
|
|
3
|
+
* face and the composed props of its two `conversation.chat.node` entries.
|
|
4
|
+
*/
|
|
5
|
+
import type { ModelSelection } from '@deepseek-ai/dsh-api-remotes/client';
|
|
6
|
+
import type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client';
|
|
7
|
+
import type { HostObservable, InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
8
|
+
/** Injected action face of the shadowed user-message entry. */
|
|
9
|
+
export interface MessageToolsInjected {
|
|
10
|
+
hooks: {
|
|
11
|
+
/** The session's shared model directory (the composer seat's own store; a frozen empty stub when ui-model-selection is absent). */
|
|
12
|
+
modelDirectory: HostObservable<ModelDirectoryState>;
|
|
13
|
+
};
|
|
14
|
+
/** Whether the editor's model chip can switch models in this session. */
|
|
15
|
+
modelsAvailable: boolean;
|
|
16
|
+
/** Refresh the advisory model directory (fire-and-forget; errors land on the store). */
|
|
17
|
+
loadModels: () => void;
|
|
18
|
+
/** Submit a provider/model/effort selection; resolves to host acceptance. */
|
|
19
|
+
selectModel: (selection: ModelSelection) => Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Edit one user message in place: replace it (and the surface tail) with
|
|
22
|
+
* the edited text, then start the regeneration turn.
|
|
23
|
+
*/
|
|
24
|
+
editMessage: (targetSeq: number, text: string) => Promise<void>;
|
|
25
|
+
/** Withdraw one user message and everything after it. */
|
|
26
|
+
withdrawMessage: (targetSeq: number) => Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Backfill text into the session's composer draft (append on a new line
|
|
29
|
+
* when the draft is non-empty) and surface an info notice; never sends.
|
|
30
|
+
* Wired to a successful withdrawal — the「重新编辑」divider action shares
|
|
31
|
+
* the same path.
|
|
32
|
+
*/
|
|
33
|
+
backfillDraft: (text: string) => void;
|
|
34
|
+
}
|
|
35
|
+
/** Full props of the shadowed 'user'/'steering'/'message-tools-edited'/'message-tools-restored' renderer (all keys share it). */
|
|
36
|
+
export type UserMessageViewProps = PropsRuntime<'conversation.chat.node', 'user' | 'steering' | 'message-tools-edited' | 'message-tools-restored'> & InjectFace<MessageToolsInjected> & PropsLocale<'message-tools'>;
|
|
37
|
+
/** Injected action face of the withdrawal divider entry. */
|
|
38
|
+
export interface WithdrawnDividerInjected {
|
|
39
|
+
/** Restore the span's withdrawn user message as a fresh tail message. */
|
|
40
|
+
restoreMessage: (targetSeq: number) => Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
/** Full props of the withdrawal divider renderer. */
|
|
43
|
+
export type WithdrawnDividerViewProps = PropsRuntime<'conversation.chat.node', 'message-tools-withdrawn'> & InjectFace<WithdrawnDividerInjected> & PropsLocale<'message-tools'>;
|
|
44
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAA;AAC5F,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAO7G,+DAA+D;AAC/D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE;QACL,mIAAmI;QACnI,cAAc,EAAE,cAAc,CAAC,mBAAmB,CAAC,CAAA;KACpD,CAAA;IACD,yEAAyE;IACzE,eAAe,EAAE,OAAO,CAAA;IACxB,wFAAwF;IACxF,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,6EAA6E;IAC7E,WAAW,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5D;;;OAGG;IACH,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,yDAAyD;IACzD,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD;;;;;OAKG;IACH,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACtC;AAED,iIAAiI;AACjI,MAAM,MAAM,oBAAoB,GAC9B,YAAY,CAAC,wBAAwB,EAAE,MAAM,GAAG,UAAU,GAAG,sBAAsB,GAAG,wBAAwB,CAAC,GAC7G,UAAU,CAAC,oBAAoB,CAAC,GAChC,WAAW,CAAC,eAAe,CAAC,CAAA;AAEhC,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,yEAAyE;IACzE,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrD;AAED,qDAAqD;AACrD,MAAM,MAAM,yBAAyB,GACnC,YAAY,CAAC,wBAAwB,EAAE,yBAAyB,CAAC,GAC/D,UAAU,CAAC,wBAAwB,CAAC,GACpC,WAAW,CAAC,eAAe,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Withdraw-with-backfill choreography for the shadowed user renderer: a
|
|
3
|
+
* successful withdrawal automatically backfills the withdrawn message's
|
|
4
|
+
* original text into the session composer draft (never sends); a failed
|
|
5
|
+
* withdrawal backfills nothing. Kept free of services so the unit tests
|
|
6
|
+
* drive it with stub verbs (the edit-in-place.ts pattern). The in-place EDIT
|
|
7
|
+
* path never goes through here — editing has its own channel.
|
|
8
|
+
*/
|
|
9
|
+
/** The verbs of one withdraw-with-backfill, bound to a session by the caller. */
|
|
10
|
+
export interface WithdrawBackfillSteps {
|
|
11
|
+
/** Withdraw the target message (rejects on failure). */
|
|
12
|
+
withdraw: () => Promise<void>;
|
|
13
|
+
/** Backfill text into the composer draft. */
|
|
14
|
+
backfill: (text: string) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Withdraw one message, then backfill its text into the draft — only after
|
|
18
|
+
* the withdrawal lands, and only when there is text to fill (an image-only
|
|
19
|
+
* message has no draft-worthy text).
|
|
20
|
+
* @param steps - the withdraw/backfill verbs.
|
|
21
|
+
* @param text - the withdrawn message's original text.
|
|
22
|
+
* @returns completion; a failed withdrawal rejects without backfilling.
|
|
23
|
+
*/
|
|
24
|
+
export declare function withdrawAndBackfill(steps: WithdrawBackfillSteps, text: string): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=withdraw-backfill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withdraw-backfill.d.ts","sourceRoot":"","sources":["../../../src/client/withdraw-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,6CAA6C;IAC7C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,qBAAqB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnG"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Withdraw-with-backfill choreography for the shadowed user renderer: a
|
|
3
|
+
* successful withdrawal automatically backfills the withdrawn message's
|
|
4
|
+
* original text into the session composer draft (never sends); a failed
|
|
5
|
+
* withdrawal backfills nothing. Kept free of services so the unit tests
|
|
6
|
+
* drive it with stub verbs (the edit-in-place.ts pattern). The in-place EDIT
|
|
7
|
+
* path never goes through here — editing has its own channel.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Withdraw one message, then backfill its text into the draft — only after
|
|
11
|
+
* the withdrawal lands, and only when there is text to fill (an image-only
|
|
12
|
+
* message has no draft-worthy text).
|
|
13
|
+
* @param steps - the withdraw/backfill verbs.
|
|
14
|
+
* @param text - the withdrawn message's original text.
|
|
15
|
+
* @returns completion; a failed withdrawal rejects without backfilling.
|
|
16
|
+
*/
|
|
17
|
+
export async function withdrawAndBackfill(steps, text) {
|
|
18
|
+
await steps.withdraw();
|
|
19
|
+
if (text !== '')
|
|
20
|
+
steps.backfill(text);
|
|
21
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Withdrawal projection on the chat node store: the Definition that claims
|
|
3
|
+
* message-tools replacement events into divider rows, the Definitions that
|
|
4
|
+
* claim message-tools restore events into replayed rows (user-message bubbles
|
|
5
|
+
* and assistant-text lines), the Definition that claims edit replacements
|
|
6
|
+
* into edited bubbles, plus the pure folds the renderers use (hidden spans,
|
|
7
|
+
* span counts, read-only replay entries). The assembler matches every
|
|
8
|
+
* Definition per event and offers no suppression seam; hiding happens in the
|
|
9
|
+
* renderers and the DOM hider.
|
|
10
|
+
*/
|
|
11
|
+
import type { ChatConversationViewNode, ConversationNodeDefinition } from '@deepseek-ai/dsh-client-runtime/client';
|
|
12
|
+
/** Chat node data of one withdrawal divider row. */
|
|
13
|
+
export interface WithdrawnDividerData {
|
|
14
|
+
/** Seq of the replacement event; the divider anchors here and the hidden span ends (exclusive). */
|
|
15
|
+
readonly seq: number;
|
|
16
|
+
/** First hidden seq: the withdrawn user message itself. */
|
|
17
|
+
readonly hiddenStartSeq: number;
|
|
18
|
+
}
|
|
19
|
+
/** Chat node data of one edited-message bubble row (the edit replacement itself). */
|
|
20
|
+
export interface EditedMessageData {
|
|
21
|
+
/** Seq of the edit replacement event; the bubble anchors here and the hidden span ends (exclusive). */
|
|
22
|
+
readonly seq: number;
|
|
23
|
+
/** Unix epoch ms from the edit replacement event. */
|
|
24
|
+
readonly time: number;
|
|
25
|
+
/** The edited content (what the model now reads in place). */
|
|
26
|
+
readonly content: readonly unknown[];
|
|
27
|
+
/** First hidden seq: the pre-edit message the replacement covers. */
|
|
28
|
+
readonly hiddenStartSeq: number;
|
|
29
|
+
}
|
|
30
|
+
/** Chat node data of one restored (replayed at the tail) user message. */
|
|
31
|
+
export interface RestoredMessageData {
|
|
32
|
+
/** Seq of the restore event. */
|
|
33
|
+
readonly seq: number;
|
|
34
|
+
/** Unix epoch ms from the restore event. */
|
|
35
|
+
readonly time: number;
|
|
36
|
+
/** Seq of the withdrawn original this replays, when cited. */
|
|
37
|
+
readonly restoredFromSeq: number | undefined;
|
|
38
|
+
/** The replayed content blocks (verbatim — images included). */
|
|
39
|
+
readonly content: readonly unknown[];
|
|
40
|
+
/** The original text, replayed verbatim. */
|
|
41
|
+
readonly text: string;
|
|
42
|
+
}
|
|
43
|
+
/** Chat node data of one restored assistant-text replay line. */
|
|
44
|
+
export interface RestoredAssistantMessageData {
|
|
45
|
+
/** Seq of the restore-assistant replay event. */
|
|
46
|
+
readonly seq: number;
|
|
47
|
+
/** Unix epoch ms from the replay event. */
|
|
48
|
+
readonly time: number;
|
|
49
|
+
/** Seq of the withdrawn assistant message this replays, when cited. */
|
|
50
|
+
readonly restoredFromSeq: number | undefined;
|
|
51
|
+
/** The assistant reply text (model-facing frame stripped for display). */
|
|
52
|
+
readonly text: string;
|
|
53
|
+
}
|
|
54
|
+
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
|
|
55
|
+
interface ChatNodeDataMap {
|
|
56
|
+
/** message-tools withdrawal divider marking where a span was hidden. */
|
|
57
|
+
'message-tools-withdrawn': WithdrawnDividerData;
|
|
58
|
+
/** message-tools restore row: a withdrawn user message replayed at the tail. */
|
|
59
|
+
'message-tools-restored': RestoredMessageData;
|
|
60
|
+
/** message-tools restore line: a withdrawn assistant reply's text replayed at the tail. */
|
|
61
|
+
'message-tools-restored-assistant': RestoredAssistantMessageData;
|
|
62
|
+
/** message-tools edit row: the edit replacement rendered as the edited bubble in place. */
|
|
63
|
+
'message-tools-edited': EditedMessageData;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The withdrawal divider Definition: one single-event Context per
|
|
68
|
+
* message-tools replacement event, anchored at the replacement's seq so the
|
|
69
|
+
* divider sits exactly where the span was hidden.
|
|
70
|
+
*/
|
|
71
|
+
export declare const withdrawnDividerDefinition: ConversationNodeDefinition<WithdrawnDividerData>;
|
|
72
|
+
/**
|
|
73
|
+
* The edited-bubble Definition: one single-event Context per message-tools
|
|
74
|
+
* edit replacement event — the replacement's content IS the edited text, so
|
|
75
|
+
* the row renders as a normal user bubble (with an「已编辑」badge) exactly
|
|
76
|
+
* where the span was discarded.
|
|
77
|
+
*/
|
|
78
|
+
export declare const editedMessageDefinition: ConversationNodeDefinition<EditedMessageData>;
|
|
79
|
+
/**
|
|
80
|
+
* Fold the hidden seq spans out of the live chat nodes, flattened to
|
|
81
|
+
* `[start, endExclusive, ...]` pairs ordered by start. Both withdrawal
|
|
82
|
+
* dividers and edit bubbles carry `{ seq, hiddenStartSeq }` spans. The flat
|
|
83
|
+
* number array keeps the useSession selector stable under shallowEqual until
|
|
84
|
+
* a withdrawal or edit lands.
|
|
85
|
+
* @param nodes - every materialized chat node (any order).
|
|
86
|
+
* @returns the flattened hidden spans.
|
|
87
|
+
*/
|
|
88
|
+
export declare function foldHiddenRanges(nodes: readonly ChatConversationViewNode[]): number[];
|
|
89
|
+
/**
|
|
90
|
+
* The restore-row Definition: one single-event Context per message-tools
|
|
91
|
+
* user-message restore event (append-surface, plugin-tagged), replaying the
|
|
92
|
+
* withdrawn message's content at the tail.
|
|
93
|
+
*/
|
|
94
|
+
export declare const restoredMessageDefinition: ConversationNodeDefinition<RestoredMessageData>;
|
|
95
|
+
/**
|
|
96
|
+
* The restore-assistant-line Definition: one single-event Context per
|
|
97
|
+
* message-tools assistant-text restore replay (append-surface, op
|
|
98
|
+
* 'restore-assistant'), rendering the reply text at the tail. The
|
|
99
|
+
* model-facing frame is stripped for display — it belongs to the model
|
|
100
|
+
* context, not the transcript row.
|
|
101
|
+
*/
|
|
102
|
+
export declare const restoredAssistantMessageDefinition: ConversationNodeDefinition<RestoredAssistantMessageData>;
|
|
103
|
+
/**
|
|
104
|
+
* Count the user-visible messages hidden by one withdrawn span. This mirrors
|
|
105
|
+
* {@link collectWithdrawnEntries}: context injections, tool calls, turn tails,
|
|
106
|
+
* and other UI chrome are not counted as “messages”, while restored/edited
|
|
107
|
+
* rows and reasoning-only assistant steps are.
|
|
108
|
+
* @param nodes - every materialized chat node.
|
|
109
|
+
* @param startSeq - first hidden seq (inclusive).
|
|
110
|
+
* @param endSeq - the divider's seq (exclusive).
|
|
111
|
+
* @returns the hidden message count.
|
|
112
|
+
*/
|
|
113
|
+
export declare function countHiddenInSpan(nodes: readonly ChatConversationViewNode[], startSeq: number, endSeq: number): number;
|
|
114
|
+
/** One read-only replay entry of the divider's expand area. */
|
|
115
|
+
export interface WithdrawnEntry {
|
|
116
|
+
/** Source node kind ('user' original or 'assistant-step' summary). */
|
|
117
|
+
readonly kind: 'user' | 'assistant';
|
|
118
|
+
/** The entry's text (full for user originals, a text join for assistant steps). */
|
|
119
|
+
readonly text: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Collect the read-only replay of one withdrawn span: user originals and
|
|
123
|
+
* assistant text, in anchor order. Other kinds carry no user-readable text
|
|
124
|
+
* and are skipped. Assistant-step data is kind-keyed (`data.blocks`, with
|
|
125
|
+
* `data.finalNode.blocks` once settled — `conversation-nodes/assistant.ts`),
|
|
126
|
+
* not type-keyed like message content. Reads the live node store — call from
|
|
127
|
+
* event handlers.
|
|
128
|
+
* @param nodes - every materialized chat node.
|
|
129
|
+
* @param startSeq - first hidden seq (inclusive).
|
|
130
|
+
* @param endSeq - the divider's seq (exclusive).
|
|
131
|
+
* @returns the replay entries.
|
|
132
|
+
*/
|
|
133
|
+
export declare function collectWithdrawnEntries(nodes: readonly ChatConversationViewNode[], startSeq: number, endSeq: number): WithdrawnEntry[];
|
|
134
|
+
/**
|
|
135
|
+
* Whether one withdrawn span has a LIVE restore row (a restored node citing
|
|
136
|
+
* the span's first hidden seq and not itself inside a hidden span). When the
|
|
137
|
+
* restore rows are withdrawn again, the badge clears and the divider's
|
|
138
|
+
* restore action re-enables — the restore events stay in the log either way.
|
|
139
|
+
* @param nodes - every materialized chat node.
|
|
140
|
+
* @param hiddenStartSeq - the span's first hidden seq.
|
|
141
|
+
* @param ranges - the flattened hidden spans from foldHiddenRanges.
|
|
142
|
+
* @returns true when a live restore row for the span exists.
|
|
143
|
+
*/
|
|
144
|
+
export declare function hasRestoreForSpan(nodes: readonly ChatConversationViewNode[], hiddenStartSeq: number, ranges: readonly number[]): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Whether a span's restore rows have themselves been withdrawn by a later
|
|
147
|
+
* span. The earlier divider remains as history, but the later divider is the
|
|
148
|
+
* active restore point; offering restore on both would duplicate the same
|
|
149
|
+
* logical content when the user toggles withdraw → restore → withdraw.
|
|
150
|
+
* @param nodes - every materialized chat node.
|
|
151
|
+
* @param hiddenStartSeq - the span's first hidden seq.
|
|
152
|
+
* @param ranges - the flattened hidden spans from foldHiddenRanges.
|
|
153
|
+
* @returns true when this span's restore rows are hidden inside a later span.
|
|
154
|
+
*/
|
|
155
|
+
export declare function isRestoreSuperseded(nodes: readonly ChatConversationViewNode[], hiddenStartSeq: number, ranges: readonly number[]): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Whether a node seq falls inside a hidden span.
|
|
158
|
+
* @param ranges - the flattened spans from foldHiddenRanges.
|
|
159
|
+
* @param seq - the node seq to test.
|
|
160
|
+
* @returns true when the seq is hidden.
|
|
161
|
+
*/
|
|
162
|
+
export declare function isSeqHidden(ranges: readonly number[], seq: number): boolean;
|
|
163
|
+
//# sourceMappingURL=withdrawn-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withdrawn-node.d.ts","sourceRoot":"","sources":["../../../src/client/withdrawn-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { isMessageToolsEdit, isMessageToolsReplacement, isMessageToolsRestore, isMessageToolsRestoreAssistant, stripRestoreAssistantFrame, } from '../marker.js';
|
|
2
|
+
/**
|
|
3
|
+
* The withdrawal divider Definition: one single-event Context per
|
|
4
|
+
* message-tools replacement event, anchored at the replacement's seq so the
|
|
5
|
+
* divider sits exactly where the span was hidden.
|
|
6
|
+
*/
|
|
7
|
+
export const withdrawnDividerDefinition = {
|
|
8
|
+
kind: 'message-tools-withdrawn',
|
|
9
|
+
target: 'chat',
|
|
10
|
+
match: event => isMessageToolsReplacement(event)
|
|
11
|
+
? { id: String(event.seq), role: 'start' }
|
|
12
|
+
: null,
|
|
13
|
+
start: (_context, match) => {
|
|
14
|
+
const event = match.event;
|
|
15
|
+
if (!isMessageToolsReplacement(event)) {
|
|
16
|
+
throw new Error('message-tools-withdrawn start requires a message-tools replacement event');
|
|
17
|
+
}
|
|
18
|
+
return { seq: event.seq, hiddenStartSeq: event.surfaceOp.start };
|
|
19
|
+
},
|
|
20
|
+
update: context => context.state,
|
|
21
|
+
buildViewNode: context => context.state === undefined
|
|
22
|
+
? null
|
|
23
|
+
: {
|
|
24
|
+
key: context.key,
|
|
25
|
+
kind: 'message-tools-withdrawn',
|
|
26
|
+
id: context.id,
|
|
27
|
+
target: 'chat',
|
|
28
|
+
anchorSeq: context.state.seq,
|
|
29
|
+
location: context.start?.location ?? { kind: 'unresolved' },
|
|
30
|
+
visibility: 'visible',
|
|
31
|
+
data: context.state,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The edited-bubble Definition: one single-event Context per message-tools
|
|
36
|
+
* edit replacement event — the replacement's content IS the edited text, so
|
|
37
|
+
* the row renders as a normal user bubble (with an「已编辑」badge) exactly
|
|
38
|
+
* where the span was discarded.
|
|
39
|
+
*/
|
|
40
|
+
export const editedMessageDefinition = {
|
|
41
|
+
kind: 'message-tools-edited',
|
|
42
|
+
target: 'chat',
|
|
43
|
+
match: event => isMessageToolsEdit(event)
|
|
44
|
+
? { id: String(event.seq), role: 'start' }
|
|
45
|
+
: null,
|
|
46
|
+
start: (_context, match) => {
|
|
47
|
+
const event = match.event;
|
|
48
|
+
if (!isMessageToolsEdit(event)) {
|
|
49
|
+
throw new Error('message-tools-edited start requires a message-tools edit replacement event');
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
seq: event.seq,
|
|
53
|
+
time: event.time,
|
|
54
|
+
content: event.data.content,
|
|
55
|
+
hiddenStartSeq: event.surfaceOp.start,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
update: context => context.state,
|
|
59
|
+
buildViewNode: context => context.state === undefined
|
|
60
|
+
? null
|
|
61
|
+
: {
|
|
62
|
+
key: context.key,
|
|
63
|
+
kind: 'message-tools-edited',
|
|
64
|
+
id: context.id,
|
|
65
|
+
target: 'chat',
|
|
66
|
+
anchorSeq: context.state.seq,
|
|
67
|
+
location: context.start?.location ?? { kind: 'unresolved' },
|
|
68
|
+
visibility: 'visible',
|
|
69
|
+
data: context.state,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Fold the hidden seq spans out of the live chat nodes, flattened to
|
|
74
|
+
* `[start, endExclusive, ...]` pairs ordered by start. Both withdrawal
|
|
75
|
+
* dividers and edit bubbles carry `{ seq, hiddenStartSeq }` spans. The flat
|
|
76
|
+
* number array keeps the useSession selector stable under shallowEqual until
|
|
77
|
+
* a withdrawal or edit lands.
|
|
78
|
+
* @param nodes - every materialized chat node (any order).
|
|
79
|
+
* @returns the flattened hidden spans.
|
|
80
|
+
*/
|
|
81
|
+
export function foldHiddenRanges(nodes) {
|
|
82
|
+
const pairs = [];
|
|
83
|
+
for (const node of nodes) {
|
|
84
|
+
if (node.kind !== 'message-tools-withdrawn' && node.kind !== 'message-tools-edited')
|
|
85
|
+
continue;
|
|
86
|
+
const data = node.data;
|
|
87
|
+
pairs.push([data.hiddenStartSeq, data.seq]);
|
|
88
|
+
}
|
|
89
|
+
pairs.sort((left, right) => left[0] - right[0]);
|
|
90
|
+
return pairs.flat();
|
|
91
|
+
}
|
|
92
|
+
/** Join text blocks into one plain string. */
|
|
93
|
+
function joinText(blocks) {
|
|
94
|
+
return blocks
|
|
95
|
+
.filter((block) => block.type === 'text'
|
|
96
|
+
&& typeof block.text === 'string')
|
|
97
|
+
.map(block => block.text)
|
|
98
|
+
.join('');
|
|
99
|
+
}
|
|
100
|
+
/** Join AssistantBlock text (kind-keyed, unlike message content's type-keyed blocks). */
|
|
101
|
+
function joinAssistantText(blocks) {
|
|
102
|
+
const text = blocks
|
|
103
|
+
.filter((block) => block.kind === 'text'
|
|
104
|
+
&& typeof block.text === 'string')
|
|
105
|
+
.map(block => block.text)
|
|
106
|
+
.join('');
|
|
107
|
+
if (text !== '')
|
|
108
|
+
return text;
|
|
109
|
+
// Interrupted assistant steps may contain only reasoning; preserve it in
|
|
110
|
+
// the divider expand instead of showing an empty span.
|
|
111
|
+
return blocks
|
|
112
|
+
.filter((block) => block.kind === 'reasoning'
|
|
113
|
+
&& typeof block.text === 'string')
|
|
114
|
+
.map(block => block.text)
|
|
115
|
+
.join('');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The restore-row Definition: one single-event Context per message-tools
|
|
119
|
+
* user-message restore event (append-surface, plugin-tagged), replaying the
|
|
120
|
+
* withdrawn message's content at the tail.
|
|
121
|
+
*/
|
|
122
|
+
export const restoredMessageDefinition = {
|
|
123
|
+
kind: 'message-tools-restored',
|
|
124
|
+
target: 'chat',
|
|
125
|
+
match: event => isMessageToolsRestore(event)
|
|
126
|
+
? { id: String(event.seq), role: 'start' }
|
|
127
|
+
: null,
|
|
128
|
+
start: (_context, match) => {
|
|
129
|
+
const event = match.event;
|
|
130
|
+
if (!isMessageToolsRestore(event)) {
|
|
131
|
+
throw new Error('message-tools-restored start requires a message-tools restore event');
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
seq: event.seq,
|
|
135
|
+
time: event.time,
|
|
136
|
+
restoredFromSeq: event.sourceEventSeqs?.[0],
|
|
137
|
+
content: event.data.content,
|
|
138
|
+
text: joinText(event.data.content),
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
update: context => context.state,
|
|
142
|
+
buildViewNode: context => context.state === undefined
|
|
143
|
+
? null
|
|
144
|
+
: {
|
|
145
|
+
key: context.key,
|
|
146
|
+
kind: 'message-tools-restored',
|
|
147
|
+
id: context.id,
|
|
148
|
+
target: 'chat',
|
|
149
|
+
anchorSeq: context.state.seq,
|
|
150
|
+
location: context.start?.location ?? { kind: 'unresolved' },
|
|
151
|
+
visibility: 'visible',
|
|
152
|
+
data: context.state,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* The restore-assistant-line Definition: one single-event Context per
|
|
157
|
+
* message-tools assistant-text restore replay (append-surface, op
|
|
158
|
+
* 'restore-assistant'), rendering the reply text at the tail. The
|
|
159
|
+
* model-facing frame is stripped for display — it belongs to the model
|
|
160
|
+
* context, not the transcript row.
|
|
161
|
+
*/
|
|
162
|
+
export const restoredAssistantMessageDefinition = {
|
|
163
|
+
kind: 'message-tools-restored-assistant',
|
|
164
|
+
target: 'chat',
|
|
165
|
+
match: event => isMessageToolsRestoreAssistant(event)
|
|
166
|
+
? { id: String(event.seq), role: 'start' }
|
|
167
|
+
: null,
|
|
168
|
+
start: (_context, match) => {
|
|
169
|
+
const event = match.event;
|
|
170
|
+
if (!isMessageToolsRestoreAssistant(event)) {
|
|
171
|
+
throw new Error('message-tools-restored-assistant start requires a restore-assistant replay event');
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
seq: event.seq,
|
|
175
|
+
time: event.time,
|
|
176
|
+
restoredFromSeq: event.sourceEventSeqs?.[0],
|
|
177
|
+
text: stripRestoreAssistantFrame(joinText(event.data.content)),
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
update: context => context.state,
|
|
181
|
+
buildViewNode: context => context.state === undefined
|
|
182
|
+
? null
|
|
183
|
+
: {
|
|
184
|
+
key: context.key,
|
|
185
|
+
kind: 'message-tools-restored-assistant',
|
|
186
|
+
id: context.id,
|
|
187
|
+
target: 'chat',
|
|
188
|
+
anchorSeq: context.state.seq,
|
|
189
|
+
location: context.start?.location ?? { kind: 'unresolved' },
|
|
190
|
+
visibility: 'visible',
|
|
191
|
+
data: context.state,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Count the user-visible messages hidden by one withdrawn span. This mirrors
|
|
196
|
+
* {@link collectWithdrawnEntries}: context injections, tool calls, turn tails,
|
|
197
|
+
* and other UI chrome are not counted as “messages”, while restored/edited
|
|
198
|
+
* rows and reasoning-only assistant steps are.
|
|
199
|
+
* @param nodes - every materialized chat node.
|
|
200
|
+
* @param startSeq - first hidden seq (inclusive).
|
|
201
|
+
* @param endSeq - the divider's seq (exclusive).
|
|
202
|
+
* @returns the hidden message count.
|
|
203
|
+
*/
|
|
204
|
+
export function countHiddenInSpan(nodes, startSeq, endSeq) {
|
|
205
|
+
return collectWithdrawnEntries(nodes, startSeq, endSeq).length;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Collect the read-only replay of one withdrawn span: user originals and
|
|
209
|
+
* assistant text, in anchor order. Other kinds carry no user-readable text
|
|
210
|
+
* and are skipped. Assistant-step data is kind-keyed (`data.blocks`, with
|
|
211
|
+
* `data.finalNode.blocks` once settled — `conversation-nodes/assistant.ts`),
|
|
212
|
+
* not type-keyed like message content. Reads the live node store — call from
|
|
213
|
+
* event handlers.
|
|
214
|
+
* @param nodes - every materialized chat node.
|
|
215
|
+
* @param startSeq - first hidden seq (inclusive).
|
|
216
|
+
* @param endSeq - the divider's seq (exclusive).
|
|
217
|
+
* @returns the replay entries.
|
|
218
|
+
*/
|
|
219
|
+
export function collectWithdrawnEntries(nodes, startSeq, endSeq) {
|
|
220
|
+
const inSpan = nodes
|
|
221
|
+
.filter(node => node.anchorSeq >= startSeq && node.anchorSeq < endSeq)
|
|
222
|
+
.sort((left, right) => left.anchorSeq - right.anchorSeq);
|
|
223
|
+
const entries = [];
|
|
224
|
+
for (const node of inSpan) {
|
|
225
|
+
if (node.kind === 'user' || node.kind === 'steering' || node.kind === 'message-tools-edited') {
|
|
226
|
+
const content = node.data.content ?? [];
|
|
227
|
+
const text = joinText(content);
|
|
228
|
+
if (text !== '')
|
|
229
|
+
entries.push({ kind: 'user', text });
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (node.kind === 'message-tools-restored') {
|
|
233
|
+
const text = node.data.text ?? '';
|
|
234
|
+
if (text !== '')
|
|
235
|
+
entries.push({ kind: 'user', text });
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (node.kind === 'message-tools-restored-assistant') {
|
|
239
|
+
const text = node.data.text ?? '';
|
|
240
|
+
if (text !== '')
|
|
241
|
+
entries.push({ kind: 'assistant', text });
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (node.kind === 'assistant-step') {
|
|
245
|
+
const data = node.data;
|
|
246
|
+
const text = joinAssistantText(data.finalNode?.blocks ?? data.blocks ?? []);
|
|
247
|
+
if (text !== '')
|
|
248
|
+
entries.push({ kind: 'assistant', text });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return entries;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Whether one withdrawn span has a LIVE restore row (a restored node citing
|
|
255
|
+
* the span's first hidden seq and not itself inside a hidden span). When the
|
|
256
|
+
* restore rows are withdrawn again, the badge clears and the divider's
|
|
257
|
+
* restore action re-enables — the restore events stay in the log either way.
|
|
258
|
+
* @param nodes - every materialized chat node.
|
|
259
|
+
* @param hiddenStartSeq - the span's first hidden seq.
|
|
260
|
+
* @param ranges - the flattened hidden spans from foldHiddenRanges.
|
|
261
|
+
* @returns true when a live restore row for the span exists.
|
|
262
|
+
*/
|
|
263
|
+
export function hasRestoreForSpan(nodes, hiddenStartSeq, ranges) {
|
|
264
|
+
return nodes.some(node => node.kind === 'message-tools-restored'
|
|
265
|
+
&& node.data.restoredFromSeq === hiddenStartSeq
|
|
266
|
+
&& !isSeqHidden(ranges, node.anchorSeq));
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Whether a span's restore rows have themselves been withdrawn by a later
|
|
270
|
+
* span. The earlier divider remains as history, but the later divider is the
|
|
271
|
+
* active restore point; offering restore on both would duplicate the same
|
|
272
|
+
* logical content when the user toggles withdraw → restore → withdraw.
|
|
273
|
+
* @param nodes - every materialized chat node.
|
|
274
|
+
* @param hiddenStartSeq - the span's first hidden seq.
|
|
275
|
+
* @param ranges - the flattened hidden spans from foldHiddenRanges.
|
|
276
|
+
* @returns true when this span's restore rows are hidden inside a later span.
|
|
277
|
+
*/
|
|
278
|
+
export function isRestoreSuperseded(nodes, hiddenStartSeq, ranges) {
|
|
279
|
+
return nodes.some(node => (node.kind === 'message-tools-restored' || node.kind === 'message-tools-restored-assistant')
|
|
280
|
+
&& node.data.restoredFromSeq === hiddenStartSeq
|
|
281
|
+
&& isSeqHidden(ranges, node.anchorSeq));
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Whether a node seq falls inside a hidden span.
|
|
285
|
+
* @param ranges - the flattened spans from foldHiddenRanges.
|
|
286
|
+
* @param seq - the node seq to test.
|
|
287
|
+
* @returns true when the seq is hidden.
|
|
288
|
+
*/
|
|
289
|
+
export function isSeqHidden(ranges, seq) {
|
|
290
|
+
for (let index = 0; index + 1 < ranges.length; index += 2) {
|
|
291
|
+
const start = ranges[index];
|
|
292
|
+
const end = ranges[index + 1];
|
|
293
|
+
if (start !== undefined && end !== undefined && seq >= start && seq < end)
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* message-tools host half: the `messageTools` Typert Remote service. Its
|
|
3
|
+
* withdraw/edit methods land surface replacements — the compaction mechanism —
|
|
4
|
+
* so the target message and everything after it leave the model-visible
|
|
5
|
+
* surface (edit replacements carry the edited text as their content), and its
|
|
6
|
+
* restore method tail-replays a withdrawn span's replayable entries, while
|
|
7
|
+
* the browser half shadows the user-message renderer and projects the
|
|
8
|
+
* withdrawal divider, edited bubble, and restored rows.
|
|
9
|
+
* @module @khorsheed/dsh-client-message-tools
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
13
|
+
import type { MessageToolsEditRequest, MessageToolsEditResult, MessageToolsRestoreRequest, MessageToolsRestoreResult, MessageToolsWithdrawRequest, MessageToolsWithdrawResult } from './types.ts';
|
|
14
|
+
export type * from './types.ts';
|
|
15
|
+
declare module '@deepseek-ai/cordis' {
|
|
16
|
+
interface Context {
|
|
17
|
+
/** Withdrawal Remote owned by the message-tools plugin. */
|
|
18
|
+
messageTools: MessageToolsService;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* messageTools Remote service: appends the withdrawal replacement to the
|
|
23
|
+
* owning Session and flushes it durable. The replacement event's
|
|
24
|
+
* plugin-tagged source is the audit trail; no new session event type is
|
|
25
|
+
* introduced, so session reload stays safe for harnesses without this plugin.
|
|
26
|
+
*/
|
|
27
|
+
export declare class MessageToolsService extends TypertRemoteService {
|
|
28
|
+
static inject: string[];
|
|
29
|
+
/**
|
|
30
|
+
* @param ctx - host context carrying the session store.
|
|
31
|
+
*/
|
|
32
|
+
constructor(ctx: Context);
|
|
33
|
+
/**
|
|
34
|
+
* Withdraw a user message: replace it and every surface node after it with
|
|
35
|
+
* a minimal plugin-sourced placeholder, hiding the span from the model.
|
|
36
|
+
* @param request - session identity and target message seq.
|
|
37
|
+
* @returns the replacement receipt, or a rejection from the closed failure union.
|
|
38
|
+
*/
|
|
39
|
+
withdraw(request: MessageToolsWithdrawRequest): Promise<MessageToolsWithdrawResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Restore a withdrawn span: replay every replayable entry of it (user
|
|
42
|
+
* messages verbatim — including the last edit's new text — and framed
|
|
43
|
+
* assistant text; tool calls/results never replay) as fresh plugin-tagged
|
|
44
|
+
* `user/message` appends at the conversation tail, in original order, each
|
|
45
|
+
* citing its original event in `sourceEventSeqs`. The surface replace model
|
|
46
|
+
* is positional — a replaced span folds into one node — so the span cannot
|
|
47
|
+
* be restored in place; the replayed entries are simply the newest messages
|
|
48
|
+
* the model sees. Assistant text replays as framed user-role messages:
|
|
49
|
+
* `assistant/message` cannot carry a plugin source and the turn/step trace
|
|
50
|
+
* forbids assistant appends outside a step.
|
|
51
|
+
* @param request - session identity and withdrawn message seq.
|
|
52
|
+
* @returns the restore receipt, or a rejection from the closed failure union.
|
|
53
|
+
*/
|
|
54
|
+
restore(request: MessageToolsRestoreRequest): Promise<MessageToolsRestoreResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Edit a user message in place: replace it and every surface node after it
|
|
57
|
+
* with the edited text itself (no placeholder, no tail duplicate — the
|
|
58
|
+
* edited text appears exactly once, in the replacement event), then start a
|
|
59
|
+
* regeneration turn. The harness has no wake-without-append path, so the
|
|
60
|
+
* turn is started by `agent.followup` carrying a minimal plugin-sourced
|
|
61
|
+
* trigger message (the ankh-guard/schedule pattern); the trigger is not the
|
|
62
|
+
* edited text and lands once, as itself.
|
|
63
|
+
* @param request - session identity, target seq, edited text.
|
|
64
|
+
* @returns the replacement receipt and whether the turn was triggered, or a rejection.
|
|
65
|
+
*/
|
|
66
|
+
edit(request: MessageToolsEditRequest): Promise<MessageToolsEditResult>;
|
|
67
|
+
}
|
|
68
|
+
export default MessageToolsService;
|
|
69
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAMlD,OAAO,EAAU,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAM9E,OAAO,KAAK,EACV,uBAAuB,EAAE,sBAAsB,EAC/C,0BAA0B,EAAE,yBAAyB,EACrD,2BAA2B,EAAE,0BAA0B,EACxD,MAAM,YAAY,CAAA;AAEnB,mBAAmB,YAAY,CAAA;AAE/B,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,2DAA2D;QAC3D,YAAY,EAAE,mBAAmB,CAAA;KAClC;CACF;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,mBAAmB;IAC1D,MAAM,CAAC,MAAM,WAAyB;IAEtC;;OAEG;IACH,YAAY,GAAG,EAAE,OAAO,EAEvB;IAED;;;;;OAKG;IAEG,QAAQ,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAiBxF;IAED;;;;;;;;;;;;;OAaG;IAEG,OAAO,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAwBrF;IAED;;;;;;;;;;OAUG;IAEG,IAAI,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAqB5E;CACF;eAEc,mBAAmB"}
|