@khorsheed/dsh-message-timeline 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.
Files changed (38) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.en.md +79 -0
  3. package/README.i18n.yaml +6 -0
  4. package/README.md +79 -0
  5. package/cordis.patch.yml +6 -0
  6. package/lib/client.js +521 -0
  7. package/lib/client.js.map +1 -0
  8. package/lib/index.js +11 -0
  9. package/lib/invariant.js +28 -0
  10. package/lib/tsconfig.tsbuildinfo +1 -0
  11. package/lib/types/client/TimelineRail.d.ts +10 -0
  12. package/lib/types/client/TimelineRail.d.ts.map +1 -0
  13. package/lib/types/client/TimelineRail.js +152 -0
  14. package/lib/types/client/config.d.ts +39 -0
  15. package/lib/types/client/config.d.ts.map +1 -0
  16. package/lib/types/client/config.js +30 -0
  17. package/lib/types/client/index.d.ts +29 -0
  18. package/lib/types/client/index.d.ts.map +1 -0
  19. package/lib/types/client/index.js +49 -0
  20. package/lib/types/client/locales.d.ts +20 -0
  21. package/lib/types/client/locales.d.ts.map +1 -0
  22. package/lib/types/client/locales.js +11 -0
  23. package/lib/types/client/preview.d.ts +13 -0
  24. package/lib/types/client/preview.d.ts.map +1 -0
  25. package/lib/types/client/preview.js +13 -0
  26. package/lib/types/client/rail-tracker.d.ts +84 -0
  27. package/lib/types/client/rail-tracker.d.ts.map +1 -0
  28. package/lib/types/client/rail-tracker.js +240 -0
  29. package/lib/types/client/slots.d.ts +62 -0
  30. package/lib/types/client/slots.d.ts.map +1 -0
  31. package/lib/types/client/slots.js +1 -0
  32. package/lib/types/index.d.ts +9 -0
  33. package/lib/types/index.d.ts.map +1 -0
  34. package/lib/types/index.js +8 -0
  35. package/lib/types/invariant.d.ts +16 -0
  36. package/lib/types/invariant.d.ts.map +1 -0
  37. package/lib/types/invariant.js +26 -0
  38. package/package.json +79 -0
@@ -0,0 +1,240 @@
1
+ /** Horizontal inset of the rail from the scrollport's left edge (px). */
2
+ const RAIL_LEFT_INSET = 6;
3
+ /** Vertical padding above and below the rail inside the scrollport (px). */
4
+ const RAIL_VERTICAL_PADDING = 8;
5
+ /** Keep the target row this far below the scrollport top after a jump (px). */
6
+ const JUMP_OFFSET = 16;
7
+ /** Idle state published before any session binds or while none is current. */
8
+ const IDLE = {
9
+ sessionId: undefined, ready: false, left: 0, top: 0, height: 0, scrollportWidth: 0, flowLeft: null,
10
+ activeKey: null, chatView: false,
11
+ };
12
+ /**
13
+ * Measure the panel's viewport box from one scrollport: its rect inset by the
14
+ * panel padding, minus the sticky composer seat at the bottom and the
15
+ * conversation tab strip at the top. The tabs render just above the
16
+ * scrollport, but centering reads against the whole window, so the strip
17
+ * height leaves the box either way — otherwise the list sits visibly high.
18
+ * The scrollport's own width rides along for the width-cap fallback when the
19
+ * message-flow probe is unanswered.
20
+ * @param scrollport - the official conversation scrollport element.
21
+ * @returns the panel box plus the scrollport width, or null while the
22
+ * scrollport has no laid-out size.
23
+ */
24
+ export function measureGeometry(scrollport) {
25
+ const rect = scrollport.getBoundingClientRect();
26
+ if (rect.width === 0 && rect.height === 0)
27
+ return null;
28
+ const composer = scrollport.querySelector('[data-composer-seat]');
29
+ const composerHeight = composer?.getBoundingClientRect().height ?? 0;
30
+ let topInset = RAIL_VERTICAL_PADDING;
31
+ for (const tabs of scrollport.ownerDocument.querySelectorAll('[role="tablist"]')) {
32
+ const tabsRect = tabs.getBoundingClientRect();
33
+ // Only the strip adjacent to the scrollport's top edge counts as chrome.
34
+ if (tabsRect.height > 0 && Math.abs(tabsRect.bottom - rect.top) <= 80) {
35
+ topInset += tabsRect.height;
36
+ break;
37
+ }
38
+ }
39
+ return {
40
+ left: rect.left + RAIL_LEFT_INSET,
41
+ top: rect.top + topInset,
42
+ height: Math.max(0, rect.height - composerHeight - topInset - RAIL_VERTICAL_PADDING),
43
+ width: rect.width,
44
+ };
45
+ }
46
+ /**
47
+ * The viewport x of the message flow's left edge: the left of the first
48
+ * rendered `[data-chat-flow-kind]` row, which sits flush inside the official
49
+ * centered content column (max 748px, `margin: 0 auto`). Every flow row
50
+ * shares that edge, so the first one found suffices. The panel's right edge
51
+ * stays left of it — the panel may only occupy the scrollport's left gutter.
52
+ * @param scrollport - the official conversation scrollport element.
53
+ * @returns the flow's left edge, or null while no flow row is rendered.
54
+ */
55
+ export function flowLeftX(scrollport) {
56
+ const row = scrollport.querySelector('[data-chat-flow-kind]');
57
+ return row === null ? null : row.getBoundingClientRect().left;
58
+ }
59
+ /**
60
+ * Resolve the key of the user-message row the reading position belongs to:
61
+ * the first matching row whose bottom is still inside the viewport, or —
62
+ * while the reader sits inside a long assistant answer with no user row
63
+ * visible — the nearest user row above the viewport, so the lit tick stays
64
+ * anchored to the question being answered instead of jumping to the
65
+ * session's latest message.
66
+ * @param scrollport - the official conversation scrollport element.
67
+ * @param includeSteering - whether steering rows count as user messages.
68
+ * @returns the row's anchor key, or null when no user row is rendered.
69
+ */
70
+ export function activeRowKey(scrollport, includeSteering) {
71
+ const viewTop = scrollport.getBoundingClientRect().top;
72
+ let lastAbove = null;
73
+ for (const row of scrollport.querySelectorAll('[data-chat-flow-kind]')) {
74
+ const kind = row.dataset.chatFlowKind;
75
+ if (kind !== 'user' && !(includeSteering && kind === 'steering'))
76
+ continue;
77
+ if (row.getBoundingClientRect().bottom <= viewTop) {
78
+ lastAbove = row.dataset.chatAnchorKey ?? lastAbove;
79
+ continue;
80
+ }
81
+ return row.dataset.chatAnchorKey ?? null;
82
+ }
83
+ return lastAbove;
84
+ }
85
+ /**
86
+ * Scroll one user-message row to the top of the transcript scrollport.
87
+ * @param scrollport - the official conversation scrollport element.
88
+ * @param key - the target node's anchor key.
89
+ * @returns whether the row was found and scrolled.
90
+ */
91
+ export function jumpRow(scrollport, key) {
92
+ const row = scrollport.querySelector(`[data-chat-anchor-key=${JSON.stringify(key)}]`);
93
+ if (row === null)
94
+ return false;
95
+ const target = row.getBoundingClientRect().top - scrollport.getBoundingClientRect().top
96
+ + scrollport.scrollTop - JUMP_OFFSET;
97
+ scrollport.scrollTop = Math.max(0, target);
98
+ return true;
99
+ }
100
+ /**
101
+ * Install the rail tracker: bind the current session's scrollport, publish
102
+ * geometry/active updates on scroll and resize, and answer jump requests.
103
+ * Follows the current session through the sessions list and provide channels
104
+ * (the interface renders one conversation at a time, so one tracker suffices).
105
+ * @param ctx - client root context (sessions service).
106
+ * @param includeSteering - whether steering rows count as user dots.
107
+ * @returns the tracker face.
108
+ */
109
+ export function installRailTracker(ctx, includeSteering) {
110
+ const listeners = new Set();
111
+ let state = IDLE;
112
+ let activeSession;
113
+ let scrollport = null;
114
+ let resizeObserver;
115
+ let rafPending = false;
116
+ let warned = false;
117
+ const nextFrame = typeof requestAnimationFrame === 'function'
118
+ ? requestAnimationFrame
119
+ : (callback) => { setTimeout(callback, 16); };
120
+ const same = (left, right) => left.sessionId === right.sessionId && left.ready === right.ready
121
+ && left.left === right.left && left.top === right.top
122
+ && left.height === right.height && left.scrollportWidth === right.scrollportWidth
123
+ && left.flowLeft === right.flowLeft
124
+ && left.activeKey === right.activeKey
125
+ && left.chatView === right.chatView;
126
+ const publish = (next) => {
127
+ if (same(state, next))
128
+ return;
129
+ state = next;
130
+ for (const fn of [...listeners])
131
+ fn();
132
+ };
133
+ const update = () => {
134
+ if (scrollport === null)
135
+ return;
136
+ const geometry = measureGeometry(scrollport);
137
+ if (geometry === null)
138
+ return;
139
+ publish({
140
+ sessionId: activeSession,
141
+ ready: true,
142
+ left: geometry.left,
143
+ top: geometry.top,
144
+ height: geometry.height,
145
+ scrollportWidth: geometry.width,
146
+ flowLeft: flowLeftX(scrollport),
147
+ activeKey: activeRowKey(scrollport, includeSteering),
148
+ chatView: scrollport.querySelector('[data-chat-flow]') !== null,
149
+ });
150
+ };
151
+ const scheduleUpdate = () => {
152
+ if (rafPending)
153
+ return;
154
+ rafPending = true;
155
+ nextFrame(() => {
156
+ rafPending = false;
157
+ update();
158
+ });
159
+ };
160
+ const onScroll = () => { scheduleUpdate(); };
161
+ const teardownBind = () => {
162
+ if (scrollport !== null)
163
+ scrollport.removeEventListener('scroll', onScroll);
164
+ scrollport = null;
165
+ resizeObserver?.disconnect();
166
+ resizeObserver = undefined;
167
+ rafPending = false;
168
+ };
169
+ const bind = (sessionId) => {
170
+ if (sessionId === activeSession && scrollport !== null)
171
+ return;
172
+ teardownBind();
173
+ activeSession = sessionId;
174
+ if (sessionId === undefined) {
175
+ publish(IDLE);
176
+ return;
177
+ }
178
+ nextFrame(() => {
179
+ const found = document.querySelector('[data-conversation-scroll]');
180
+ if (found === null) {
181
+ if (!warned) {
182
+ warned = true;
183
+ console.warn('message-timeline: [data-conversation-scroll] not found; the rail stays hidden');
184
+ }
185
+ publish(IDLE);
186
+ return;
187
+ }
188
+ scrollport = found;
189
+ scrollport.addEventListener('scroll', onScroll, { passive: true });
190
+ if (typeof ResizeObserver === 'function') {
191
+ resizeObserver = new ResizeObserver(scheduleUpdate);
192
+ resizeObserver.observe(scrollport);
193
+ const composer = scrollport.querySelector('[data-composer-seat]');
194
+ if (composer !== null)
195
+ resizeObserver.observe(composer);
196
+ }
197
+ update();
198
+ });
199
+ };
200
+ const bindCurrent = () => {
201
+ bind(ctx.sessions.list.getSnapshot().current);
202
+ };
203
+ const stopList = ctx.sessions.list.subscribe(bindCurrent);
204
+ const stopProvide = ctx.sessions.currentProvideInfo.subscribe(bindCurrent);
205
+ bindCurrent();
206
+ // Layout fallbacks beyond the scrollport's own ResizeObserver: a window
207
+ // resize re-measures, and a body MutationObserver catches panel folds and
208
+ // other layout changes that resize the scrollport without a window event.
209
+ // Both go through the rAF-throttled scheduleUpdate, and publish() skips
210
+ // identical geometry, so the cost stays one measurement per changed frame.
211
+ const onWindowResize = () => { scheduleUpdate(); };
212
+ if (typeof window !== 'undefined')
213
+ window.addEventListener('resize', onWindowResize);
214
+ let mutationObserver;
215
+ if (typeof MutationObserver === 'function' && typeof document !== 'undefined') {
216
+ mutationObserver = new MutationObserver(scheduleUpdate);
217
+ mutationObserver.observe(document.body, { childList: true, subtree: true });
218
+ }
219
+ return {
220
+ state: {
221
+ getSnapshot: () => state,
222
+ subscribe: (fn) => {
223
+ listeners.add(fn);
224
+ return () => { listeners.delete(fn); };
225
+ },
226
+ },
227
+ jumpTo: (key) => {
228
+ if (scrollport !== null)
229
+ jumpRow(scrollport, key);
230
+ },
231
+ dispose: () => {
232
+ stopList();
233
+ stopProvide();
234
+ if (typeof window !== 'undefined')
235
+ window.removeEventListener('resize', onWindowResize);
236
+ mutationObserver?.disconnect();
237
+ teardownBind();
238
+ },
239
+ };
240
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Slot-facing types of the message-timeline client half: the injected action
3
+ * face (jump/loadOlder plus the rail geometry hook) and the composed props of
4
+ * the `conversation.session.header.utilities` entry.
5
+ */
6
+ import type { ChatConversationViewNode } from '@deepseek-ai/dsh-client-runtime/client';
7
+ import type { HostObservable, InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
8
+ /**
9
+ * Live rail geometry plus the currently visible user message, published by
10
+ * the apply-world DOM tracker through the reserved hooks compartment.
11
+ */
12
+ export interface TimelineRailState {
13
+ /** The session whose scrollport the state describes; undefined while none is current. */
14
+ sessionId: string | undefined;
15
+ /** False while the official DOM probe is unanswered (rail hidden). */
16
+ ready: boolean;
17
+ /** Viewport x of the rail's left edge. */
18
+ left: number;
19
+ /** Viewport y of the rail's top edge (below the session header). */
20
+ top: number;
21
+ /** Rail height in px (scrollport minus the composer seat). */
22
+ height: number;
23
+ /** Scrollport width in px (the degraded width cap when the flow probe fails). */
24
+ scrollportWidth: number;
25
+ /**
26
+ * Viewport x of the message flow's left edge (the official centered
27
+ * content column), or null while the probe is unanswered. The panel's
28
+ * right edge never crosses it, so the width adapts to the left gutter.
29
+ */
30
+ flowLeft: number | null;
31
+ /** Key of the user message row nearest the visible top, or null. */
32
+ activeKey: string | null;
33
+ /** Whether the chat view (not trajectory or another tab) is rendered. */
34
+ chatView: boolean;
35
+ }
36
+ /** Injected action face of the header-utilities entry. */
37
+ export interface TimelineRailInjected {
38
+ /** Whether steering messages count as rows (config). */
39
+ includeSteering: boolean;
40
+ /** Timeline panel width in px (config). */
41
+ panelWidth: number;
42
+ /** History pages to prefetch when the panel opens (config). */
43
+ initialPages: number;
44
+ /** Pull one older history page for the rendered session. */
45
+ loadOlder: () => Promise<void>;
46
+ /** Scroll the transcript to the user message row addressed by `key`. */
47
+ jumpTo: (key: string) => void;
48
+ hooks: {
49
+ /** Live panel geometry and active marker for the current session. */
50
+ rail: HostObservable<TimelineRailState>;
51
+ };
52
+ }
53
+ /** One row's data: the addressed node key plus the node itself, for preview. */
54
+ export interface TimelineItem {
55
+ /** Stable chat node key (the `data-chat-anchor-key` the jump targets). */
56
+ readonly key: string;
57
+ /** The underlying chat node, for content preview. */
58
+ readonly node: ChatConversationViewNode;
59
+ }
60
+ /** Full props of the header-utilities entry. */
61
+ export type TimelineRailProps = PropsRuntime<'conversation.session.header.utilities'> & InjectFace<TimelineRailInjected> & PropsLocale<'message-timeline'>;
62
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACtF,OAAO,KAAK,EACV,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EACtD,MAAM,kCAAkC,CAAA;AAOzC;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,sEAAsE;IACtE,KAAK,EAAE,OAAO,CAAA;IACd,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAA;IACX,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAA;IACd,iFAAiF;IACjF,eAAe,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,yEAAyE;IACzE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,0DAA0D;AAC1D,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,eAAe,EAAE,OAAO,CAAA;IACxB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAA;IACpB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B,wEAAwE;IACxE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,KAAK,EAAE;QACL,qEAAqE;QACrE,IAAI,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAA;KACxC,CAAA;CACF;AAED,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAA;CACxC;AAED,gDAAgD;AAChD,MAAM,MAAM,iBAAiB,GAC3B,YAAY,CAAC,uCAAuC,CAAC,GACnD,UAAU,CAAC,oBAAoB,CAAC,GAChC,WAAW,CAAC,kBAAkB,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Message timeline plugin, node half. Pure UI plugin: the empty apply exists
3
+ * so the plugin appears in the host cordis.yml / Loader; the browser half
4
+ * ships via exports["./client"], discovered through the package.json
5
+ * dsh.client declaration.
6
+ */
7
+ /** Host plugin body — no host-side behavior for this surface plugin. */
8
+ export declare function apply(): void;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wEAAwE;AACxE,wBAAgB,KAAK,IAAI,IAAI,CAAG"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Message timeline plugin, node half. Pure UI plugin: the empty apply exists
3
+ * so the plugin appears in the host cordis.yml / Loader; the browser half
4
+ * ships via exports["./client"], discovered through the package.json
5
+ * dsh.client declaration.
6
+ */
7
+ /** Host plugin body — no host-side behavior for this surface plugin. */
8
+ export function apply() { }
@@ -0,0 +1,16 @@
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,oCAAoC;AACpC,eAAO,MAAM,IAAI,sCAAsC,CAAA;AACvD,2EAA2E;AAC3E,eAAO,MAAM,MAAM,UAAiB,CAAA;AAapC;;;;GAIG;AACH,eAAO,MAAM,KAAK,QAAS,OAAO,KAAG,OAAO,CAAC,MAAM,IAAI,CACU,CAAA"}
@@ -0,0 +1,26 @@
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 */
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@khorsheed/dsh-message-timeline",
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.1.0",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./invariant": {
14
+ "types": "./lib/types/invariant.d.ts",
15
+ "default": "./lib/invariant.js"
16
+ },
17
+ "./client": {
18
+ "types": "./lib/types/client/index.d.ts",
19
+ "default": "./lib/client.js"
20
+ },
21
+ "./src/*": "./src/*",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "dsh": {
25
+ "bundle": {
26
+ "patch": "./cordis.patch.yml"
27
+ },
28
+ "client": {
29
+ "inject": [
30
+ "@deepseek-ai/dsh-client-runtime",
31
+ "@deepseek-ai/dsh-client-locale",
32
+ "@deepseek-ai/dsh-client-ui-conversation"
33
+ ],
34
+ "platform": "web"
35
+ },
36
+ "compat": {
37
+ "minHost": "0.1.0-rc.6",
38
+ "verifiedHost": "0.1.1-rc.2"
39
+ }
40
+ },
41
+ "license": "MIT",
42
+ "peerDependencies": {
43
+ "@deepseek-ai/cordis": "^4.0.1",
44
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
45
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
46
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
47
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
48
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
49
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
50
+ "react": "^18.2.0"
51
+ },
52
+ "devDependencies": {
53
+ "@deepseek-ai/cordis": "^4.0.1",
54
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.1",
55
+ "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.1",
56
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.1-rc.1",
57
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.1-rc.1",
58
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.1",
59
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.1",
60
+ "@deepseek-ai/dsh-llm": "^0.1.1-rc.1",
61
+ "@testing-library/react": "^16.1.0",
62
+ "@types/node": "^26.2.0",
63
+ "@types/react": "~18.3.1",
64
+ "@types/react-dom": "~18.3.1",
65
+ "react": "^18.2.0",
66
+ "react-dom": "^18.2.0"
67
+ },
68
+ "files": [
69
+ "lib",
70
+ "cordis.patch.yml",
71
+ "CHANGELOG.md"
72
+ ],
73
+ "keywords": [
74
+ "dsh",
75
+ "dsh-plugin",
76
+ "cordis",
77
+ "message-timeline"
78
+ ]
79
+ }