@yushaw/sanqian-chat 0.2.6 → 0.2.9

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/README.md CHANGED
@@ -99,6 +99,19 @@ src/renderer/styles/
99
99
 
100
100
  ## Changelog
101
101
 
102
+ ### 0.2.7 (2026-01-06)
103
+ - **Added**: External resource reference support
104
+ - `useResourcePicker` hook - manage resource picker state, search, pagination
105
+ - `ResourcePicker` component - two-level picker (providers → resources)
106
+ - `ResourceChip` / `ResourceChipList` - display attached resources
107
+ - `AddResourceButton` - + button with dropdown menu
108
+ - **Added**: `ChatAdapter` extensions: `listResourceProviders`, `getResourceList`, `onLocaleChanged`
109
+ - **Added**: `FloatingWindow.notifyLocaleChanged(locale)` and `ChatPanel.notifyLocaleChanged(locale)` - notify renderer when locale changes
110
+ - **Added**: `sendMessage` now accepts `options.attachedResources`
111
+ - **Added**: Message rendering displays attached resources in user messages
112
+ - **Added**: New types: `AttachedResource`, `ResourcePickerItem`, `ContextProviderInfo`
113
+ - **Changed**: `AppContextProvider.getList` now accepts `options` parameter for search/pagination
114
+
102
115
  ### 2025-01-03
103
116
  - **Added**: Focus persistence - input keeps focus when clicking empty areas (unless selecting text)
104
117
  - **Added**: `Cmd+N` (macOS) / `Ctrl+N` (Windows) keyboard shortcut for new chat
@@ -0,0 +1,86 @@
1
+ // src/preload/factories.ts
2
+ import { ipcRenderer } from "electron";
3
+ function createSanqianChatApi() {
4
+ return {
5
+ connect: () => ipcRenderer.invoke("sanqian-chat:connect"),
6
+ isConnected: () => ipcRenderer.invoke("sanqian-chat:isConnected"),
7
+ stream: (params) => ipcRenderer.invoke("sanqian-chat:stream", params),
8
+ cancelStream: (params) => ipcRenderer.invoke("sanqian-chat:cancelStream", params),
9
+ onStreamEvent: (callback) => {
10
+ const handler = (_, data) => {
11
+ callback(data.streamId, data.event);
12
+ };
13
+ ipcRenderer.on("sanqian-chat:streamEvent", handler);
14
+ return () => ipcRenderer.removeListener("sanqian-chat:streamEvent", handler);
15
+ },
16
+ sendHitlResponse: (params) => ipcRenderer.invoke("sanqian-chat:hitlResponse", params),
17
+ listConversations: (params) => ipcRenderer.invoke("sanqian-chat:listConversations", params),
18
+ getConversation: (params) => ipcRenderer.invoke("sanqian-chat:getConversation", params),
19
+ deleteConversation: (params) => ipcRenderer.invoke("sanqian-chat:deleteConversation", params),
20
+ hide: () => ipcRenderer.invoke("sanqian-chat:hide"),
21
+ setAlwaysOnTop: (params) => ipcRenderer.invoke("sanqian-chat:setAlwaysOnTop", params),
22
+ getAlwaysOnTop: () => ipcRenderer.invoke("sanqian-chat:getAlwaysOnTop"),
23
+ getUiConfig: () => ipcRenderer.invoke("sanqian-chat:getUiConfig"),
24
+ setBackgroundColor: (params) => ipcRenderer.invoke("sanqian-chat:setBackgroundColor", params),
25
+ getPlatform: () => process.platform,
26
+ // Resource Picker
27
+ listResourceProviders: () => ipcRenderer.invoke("sanqian-chat:listResourceProviders"),
28
+ getResourceList: (params) => ipcRenderer.invoke("sanqian-chat:getResourceList", params),
29
+ // Locale
30
+ onLocaleChanged: (callback) => {
31
+ const handler = (_, data) => {
32
+ callback(data.locale);
33
+ };
34
+ ipcRenderer.on("sanqian-chat:localeChanged", handler);
35
+ return () => ipcRenderer.removeListener("sanqian-chat:localeChanged", handler);
36
+ }
37
+ };
38
+ }
39
+ function createChatPanelApi() {
40
+ return {
41
+ // Mode
42
+ getMode: () => ipcRenderer.invoke("chatPanel:getMode"),
43
+ setMode: (mode) => ipcRenderer.invoke("chatPanel:setMode", mode),
44
+ toggleMode: () => ipcRenderer.invoke("chatPanel:toggleMode"),
45
+ onModeChanged: (callback) => {
46
+ const handler = (_, data) => {
47
+ callback(data.mode);
48
+ };
49
+ ipcRenderer.on("chatPanel:modeChanged", handler);
50
+ return () => ipcRenderer.removeListener("chatPanel:modeChanged", handler);
51
+ },
52
+ // Visibility
53
+ isVisible: () => ipcRenderer.invoke("chatPanel:isVisible"),
54
+ show: () => ipcRenderer.invoke("chatPanel:show"),
55
+ hide: () => ipcRenderer.invoke("chatPanel:hide"),
56
+ toggle: () => ipcRenderer.invoke("chatPanel:toggle"),
57
+ onVisibilityChanged: (callback) => {
58
+ const handler = (_, data) => {
59
+ callback(data.visible);
60
+ };
61
+ ipcRenderer.on("chatPanel:visibilityChanged", handler);
62
+ return () => ipcRenderer.removeListener("chatPanel:visibilityChanged", handler);
63
+ },
64
+ // Attach state
65
+ getAttachState: () => ipcRenderer.invoke("chatPanel:getAttachState"),
66
+ toggleAttach: () => ipcRenderer.invoke("chatPanel:toggleAttach"),
67
+ onAttachStateChanged: (callback) => {
68
+ const handler = (_, data) => {
69
+ callback(data.state);
70
+ };
71
+ ipcRenderer.on("chatPanel:attachStateChanged", handler);
72
+ return () => ipcRenderer.removeListener("chatPanel:attachStateChanged", handler);
73
+ },
74
+ // Width
75
+ getWidth: () => ipcRenderer.invoke("chatPanel:getWidth"),
76
+ setWidth: (width, animate) => ipcRenderer.invoke("chatPanel:setWidth", { width, animate }),
77
+ onResizeEnd: () => ipcRenderer.invoke("chatPanel:onResizeEnd"),
78
+ // UI Config
79
+ getUiConfig: () => ipcRenderer.invoke("chatPanel:getUiConfig")
80
+ };
81
+ }
82
+
83
+ export {
84
+ createSanqianChatApi,
85
+ createChatPanelApi
86
+ };
@@ -1,5 +1,5 @@
1
- import { HitlInterruptPayload, HitlResponse, SanqianSDK } from '@yushaw/sanqian-sdk';
2
- export { ChatStreamEvent, HitlInterruptPayload, HitlInterruptType, HitlResponse, HitlRiskLevel, ChatMessage as SdkChatMessage, ConversationDetail as SdkConversationDetail, ConversationInfo as SdkConversationInfo, ToolCall as SdkToolCall } from '@yushaw/sanqian-sdk';
1
+ import { ResourceType, HitlInterruptPayload, ContextListItem, HitlResponse, ResourceListOptions, SanqianSDK } from '@yushaw/sanqian-sdk';
2
+ export { ChatStreamEvent, ContextListItem, HitlInterruptPayload, HitlInterruptType, HitlResponse, HitlRiskLevel, ResourceListOptions, ResourceListResult, ResourceType, ChatMessage as SdkChatMessage, ConversationDetail as SdkConversationDetail, ConversationInfo as SdkConversationInfo, ToolCall as SdkToolCall } from '@yushaw/sanqian-sdk';
3
3
 
4
4
  /**
5
5
  * @yushaw/sanqian-chat Core Types
@@ -120,6 +120,8 @@ interface ChatMessage {
120
120
  finalContent?: string;
121
121
  isComplete?: boolean;
122
122
  filePaths?: string[];
123
+ /** Attached external resources (for user messages) */
124
+ attachedResources?: AttachedResource[];
123
125
  }
124
126
  /** Conversation info for UI */
125
127
  interface ConversationInfo {
@@ -307,6 +309,86 @@ interface ChatPanelConfig {
307
309
  */
308
310
  uiConfig?: ChatUiConfigSerializable;
309
311
  }
312
+ /**
313
+ * Context provider info for UI display (in + menu)
314
+ */
315
+ interface ContextProviderInfo {
316
+ /** Full provider ID (format: "appName:providerId") */
317
+ id: string;
318
+ /** Display name */
319
+ name: string;
320
+ /** Description */
321
+ description: string;
322
+ /** App name (extracted from id) */
323
+ appName: string;
324
+ /** Whether app is currently connected */
325
+ isConnected?: boolean;
326
+ /** Whether provider supports getList (resource picker) */
327
+ hasGetList?: boolean;
328
+ /** Whether provider supports getCurrent (auto-inject) */
329
+ hasGetCurrent?: boolean;
330
+ }
331
+ /**
332
+ * Resource item for picker display (extends ContextListItem with provider info)
333
+ */
334
+ interface ResourcePickerItem extends ContextListItem {
335
+ /** Full provider ID that owns this resource */
336
+ providerId: string;
337
+ /** Provider display name */
338
+ providerName?: string;
339
+ }
340
+ /**
341
+ * Attached resource reference (selected by user)
342
+ */
343
+ interface AttachedResource {
344
+ /** Full provider ID (format: "appName:providerId") */
345
+ providerId: string;
346
+ /** Resource ID (from ContextListItem.id) */
347
+ resourceId: string;
348
+ /** Display title */
349
+ title: string;
350
+ /** Optional summary for tooltip */
351
+ summary?: string;
352
+ /** Resource type for icon/styling */
353
+ type?: ResourceType;
354
+ /** Icon emoji or URL */
355
+ icon?: string;
356
+ }
357
+ /**
358
+ * Resource picker state
359
+ */
360
+ interface ResourcePickerState {
361
+ /** Currently loading */
362
+ isLoading: boolean;
363
+ /** Search query */
364
+ query: string;
365
+ /** Available items grouped by provider */
366
+ itemsByProvider: Record<string, ResourcePickerItem[]>;
367
+ /** Whether more items are available (for pagination) */
368
+ hasMoreByProvider: Record<string, boolean>;
369
+ /** Error message if any */
370
+ error?: string;
371
+ }
372
+ /**
373
+ * Attachment menu item type
374
+ */
375
+ type AttachmentMenuItemType = 'upload' | 'provider';
376
+ /**
377
+ * Attachment menu item for + button dropdown
378
+ */
379
+ interface AttachmentMenuItem {
380
+ type: AttachmentMenuItemType;
381
+ /** For 'upload': 'file' or 'image'. For 'provider': provider ID */
382
+ id: string;
383
+ /** Display label */
384
+ label: string;
385
+ /** Icon emoji or URL */
386
+ icon?: string;
387
+ /** Description or subtitle */
388
+ description?: string;
389
+ /** Whether currently available (e.g., app connected) */
390
+ available?: boolean;
391
+ }
310
392
 
311
393
  /**
312
394
  * Chat Adapter Interface
@@ -390,10 +472,23 @@ interface ChatAdapter {
390
472
  deleteConversation(id: string): Promise<void>;
391
473
  chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void, options?: {
392
474
  agentId?: string | null;
475
+ /** Attached context provider IDs (e.g., ["sanqian-notes:notes"]) - for getCurrent */
476
+ attachedContexts?: string[];
477
+ /** Attached resource references (e.g., ["sanqian-notes:notes:abc123"]) - for getById */
478
+ attachedResources?: string[];
393
479
  }): Promise<{
394
480
  cancel: () => void;
395
481
  }>;
396
482
  sendHitlResponse?(response: HitlResponse, runId?: string): void;
483
+ /** List available context providers */
484
+ listResourceProviders?(): Promise<ContextProviderInfo[]>;
485
+ /** Get resource list from a provider with search/pagination */
486
+ getResourceList?(providerId: string, options?: ResourceListOptions): Promise<{
487
+ items: ResourcePickerItem[];
488
+ hasMore?: boolean;
489
+ }>;
490
+ /** Subscribe to locale change events */
491
+ onLocaleChanged?(callback: (locale: string) => void): () => void;
397
492
  cleanup?(): void;
398
493
  }
399
494
  /** SDK adapter config */
@@ -480,4 +575,4 @@ declare function parseToolCalls(toolCalls: unknown): ToolCall[] | undefined;
480
575
  */
481
576
  declare function mergeConsecutiveAssistantMessages(rawMessages: ApiMessage[]): ChatMessage[];
482
577
 
483
- export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type Locale, type MessageBlock, type MessageRole, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
578
+ export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type Locale, type MessageBlock, type MessageRole, type ResourcePickerItem, type ResourcePickerState, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
@@ -1,5 +1,5 @@
1
- import { HitlInterruptPayload, HitlResponse, SanqianSDK } from '@yushaw/sanqian-sdk';
2
- export { ChatStreamEvent, HitlInterruptPayload, HitlInterruptType, HitlResponse, HitlRiskLevel, ChatMessage as SdkChatMessage, ConversationDetail as SdkConversationDetail, ConversationInfo as SdkConversationInfo, ToolCall as SdkToolCall } from '@yushaw/sanqian-sdk';
1
+ import { ResourceType, HitlInterruptPayload, ContextListItem, HitlResponse, ResourceListOptions, SanqianSDK } from '@yushaw/sanqian-sdk';
2
+ export { ChatStreamEvent, ContextListItem, HitlInterruptPayload, HitlInterruptType, HitlResponse, HitlRiskLevel, ResourceListOptions, ResourceListResult, ResourceType, ChatMessage as SdkChatMessage, ConversationDetail as SdkConversationDetail, ConversationInfo as SdkConversationInfo, ToolCall as SdkToolCall } from '@yushaw/sanqian-sdk';
3
3
 
4
4
  /**
5
5
  * @yushaw/sanqian-chat Core Types
@@ -120,6 +120,8 @@ interface ChatMessage {
120
120
  finalContent?: string;
121
121
  isComplete?: boolean;
122
122
  filePaths?: string[];
123
+ /** Attached external resources (for user messages) */
124
+ attachedResources?: AttachedResource[];
123
125
  }
124
126
  /** Conversation info for UI */
125
127
  interface ConversationInfo {
@@ -307,6 +309,86 @@ interface ChatPanelConfig {
307
309
  */
308
310
  uiConfig?: ChatUiConfigSerializable;
309
311
  }
312
+ /**
313
+ * Context provider info for UI display (in + menu)
314
+ */
315
+ interface ContextProviderInfo {
316
+ /** Full provider ID (format: "appName:providerId") */
317
+ id: string;
318
+ /** Display name */
319
+ name: string;
320
+ /** Description */
321
+ description: string;
322
+ /** App name (extracted from id) */
323
+ appName: string;
324
+ /** Whether app is currently connected */
325
+ isConnected?: boolean;
326
+ /** Whether provider supports getList (resource picker) */
327
+ hasGetList?: boolean;
328
+ /** Whether provider supports getCurrent (auto-inject) */
329
+ hasGetCurrent?: boolean;
330
+ }
331
+ /**
332
+ * Resource item for picker display (extends ContextListItem with provider info)
333
+ */
334
+ interface ResourcePickerItem extends ContextListItem {
335
+ /** Full provider ID that owns this resource */
336
+ providerId: string;
337
+ /** Provider display name */
338
+ providerName?: string;
339
+ }
340
+ /**
341
+ * Attached resource reference (selected by user)
342
+ */
343
+ interface AttachedResource {
344
+ /** Full provider ID (format: "appName:providerId") */
345
+ providerId: string;
346
+ /** Resource ID (from ContextListItem.id) */
347
+ resourceId: string;
348
+ /** Display title */
349
+ title: string;
350
+ /** Optional summary for tooltip */
351
+ summary?: string;
352
+ /** Resource type for icon/styling */
353
+ type?: ResourceType;
354
+ /** Icon emoji or URL */
355
+ icon?: string;
356
+ }
357
+ /**
358
+ * Resource picker state
359
+ */
360
+ interface ResourcePickerState {
361
+ /** Currently loading */
362
+ isLoading: boolean;
363
+ /** Search query */
364
+ query: string;
365
+ /** Available items grouped by provider */
366
+ itemsByProvider: Record<string, ResourcePickerItem[]>;
367
+ /** Whether more items are available (for pagination) */
368
+ hasMoreByProvider: Record<string, boolean>;
369
+ /** Error message if any */
370
+ error?: string;
371
+ }
372
+ /**
373
+ * Attachment menu item type
374
+ */
375
+ type AttachmentMenuItemType = 'upload' | 'provider';
376
+ /**
377
+ * Attachment menu item for + button dropdown
378
+ */
379
+ interface AttachmentMenuItem {
380
+ type: AttachmentMenuItemType;
381
+ /** For 'upload': 'file' or 'image'. For 'provider': provider ID */
382
+ id: string;
383
+ /** Display label */
384
+ label: string;
385
+ /** Icon emoji or URL */
386
+ icon?: string;
387
+ /** Description or subtitle */
388
+ description?: string;
389
+ /** Whether currently available (e.g., app connected) */
390
+ available?: boolean;
391
+ }
310
392
 
311
393
  /**
312
394
  * Chat Adapter Interface
@@ -390,10 +472,23 @@ interface ChatAdapter {
390
472
  deleteConversation(id: string): Promise<void>;
391
473
  chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void, options?: {
392
474
  agentId?: string | null;
475
+ /** Attached context provider IDs (e.g., ["sanqian-notes:notes"]) - for getCurrent */
476
+ attachedContexts?: string[];
477
+ /** Attached resource references (e.g., ["sanqian-notes:notes:abc123"]) - for getById */
478
+ attachedResources?: string[];
393
479
  }): Promise<{
394
480
  cancel: () => void;
395
481
  }>;
396
482
  sendHitlResponse?(response: HitlResponse, runId?: string): void;
483
+ /** List available context providers */
484
+ listResourceProviders?(): Promise<ContextProviderInfo[]>;
485
+ /** Get resource list from a provider with search/pagination */
486
+ getResourceList?(providerId: string, options?: ResourceListOptions): Promise<{
487
+ items: ResourcePickerItem[];
488
+ hasMore?: boolean;
489
+ }>;
490
+ /** Subscribe to locale change events */
491
+ onLocaleChanged?(callback: (locale: string) => void): () => void;
397
492
  cleanup?(): void;
398
493
  }
399
494
  /** SDK adapter config */
@@ -480,4 +575,4 @@ declare function parseToolCalls(toolCalls: unknown): ToolCall[] | undefined;
480
575
  */
481
576
  declare function mergeConsecutiveAssistantMessages(rawMessages: ApiMessage[]): ChatMessage[];
482
577
 
483
- export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type Locale, type MessageBlock, type MessageRole, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
578
+ export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type Locale, type MessageBlock, type MessageRole, type ResourcePickerItem, type ResourcePickerState, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
@@ -412,7 +412,8 @@ function createChatAdapter(config) {
412
412
  sdkMessages,
413
413
  {
414
414
  conversationId,
415
- persistHistory: config.persistHistory ?? false
415
+ persistHistory: config.persistHistory ?? false,
416
+ attachedResources: options?.attachedResources
416
417
  }
417
418
  );
418
419
  return processStreamEvents(stream, onEvent, sdk, (id) => {
@@ -635,7 +636,10 @@ function createSdkAdapter(config) {
635
636
  const stream = sdk.chatStream(
636
637
  agentId,
637
638
  sdkMessages,
638
- { conversationId }
639
+ {
640
+ conversationId,
641
+ attachedResources: options?.attachedResources
642
+ }
639
643
  );
640
644
  return processStreamEvents(stream, onEvent, sdk, (id) => {
641
645
  currentRunId = id;
@@ -373,7 +373,8 @@ function createChatAdapter(config) {
373
373
  sdkMessages,
374
374
  {
375
375
  conversationId,
376
- persistHistory: config.persistHistory ?? false
376
+ persistHistory: config.persistHistory ?? false,
377
+ attachedResources: options?.attachedResources
377
378
  }
378
379
  );
379
380
  return processStreamEvents(stream, onEvent, sdk, (id) => {
@@ -596,7 +597,10 @@ function createSdkAdapter(config) {
596
597
  const stream = sdk.chatStream(
597
598
  agentId,
598
599
  sdkMessages,
599
- { conversationId }
600
+ {
601
+ conversationId,
602
+ attachedResources: options?.attachedResources
603
+ }
600
604
  );
601
605
  return processStreamEvents(stream, onEvent, sdk, (id) => {
602
606
  currentRunId = id;
@@ -8,6 +8,8 @@ import { BrowserWindow, WebContents } from 'electron';
8
8
  * Application-facing types that hide SDK implementation details.
9
9
  * These provide a stable API even as the underlying SDK evolves.
10
10
  */
11
+ /** Resource type for display styling (can be any string for custom types) */
12
+ type AppResourceType = string;
11
13
  /**
12
14
  * Context data returned by a provider
13
15
  */
@@ -22,6 +24,16 @@ interface AppContextData {
22
24
  summary?: string;
23
25
  /** Version for change detection (optional, Sanqian uses content hash if not provided) */
24
26
  version?: string;
27
+ /** Resource type for display styling */
28
+ type?: AppResourceType;
29
+ /** Additional metadata (accessible in templates via {{metadata.xxx}}) */
30
+ metadata?: Record<string, unknown>;
31
+ /**
32
+ * Custom injection template using Mustache-style variables.
33
+ * Available variables: {{title}}, {{content}}, {{summary}}, {{type}}, {{metadata.xxx}}
34
+ * @example "# {{title}}\n\n{{content}}"
35
+ */
36
+ template?: string;
25
37
  }
26
38
  /**
27
39
  * List item for context provider's getList method
@@ -35,6 +47,34 @@ interface AppContextListItem {
35
47
  summary?: string;
36
48
  /** Optional icon */
37
49
  icon?: string;
50
+ /** Resource type for display styling */
51
+ type?: AppResourceType;
52
+ /** Last updated timestamp (ISO 8601) */
53
+ updatedAt?: string;
54
+ /** Group name for categorization in picker UI */
55
+ group?: string;
56
+ /** Tags for filtering */
57
+ tags?: string[];
58
+ }
59
+ /**
60
+ * Options for getList() pagination and search
61
+ */
62
+ interface AppResourceListOptions {
63
+ /** Search query string */
64
+ query?: string;
65
+ /** Pagination offset (default: 0) */
66
+ offset?: number;
67
+ /** Max items to return (default: 20) */
68
+ limit?: number;
69
+ }
70
+ /**
71
+ * Result from getList() with pagination info
72
+ */
73
+ interface AppResourceListResult {
74
+ /** List items */
75
+ items: AppContextListItem[];
76
+ /** Whether more items are available */
77
+ hasMore?: boolean;
38
78
  }
39
79
  /**
40
80
  * Context provider definition
@@ -48,8 +88,8 @@ interface AppContextProvider {
48
88
  description: string;
49
89
  /** Get current state (for dynamic context) */
50
90
  getCurrent?: () => Promise<AppContextData | null>;
51
- /** Get list of available contexts */
52
- getList?: () => Promise<AppContextListItem[]>;
91
+ /** Get list of available resources with pagination and search */
92
+ getList?: (options?: AppResourceListOptions) => Promise<AppResourceListResult>;
53
93
  /** Get full content by ID */
54
94
  getById?: (id: string) => Promise<AppContextData | null>;
55
95
  }
@@ -207,6 +247,11 @@ declare class SanqianAppClient {
207
247
  * Ensure SDK is ready (connects if needed, waits for registration)
208
248
  */
209
249
  ensureReady(): Promise<void>;
250
+ /**
251
+ * Get the port number for the Sanqian backend
252
+ * @throws Error if not connected
253
+ */
254
+ getPort(): number;
210
255
  /**
211
256
  * Request persistent connection (enables auto-reconnect)
212
257
  * Call when a component needs the connection to stay alive
@@ -224,6 +269,11 @@ declare class SanqianAppClient {
224
269
  createAgent(config: AppAgentConfig): Promise<{
225
270
  agentId: string;
226
271
  }>;
272
+ /**
273
+ * Update context providers dynamically
274
+ * Use this to update provider names/descriptions when locale changes
275
+ */
276
+ updateContexts(contexts: AppContextProvider[]): Promise<void>;
227
277
  /**
228
278
  * Get embedding configuration from Sanqian
229
279
  * @returns Embedding config or null if not available
@@ -619,6 +669,18 @@ declare class FloatingWindow {
619
669
  isVisible(): boolean;
620
670
  destroy(): void;
621
671
  getWindow(): BrowserWindow | null;
672
+ /**
673
+ * Notify renderer that locale has changed.
674
+ * Call this after updating contexts with new locale via client.updateContexts().
675
+ *
676
+ * @example
677
+ * ```typescript
678
+ * // When locale changes
679
+ * await client.updateContexts(getLocalizedContexts(newLocale));
680
+ * floatingWindow.notifyLocaleChanged(newLocale);
681
+ * ```
682
+ */
683
+ notifyLocaleChanged(locale: string): void;
622
684
  }
623
685
 
624
686
  /**
@@ -718,6 +780,18 @@ declare class ChatPanel {
718
780
  * Get webContents (for IPC)
719
781
  */
720
782
  getWebContents(): WebContents;
783
+ /**
784
+ * Notify renderer that locale has changed.
785
+ * Call this after updating contexts with new locale via client.updateContexts().
786
+ *
787
+ * @example
788
+ * ```typescript
789
+ * // When locale changes
790
+ * await client.updateContexts(getLocalizedContexts(newLocale));
791
+ * chatPanel.notifyLocaleChanged(newLocale);
792
+ * ```
793
+ */
794
+ notifyLocaleChanged(locale: string): void;
721
795
  /**
722
796
  * Destroy panel
723
797
  */
@@ -839,4 +913,32 @@ declare class WindowAttachment {
839
913
  private stopPolling;
840
914
  }
841
915
 
842
- export { type AppAgentConfig, type AppClientEvent, type AppClientEventHandlers, type AppConfig, type AppContextData, type AppContextListItem, type AppContextProvider, type AppEmbeddingConfig, type AppJsonSchemaProperty, type AppToolDefinition, type AttachConfig, type AttachPosition, type AttachState, ChatPanel, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatUiConfigSerializable, FloatingWindow, type FloatingWindowConfig, type FloatingWindowOptions, SanqianAppClient, WindowAttachment, type WindowPosition };
916
+ /**
917
+ * @yushaw/sanqian-chat Main
918
+ *
919
+ * Electron main process module
920
+ *
921
+ * Provides:
922
+ * - SanqianAppClient: Facade for SDK, stable application-facing API
923
+ * - FloatingWindow: Floating chat window manager
924
+ * - ChatPanel: Embedded + Floating chat panel (new)
925
+ * - WindowAttachment: Floating window attachment (new)
926
+ * - getPreloadPath: Get the path to the built preload script
927
+ */
928
+ /**
929
+ * Get the path to the chat SDK's built preload script.
930
+ * Use this when creating ChatPanel or FloatingWindow.
931
+ *
932
+ * @example
933
+ * ```typescript
934
+ * import { ChatPanel, getPreloadPath } from '@yushaw/sanqian-chat/main'
935
+ *
936
+ * const chatPanel = new ChatPanel({
937
+ * preloadPath: getPreloadPath(),
938
+ * // ...
939
+ * })
940
+ * ```
941
+ */
942
+ declare function getPreloadPath(): string;
943
+
944
+ export { type AppAgentConfig, type AppClientEvent, type AppClientEventHandlers, type AppConfig, type AppContextData, type AppContextListItem, type AppContextProvider, type AppEmbeddingConfig, type AppJsonSchemaProperty, type AppResourceListOptions, type AppResourceListResult, type AppResourceType, type AppToolDefinition, type AttachConfig, type AttachPosition, type AttachState, ChatPanel, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatUiConfigSerializable, FloatingWindow, type FloatingWindowConfig, type FloatingWindowOptions, SanqianAppClient, WindowAttachment, type WindowPosition, getPreloadPath };