@ahggg/dsh-side-chat 0.7.3 → 1.0.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 (79) hide show
  1. package/README.md +15 -12
  2. package/README.zh-CN.md +15 -12
  3. package/lib/client/apply.js +48 -32
  4. package/lib/client/contracts.d.ts +3 -58
  5. package/lib/client/dsh/ReadOnlyConversation.d.ts +10 -0
  6. package/lib/client/dsh/ReadOnlyConversation.js +91 -0
  7. package/lib/client/dsh/SideChatOverlay.d.ts +7 -0
  8. package/lib/client/{rc6/Rc6SideChatOverlay.js → dsh/SideChatOverlay.js} +26 -56
  9. package/lib/client/{rc6 → dsh}/context.d.ts +4 -6
  10. package/lib/client/dsh/conversation-store.d.ts +22 -0
  11. package/lib/client/dsh/conversation-store.js +19 -0
  12. package/lib/client/dsh/primitive-labels.d.ts +2 -0
  13. package/lib/client/dsh/primitive-labels.js +4 -0
  14. package/lib/client/{rc6 → dsh}/remote-adapter.d.ts +2 -2
  15. package/lib/client/dsh/remote-adapter.js +22 -0
  16. package/lib/client/dsh/sessions-adapter.d.ts +56 -0
  17. package/lib/client/dsh/sessions-adapter.js +185 -0
  18. package/lib/client/index.d.ts +2 -2
  19. package/lib/client/panel/SelectionQuote.js +8 -1
  20. package/lib/client/panel/SideChatErrorState.js +1 -1
  21. package/lib/client/panel/SideChatModelSelect.js +37 -18
  22. package/lib/client/panel/SideChatPanel.js +12 -5
  23. package/lib/client/panel/messages.d.ts +1 -1
  24. package/lib/client/panel/messages.js +8 -8
  25. package/lib/client/parent-composer/ParentConversationAnnotations.d.ts +3 -2
  26. package/lib/client/parent-composer/ParentConversationAnnotations.js +50 -30
  27. package/lib/client/parent-composer/add-to-conversation.d.ts +14 -28
  28. package/lib/client/parent-composer/add-to-conversation.js +131 -545
  29. package/lib/client/parent-composer/annotation-persistence.d.ts +8 -8
  30. package/lib/client/parent-composer/annotation-persistence.js +106 -102
  31. package/lib/client/parent-composer/composer-reference.d.ts +17 -55
  32. package/lib/client/parent-composer/composer-reference.js +20 -72
  33. package/lib/client/parent-composer/referenced-conversation.d.ts +5 -7
  34. package/lib/client/parent-composer/referenced-conversation.js +21 -73
  35. package/lib/client/selection/ConversationAnnotationMarkers.js +5 -1
  36. package/lib/client/selection/SelectionActions.js +2 -1
  37. package/lib/client/selection/conversation-dom.d.ts +5 -0
  38. package/lib/client/selection/conversation-dom.js +24 -0
  39. package/lib/client/selection/selection-controller.d.ts +1 -1
  40. package/lib/client/selection/selection-controller.js +7 -3
  41. package/lib/client/selection/selection-normalizer.js +1 -1
  42. package/lib/client/side-chat-controller.d.ts +28 -41
  43. package/lib/client/side-chat-controller.js +245 -337
  44. package/lib/client.js +81 -52
  45. package/lib/host/index.d.ts +1 -1
  46. package/lib/host/index.js +1 -1
  47. package/lib/host/read-only-chat-service.d.ts +30 -0
  48. package/lib/host/read-only-chat-service.js +314 -0
  49. package/lib/index.d.ts +16 -8
  50. package/lib/index.js +34 -13
  51. package/lib/remote.d.ts +23 -14
  52. package/lib/remote.js +2 -5
  53. package/lib/shared/constants.d.ts +0 -13
  54. package/lib/shared/constants.js +0 -13
  55. package/lib/shared/contracts.d.ts +52 -19
  56. package/lib/shared/contracts.js +2 -3
  57. package/lib/shared/error-codes.d.ts +1 -1
  58. package/lib/shared/error-codes.js +2 -2
  59. package/lib/shared/{archived-wire.d.ts → side-chat-wire.d.ts} +109 -20
  60. package/lib/shared/side-chat-wire.js +34 -0
  61. package/lib/typert.d.ts +3 -68
  62. package/lib/typert.js +60 -4
  63. package/package.json +76 -56
  64. package/lib/client/rc6/ArchivedConversation.d.ts +0 -14
  65. package/lib/client/rc6/ArchivedConversation.js +0 -231
  66. package/lib/client/rc6/Rc6SideChatOverlay.d.ts +0 -7
  67. package/lib/client/rc6/SideChatTool.d.ts +0 -22
  68. package/lib/client/rc6/SideChatTool.js +0 -404
  69. package/lib/client/rc6/primitive-labels.d.ts +0 -7
  70. package/lib/client/rc6/primitive-labels.js +0 -60
  71. package/lib/client/rc6/remote-adapter.js +0 -34
  72. package/lib/client/rc6/runtime-compat.d.ts +0 -120
  73. package/lib/client/rc6/runtime-compat.js +0 -187
  74. package/lib/client/rc6/sessions-adapter.d.ts +0 -64
  75. package/lib/client/rc6/sessions-adapter.js +0 -387
  76. package/lib/host/archived-fork-service.d.ts +0 -28
  77. package/lib/host/archived-fork-service.js +0 -309
  78. package/lib/shared/archived-wire.js +0 -47
  79. /package/lib/client/{rc6 → dsh}/context.js +0 -0
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import { restoreDomConversationSelection } from './selection-controller.js';
4
+ import { conversationChatRoot } from './conversation-dom.js';
4
5
  const ANNOTATION_HIGHLIGHT = 'dsh-side-chat-annotations';
5
6
  const ACTIVE_ANNOTATION_HIGHLIGHT = 'dsh-side-chat-active-annotation';
6
7
  function clamp(value, minimum, maximum) {
@@ -144,7 +145,8 @@ export function ConversationAnnotationMarkers({ annotations, activeAnnotationInd
144
145
  let resizeObserver;
145
146
  const refresh = () => {
146
147
  animationFrame = undefined;
147
- const conversationRoot = document.querySelector('[data-chat-flow]');
148
+ const sessionId = annotations[0]?.selection.parentSessionId;
149
+ const conversationRoot = sessionId === undefined ? null : conversationChatRoot(sessionId) ?? null;
148
150
  if (conversationRoot !== observedConversationRoot) {
149
151
  resizeObserver?.disconnect();
150
152
  observedConversationRoot = conversationRoot;
@@ -157,6 +159,8 @@ export function ConversationAnnotationMarkers({ annotations, activeAnnotationInd
157
159
  const activeRanges = [];
158
160
  if (conversationRoot !== null) {
159
161
  for (const annotation of annotations) {
162
+ if (annotation.selection.parentSessionId !== sessionId)
163
+ continue;
160
164
  const restored = restoreDomConversationSelection({
161
165
  selection: annotation.selection,
162
166
  conversationRoot,
@@ -148,7 +148,8 @@ export function SelectionActions({ selection, touchInteraction = false, askDisab
148
148
  closeEditor();
149
149
  return;
150
150
  }
151
- if (event.key === 'Enter' && !event.shiftKey) {
151
+ if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing
152
+ && event.keyCode !== 229) {
152
153
  event.preventDefault();
153
154
  saveAnnotation();
154
155
  }
@@ -0,0 +1,5 @@
1
+ import type { SessionId } from '../../shared/contracts.js';
2
+ /** Resolve the exact Session, never the first chat/composer in a multi-panel UI. */
3
+ export declare function conversationElement(sessionId: SessionId): HTMLElement | undefined;
4
+ export declare function conversationChatRoot(sessionId: SessionId): HTMLElement | undefined;
5
+ export declare function focusConversationComposer(sessionId: SessionId): void;
@@ -0,0 +1,24 @@
1
+ /** Resolve the exact Session, never the first chat/composer in a multi-panel UI. */
2
+ export function conversationElement(sessionId) {
3
+ return [...document.querySelectorAll('[data-conversation-session]')]
4
+ .find(element => element.dataset['conversationSession'] === sessionId
5
+ && element.closest('[data-side-chat-panel], [hidden], [aria-hidden="true"]') === null);
6
+ }
7
+ export function conversationChatRoot(sessionId) {
8
+ return conversationElement(sessionId)?.querySelector('[data-chat-flow]') ?? undefined;
9
+ }
10
+ export function focusConversationComposer(sessionId) {
11
+ // Workspace navigation schedules React rendering; focus after that commit,
12
+ // and never fall back to a different Session's composer.
13
+ window.requestAnimationFrame(() => {
14
+ const input = conversationElement(sessionId)?.querySelector([
15
+ '[data-composer-seat] textarea', '[data-composer-seat] [role="textbox"]',
16
+ '[data-composer-seat] [contenteditable="true"]',
17
+ ].join(', '));
18
+ if (input === undefined || input === null)
19
+ return;
20
+ input.focus();
21
+ if (input instanceof HTMLTextAreaElement)
22
+ input.setSelectionRange(input.value.length, input.value.length);
23
+ });
24
+ }
@@ -20,7 +20,7 @@ export interface RestoredConversationSelection {
20
20
  readonly browserRange: Range;
21
21
  }
22
22
  /**
23
- * Locked-commit fallback capture. It intentionally fails closed unless every
23
+ * Capture against DSH's public Chat anchors. Fail closed unless every
24
24
  * selected text node belongs to one public Chat anchor.
25
25
  */
26
26
  export declare function captureDomConversationSelection(input: {
@@ -11,6 +11,10 @@ function excluded(node, root) {
11
11
  '[data-selection-exclude]',
12
12
  '[data-side-chat-panel]',
13
13
  '[aria-hidden="true"]',
14
+ '[hidden]',
15
+ '[inert]',
16
+ 'script',
17
+ 'style',
14
18
  'button',
15
19
  'textarea',
16
20
  'input',
@@ -23,7 +27,7 @@ function acceptedTextNodes(anchor, root) {
23
27
  let current = walker.nextNode();
24
28
  while (current !== null) {
25
29
  const text = current;
26
- if (!excluded(text, root))
30
+ if (!excluded(text, root) && anchorOf(text) === anchor)
27
31
  nodes.push(text);
28
32
  current = walker.nextNode();
29
33
  }
@@ -50,7 +54,7 @@ function selectedSlice(range, node) {
50
54
  return { start, end, text: node.data.slice(start, end) };
51
55
  }
52
56
  /**
53
- * Locked-commit fallback capture. It intentionally fails closed unless every
57
+ * Capture against DSH's public Chat anchors. Fail closed unless every
54
58
  * selected text node belongs to one public Chat anchor.
55
59
  */
56
60
  export async function captureDomConversationSelection(input) {
@@ -65,7 +69,7 @@ export async function captureDomConversationSelection(input) {
65
69
  const startAnchor = anchorOf(range.startContainer);
66
70
  const endAnchor = anchorOf(range.endContainer);
67
71
  if (startAnchor === null || endAnchor === null || startAnchor !== endAnchor) {
68
- throw new SelectionValidationError('selection_crosses_unsupported_nodes', 'This compatibility adapter supports a selection inside one message only.');
72
+ throw new SelectionValidationError('selection_crosses_unsupported_nodes', 'Select a passage inside one message.');
69
73
  }
70
74
  const descriptor = input.resolver.resolve(startAnchor);
71
75
  if (descriptor === undefined) {
@@ -27,7 +27,7 @@ export function finalizeConversationSelection(input) {
27
27
  throw new SelectionValidationError('selection_too_large', 'The selected text is too large.');
28
28
  }
29
29
  if (!fragment.modelVisible || !fragment.settled) {
30
- throw new SelectionValidationError('fork_unavailable', 'Wait for this message to finish before branching.');
30
+ throw new SelectionValidationError('context_unavailable', 'Wait for this message to finish before opening Side Chat.');
31
31
  }
32
32
  if (!Number.isSafeInteger(fragment.seq) || fragment.seq < 0) {
33
33
  throw new SelectionValidationError('selection_stale', 'The selected message is no longer available.');
@@ -1,17 +1,20 @@
1
- import type { ConversationSelection, SessionId, SideChatModelSelection, SideChatPromptPart, SideChatRemote, SideChatState } from '../shared/contracts.js';
2
- import type { HostObservable } from '../shared/observable.js';
3
- import type { SideChatActionResult, SideChatClientSessions, SideChatQuestionAnswer } from './contracts.js';
4
- /** Small controller for the stock rc.6 archived-fork path. */
5
- export declare class SideChatController implements HostObservable<SideChatState> {
1
+ import type { ConversationSelection, SessionId, SideChatModelSelection, SideChatRemote, SideChatState } from '../shared/contracts.js';
2
+ import type { SideChatActionResult, SideChatClientSessions } from './contracts.js';
3
+ /** Owns only plugin UI state and a cancellable model stream, never a child Session. */
4
+ export declare class SideChatController {
6
5
  private readonly remote;
7
6
  private readonly sessions;
8
7
  private readonly observable;
9
- private lease;
10
- private childUnsubscribe;
11
- private opening;
12
- private closing;
13
- private closeRequested;
8
+ private generation;
9
+ private modelGeneration;
10
+ private requestSequence;
14
11
  private disposed;
12
+ private releaseParent;
13
+ private creating;
14
+ private closing;
15
+ private running;
16
+ private cancelling;
17
+ private pendingRequest;
15
18
  constructor(remote: SideChatRemote, sessions: SideChatClientSessions);
16
19
  getSnapshot: () => SideChatState;
17
20
  subscribe: (listener: () => void) => (() => void);
@@ -19,38 +22,22 @@ export declare class SideChatController implements HostObservable<SideChatState>
19
22
  readonly parentSessionId?: SessionId;
20
23
  readonly selection?: ConversationSelection;
21
24
  readonly draft?: string;
22
- }): SideChatActionResult<void>;
23
- setDraft(draft: string): SideChatActionResult<void>;
24
- clearSelection(): SideChatActionResult<void>;
25
- initializeModel(selection: SideChatModelSelection): SideChatActionResult<SideChatModelSelection>;
26
- selectModel(selection: SideChatModelSelection): Promise<SideChatActionResult<SideChatModelSelection>>;
27
- sendFirst(question: string): Promise<SideChatActionResult<void>>;
28
- send(text: string, mode?: 'queue' | 'steer'): Promise<SideChatActionResult<void>>;
29
- sendParts(content: readonly SideChatPromptPart[], mode?: 'queue' | 'steer'): Promise<SideChatActionResult<void>>;
30
- updateQueue(itemId: string, action: {
31
- readonly kind: 'edit';
32
- readonly content: readonly SideChatPromptPart[];
33
- } | {
34
- readonly kind: 'remove';
35
- } | {
36
- readonly kind: 'steer';
37
- }): Promise<SideChatActionResult<void>>;
38
- cancel(): Promise<SideChatActionResult<void>>;
39
- respondApproval(interactionId: string, decision: 'approve' | 'decline'): Promise<SideChatActionResult<void>>;
40
- respondQuestion(interactionId: string, answer: SideChatQuestionAnswer | null): Promise<SideChatActionResult<void>>;
41
- close(): Promise<SideChatActionResult<void>>;
42
- retry(): Promise<SideChatActionResult<unknown>>;
25
+ }): SideChatActionResult;
26
+ setDraft(draft: string): SideChatActionResult;
27
+ clearSelection(): SideChatActionResult;
28
+ initializeModel(model: SideChatModelSelection): SideChatActionResult<SideChatModelSelection>;
29
+ selectModel(model: SideChatModelSelection): Promise<SideChatActionResult<SideChatModelSelection>>;
30
+ sendFirst(question: string): Promise<SideChatActionResult>;
31
+ /** Resolves at admission; the independently consumed stream continues to update the transcript. */
32
+ send(question: string): Promise<SideChatActionResult>;
33
+ cancel(): Promise<SideChatActionResult>;
34
+ retry(): Promise<SideChatActionResult>;
35
+ close(): Promise<SideChatActionResult>;
43
36
  dispose(): Promise<void>;
44
- private createOpenAndPrompt;
45
- private publishCreated;
46
- private closeChild;
47
- private performClose;
48
- private bindingTarget;
49
- private attachLease;
50
- private detachLease;
51
- private updateFromChild;
37
+ private consume;
38
+ private streamFailure;
39
+ private closeCurrent;
52
40
  private fail;
41
+ private disposeStream;
53
42
  private invoke;
54
- private reset;
55
- private publish;
56
43
  }