dsh-sidenote 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/LICENSE +21 -0
- package/README.md +60 -0
- package/cordis.patch.yml +16 -0
- package/lib/client.js +2143 -0
- package/lib/index.js +6 -0
- package/lib/types/client/annotate/anchor.d.ts +57 -0
- package/lib/types/client/annotate/chip.d.ts +11 -0
- package/lib/types/client/annotate/draft.d.ts +29 -0
- package/lib/types/client/annotate/format.d.ts +52 -0
- package/lib/types/client/annotate/index.d.ts +2 -0
- package/lib/types/client/annotate/model.d.ts +59 -0
- package/lib/types/client/annotate/overlay.d.ts +27 -0
- package/lib/types/client/annotate/selection.d.ts +49 -0
- package/lib/types/client/bridge.d.ts +19 -0
- package/lib/types/client/index.d.ts +12 -0
- package/lib/types/client/locales.d.ts +77 -0
- package/lib/types/client/sidechat/SideChatPanel.d.ts +2 -0
- package/lib/types/client/sidechat/composer.d.ts +26 -0
- package/lib/types/client/sidechat/index.d.ts +2 -0
- package/lib/types/client/sidechat/model.d.ts +101 -0
- package/lib/types/client/sidechat/open.d.ts +16 -0
- package/lib/types/client/sidechat/slash.d.ts +12 -0
- package/lib/types/context-types.d.ts +335 -0
- package/lib/types/index.d.ts +9 -0
- package/package.json +96 -0
- package/src/client/annotate/anchor.ts +158 -0
- package/src/client/annotate/annotate.module.css +301 -0
- package/src/client/annotate/chip.tsx +115 -0
- package/src/client/annotate/css.d.ts +9 -0
- package/src/client/annotate/draft.ts +67 -0
- package/src/client/annotate/format.ts +83 -0
- package/src/client/annotate/index.tsx +66 -0
- package/src/client/annotate/model.ts +138 -0
- package/src/client/annotate/overlay.tsx +531 -0
- package/src/client/annotate/selection.ts +203 -0
- package/src/client/bridge.ts +19 -0
- package/src/client/index.tsx +22 -0
- package/src/client/locales.ts +175 -0
- package/src/client/sidechat/SideChatPanel.tsx +366 -0
- package/src/client/sidechat/composer.ts +129 -0
- package/src/client/sidechat/css-modules.d.ts +5 -0
- package/src/client/sidechat/index.tsx +56 -0
- package/src/client/sidechat/model.ts +331 -0
- package/src/client/sidechat/open.ts +66 -0
- package/src/client/sidechat/sidechat.module.css +230 -0
- package/src/client/sidechat/slash.ts +84 -0
- package/src/context-types.ts +369 -0
- package/src/index.ts +13 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /side 斜杠命令(spike 落点):经 client 侧 `ctx.commandUi.register`
|
|
3
|
+
* (dsh-client-ui-commands 的 CommandContribution,ui 形态 popupSelect)
|
|
4
|
+
* 注册。popupSelect 是该注册面唯一的 UI 形态 —— 菜单行被选中后弹一个
|
|
5
|
+
* 选项壳,选项 = 新建 + 当前会话已并存的侧边聊天(聚焦)。
|
|
6
|
+
*
|
|
7
|
+
* 服务经 ctx.get 惰性解析(commandUi 不在 inject 清单里):服务缺失、
|
|
8
|
+
* 注册抛错(如与 host 命令撞名)都降级为「只有 Tab 入口」,绝不影响面板。
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '../../context-types.ts'
|
|
11
|
+
import { canForkFrom, collectSideTabs } from './model.ts'
|
|
12
|
+
import { openOrFocusSideChat } from './open.ts'
|
|
13
|
+
import { t } from '../locales.ts'
|
|
14
|
+
|
|
15
|
+
/** dsh-client-ui-commands ClientSessionContext 的最小镜像(只有 sessionId)。 */
|
|
16
|
+
interface CommandSession {
|
|
17
|
+
readonly sessionId: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** popupSelect 选项行镜像。 */
|
|
21
|
+
interface SelectOption {
|
|
22
|
+
readonly id: string
|
|
23
|
+
readonly label: string
|
|
24
|
+
readonly detail?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** ctx.commandUi 的最小镜像(CommandUiContract.register)。 */
|
|
28
|
+
interface CommandUiService {
|
|
29
|
+
register(contribution: {
|
|
30
|
+
readonly name: string
|
|
31
|
+
readonly description: string
|
|
32
|
+
available(session: CommandSession): boolean
|
|
33
|
+
readonly ui: {
|
|
34
|
+
readonly kind: 'popupSelect'
|
|
35
|
+
options(session: CommandSession, signal: AbortSignal): Promise<readonly SelectOption[]>
|
|
36
|
+
onSelect(option: SelectOption, session: CommandSession): void | Promise<void>
|
|
37
|
+
}
|
|
38
|
+
}): () => void
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 注册 /side 命令;不可行时静默降级(返回 undefined)。 */
|
|
42
|
+
export function registerSideCommand(ctx: Context): void {
|
|
43
|
+
let commandUi: CommandUiService | undefined
|
|
44
|
+
try {
|
|
45
|
+
commandUi = ctx.get('commandUi') as CommandUiService | undefined
|
|
46
|
+
} catch {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
if (commandUi === undefined || typeof commandUi.register !== 'function') return
|
|
50
|
+
try {
|
|
51
|
+
ctx.effect(() => commandUi.register({
|
|
52
|
+
name: 'side',
|
|
53
|
+
description: t('cmdDesc'),
|
|
54
|
+
available: (session) => canForkFrom(ctx, session.sessionId),
|
|
55
|
+
ui: {
|
|
56
|
+
kind: 'popupSelect',
|
|
57
|
+
options: (session) => {
|
|
58
|
+
const options: SelectOption[] = [
|
|
59
|
+
{ id: 'new', label: t('cmdNew'), detail: t('cmdNewDetail') },
|
|
60
|
+
]
|
|
61
|
+
// 已并存的侧边聊天列为聚焦项(命令弹层即多实例管理入口)。
|
|
62
|
+
const snapshot = ctx.betterSidebar.getSnapshot()
|
|
63
|
+
if (snapshot.sessionId === session.sessionId && snapshot.state !== undefined) {
|
|
64
|
+
for (const tab of collectSideTabs(snapshot.state)) {
|
|
65
|
+
options.push({ id: `focus:${tab.id}`, label: t('cmdFocus', { title: tab.title }), detail: t('cmdFocusDetail') })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return Promise.resolve(options)
|
|
69
|
+
},
|
|
70
|
+
onSelect: (option, session) => {
|
|
71
|
+
if (option.id === 'new') {
|
|
72
|
+
openOrFocusSideChat(ctx, session.sessionId)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
if (option.id.startsWith('focus:')) {
|
|
76
|
+
ctx.betterSidebar.activateTab(option.id.slice('focus:'.length), { sessionId: session.sessionId })
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
}), 'dsh-sidenote: /side command')
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn('[dsh-sidenote] /side 命令注册失败(Tab 入口不受影响):', error)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural mirror of the runtime surfaces dsh-sidenote consumes.
|
|
3
|
+
*
|
|
4
|
+
* Third-party plugins resolve outside the DSH monorepo's single cordis
|
|
5
|
+
* instance, so upstream `declare module 'cordis'` augmentations never reach
|
|
6
|
+
* our `Context` type. We mirror the runtime shapes here instead — drift from
|
|
7
|
+
* upstream is contained to this file. Each section names its authority (the
|
|
8
|
+
* upstream .d.ts it mirrors); extend sections as features need more surface,
|
|
9
|
+
* keeping the mirror honest (only declare what actually exists at runtime).
|
|
10
|
+
*
|
|
11
|
+
* Authorities:
|
|
12
|
+
* - betterSidebar: dsh-better-sidebar `src/client/service.ts` (+ docs/external-plugin-guide.md)
|
|
13
|
+
* - sessions/workspaces: `@deepseek-ai/dsh-client-runtime` lib/types/client/contract/{sessions,session,workspaces}.d.ts
|
|
14
|
+
* (local checkout: <dsh install>/node_modules/@deepseek-ai/dsh-client-runtime/lib/types/client/...)
|
|
15
|
+
* - conversation input / slots: `@deepseek-ai/dsh-client-ui-conversation` / `dsh-client-ui-slots` type surfaces
|
|
16
|
+
*/
|
|
17
|
+
import type { Context as CordisContext } from 'cordis'
|
|
18
|
+
import type { ReactNode } from 'react'
|
|
19
|
+
|
|
20
|
+
export type SessionId = string
|
|
21
|
+
|
|
22
|
+
/** Minimal observable snapshot shape (identity-stable, useSyncExternalStore-ready). */
|
|
23
|
+
export interface ObservableSnapshot<T> {
|
|
24
|
+
getSnapshot(): T
|
|
25
|
+
subscribe(listener: () => void): () => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ── betterSidebar (dsh-better-sidebar client-half service) ──────────────────
|
|
29
|
+
|
|
30
|
+
export interface SessionScope {
|
|
31
|
+
sessionId: SessionId
|
|
32
|
+
cwd?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A live sidebar tab instance. `meta` is plugin-owned JSON persisted with the layout. */
|
|
36
|
+
export interface SidebarTab {
|
|
37
|
+
id: string
|
|
38
|
+
type: string
|
|
39
|
+
title: string
|
|
40
|
+
path?: string
|
|
41
|
+
meta?: unknown
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 侧栏状态 = splits/bottomSplits 两棵 split/leaf 树(权威:dsh-better-sidebar
|
|
46
|
+
* src/client/state.ts):leaf 持有 tabs;split 递归分栏。
|
|
47
|
+
*/
|
|
48
|
+
export interface SidebarLeafNode {
|
|
49
|
+
kind: 'leaf'
|
|
50
|
+
id: string
|
|
51
|
+
tabs: SidebarTab[]
|
|
52
|
+
active: string | null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface SidebarSplitNode {
|
|
56
|
+
kind: 'split'
|
|
57
|
+
id: string
|
|
58
|
+
dir: 'row' | 'col'
|
|
59
|
+
sizes: number[]
|
|
60
|
+
children: SidebarTreeNode[]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type SidebarTreeNode = SidebarLeafNode | SidebarSplitNode
|
|
64
|
+
|
|
65
|
+
export interface SidebarState {
|
|
66
|
+
/** 右侧面板的 split 树。 */
|
|
67
|
+
splits: SidebarTreeNode
|
|
68
|
+
/** 底部面板的 split 树。 */
|
|
69
|
+
bottomSplits: SidebarTreeNode
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SidebarSnapshot {
|
|
73
|
+
sessionId?: SessionId
|
|
74
|
+
state?: SidebarState
|
|
75
|
+
prefs?: Record<string, unknown>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** better-sidebar's store handle (external tabs treat it as opaque). */
|
|
79
|
+
export type SidebarStore = unknown
|
|
80
|
+
|
|
81
|
+
export interface TabComponentProps {
|
|
82
|
+
ctx: Context
|
|
83
|
+
store: SidebarStore
|
|
84
|
+
scope: SessionScope
|
|
85
|
+
tab: SidebarTab
|
|
86
|
+
visible: boolean
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface TabDescriptor {
|
|
90
|
+
id: string
|
|
91
|
+
title: string | (() => string)
|
|
92
|
+
icon?: ReactNode | ((size: number) => ReactNode)
|
|
93
|
+
order?: number
|
|
94
|
+
hidden?: boolean
|
|
95
|
+
available?: (ctx: Context, scope: SessionScope, state: SidebarState) => boolean
|
|
96
|
+
single?: boolean
|
|
97
|
+
dedupeKey?: (tab: SidebarTab) => string | undefined
|
|
98
|
+
createTab?: (state: SidebarState) => { tab: SidebarTab; patch?: Partial<SidebarState> } | null
|
|
99
|
+
badge?: (ctx: Context, scope: SessionScope, state: SidebarState) => string | number | null | undefined
|
|
100
|
+
onOpen?: (tab: SidebarTab, scope: SessionScope) => void
|
|
101
|
+
onActivate?: (tab: SidebarTab, scope: SessionScope) => void
|
|
102
|
+
onClose?: (tab: SidebarTab, scope: SessionScope) => void
|
|
103
|
+
component: (props: TabComponentProps) => ReactNode
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface OpenTabSeed {
|
|
107
|
+
type: string
|
|
108
|
+
title?: string
|
|
109
|
+
path?: string
|
|
110
|
+
id?: string
|
|
111
|
+
url?: string
|
|
112
|
+
meta?: unknown
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface BetterSidebarService {
|
|
116
|
+
readonly version: string
|
|
117
|
+
readonly features: readonly string[]
|
|
118
|
+
registerTab(descriptor: TabDescriptor): () => void
|
|
119
|
+
openTab(seed: OpenTabSeed, scope?: SessionScope): void
|
|
120
|
+
closeTab(tabId: string, scope?: SessionScope): void
|
|
121
|
+
activateTab(tabId: string, scope?: SessionScope): void
|
|
122
|
+
updateTab(tabId: string, patch: { title?: string; path?: string; meta?: unknown }): void
|
|
123
|
+
getTab(id: string): TabDescriptor | undefined
|
|
124
|
+
getTabs(): readonly TabDescriptor[]
|
|
125
|
+
isTabEnabled(id: string): boolean
|
|
126
|
+
getSnapshot(): SidebarSnapshot
|
|
127
|
+
subscribeState(listener: () => void): () => void
|
|
128
|
+
subscribe(listener: () => void): () => void
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ── sessions (dsh-client-runtime) ────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
export interface SessionListSnapshot {
|
|
134
|
+
/** Currently selected session id. */
|
|
135
|
+
current?: SessionId
|
|
136
|
+
[key: string]: unknown
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface ForkOptions {
|
|
140
|
+
sessionId: SessionId
|
|
141
|
+
atSeq?: number
|
|
142
|
+
increaseTitle?: boolean
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Text plus browser-owned parts; text covers everything we send today. */
|
|
146
|
+
export type PromptContentPart = { type: 'text'; text: string } | { type: string; [key: string]: unknown }
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Conversation read model (subset). Extend from
|
|
150
|
+
* dsh-client-runtime `lib/types/client/sessions/conversation.d.ts` as needed —
|
|
151
|
+
* the real shape has chat/nodes/partial/queue/running and more.
|
|
152
|
+
*/
|
|
153
|
+
export interface ConversationSnapshot {
|
|
154
|
+
sessionId: SessionId
|
|
155
|
+
nodes: readonly unknown[]
|
|
156
|
+
running: boolean
|
|
157
|
+
[key: string]: unknown
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface ISession {
|
|
161
|
+
readonly sessionId: SessionId
|
|
162
|
+
prompt(content: PromptContentPart[], mode: 'queue' | 'steer'): Promise<unknown>
|
|
163
|
+
cancel(): Promise<unknown>
|
|
164
|
+
rename(title: string): Promise<unknown>
|
|
165
|
+
loadOlder(): Promise<void>
|
|
166
|
+
command(line: string): Promise<unknown>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export type SessionFace = ISession & ObservableSnapshot<ConversationSnapshot>
|
|
170
|
+
|
|
171
|
+
export interface SessionBinding {
|
|
172
|
+
session: SessionFace
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface SessionsService {
|
|
176
|
+
list: ObservableSnapshot<SessionListSnapshot>
|
|
177
|
+
fork(opts: ForkOptions): Promise<SessionId>
|
|
178
|
+
binding(id: SessionId): SessionBinding | undefined
|
|
179
|
+
scope(id: SessionId): Context | undefined
|
|
180
|
+
open(id: SessionId): void
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ── workspaces (dsh-client-runtime) ─────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
export interface WorkspacesService {
|
|
186
|
+
archiveSession(sessionId: SessionId): Promise<void>
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ── conversation input machine (dsh-client-ui-conversation; lazy via ctx.get) ─
|
|
190
|
+
|
|
191
|
+
export interface SessionInput {
|
|
192
|
+
setDraft(text: string): void
|
|
193
|
+
submit(mode?: string): void
|
|
194
|
+
notify(level: string, text: string): void
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface ConversationService {
|
|
198
|
+
input: {
|
|
199
|
+
for(ctx: Context): SessionInput | undefined
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── connection(dsh-client-connection;sessions RPC 直达面)──────────────────
|
|
204
|
+
|
|
205
|
+
/** 会话的当前模型选择(`session.models` 的 current 字段)。 */
|
|
206
|
+
export interface ModelSelection {
|
|
207
|
+
provider: string
|
|
208
|
+
model: string
|
|
209
|
+
reasoningEffort?: string
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface SessionModelsResult {
|
|
213
|
+
current: ModelSelection
|
|
214
|
+
routable: boolean
|
|
215
|
+
groups: readonly unknown[]
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface RpcEnvelope<T> {
|
|
219
|
+
result: { ok: true; value: T } | { ok: false; error: { message?: string } }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface ConnectionSessionsApi {
|
|
223
|
+
models(payload: { sessionId: SessionId }): Promise<RpcEnvelope<SessionModelsResult>>
|
|
224
|
+
selectModel(payload: { sessionId: SessionId; provider: string; model: string; reasoningEffort?: string }): Promise<RpcEnvelope<{ selected: ModelSelection }>>
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface ConnectionService {
|
|
228
|
+
api: { sessions: ConnectionSessionsApi }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Context ──────────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
// ── locale(dsh-client-locale;DSH 通用设置的语言偏好,Host-backed)────────────
|
|
234
|
+
|
|
235
|
+
export interface LocaleService {
|
|
236
|
+
getSnapshot(): { active: string }
|
|
237
|
+
subscribe(fn: () => void): () => void
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface Context extends CordisContext {
|
|
241
|
+
betterSidebar: BetterSidebarService
|
|
242
|
+
sessions: SessionsService
|
|
243
|
+
workspaces: WorkspacesService
|
|
244
|
+
connection: ConnectionService
|
|
245
|
+
locale: LocaleService
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ── sidechat 扩展(WI-01)───────────────────────────────────────────────────
|
|
249
|
+
// 下列声明经接口合并补进上方镜像;权威来源逐节标注。
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* 会话列表快照补全(权威:dsh-client-runtime
|
|
253
|
+
* lib/types/client/sessions/service.d.ts 的 SessionListState):
|
|
254
|
+
* phase 标「首次成功拉取」就绪边;byId 含已归档行(归档过滤在
|
|
255
|
+
* workspace UI 层,store 本身携带全部行)。
|
|
256
|
+
*/
|
|
257
|
+
export interface SessionListSnapshot {
|
|
258
|
+
phase?: 'pending' | 'ready'
|
|
259
|
+
byId?: Record<string, { blank?: boolean } | undefined>
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 会话快照补全(权威:dsh-client-runtime sessions/conversation.d.ts):
|
|
264
|
+
* openState 是历史窗口生命周期(cold = 未开窗口);partial/runningCalls
|
|
265
|
+
* 承载在途流式输出。面板折叠函数对三者全部容错(缺省按空处理)。
|
|
266
|
+
*/
|
|
267
|
+
export interface ConversationSnapshot {
|
|
268
|
+
openState?: 'cold' | 'loading' | 'open' | 'error'
|
|
269
|
+
partial?: unknown
|
|
270
|
+
runningCalls?: readonly unknown[]
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* input 草稿机的 state store(权威:dsh-client-ui-conversation
|
|
275
|
+
* input/contract.d.ts 的 SessionInput.state)——完整声明见下方 annotate 扩展节
|
|
276
|
+
* (接口合并;draft 字段两侧都依赖)。
|
|
277
|
+
*/
|
|
278
|
+
|
|
279
|
+
export interface Context {
|
|
280
|
+
/**
|
|
281
|
+
* 惰性非追踪服务读(运行时事实:context proxy 的 reflect.get)——
|
|
282
|
+
* 读取不在 inject 清单里的服务(conversation / commandUi)的唯一通道。
|
|
283
|
+
* (`ctx.effect` 无需镜像:公开 cordis 包的 fiber.d.ts 已通过接口合并
|
|
284
|
+
* 提供 `effect(execute, label?)`。)
|
|
285
|
+
*/
|
|
286
|
+
get(name: string): unknown
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ── annotate 扩展 ────────────────────────────────────────────────────────────
|
|
290
|
+
// Mirrors consumed by src/client/annotate/** (Workitem 02). Authorities:
|
|
291
|
+
// - slots service: `@deepseek-ai/dsh-client-runtime` lib/types/client/slots.d.ts
|
|
292
|
+
// (SlotRegistry; register/inject) + `@deepseek-ai/dsh-client-ui-slots` index.d.ts
|
|
293
|
+
// - input.dock owner share: `@deepseek-ai/dsh-client-ui-conversation`
|
|
294
|
+
// lib/types/client/contract/slots.d.ts (`conversation.input.dock` → InputZone)
|
|
295
|
+
// - SessionInput: `.../lib/types/client/input/contract.d.ts`
|
|
296
|
+
|
|
297
|
+
/** Registration options subset passed to `ctx.slots.register` (same shape better-sidebar mirrors). */
|
|
298
|
+
export interface SlotRegisterOptions {
|
|
299
|
+
name: string
|
|
300
|
+
key?: string
|
|
301
|
+
id?: string
|
|
302
|
+
order?: number
|
|
303
|
+
label?: string | (() => string)
|
|
304
|
+
select?: (owner: unknown) => unknown
|
|
305
|
+
priority?: number
|
|
306
|
+
locale?: string
|
|
307
|
+
registrant?: string
|
|
308
|
+
inject?: (...args: any[]) => Record<string, unknown>
|
|
309
|
+
children?: Record<string, unknown>
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** The client slots service face (register returns the disposer). */
|
|
313
|
+
export interface SlotsService {
|
|
314
|
+
register(options: SlotRegisterOptions, component: unknown): () => void
|
|
315
|
+
/** Run a callback for each declaration lifetime of a slot (no-op while undeclared). */
|
|
316
|
+
inject(key: string, callback: () => (() => void) | void): () => void
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Published composer input state (subset of the real InputState). */
|
|
320
|
+
export interface InputStateSnapshot {
|
|
321
|
+
readonly draft: string
|
|
322
|
+
readonly phase: string
|
|
323
|
+
readonly queue?: readonly unknown[]
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Session input snapshot as exposed on `InputZone.input`. */
|
|
327
|
+
export interface ConversationSnapshotLite {
|
|
328
|
+
sessionId: SessionId
|
|
329
|
+
running: boolean
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Owner share of the `conversation.input.dock` slot (point-in-time snapshots). */
|
|
333
|
+
export interface InputZone {
|
|
334
|
+
readonly session: ConversationSnapshotLite
|
|
335
|
+
readonly input: InputStateSnapshot
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* U+FFFC reference-chip insert (spike result: NOT used — serialization of the
|
|
340
|
+
* chip routes through the source owner's ReferenceCodec, which is
|
|
341
|
+
* package-internal to ui-input-trigger with no plugin-facing registry; an
|
|
342
|
+
* unowned source would mark the occurrence invalid and fail submit. Mirrored
|
|
343
|
+
* here only to document the probe target).
|
|
344
|
+
*/
|
|
345
|
+
export interface ReferenceInsert {
|
|
346
|
+
readonly source: string
|
|
347
|
+
readonly ref: string
|
|
348
|
+
readonly label: string
|
|
349
|
+
readonly clipboardText: string
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export interface TokenSpan {
|
|
353
|
+
readonly start: number
|
|
354
|
+
readonly end: number
|
|
355
|
+
readonly draftRev: number
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export interface Context {
|
|
359
|
+
/** The slot registry (provided by dsh-client-runtime, mounted before this plugin). */
|
|
360
|
+
slots: SlotsService
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** SessionInput completion for annotate: the live state store + the (unused) chip insert face. */
|
|
364
|
+
export interface SessionInput {
|
|
365
|
+
/** Input state store (draft reads + subscribe for the send-edge watch). */
|
|
366
|
+
readonly state: ObservableSnapshot<InputStateSnapshot>
|
|
367
|
+
/** Spike-only mirror; see {@link ReferenceInsert}. Not called by annotate. */
|
|
368
|
+
insertReference(ref: ReferenceInsert, span: TokenSpan): boolean
|
|
369
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-sidenote host half: currently empty by design — every feature is
|
|
3
|
+
* client-side (sessions RPC + the betterSidebar service). A host half would
|
|
4
|
+
* only appear for capabilities the client cannot reach (none today).
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from './context-types.ts'
|
|
7
|
+
|
|
8
|
+
/** Plugin identity for cordis.yml rows. */
|
|
9
|
+
export const name = 'dsh-sidenote'
|
|
10
|
+
|
|
11
|
+
export function apply(_ctx: Context): void {
|
|
12
|
+
// No host services yet.
|
|
13
|
+
}
|