@memberjunction/ng-conversations 6.1.0-edge.2 → 6.1.0-edge.3

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.
Files changed (39) hide show
  1. package/LICENSE +180 -4
  2. package/README.md +32 -1
  3. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +159 -9
  4. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  5. package/dist/lib/components/conversation/conversation-chat-area.component.js +497 -106
  6. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  7. package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts +14 -2
  8. package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts.map +1 -1
  9. package/dist/lib/components/conversation/pinned-messages-panel.component.js +37 -19
  10. package/dist/lib/components/conversation/pinned-messages-panel.component.js.map +1 -1
  11. package/dist/lib/components/message/message-input.component.d.ts +7 -3
  12. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  13. package/dist/lib/components/message/message-input.component.js +78 -159
  14. package/dist/lib/components/message/message-input.component.js.map +1 -1
  15. package/dist/lib/components/message/message-list.component.d.ts +287 -7
  16. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  17. package/dist/lib/components/message/message-list.component.js +850 -56
  18. package/dist/lib/components/message/message-list.component.js.map +1 -1
  19. package/dist/lib/services/conversation-agent.service.d.ts +98 -16
  20. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  21. package/dist/lib/services/conversation-agent.service.js +218 -69
  22. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  23. package/dist/lib/services/conversation-detail-window.store.d.ts +206 -0
  24. package/dist/lib/services/conversation-detail-window.store.d.ts.map +1 -0
  25. package/dist/lib/services/conversation-detail-window.store.js +503 -0
  26. package/dist/lib/services/conversation-detail-window.store.js.map +1 -0
  27. package/dist/lib/utils/agent-artifact-summary.d.ts +54 -0
  28. package/dist/lib/utils/agent-artifact-summary.d.ts.map +1 -0
  29. package/dist/lib/utils/agent-artifact-summary.js +49 -0
  30. package/dist/lib/utils/agent-artifact-summary.js.map +1 -0
  31. package/dist/lib/utils/conversation-detail-window.d.ts +49 -0
  32. package/dist/lib/utils/conversation-detail-window.d.ts.map +1 -0
  33. package/dist/lib/utils/conversation-detail-window.js +86 -0
  34. package/dist/lib/utils/conversation-detail-window.js.map +1 -0
  35. package/dist/lib/utils/date-jump.d.ts +65 -0
  36. package/dist/lib/utils/date-jump.d.ts.map +1 -0
  37. package/dist/lib/utils/date-jump.js +106 -0
  38. package/dist/lib/utils/date-jump.js.map +1 -0
  39. package/package.json +32 -32
@@ -0,0 +1,206 @@
1
+ import type { UserInfo } from '@memberjunction/core';
2
+ import type { MJConversationDetailEntity, LoadDetailWindowParams, DetailWindowLoadResult } from '@memberjunction/core-entities';
3
+ import { ConversationDetailWindowCursor } from '../utils/conversation-detail-window';
4
+ /**
5
+ * The engine surface this store needs. Declared structurally rather than importing
6
+ * ConversationEngine so tests can pass a plain `{ LoadDetailWindow: vi.fn() }` with no
7
+ * TestBed and no engine bootstrap.
8
+ */
9
+ export interface DetailWindowLoader {
10
+ LoadDetailWindow(params: LoadDetailWindowParams, contextUser: UserInfo): Promise<DetailWindowLoadResult>;
11
+ }
12
+ /**
13
+ * Peripheral data for the loaded window, keyed the way the chat area already consumes it.
14
+ *
15
+ * Accumulates across pages: an older page brings its own artifacts and ratings, which merge
16
+ * into these maps rather than replacing them.
17
+ */
18
+ export type ConversationDetailWindowPeripherals = Pick<DetailWindowLoadResult, 'AgentRunsByDetailId' | 'UserAvatars' | 'RatingsByDetailId' | 'ArtifactsByDetailId'>;
19
+ /** What the chat area binds to after any store operation. */
20
+ export interface ConversationDetailWindowSnapshot extends ConversationDetailWindowPeripherals {
21
+ ConversationID: string | null;
22
+ /** Loaded rows, chronological by Sequence. A NEW array each call, so ngOnChanges fires. */
23
+ Details: MJConversationDetailEntity[];
24
+ Cursor: ConversationDetailWindowCursor;
25
+ /** Pinned rows for the pins panel, INCLUDING pins older than the window. */
26
+ PinnedDetails: MJConversationDetailEntity[];
27
+ /**
28
+ * TRUE total pins in the conversation, which is NOT `PinnedDetails.length` until the
29
+ * panel has been opened — the open path reads only the count so the chip has a number
30
+ * without hydrating anything.
31
+ */
32
+ PinnedTotalCount: number;
33
+ IsLoadingLatest: boolean;
34
+ IsLoadingOlder: boolean;
35
+ /**
36
+ * True when the most recent load FAILED, as opposed to returning nothing.
37
+ *
38
+ * Exists so an empty transcript can be told apart from a broken one. Without it a failed
39
+ * first read renders as a conversation with no messages, which reads to the user as "my
40
+ * conversation is gone" — the one failure mode worse than an error message.
41
+ */
42
+ LoadFailed: boolean;
43
+ }
44
+ /**
45
+ * Holds ONE conversation's loaded transcript window and the cursors for paging further back.
46
+ *
47
+ * Owned per chat-area instance (not a singleton) so two hosts can never share window state.
48
+ *
49
+ * Contracts:
50
+ * - {@link Reset} bumps a generation counter; every in-flight load checks it after each
51
+ * await and discards its result if the conversation changed underneath.
52
+ * - {@link LoadOlder} is a no-op while a load is in flight or when nothing is above.
53
+ * - Merges dedupe by normalized ID and re-sort by `Sequence` — session expansion can
54
+ * return rows an earlier page already had.
55
+ * - {@link ApplyLocalDetail} never hits the network; it is the send/stream/edit path.
56
+ * - Pins live in their own array. They are NOT unioned into the transcript, because a pin
57
+ * below the window's oldest Sequence would break contiguity and the paging cursor.
58
+ */
59
+ export declare class ConversationDetailWindowStore {
60
+ private loader;
61
+ private conversationId;
62
+ private loadedDetails;
63
+ private pinnedDetails;
64
+ private pinnedTotalCount;
65
+ private peripherals;
66
+ private cursor;
67
+ private generation;
68
+ private isLoadingLatest;
69
+ private isLoadingOlder;
70
+ private loadFailed;
71
+ constructor(loader: DetailWindowLoader);
72
+ /** Drops all state and invalidates in-flight loads. Call on every conversation switch. */
73
+ Reset(conversationId: string | null): void;
74
+ /**
75
+ * Fetches a page, WIDENING the raw over-read until it yields a full page of timeline
76
+ * items (or the conversation runs out).
77
+ *
78
+ * Raw rows are not display items: a realtime session folds many stamped rows into ONE
79
+ * timeline card, so the default `3 × pageSize` over-read can come back holding two or
80
+ * three items instead of ten. Taking that at face value gives the reader a stubby page
81
+ * and makes the sentinel fire again immediately — which is worse than one wider read.
82
+ *
83
+ * Bounded by {@link MAX_OVERREAD_ATTEMPTS}: a conversation that is almost entirely one
84
+ * long session would otherwise grow the read until it pulled the whole table.
85
+ */
86
+ private fetchPageFillingTimeline;
87
+ /**
88
+ * First paint: the newest page of a conversation.
89
+ *
90
+ * The loader is not expected to throw — `LoadDetailWindow` logs and returns an empty
91
+ * window on a failed read — but a transport-layer rejection would otherwise strand
92
+ * `isLoadingLatest` at true and wedge the store, so the flag is cleared in `finally`.
93
+ */
94
+ LoadLatest(conversationId: string, contextUser: UserInfo): Promise<void>;
95
+ /**
96
+ * Prepends the next older page. No-op when nothing is above or a load is running.
97
+ *
98
+ * `isLoadingOlder` is cleared in `finally` for the same reason as {@link LoadLatest} —
99
+ * and it matters more here, because a stuck flag makes {@link CanLoadOlder} false
100
+ * forever, which silently kills the transcript's "earlier messages" sentinel.
101
+ */
102
+ LoadOlder(contextUser: UserInfo): Promise<void>;
103
+ /**
104
+ * Re-fetches the tail and folds it into the existing window.
105
+ *
106
+ * The refresh-in-place counterpart to {@link LoadLatest}: used when something changed at
107
+ * the tail (an agent finished, artifacts were written) and the transcript needs to pick
108
+ * it up. Unlike `LoadLatest` it does NOT `Reset`, so a user who has paged up five times
109
+ * keeps those pages instead of being yanked back to a 10-row tail.
110
+ *
111
+ * Reads via {@link fetchRefreshedTail}, which keeps paging down until it reaches rows the
112
+ * window already holds — otherwise a conversation that grew by more than a page leaves a
113
+ * hole in the middle of the merged set that nothing in the UI can reveal.
114
+ */
115
+ RefreshLatest(contextUser: UserInfo): Promise<void>;
116
+ /**
117
+ * Reads the newest page, then keeps paging down until the read reaches back to
118
+ * `previousNewest` — the newest row the window already holds.
119
+ *
120
+ * One page is enough in the normal case: a refresh usually returns rows we already have
121
+ * plus the handful that arrived since. The loop is for the case that is not normal, where
122
+ * the tail has moved further than a page and a single read would land entirely above the
123
+ * loaded set.
124
+ *
125
+ * @returns Every page read, plus whether the reads joined up with the loaded window.
126
+ */
127
+ private fetchRefreshedTail;
128
+ /** True when this page reaches back into the loaded window, so nothing sits between them. */
129
+ private tailIsJoined;
130
+ /**
131
+ * Folds a refreshed tail into the window, choosing what to keep by whether the read
132
+ * joined up with the rows already loaded.
133
+ */
134
+ private applyRefreshedTail;
135
+ /** Merges the refreshed rows per the three refresh cases. @returns the new `HasMoreAbove`. */
136
+ private mergeRefreshedRows;
137
+ /**
138
+ * Cheap single-value reads for TEMPLATE binding.
139
+ *
140
+ * These exist because the chat area exposes `HasMoreAbove`, `IsLoadingOlder`,
141
+ * `PinnedTotalCount`, and `PinnedDetails` as getters bound in its template, so every
142
+ * change-detection cycle reads all four. Routing those through {@link GetSnapshot} copied
143
+ * the whole loaded window four times per cycle — during streaming, per token — on a
144
+ * component whose entire purpose is to stop doing work proportional to conversation length.
145
+ *
146
+ * {@link PinnedDetails} deliberately returns the internal array BY REFERENCE. Every mutator
147
+ * replaces that array rather than mutating it, so the reference is stable between real
148
+ * changes — which is precisely what keeps the pins panel's `ngOnChanges` from firing on
149
+ * every cycle. Callers must not mutate it.
150
+ */
151
+ get HasMoreAbove(): boolean;
152
+ get IsLoadingOlder(): boolean;
153
+ get IsLoadingLatest(): boolean;
154
+ get PinnedTotalCount(): number;
155
+ get PinnedDetails(): readonly MJConversationDetailEntity[];
156
+ /** True when the most recent load failed — an empty transcript vs. a broken one. */
157
+ get LoadFailed(): boolean;
158
+ /** True when a `LoadOlder` would actually do work. Drives the sentinel's observer. */
159
+ CanLoadOlder(): boolean;
160
+ /**
161
+ * Applies a locally-known row — sent, streamed, edited. Inserts or replaces by ID and
162
+ * re-sorts. Never queries; the caller already has the entity.
163
+ */
164
+ ApplyLocalDetail(detail: MJConversationDetailEntity): void;
165
+ /** Removes a row by ID — the delete path. */
166
+ RemoveDetail(detailId: string): void;
167
+ /**
168
+ * Records how many pins exist WITHOUT loading them.
169
+ *
170
+ * The conversation-open path calls only this: the chip needs a number, the panel needs
171
+ * nothing until it is opened, and hydrating every pin on open re-imports the cost this
172
+ * windowing work exists to remove.
173
+ */
174
+ SetPinnedCount(total: number): void;
175
+ /** Replaces the pins panel's contents. Pins outside the window are expected here. */
176
+ SetPinnedDetails(pins: MJConversationDetailEntity[]): void;
177
+ /**
178
+ * Reflects a pin/unpin the user just performed, without re-querying.
179
+ *
180
+ * Newly pinned rows go to the FRONT to match the panel's newest-pin-first order, which
181
+ * the initial `Sequence DESC` fetch also produces.
182
+ */
183
+ ApplyLocalPin(detail: MJConversationDetailEntity): void;
184
+ /**
185
+ * Everything the chat area needs to render, copied. Arrays are fresh so ngOnChanges fires.
186
+ *
187
+ * Call this from imperative paths (a load completing, a jump loop) — NOT from a template
188
+ * getter. It copies the loaded set on every call, so binding it into change detection
189
+ * pays that copy once per checked cycle for state that mostly has not moved. The cheap
190
+ * single-value accessors above exist for exactly that case.
191
+ */
192
+ GetSnapshot(): ConversationDetailWindowSnapshot;
193
+ /**
194
+ * Folds a newly-loaded page's peripherals into the accumulated maps.
195
+ *
196
+ * Later entries win on a key collision, which matters for a window refresh: a re-fetched
197
+ * newest page should replace a row's stale agent run rather than keep the old one.
198
+ */
199
+ private mergePeripherals;
200
+ /**
201
+ * Union-merges rows into the loaded set: replace-by-ID, then sort by `Sequence`.
202
+ * Dedupe is required because session expansion can re-return rows a prior page held.
203
+ */
204
+ private mergeDetails;
205
+ }
206
+ //# sourceMappingURL=conversation-detail-window.store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-detail-window.store.d.ts","sourceRoot":"","sources":["../../../src/lib/services/conversation-detail-window.store.ts"],"names":[],"mappings":"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"}