|
|
@@ -3,9 +3,12 @@ import type { Context } from '@deepseek-ai/cordis'
|
|
|
import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
|
|
|
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
|
|
import type { SessionBinding } from '@deepseek-ai/dsh-api-session-controller/client'
|
|
|
-import type { BoundActions, ObservableSnapshot } from '@deepseek-ai/dsh-client-store'
|
|
|
+import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store'
|
|
|
import type { SessionId } from '@deepseek-ai/dsh-session/types'
|
|
|
-import { resolveWorkspacePath } from '@deepseek-ai/dsh-util-workspace-path'
|
|
|
+import type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'
|
|
|
+// The `file` entry of `SidebarRightResourceParamsMap`, which types `{ params: { line } }` below.
|
|
|
+import type {} from '@deepseek-ai/dsh-client-ui-sidebar-textpreview/client'
|
|
|
+import { fileAddressFor } from '@deepseek-ai/dsh-util-workspace-path'
|
|
|
// Type-only service and declaration merges used by the apply world.
|
|
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
|
import type {} from '@deepseek-ai/dsh-client-ui-conversation/client'
|
|
|
@@ -15,7 +18,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-session/client'
|
|
|
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
|
import type {} from '@deepseek-ai/dsh-client-ui-workspace/client'
|
|
|
import type {
|
|
|
- ChatNodeTurnDataInjected, ChatScrollPosition, ChatViewInjected, DetailsInjected,
|
|
|
+ ChatNodeTurnDataInjected, ChatScrollPosition, ChatViewInjected,
|
|
|
TurnTailOwnerProps,
|
|
|
} from './contract/slots.ts'
|
|
|
import type { ChatSnapshot } from './contract/snapshot.ts'
|
|
|
@@ -25,7 +28,6 @@ import { ChatView } from './chat/ChatView.tsx'
|
|
|
import { registerChatNodeRenderers } from './chat/register-node-renderers.ts'
|
|
|
import { StatsLine } from './chat/StatsLine.tsx'
|
|
|
import { registerConversationNodes } from './conversation-nodes/register.ts'
|
|
|
-import { DetailsPanel } from './details/DetailsPanel.tsx'
|
|
|
import { en, NS, zh } from './locale.ts'
|
|
|
import { TranscriptViewRow, type TranscriptViewRowInjected } from './settings/TranscriptViewRow.tsx'
|
|
|
import { createChatStore } from './stores.ts'
|
|
|
@@ -43,8 +45,8 @@ const CHAT_NODE_INJECT: ChatNodeTurnDataInjected = {
|
|
|
|
|
|
/** Services required by the Chat target and its presentation registrations. */
|
|
|
export const inject = [
|
|
|
- 'slots', 'sessions', 'uiSession', 'uiConversation', 'layout', 'locale',
|
|
|
- 'settingsScope', 'remote', 'remote.session',
|
|
|
+ 'slots', 'sessions', 'uiSession', 'uiConversation', 'locale',
|
|
|
+ 'settingsScope', 'remote', 'remote.session', 'sidebarRight',
|
|
|
]
|
|
|
|
|
|
/**
|
|
|
@@ -103,7 +105,7 @@ export function apply(ctx: Context): void {
|
|
|
'conversation.message.images': { kind: 'single', scope: 'session' },
|
|
|
},
|
|
|
store: chatStore,
|
|
|
- inject: (sessionId: SessionId, actions: BoundActions<typeof chatStore>): ChatViewInjected => {
|
|
|
+ inject: (sessionId: SessionId): ChatViewInjected => {
|
|
|
const binding = ctx.sessions.binding(sessionId)
|
|
|
if (binding === undefined) throw new Error(`ui-chat: unknown session "${sessionId}"`)
|
|
|
const session = binding.session
|
|
|
@@ -114,17 +116,25 @@ export function apply(ctx: Context): void {
|
|
|
chatNode: key => chat.getSnapshot().nodes.source(key),
|
|
|
chatNodeProcess: key => chat.getSnapshot().nodes.processSource(key),
|
|
|
},
|
|
|
- openDetails: (target) => {
|
|
|
- actions.select(target)
|
|
|
- ctx.layout.openDetails()
|
|
|
- },
|
|
|
fileMentions: (owner: TurnTailOwnerProps) => ctx.get('chatFileMentions')?.forClosing(owner),
|
|
|
- openFile: async (path) => {
|
|
|
+ // Files open in the right Sidebar, not in a desktop application: the
|
|
|
+ // content stays in the product, beside the conversation that produced
|
|
|
+ // it. A relative path, or an absolute one inside the session's
|
|
|
+ // workspace, is addressed under this session's scope,
|
|
|
+ // `dsh-resource://file/session/<id>/<relative path>`; an absolute path
|
|
|
+ // elsewhere is addressed as `dsh-resource://file/absolute/<path>` and
|
|
|
+ // read through the session on screen. Which tab type claims the
|
|
|
+ // address is the Sidebar's decision, not this call site's.
|
|
|
+ // A line travels as a navigation parameter, not as part of the
|
|
|
+ // address: the file is one piece of content whether it is opened at
|
|
|
+ // its top or at line 400, so the same tab is revealed and told where
|
|
|
+ // to land.
|
|
|
+ openFile: async (path, options) => {
|
|
|
const cwd = ctx.sessions.list.getSnapshot().byId[sessionId]?.cwd
|
|
|
- const result = await ctx.remote.session.openWorkspacePath({
|
|
|
- path: resolveWorkspacePath(cwd, path),
|
|
|
- })
|
|
|
- if (!result.ok) throw new Error(`path open failed: ${result.error.message}`)
|
|
|
+ const url = fileAddressFor(sessionId, cwd, path)
|
|
|
+ if (options?.line === undefined) ctx.sidebarRight.openResource(url)
|
|
|
+ else ctx.sidebarRight.openResource(url, { params: { line: options.line } })
|
|
|
+ await Promise.resolve()
|
|
|
},
|
|
|
loadOlder: () => { void session.loadOlder() },
|
|
|
loadThrough: seq => session.loadThrough(seq),
|
|
|
@@ -160,11 +170,4 @@ export function apply(ctx: Context): void {
|
|
|
ctx.slots.inject('conversation.approval.detail', () =>
|
|
|
ctx.slots.register({ name: 'conversation.approval.detail' }, ApprovalCommand))
|
|
|
|
|
|
- ctx.slots.inject('details', () => ctx.slots.register({
|
|
|
- name: 'details',
|
|
|
- locale: NS,
|
|
|
- children: { 'conversation.details.tool': { kind: 'single', scope: 'session' } },
|
|
|
- store: chatStore,
|
|
|
- inject: (): DetailsInjected => ({ closeDetails: () => { ctx.layout.closeDetails() } }),
|
|
|
- }, DetailsPanel))
|
|
|
}
|