@khorsheed/dsh-message-timeline 0.2.4 → 0.2.6
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 +17 -0
- package/README.en.md +15 -11
- package/README.i18n.yaml +2 -2
- package/README.md +15 -11
- package/lib/client.js +339 -53
- package/lib/types/client/TimelineRail.d.ts +1 -1
- package/lib/types/client/TimelineRail.js +208 -46
- package/lib/types/client/config.d.ts +5 -2
- package/lib/types/client/index.d.ts +9 -5
- package/lib/types/client/index.js +5 -1
- package/lib/types/client/locales.d.ts +6 -0
- package/lib/types/client/locales.js +8 -2
- package/lib/types/client/outline.d.ts +12 -0
- package/lib/types/client/outline.js +40 -0
- package/lib/types/client/rail-tracker.d.ts +6 -3
- package/lib/types/client/rail-tracker.js +24 -7
- package/lib/types/client/rows.d.ts +77 -0
- package/lib/types/client/rows.js +86 -0
- package/lib/types/client/slots.d.ts +17 -4
- package/lib/types/client/timeline-kinds.d.ts +7 -2
- package/lib/types/client/timeline-kinds.js +8 -3
- package/package.json +16 -11
- package/lib/invariant.js +0 -28
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -26
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rail row assembly: the merge of the loaded chat window's user rows with the
|
|
3
|
+
* host turnOutline projection into the full timeline. The outline names every
|
|
4
|
+
* started turn (its `turn/start` seq plus a bounded first-prompt preview); the
|
|
5
|
+
* loaded window supplies richer previews, steering rows, and message-tools
|
|
6
|
+
* bubbles for the turns it covers. A turn seen in the window renders its
|
|
7
|
+
* loaded rows and drops out of the outline; a turn outside the window renders
|
|
8
|
+
* as an outline row that pages history in on click.
|
|
9
|
+
* @module @khorsheed/dsh-message-timeline/client/rows
|
|
10
|
+
*/
|
|
11
|
+
import type { ChatConversationViewNode } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
12
|
+
import type { TurnOutlineEntry } from './outline.ts';
|
|
13
|
+
import type { TimelineItem } from './slots.ts';
|
|
14
|
+
/** One loaded-window row: jumps straight to its transcript anchor. */
|
|
15
|
+
export interface LoadedRailRow {
|
|
16
|
+
readonly kind: 'loaded';
|
|
17
|
+
/** Row identity: the node key, which the tracker's active marker also speaks. */
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** The chat node key (the `data-chat-anchor-key` the jump targets). */
|
|
20
|
+
readonly key: string;
|
|
21
|
+
/** The underlying chat node, for content preview. */
|
|
22
|
+
readonly node: ChatConversationViewNode;
|
|
23
|
+
/** Transcript position (the node's anchor seq) — the merge key. */
|
|
24
|
+
readonly anchorSeq: number;
|
|
25
|
+
/** The owning outline turn, or undefined outside every outline range (a stale outline). */
|
|
26
|
+
readonly turn: number | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* One outline-only row: the turn's events are outside the loaded window, so
|
|
30
|
+
* the row previews from the wire entry and confirming it pages history back
|
|
31
|
+
* through the turn's `turn/start` seq before landing.
|
|
32
|
+
*/
|
|
33
|
+
export interface OutlineRailRow {
|
|
34
|
+
readonly kind: 'outline';
|
|
35
|
+
/** Row identity (`turn:<n>` — the prefix cannot collide with a node key). */
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly turn: number;
|
|
38
|
+
/** The turn's `turn/start` seq — the loadThrough target. */
|
|
39
|
+
readonly seq: TurnOutlineEntry['seq'];
|
|
40
|
+
/** Same position as `seq`, as the merge key. */
|
|
41
|
+
readonly anchorSeq: number;
|
|
42
|
+
/** Bounded first-prompt preview ('' for a textless prompt). */
|
|
43
|
+
readonly prompt: string;
|
|
44
|
+
}
|
|
45
|
+
/** One timeline row. */
|
|
46
|
+
export type RailRow = LoadedRailRow | OutlineRailRow;
|
|
47
|
+
/**
|
|
48
|
+
* The first visible loaded row of a turn, of any node kind: the landing
|
|
49
|
+
* target for a turn with no user-kind row (an automated turn renders a
|
|
50
|
+
* `turn-trigger` card, not a user bubble), mirroring the official rail's
|
|
51
|
+
* `user ?? loaded[0]` anchor. Only nodes in the host's visible `order`
|
|
52
|
+
* qualify — a store-only or hidden node is not a jump target.
|
|
53
|
+
* @param order - visible chat node keys in render order.
|
|
54
|
+
* @param nodes - the live node store.
|
|
55
|
+
* @param seq - the turn's `turn/start` seq (inclusive).
|
|
56
|
+
* @param end - the next turn's start seq (exclusive), or Infinity for the last turn.
|
|
57
|
+
* @returns the anchor key, or null when the turn has no visible loaded row.
|
|
58
|
+
*/
|
|
59
|
+
export declare function firstVisibleKeyInTurn(order: readonly string[], nodes: {
|
|
60
|
+
get(key: string): ChatConversationViewNode | undefined;
|
|
61
|
+
}, seq: number, end: number): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Merge loaded rows with the outline into the full row list. Both inputs are
|
|
64
|
+
* transcript-ordered (loaded rows anchorSeq-ascending, the outline strictly
|
|
65
|
+
* increasing by turn), so one pass interleaves them without a final sort: an
|
|
66
|
+
* outline entry whose seq range holds at least one loaded row is covered and
|
|
67
|
+
* emits nothing; every other entry emits its outline row at the range's
|
|
68
|
+
* position. A loaded row belongs to the last entry whose start it follows —
|
|
69
|
+
* when the window runs ahead of the projection feed, the newest rows read as
|
|
70
|
+
* the last named turn until the feed catches up (suppressing its outline row
|
|
71
|
+
* beats duplicating the turn).
|
|
72
|
+
* @param loaded - the window's user rows, anchorSeq-ascending.
|
|
73
|
+
* @param outline - narrowed outline entries, or undefined (capability absent).
|
|
74
|
+
* @returns the merged row list, transcript-ordered.
|
|
75
|
+
*/
|
|
76
|
+
export declare function mergeRows(loaded: readonly TimelineItem[], outline: readonly TurnOutlineEntry[] | undefined): readonly RailRow[];
|
|
77
|
+
//# sourceMappingURL=rows.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first visible loaded row of a turn, of any node kind: the landing
|
|
3
|
+
* target for a turn with no user-kind row (an automated turn renders a
|
|
4
|
+
* `turn-trigger` card, not a user bubble), mirroring the official rail's
|
|
5
|
+
* `user ?? loaded[0]` anchor. Only nodes in the host's visible `order`
|
|
6
|
+
* qualify — a store-only or hidden node is not a jump target.
|
|
7
|
+
* @param order - visible chat node keys in render order.
|
|
8
|
+
* @param nodes - the live node store.
|
|
9
|
+
* @param seq - the turn's `turn/start` seq (inclusive).
|
|
10
|
+
* @param end - the next turn's start seq (exclusive), or Infinity for the last turn.
|
|
11
|
+
* @returns the anchor key, or null when the turn has no visible loaded row.
|
|
12
|
+
*/
|
|
13
|
+
export function firstVisibleKeyInTurn(order, nodes, seq, end) {
|
|
14
|
+
for (const key of order) {
|
|
15
|
+
const node = nodes.get(key);
|
|
16
|
+
if (node === undefined)
|
|
17
|
+
continue;
|
|
18
|
+
if (node.anchorSeq < seq)
|
|
19
|
+
continue;
|
|
20
|
+
if (node.anchorSeq >= end)
|
|
21
|
+
break;
|
|
22
|
+
if (node.visibility === 'hidden')
|
|
23
|
+
continue;
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Wrap one loaded item as a rail row.
|
|
30
|
+
* @param item - the window row.
|
|
31
|
+
* @param turn - its owning outline turn, or undefined.
|
|
32
|
+
* @returns the loaded rail row.
|
|
33
|
+
*/
|
|
34
|
+
function toLoadedRow(item, turn) {
|
|
35
|
+
return { kind: 'loaded', id: item.key, key: item.key, node: item.node, anchorSeq: item.node.anchorSeq, turn };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Merge loaded rows with the outline into the full row list. Both inputs are
|
|
39
|
+
* transcript-ordered (loaded rows anchorSeq-ascending, the outline strictly
|
|
40
|
+
* increasing by turn), so one pass interleaves them without a final sort: an
|
|
41
|
+
* outline entry whose seq range holds at least one loaded row is covered and
|
|
42
|
+
* emits nothing; every other entry emits its outline row at the range's
|
|
43
|
+
* position. A loaded row belongs to the last entry whose start it follows —
|
|
44
|
+
* when the window runs ahead of the projection feed, the newest rows read as
|
|
45
|
+
* the last named turn until the feed catches up (suppressing its outline row
|
|
46
|
+
* beats duplicating the turn).
|
|
47
|
+
* @param loaded - the window's user rows, anchorSeq-ascending.
|
|
48
|
+
* @param outline - narrowed outline entries, or undefined (capability absent).
|
|
49
|
+
* @returns the merged row list, transcript-ordered.
|
|
50
|
+
*/
|
|
51
|
+
export function mergeRows(loaded, outline) {
|
|
52
|
+
if (outline === undefined)
|
|
53
|
+
return loaded.map(item => toLoadedRow(item, undefined));
|
|
54
|
+
const rows = [];
|
|
55
|
+
let cursor = 0;
|
|
56
|
+
for (const [index, entry] of outline.entries()) {
|
|
57
|
+
const end = outline.at(index + 1)?.seq ?? Number.POSITIVE_INFINITY;
|
|
58
|
+
let covered = false;
|
|
59
|
+
for (;;) {
|
|
60
|
+
const item = loaded[cursor];
|
|
61
|
+
if (item === undefined || item.node.anchorSeq >= end)
|
|
62
|
+
break;
|
|
63
|
+
rows.push(toLoadedRow(item, item.node.anchorSeq >= entry.seq ? entry.turn : undefined));
|
|
64
|
+
covered = covered || item.node.anchorSeq >= entry.seq;
|
|
65
|
+
cursor += 1;
|
|
66
|
+
}
|
|
67
|
+
if (!covered) {
|
|
68
|
+
rows.push({
|
|
69
|
+
kind: 'outline',
|
|
70
|
+
id: `turn:${entry.turn}`,
|
|
71
|
+
turn: entry.turn,
|
|
72
|
+
seq: entry.seq,
|
|
73
|
+
anchorSeq: entry.seq,
|
|
74
|
+
prompt: entry.prompt,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (;;) {
|
|
79
|
+
const item = loaded[cursor];
|
|
80
|
+
if (item === undefined)
|
|
81
|
+
break;
|
|
82
|
+
rows.push(toLoadedRow(item, undefined));
|
|
83
|
+
cursor += 1;
|
|
84
|
+
}
|
|
85
|
+
return rows;
|
|
86
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* the `conversation.session.header.utilities` entry.
|
|
5
5
|
*/
|
|
6
6
|
import type { ChatConversationViewNode } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
7
|
+
import type { ISession } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
7
8
|
import type { HostObservable, InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
8
9
|
/**
|
|
9
10
|
* Live rail geometry plus the currently visible user message, published by
|
|
@@ -43,12 +44,24 @@ export interface TimelineRailInjected {
|
|
|
43
44
|
includeSteering: boolean;
|
|
44
45
|
/** Timeline panel width in px (config). */
|
|
45
46
|
panelWidth: number;
|
|
46
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* History pages to prefetch when the panel opens (config). Applies only to
|
|
49
|
+
* the window-paging fallback: when the host serves the turnOutline
|
|
50
|
+
* projection the list starts complete and nothing prefetches.
|
|
51
|
+
*/
|
|
47
52
|
initialPages: number;
|
|
48
|
-
/** Pull one older history page for the rendered session. */
|
|
53
|
+
/** Pull one older history page for the rendered session (fallback path). */
|
|
49
54
|
loadOlder: () => Promise<void>;
|
|
50
|
-
/**
|
|
51
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Page history back until the window covers `seq` (the session jump
|
|
57
|
+
* loader): how an outline-only row's turn becomes loaded.
|
|
58
|
+
*/
|
|
59
|
+
loadThrough: ISession['loadThrough'];
|
|
60
|
+
/**
|
|
61
|
+
* Scroll the transcript to the user message row addressed by `key`.
|
|
62
|
+
* @returns whether the row was found and scrolled.
|
|
63
|
+
*/
|
|
64
|
+
jumpTo: (key: string) => boolean;
|
|
52
65
|
hooks: {
|
|
53
66
|
/** Live panel geometry and active marker for the current session. */
|
|
54
67
|
rail: HostObservable<TimelineRailState>;
|
|
@@ -12,10 +12,15 @@
|
|
|
12
12
|
export declare const EDIT_KIND = "message-tools-edited";
|
|
13
13
|
/** A message-tools restored row (a withdrawn message replayed at the tail). */
|
|
14
14
|
export declare const RESTORED_KIND = "message-tools-restored";
|
|
15
|
+
/** An automated turn's trigger card (restart recovery, scheduled job, …). */
|
|
16
|
+
export declare const TRIGGER_KIND = "turn-trigger";
|
|
15
17
|
/**
|
|
16
18
|
* Whether a node kind renders as a timeline row. The rail lists user messages
|
|
17
|
-
* (and optionally steering),
|
|
18
|
-
*
|
|
19
|
+
* (and optionally steering), the message-tools edited/restored bubbles that
|
|
20
|
+
* the host `order` does not always surface, and automated-turn trigger cards
|
|
21
|
+
* (whose turns have no human prompt but real transcript content). The same
|
|
22
|
+
* set drives the reading-position anchor, so landing on a trigger card keeps
|
|
23
|
+
* the selection on its turn instead of moving to the next user row.
|
|
19
24
|
* @param kind - the node's kind string.
|
|
20
25
|
* @param includeSteering - whether steering rows count as user messages.
|
|
21
26
|
* @returns true when the kind belongs on the timeline.
|
|
@@ -12,17 +12,22 @@
|
|
|
12
12
|
export const EDIT_KIND = 'message-tools-edited';
|
|
13
13
|
/** A message-tools restored row (a withdrawn message replayed at the tail). */
|
|
14
14
|
export const RESTORED_KIND = 'message-tools-restored';
|
|
15
|
+
/** An automated turn's trigger card (restart recovery, scheduled job, …). */
|
|
16
|
+
export const TRIGGER_KIND = 'turn-trigger';
|
|
15
17
|
/**
|
|
16
18
|
* Whether a node kind renders as a timeline row. The rail lists user messages
|
|
17
|
-
* (and optionally steering),
|
|
18
|
-
*
|
|
19
|
+
* (and optionally steering), the message-tools edited/restored bubbles that
|
|
20
|
+
* the host `order` does not always surface, and automated-turn trigger cards
|
|
21
|
+
* (whose turns have no human prompt but real transcript content). The same
|
|
22
|
+
* set drives the reading-position anchor, so landing on a trigger card keeps
|
|
23
|
+
* the selection on its turn instead of moving to the next user row.
|
|
19
24
|
* @param kind - the node's kind string.
|
|
20
25
|
* @param includeSteering - whether steering rows count as user messages.
|
|
21
26
|
* @returns true when the kind belongs on the timeline.
|
|
22
27
|
*/
|
|
23
28
|
export function isTimelineRowKind(kind, includeSteering) {
|
|
24
29
|
return kind === 'user' || (includeSteering && kind === 'steering')
|
|
25
|
-
|| kind === EDIT_KIND || kind === RESTORED_KIND;
|
|
30
|
+
|| kind === EDIT_KIND || kind === RESTORED_KIND || kind === TRIGGER_KIND;
|
|
26
31
|
}
|
|
27
32
|
/**
|
|
28
33
|
* Whether a node kind carries a hidden span (`{ hiddenStartSeq, seq }`) that
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khorsheed/dsh-message-timeline",
|
|
3
3
|
"description": "Message timeline: a flat floating list over the conversation scrollport that jumps to any user message, with a tick-only rest state",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -10,10 +10,6 @@
|
|
|
10
10
|
"types": "./lib/types/index.d.ts",
|
|
11
11
|
"default": "./lib/index.js"
|
|
12
12
|
},
|
|
13
|
-
"./invariant": {
|
|
14
|
-
"types": "./lib/types/invariant.d.ts",
|
|
15
|
-
"default": "./lib/invariant.js"
|
|
16
|
-
},
|
|
17
13
|
"./client": {
|
|
18
14
|
"types": "./lib/types/client/index.d.ts",
|
|
19
15
|
"default": "./lib/client.js"
|
|
@@ -36,19 +32,28 @@
|
|
|
36
32
|
},
|
|
37
33
|
"compat": {
|
|
38
34
|
"minHost": "0.1.2-rc.1",
|
|
39
|
-
"
|
|
35
|
+
"notes": "Whole-session list relies on the host turnOutline projection (dsh-session-turn-outline, mounted by the official web bundle since 0.1.2-alpha.3 — below the minHost floor); assemblies without the unit degrade to window paging (prefetch on open plus panel-top loadOlder), fully functional but the first message appears only once paged in.",
|
|
36
|
+
"verifiedHost": "0.2.1-rc.1"
|
|
40
37
|
}
|
|
41
38
|
},
|
|
42
39
|
"license": "MIT",
|
|
40
|
+
"engines": {
|
|
41
|
+
"dsh": ">=0.1.2-rc.1 <0.3.0-0"
|
|
42
|
+
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
45
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
46
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
47
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.2.0-rc.1",
|
|
48
|
-
"@deepseek-ai/dsh-
|
|
49
|
-
"@deepseek-ai/dsh-
|
|
45
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
46
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
47
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
48
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
49
|
+
"@deepseek-ai/dsh-session-turn-outline": "^0.1.2-alpha.3 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
50
50
|
"react": "^18.2.0"
|
|
51
51
|
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@deepseek-ai/dsh-session-turn-outline": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
56
|
+
},
|
|
52
57
|
"files": [
|
|
53
58
|
"lib",
|
|
54
59
|
"cordis.patch.yml",
|
package/lib/invariant.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
//#region lib/types/invariant.js
|
|
2
|
-
/**
|
|
3
|
-
* Package-owned invariant companion for `@khorsheed/dsh-message-timeline`.
|
|
4
|
-
* @module @khorsheed/dsh-message-timeline/invariant
|
|
5
|
-
*/
|
|
6
|
-
const PACKAGE_NAME = "@khorsheed/dsh-message-timeline";
|
|
7
|
-
/** Cordis companion plugin name. */
|
|
8
|
-
const name = "client-message-timeline-invariant";
|
|
9
|
-
/** Service required before the companion can reserve package ownership. */
|
|
10
|
-
const inject = ["invariants"];
|
|
11
|
-
/**
|
|
12
|
-
* No runtime invariant: the rail contribution is one slot entry whose disposal
|
|
13
|
-
* is proven by the HMR-safety spec — the plugin writes no session state, emits
|
|
14
|
-
* no cordis events, and reads the conversation snapshot through the framework
|
|
15
|
-
* hook only. Its DOM probe targets the official chat row attributes
|
|
16
|
-
* ([data-chat-anchor-key] / [data-conversation-scroll]) read-only and
|
|
17
|
-
* degrades to a hidden rail when they change, so no second authority exists to
|
|
18
|
-
* check at runtime.
|
|
19
|
-
*/
|
|
20
|
-
const install = () => {};
|
|
21
|
-
/**
|
|
22
|
-
* Register this package's invariant companion.
|
|
23
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
24
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
25
|
-
*/
|
|
26
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
27
|
-
//#endregion
|
|
28
|
-
export { apply, inject, name };
|
package/lib/types/invariant.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package-owned invariant companion for `@khorsheed/dsh-message-timeline`.
|
|
3
|
-
* @module @khorsheed/dsh-message-timeline/invariant
|
|
4
|
-
*/
|
|
5
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
-
/** Cordis companion plugin name. */
|
|
7
|
-
export declare const name = "client-message-timeline-invariant";
|
|
8
|
-
/** Service required before the companion can reserve package ownership. */
|
|
9
|
-
export declare const inject: string[];
|
|
10
|
-
/**
|
|
11
|
-
* Register this package's invariant companion.
|
|
12
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
-
*/
|
|
15
|
-
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
-
//# sourceMappingURL=invariant.d.ts.map
|
package/lib/types/invariant.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package-owned invariant companion for `@khorsheed/dsh-message-timeline`.
|
|
3
|
-
* @module @khorsheed/dsh-message-timeline/invariant
|
|
4
|
-
*/
|
|
5
|
-
const PACKAGE_NAME = '@khorsheed/dsh-message-timeline';
|
|
6
|
-
/** Cordis companion plugin name. */
|
|
7
|
-
export const name = 'client-message-timeline-invariant';
|
|
8
|
-
/** Service required before the companion can reserve package ownership. */
|
|
9
|
-
export const inject = ['invariants'];
|
|
10
|
-
/**
|
|
11
|
-
* No runtime invariant: the rail contribution is one slot entry whose disposal
|
|
12
|
-
* is proven by the HMR-safety spec — the plugin writes no session state, emits
|
|
13
|
-
* no cordis events, and reads the conversation snapshot through the framework
|
|
14
|
-
* hook only. Its DOM probe targets the official chat row attributes
|
|
15
|
-
* ([data-chat-anchor-key] / [data-conversation-scroll]) read-only and
|
|
16
|
-
* degrades to a hidden rail when they change, so no second authority exists to
|
|
17
|
-
* check at runtime.
|
|
18
|
-
*/
|
|
19
|
-
const install = () => { };
|
|
20
|
-
/**
|
|
21
|
-
* Register this package's invariant companion.
|
|
22
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
23
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
24
|
-
*/
|
|
25
|
-
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
26
|
-
/* jscpd:ignore-end */
|