@ahggg/dsh-side-chat 0.7.1 → 0.7.2

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 (30) hide show
  1. package/lib/client/parent-composer/add-to-conversation.d.ts +15 -53
  2. package/lib/client/parent-composer/add-to-conversation.d.ts.map +1 -1
  3. package/lib/client/parent-composer/add-to-conversation.js +214 -60
  4. package/lib/client/parent-composer/add-to-conversation.js.map +1 -1
  5. package/lib/client/parent-composer/annotation-persistence.d.ts +1 -1
  6. package/lib/client/parent-composer/annotation-persistence.d.ts.map +1 -1
  7. package/lib/client/parent-composer/annotation-persistence.js +43 -40
  8. package/lib/client/parent-composer/annotation-persistence.js.map +1 -1
  9. package/lib/client/parent-composer/composer-reference.d.ts +72 -0
  10. package/lib/client/parent-composer/composer-reference.d.ts.map +1 -0
  11. package/lib/client/parent-composer/composer-reference.js +57 -0
  12. package/lib/client/parent-composer/composer-reference.js.map +1 -0
  13. package/lib/client/parent-composer/referenced-conversation.d.ts +1 -1
  14. package/lib/client/parent-composer/referenced-conversation.d.ts.map +1 -1
  15. package/lib/client/parent-composer/referenced-conversation.js +11 -18
  16. package/lib/client/parent-composer/referenced-conversation.js.map +1 -1
  17. package/lib/client/rc6/Rc6SideChatOverlay.d.ts.map +1 -1
  18. package/lib/client/rc6/Rc6SideChatOverlay.js +12 -1
  19. package/lib/client/rc6/Rc6SideChatOverlay.js.map +1 -1
  20. package/lib/client/rc6/sessions-adapter.d.ts +2 -0
  21. package/lib/client/rc6/sessions-adapter.d.ts.map +1 -1
  22. package/lib/client/rc6/sessions-adapter.js +12 -1
  23. package/lib/client/rc6/sessions-adapter.js.map +1 -1
  24. package/lib/client/selection/SelectionActions.d.ts +5 -1
  25. package/lib/client/selection/SelectionActions.d.ts.map +1 -1
  26. package/lib/client/selection/SelectionActions.js +66 -49
  27. package/lib/client/selection/SelectionActions.js.map +1 -1
  28. package/lib/client.js +331 -124
  29. package/lib/client.js.map +1 -1
  30. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -9,10 +9,57 @@ window.__ModuleLoader__.load({
9
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
10
  //#region \0dsh-side-chat-css:L2hvbWUvcnVubmVyL3dvcmsvZHNoLXNpZGUtY2hhdC9kc2gtc2lkZS1jaGF0L3NyYy9jbGllbnQvcGFuZWwvc2lkZS1jaGF0LmNzcw
11
11
  var _dsh_side_chat_css_L2hvbWUvcnVubmVyL3dvcmsvZHNoLXNpZGUtY2hhdC9kc2gtc2lkZS1jaGF0L3NyYy9jbGllbnQvcGFuZWwvc2lkZS1jaGF0LmNzcw_default = ".dsh-side-chat-overlay,\n.dsh-side-chat-parent-annotation-dock,\n.dsh-side-chat-parent-user-message {\n /* DSH rc.6 themes publish the semantic alias layer. Keep the legacy tokens\n as fallbacks for older hosts instead of falling through to light colors\n while the host has selected its dark palette. */\n --side-chat-border: var(--dsw-alias-border-l2, var(--dsw-border, #d8d8de));\n --side-chat-bg: var(--dsw-alias-bg-layer-2, var(--dsw-surface, #ffffff));\n --side-chat-muted: var(--dsw-alias-label-tertiary, var(--dsw-text-muted, #62636a));\n --side-chat-text: var(--dsw-alias-label-primary, var(--dsw-text, #19191d));\n --side-chat-accent: var(--dsw-alias-state-business-primary, #1473e6);\n --side-chat-accent-fill: var(--dsw-alias-button-info-fill, #4176e6);\n --side-chat-accent-hover: var(--dsw-alias-button-info-hover, #3467d6);\n --side-chat-accent-text: var(--dsw-alias-label-primary-foreground, #ffffff);\n color: var(--side-chat-text);\n}\n\n.dsh-side-chat-overlay {\n pointer-events: none;\n}\n\n.dsh-side-chat-overlay > * {\n pointer-events: auto;\n}\n\n.dsh-side-chat-panel {\n position: fixed;\n z-index: 70;\n right: clamp(12px, 2vw, 20px);\n bottom: clamp(12px, 2vw, 20px);\n display: flex;\n width: min(840px, calc(100vw - 32px));\n height: clamp(460px, 62dvh, 980px);\n max-height: calc(100dvh - 32px);\n min-width: 0;\n min-height: 0;\n box-sizing: border-box;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid var(--side-chat-border);\n border-radius: 28px;\n background: var(--side-chat-bg);\n color: var(--side-chat-text);\n box-shadow: 0 12px 36px rgb(0 0 0 / 11%), 0 2px 8px rgb(0 0 0 / 6%);\n}\n\n.dsh-side-chat-conversation {\n display: flex;\n height: 100%;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n}\n\n.dsh-side-chat-transcript {\n display: flex;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 12px;\n overflow-x: hidden;\n overflow-y: auto;\n padding: 18px 20px 24px;\n scrollbar-color: color-mix(in srgb, var(--side-chat-muted) 34%, transparent) transparent;\n scrollbar-width: thin;\n}\n\n.dsh-side-chat-message {\n max-width: 92%;\n padding: 10px 12px;\n border: 1px solid var(--side-chat-border);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3, var(--dsw-surface-subtle, #f7f7f9));\n}\n\n.dsh-side-chat-message[data-role=\"user\"] {\n align-self: end;\n background: var(--dsw-specific-bubble, var(--dsw-accent-soft, #eef4ff));\n}\n\n.dsh-side-chat-message[data-role=\"assistant\"] {\n align-self: start;\n min-width: 0;\n max-width: 100%;\n border-color: transparent;\n background: transparent;\n overflow-wrap: anywhere;\n}\n.dsh-side-chat-message-role,\n.dsh-side-chat-message-note { display: block; margin-bottom: 5px; color: var(--side-chat-muted); font-size: 12px; }\n.dsh-side-chat-message-text { white-space: pre-wrap; overflow-wrap: anywhere; }\n.dsh-side-chat-message pre,\n.dsh-side-chat-turn-notice pre { overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }\n.dsh-side-chat-message[data-role=\"assistant\"] :where(table) {\n display: block;\n max-width: 100%;\n overflow-x: auto;\n}\n.dsh-side-chat-reasoning {\n display: flex;\n flex-direction: column;\n}\n.dsh-side-chat-reasoning-row {\n position: relative;\n overflow: hidden;\n}\n.dsh-side-chat-reasoning[data-state=\"running\"] .dsh-side-chat-reasoning-row::after {\n position: absolute;\n inset-block: 0;\n left: 0;\n width: 300px;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, var(--dsw-alias-bg-base, #fff) 60%, transparent) 55%,\n transparent 100%\n );\n content: '';\n pointer-events: none;\n animation: dsh-side-chat-reasoning-sweep 2.6s ease-out infinite;\n}\n@keyframes dsh-side-chat-reasoning-sweep {\n 0% { left: -300px; }\n 90%, 100% { left: 100%; }\n}\n.dsh-side-chat-reasoning-leading { flex-shrink: 0; }\n.dsh-side-chat-reasoning-chevron { color: var(--dsw-alias-label-secondary, var(--side-chat-muted)); }\n.dsh-side-chat-reasoning-title { font-weight: 400; }\n.dsh-side-chat-reasoning-separator {\n width: 2px;\n height: 2px;\n flex: none;\n margin: 0 8px;\n border-radius: 1px;\n background: var(--dsw-alias-label-caption, #a1a1aa);\n}\n.dsh-side-chat-reasoning-summary {\n min-width: 0;\n flex: auto;\n overflow: hidden;\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n font-size: 14px;\n line-height: 24px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-side-chat-reasoning-summary[data-follow-end] { text-overflow: clip; }\n.dsh-side-chat-reasoning-body {\n padding: 4px 0 4px 22px;\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n font-size: 14px;\n line-height: 24px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.dsh-side-chat-reasoning-visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n}\n.dsh-side-chat-tool-branch {\n min-width: 0;\n max-width: 100%;\n flex: 0 0 auto;\n}\n.dsh-side-chat-tool {\n display: flex;\n min-width: 0;\n max-width: 100%;\n flex: 0 0 auto;\n flex-direction: column;\n border-radius: 6px;\n}\n.dsh-side-chat-tool-row {\n position: relative;\n overflow: hidden;\n}\n.dsh-side-chat-tool[data-state=\"running\"] .dsh-side-chat-tool-row::after,\n.dsh-side-chat-tool[data-state=\"pending\"] .dsh-side-chat-tool-row::after {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 300px;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, var(--dsw-alias-bg-base, #fff) 60%, transparent) 55%,\n transparent 100%\n );\n content: '';\n pointer-events: none;\n animation: dsh-side-chat-tool-sweep 2.6s ease-out infinite;\n}\n@keyframes dsh-side-chat-tool-sweep {\n 0% { left: -300px; }\n 90%, 100% { left: 100%; }\n}\n.dsh-side-chat-tool-row:focus-visible {\n border-radius: 4px;\n outline: 2px solid color-mix(in srgb, var(--side-chat-accent) 42%, transparent);\n outline-offset: 1px;\n}\n.dsh-side-chat-tool-leading { flex-shrink: 0; }\n.dsh-side-chat-tool-chevron { color: var(--dsw-alias-label-secondary, var(--side-chat-muted)); }\n.dsh-side-chat-tool-title { font-weight: 400; }\n.dsh-side-chat-tool-separator {\n width: 2px;\n height: 2px;\n flex: none;\n margin: 0 8px;\n border-radius: 1px;\n background: var(--dsw-alias-label-caption, #a1a1aa);\n}\n.dsh-side-chat-tool-summary {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n font-size: 14px;\n line-height: 24px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-side-chat-tool-error-summary { color: var(--dsw-alias-state-error-primary, var(--dsw-danger, #b42318)); }\n.dsh-side-chat-tool-body-wrap {\n display: flex;\n min-width: 0;\n flex-direction: column;\n}\n.dsh-side-chat-tool-code,\n.dsh-side-chat-tool-diff,\n.dsh-side-chat-tool-read,\n.dsh-side-chat-tool-search,\n.dsh-side-chat-tool-terminal,\n.dsh-side-chat-tool-web {\n margin: 4px 0 4px 4px;\n}\n.dsh-side-chat-tool-code { --dsl-code-block-content-font: var(--dsw-font-markdown-code-block-small); }\n.dsh-side-chat-tool-terminal {\n --dsl-terminal-font: var(--dsw-font-markdown-code-block-small);\n --dsl-terminal-line-height: 18px;\n --dsl-terminal-output-max-height: 224px;\n border: 1px solid var(--dsw-alias-border-l1, var(--side-chat-border));\n}\n.dsh-side-chat-tool-io-card {\n display: flex;\n flex-direction: column;\n margin: 4px 0 4px 4px;\n border: 1px solid var(--dsw-alias-border-l1, var(--side-chat-border));\n border-radius: 12px;\n background: var(--dsw-alias-markdown-code-block, #f6f6f7);\n font: var(--dsw-font-markdown-code-block-small, 12px/18px ui-monospace, SFMono-Regular, Consolas, monospace);\n}\n.dsh-side-chat-tool-io-section {\n display: grid;\n max-height: 150px;\n grid-template-columns: max-content minmax(0, 1fr);\n column-gap: 14px;\n align-items: baseline;\n overflow-y: auto;\n padding: 12px 16px;\n}\n.dsh-side-chat-tool-io-divider {\n height: 1px;\n flex: none;\n background: var(--dsw-alias-border-l2, var(--side-chat-border));\n}\n.dsh-side-chat-tool-io-label {\n position: sticky;\n top: 0;\n align-self: start;\n color: var(--dsw-alias-label-caption, #8d8d95);\n}\n.dsh-side-chat-tool-io-text {\n min-width: 0;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--dsw-alias-label-secondary, #4b4b52);\n}\n.dsh-side-chat-tool-io-text[data-error] { color: var(--dsw-alias-state-error-primary, var(--dsw-danger, #b42318)); }\n.dsh-side-chat-tool-subcalls {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 4px 0 2px 22px;\n padding-left: 8px;\n border-left: 1px solid var(--dsw-alias-border-l2, var(--side-chat-border));\n}\n.dsh-side-chat-tool-visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n}\n.dsh-side-chat-turn-notice,\n.dsh-side-chat-interaction,\n.dsh-side-chat-queue { padding: 10px; border: 1px solid var(--side-chat-border); border-radius: 10px; }\n.dsh-side-chat-interaction fieldset { margin: 8px 0; border: 1px solid var(--side-chat-border); border-radius: 8px; }\n.dsh-side-chat-question-option { display: flex; gap: 8px; margin: 8px 0; }\n.dsh-side-chat-interaction textarea { width: 100%; box-sizing: border-box; resize: vertical; }\n.dsh-side-chat-interaction-actions,\n.dsh-side-chat-queue > div { display: flex; justify-content: end; gap: 8px; }\n.dsh-side-chat-interaction button,\n.dsh-side-chat-queue button { min-height: 32px; border: 1px solid var(--side-chat-border); border-radius: 7px; background: var(--dsw-alias-interactive-bg-hover-solid, var(--dsw-control, #f7f7f9)); color: inherit; cursor: pointer; }\n.dsh-side-chat-composer {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: 6px;\n align-items: end;\n margin: 0 12px 12px;\n padding: 8px 8px 8px 12px;\n border: 1px solid var(--side-chat-border);\n border-radius: 24px;\n background: var(--side-chat-bg);\n box-shadow: 0 2px 8px rgb(0 0 0 / 5%);\n}\n.dsh-side-chat-composer textarea {\n grid-column: 1 / -1;\n width: 100%;\n height: auto;\n min-height: 24px;\n max-height: 160px;\n box-sizing: border-box;\n resize: none;\n overflow-y: hidden;\n padding: 6px;\n border: 0;\n outline: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n line-height: 1.5;\n}\n.dsh-side-chat-composer-actions,\n.dsh-side-chat-draft-actions {\n display: flex;\n min-width: 0;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n}\n.dsh-side-chat-model-root {\n position: relative;\n min-width: 0;\n}\n.dsh-side-chat-model-trigger {\n display: flex;\n width: auto;\n height: 28px;\n min-height: 28px;\n max-width: 220px;\n align-items: center;\n gap: 4px;\n padding: 0 4px 0 8px;\n border: 0;\n border-radius: 24px;\n outline: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary, var(--side-chat-muted));\n cursor: pointer;\n font: inherit;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n}\n.dsh-side-chat-model-trigger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5));\n}\n.dsh-side-chat-model-trigger:focus-visible {\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3, var(--side-chat-border));\n}\n.dsh-side-chat-model-trigger:disabled {\n color: var(--dsw-alias-label-dimmed, #a1a1aa);\n cursor: default;\n}\n.dsh-side-chat-model-trigger-label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-side-chat-model-trigger-effort {\n flex: none;\n color: var(--dsw-alias-label-caption, #8d8d95);\n}\n.dsh-side-chat-model-chevron {\n flex: none;\n color: var(--dsw-alias-label-caption, #8d8d95);\n transition: transform 120ms ease;\n}\n.dsh-side-chat-model-chevron-open { transform: rotate(180deg); }\n.dsh-side-chat-model-menu {\n position: absolute;\n z-index: 20;\n right: 0;\n bottom: calc(100% + 8px);\n display: flex;\n width: min(240px, calc(100vw - 32px));\n max-height: min(360px, calc(100dvh - 96px));\n flex-direction: column;\n overflow: hidden;\n padding: 4px;\n border: 1px solid var(--dsw-alias-border-inverted, var(--side-chat-border));\n border-radius: 12px;\n background: var(--dsw-specific-menu, var(--side-chat-bg));\n box-shadow: var(--dsw-shadow-lv3, 0 8px 24px rgb(0 0 0 / 16%));\n color: var(--dsw-alias-label-primary, inherit);\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2, #a1a1aa);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2, #71717a);\n}\n.dsh-side-chat-model-status,\n.dsh-side-chat-model-empty {\n padding: 10px;\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n font-size: 13px;\n line-height: 20px;\n}\n.dsh-side-chat-model-error,\n.dsh-side-chat-model-warning {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 4px;\n padding: 7px 8px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover-danger, #fff1f0);\n color: var(--dsw-alias-state-error-primary, var(--dsw-danger, #b42318));\n font-size: 12px;\n line-height: 18px;\n}\n.dsh-side-chat-model-warning {\n background: var(--dsw-alias-bg-module-platform, #fff8e6);\n color: var(--dsw-alias-state-warn-label, #8a5a00);\n}\n.dsh-side-chat-model-retry {\n min-height: 0;\n flex: none;\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n font-weight: 600;\n}\n.dsh-side-chat-model-groups {\n min-height: 0;\n overflow-y: auto;\n}\n.dsh-side-chat-model-group + .dsh-side-chat-model-group { margin-top: 4px; }\n.dsh-side-chat-model-group-title {\n position: sticky;\n z-index: 1;\n top: 0;\n padding: 5px 8px 3px;\n background: var(--dsw-specific-menu, var(--side-chat-bg));\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n font-size: 12px;\n font-weight: 500;\n line-height: 18px;\n}\n.dsh-side-chat-model-option {\n display: flex;\n width: 100%;\n min-height: 38px;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border: 0;\n border-radius: 10px;\n outline: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n text-align: left;\n}\n.dsh-side-chat-model-option:hover:not(:disabled),\n.dsh-side-chat-model-option:focus-visible {\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5));\n}\n.dsh-side-chat-model-selected { background: transparent; }\n.dsh-side-chat-model-option:disabled {\n color: var(--dsw-alias-label-dimmed, #a1a1aa);\n cursor: default;\n}\n.dsh-side-chat-model-option-copy {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-direction: column;\n}\n.dsh-side-chat-model-name {\n overflow: hidden;\n color: inherit;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-side-chat-model-description {\n overflow: hidden;\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n font-size: 12px;\n line-height: 18px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-side-chat-model-check {\n display: grid;\n flex: 0 0 18px;\n place-items: center;\n color: var(--dsw-alias-label-primary, inherit);\n}\n.dsh-side-chat-model-cell {\n display: flex;\n width: 100%;\n height: 40px;\n align-items: center;\n gap: 8px;\n padding: 0 10px;\n border: 0;\n border-radius: 10px;\n background: transparent;\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n}\n.dsh-side-chat-model-cell:hover {\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5));\n}\n.dsh-side-chat-model-cell-label {\n min-width: 0;\n flex: auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-side-chat-model-cell-value {\n min-width: 0;\n flex: 0 auto;\n overflow: hidden;\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-side-chat-model-cell-chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary, var(--side-chat-muted));\n}\n.dsh-side-chat-loading { display: grid; height: 100%; place-items: center; color: var(--side-chat-muted); }\n\n.dsh-side-chat-header {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px 12px;\n align-items: center;\n flex: 0 0 auto;\n min-height: 46px;\n box-sizing: border-box;\n padding: 5px 10px 5px 16px;\n border-bottom: 1px solid var(--side-chat-border);\n background: var(--side-chat-bg);\n}\n\n.dsh-side-chat-heading { display: flex; min-width: 0; flex-direction: column; }\n.dsh-side-chat-heading strong { font-size: 14px; font-weight: 600; }\n.dsh-side-chat-footer { color: var(--side-chat-muted); font-size: 12px; }\n.dsh-side-chat-header button,\n.dsh-side-chat-inline-actions button,\n.dsh-side-chat-error button,\n.dsh-side-chat-selection-actions button {\n min-height: 32px;\n border: 1px solid var(--side-chat-border);\n border-radius: 7px;\n background: var(--dsw-alias-interactive-bg-hover-solid, var(--dsw-control, #f7f7f9));\n color: inherit;\n cursor: pointer;\n}\n.dsh-side-chat-header button:disabled { cursor: not-allowed; opacity: .55; }\n.dsh-side-chat-header .dsh-side-chat-heading {\n align-items: flex-start;\n min-height: 0;\n padding: 4px 0;\n border: 0;\n background: transparent;\n text-align: left;\n}\n.dsh-side-chat-header-actions {\n display: flex;\n min-width: 0;\n align-items: center;\n justify-content: flex-end;\n gap: 4px;\n}\n.dsh-side-chat-header .dsh-side-chat-add-to-conversation {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n gap: 6px;\n padding: 0 9px;\n border: 0;\n border-radius: 999px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, var(--side-chat-muted));\n font: inherit;\n font-size: 13px;\n white-space: nowrap;\n}\n.dsh-side-chat-add-to-conversation:hover:not(:disabled),\n.dsh-side-chat-add-to-conversation:focus-visible {\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5));\n color: var(--side-chat-text);\n}\n.dsh-side-chat-add-to-conversation-icon {\n width: 18px;\n height: 18px;\n flex: none;\n fill: none;\n stroke: currentColor;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 1.5;\n}\n.dsh-side-chat-header .dsh-side-chat-close {\n width: 32px;\n min-height: 32px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: transparent;\n font-size: 20px;\n line-height: 1;\n}\n.dsh-side-chat-close:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5)); }\n\n.dsh-side-chat-quote { position: relative; max-width: 100%; align-self: flex-start; }\n.dsh-side-chat-quote-chip {\n display: inline-flex;\n min-height: 36px;\n align-items: center;\n overflow: hidden;\n padding: 0;\n border: 1px solid var(--side-chat-border);\n border-radius: 999px;\n background: var(--side-chat-bg);\n transition: background-color 120ms ease;\n}\n.dsh-side-chat-quote:hover .dsh-side-chat-quote-chip,\n.dsh-side-chat-quote:focus-within .dsh-side-chat-quote-chip,\n.dsh-side-chat-quote[data-expanded] .dsh-side-chat-quote-chip { background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5)); }\n.dsh-side-chat-quote-chip .dsh-side-chat-quote-trigger {\n display: inline-flex;\n min-height: 34px;\n align-items: center;\n gap: 8px;\n padding: 0 11px;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: inherit;\n}\n.dsh-side-chat-quote-trigger strong { font-size: 13px; font-weight: 500; line-height: 1; }\n.dsh-side-chat-quote-icon {\n width: 16px;\n height: 16px;\n flex: 0 0 auto;\n color: var(--side-chat-muted);\n fill: none;\n stroke: currentColor;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 1.7;\n}\n.dsh-side-chat-quote-chip .dsh-side-chat-quote-remove {\n display: grid;\n width: 0;\n height: 34px;\n min-height: 34px;\n box-sizing: border-box;\n flex: 0 0 auto;\n place-items: center;\n overflow: hidden;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: var(--side-chat-muted);\n cursor: pointer;\n font: inherit;\n font-size: 18px;\n line-height: 1;\n opacity: 0;\n pointer-events: none;\n transition: width 120ms ease, margin-left 120ms ease, opacity 100ms ease, background-color 100ms ease;\n}\n.dsh-side-chat-quote:hover .dsh-side-chat-quote-remove,\n.dsh-side-chat-quote:focus-within .dsh-side-chat-quote-remove {\n width: 30px;\n opacity: 1;\n pointer-events: auto;\n}\n.dsh-side-chat-quote-remove:hover { background: var(--dsw-alias-interactive-bg-hover, rgb(0 0 0 / 6%)); color: inherit; }\n.dsh-side-chat-quote-details {\n position: absolute;\n z-index: 10;\n bottom: calc(100% + 8px);\n left: 0;\n width: min(520px, calc(100vw - 64px));\n max-width: calc(100vw - 48px);\n box-sizing: border-box;\n padding: 14px 16px;\n border: 1px solid var(--side-chat-border);\n border-radius: 16px;\n background: var(--side-chat-bg);\n box-shadow: 0 4px 14px rgb(0 0 0 / 8%);\n font-size: 14px;\n line-height: 1.45;\n opacity: 0;\n pointer-events: none;\n transform: translate(var(--dsh-side-chat-quote-offset-x, 0px), 4px);\n visibility: hidden;\n transition: opacity 100ms ease, transform 120ms ease, visibility 100ms ease;\n}\n.dsh-side-chat-quote-details::before {\n content: '';\n position: absolute;\n right: 0;\n bottom: -8px;\n left: 0;\n height: 8px;\n}\n.dsh-side-chat-quote[data-hovered] .dsh-side-chat-quote-details,\n.dsh-side-chat-quote[data-expanded] .dsh-side-chat-quote-details {\n opacity: 1;\n pointer-events: auto;\n transform: translate(var(--dsh-side-chat-quote-offset-x, 0px), 0);\n visibility: visible;\n}\n.dsh-side-chat-annotated-user-message .dsh-side-chat-quote-details {\n top: calc(100% + 8px);\n right: 0;\n bottom: auto;\n left: auto;\n}\n.dsh-side-chat-annotated-user-message .dsh-side-chat-quote-details::before {\n top: -8px;\n bottom: auto;\n}\n.dsh-side-chat-quote-details-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n.dsh-side-chat-quote-details-header strong { color: var(--side-chat-muted); font-size: 13px; font-weight: 500; }\n.dsh-side-chat-quote-detail + .dsh-side-chat-quote-detail {\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--side-chat-border);\n}\n.dsh-side-chat-quote pre {\n max-height: 180px;\n margin: 4px 0 0;\n overflow: auto;\n white-space: pre-wrap;\n font: inherit;\n}\n.dsh-side-chat-quote-comment {\n margin-top: 9px;\n}\n.dsh-side-chat-quote-comment > strong { color: var(--side-chat-muted); font-size: 13px; font-weight: 500; }\n.dsh-side-chat-quote-comment > pre { margin-top: 4px; }\n.dsh-side-chat-error { margin: 10px 16px 0; padding: 10px 12px; border: 1px solid var(--side-chat-border); border-radius: 14px; background: var(--dsw-alias-interactive-bg-hover-danger, var(--dsw-surface-subtle, #f7f7f9)); }\n.dsh-side-chat-error { border-color: var(--dsw-alias-state-error-primary, var(--dsw-danger, #b42318)); }\n.dsh-side-chat-body { min-height: 0; flex: 1; overflow: hidden; }\n.dsh-side-chat-annotated-user-message {\n display: flex;\n max-width: 92%;\n align-self: end;\n align-items: flex-start;\n flex-direction: column;\n gap: 8px;\n}\n.dsh-side-chat-annotated-user-message .dsh-side-chat-message { max-width: 100%; align-self: stretch; }\n\n/* The DSH occurrence remains the durable serialization carrier, while this\n plugin-owned capsule supplies the visible and interactive projection. */\n[data-composer-seat] [data-decoration=\"chip\"][title=\"__dsh_side_chat_annotations__\"] {\n visibility: hidden;\n}\n.dsh-side-chat-parent-annotation-dock {\n position: relative;\n z-index: 20;\n width: calc(100% - 32px);\n max-width: var(--dsh-composer-card-max-width, 780px);\n height: 0;\n /* The terminal zero-height dock would otherwise add a second gap between\n Todo/Goal/Queue and the input card. Its child still lands 10px inside the\n following card: -gap + top:16px against the card's own 6px gap. */\n margin: calc(0px - var(--dsh-composer-stack-gap, 6px)) auto 0;\n pointer-events: none;\n}\n.dsh-side-chat-parent-annotation-dock > .dsh-side-chat-quote {\n position: absolute;\n top: 16px;\n left: 16px;\n pointer-events: auto;\n}\n.dsh-side-chat-parent-user-message {\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n}\n.dsh-side-chat-parent-user-message > .dsh-side-chat-quote { align-self: flex-end; }\n/* rc.6 caps its inner user stack at 82%; give that percentage the full chat\n column as its containing width instead of the annotation chip's shrink width. */\n.dsh-side-chat-parent-user-message-body { width: 100%; min-width: 0; }\n.dsh-side-chat-parent-user-message > .dsh-side-chat-quote .dsh-side-chat-quote-details {\n right: 0;\n left: auto;\n}\n.dsh-side-chat-draft {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n min-height: 0;\n flex: 0 0 auto;\n gap: 6px;\n align-items: end;\n margin: auto 12px 12px;\n padding: 12px;\n border: 1px solid var(--side-chat-border);\n border-radius: 24px;\n background: var(--side-chat-bg);\n box-shadow: 0 2px 8px rgb(0 0 0 / 5%);\n}\n.dsh-side-chat-draft > .dsh-side-chat-quote { grid-column: 1 / -1; }\n.dsh-side-chat-draft label {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n}\n.dsh-side-chat-draft textarea {\n grid-column: 1 / -1;\n width: 100%;\n height: auto;\n min-height: 24px;\n max-height: 160px;\n box-sizing: border-box;\n resize: none;\n overflow-y: hidden;\n padding: 6px 2px;\n border: 0;\n outline: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n line-height: 1.5;\n}\n.dsh-side-chat-panel .dsh-side-chat-send-button,\n.dsh-side-chat-panel .dsh-side-chat-stop-button {\n display: grid;\n width: 34px;\n height: 34px;\n min-height: 34px;\n flex: none;\n place-items: center;\n padding: 1px 6px;\n border: 0;\n border-radius: 999px;\n background: var(--side-chat-accent-fill);\n color: var(--side-chat-accent-text);\n cursor: pointer;\n line-height: 1;\n transform: translateY(-2px);\n transition: background-color 100ms ease;\n}\n.dsh-side-chat-panel .dsh-side-chat-send-button:hover:not(:disabled),\n.dsh-side-chat-panel .dsh-side-chat-stop-button:hover:not(:disabled) {\n background: var(--side-chat-accent-hover);\n}\n.dsh-side-chat-panel .dsh-side-chat-send-button:disabled,\n.dsh-side-chat-panel .dsh-side-chat-stop-button:disabled { cursor: default; opacity: .4; }\n.dsh-side-chat-footer { display: none; }\n.dsh-side-chat-announcer { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }\n\n.dsh-side-chat-selection-actions {\n position: fixed;\n z-index: 90;\n display: flex;\n width: max-content;\n max-width: calc(100vw - 16px);\n box-sizing: border-box;\n flex-wrap: wrap;\n gap: 0;\n padding: 0;\n border: 1px solid var(--side-chat-border);\n border-radius: 999px;\n background: var(--dsw-alias-button-floating-fill, var(--side-chat-bg));\n box-shadow: 0 4px 14px rgb(0 0 0 / 12%);\n color: var(--side-chat-text);\n overflow: hidden;\n}\n.dsh-side-chat-selection-actions button {\n min-height: 36px;\n padding: 0 14px;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n transition: background-color 120ms ease;\n}\n.dsh-side-chat-selection-actions button:hover:not(:disabled),\n.dsh-side-chat-selection-actions button:focus-visible {\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5));\n}\n.dsh-side-chat-selection-actions button + button {\n border-left: 1px solid var(--side-chat-border);\n border-radius: 0;\n}\n.dsh-side-chat-selection-actions[data-touch] button {\n min-height: 42px;\n padding-inline: 14px;\n touch-action: manipulation;\n white-space: nowrap;\n}\n\n::highlight(dsh-side-chat-annotations) {\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #1473e6) 18%, transparent);\n}\n::highlight(dsh-side-chat-active-annotation) {\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #1473e6) 32%, transparent);\n text-decoration: underline;\n text-decoration-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #1473e6) 70%, transparent);\n text-decoration-thickness: 1px;\n}\n\n.dsh-side-chat-annotation-marker,\n.dsh-side-chat-selection-marker {\n position: fixed;\n z-index: 91;\n display: grid;\n width: 22px;\n height: 22px;\n min-height: 22px;\n box-sizing: border-box;\n place-items: center;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: var(--side-chat-accent-fill);\n box-shadow: 0 2px 6px rgb(0 0 0 / 14%);\n color: var(--side-chat-accent-text);\n font: inherit;\n font-size: 11px;\n font-weight: 600;\n line-height: 1;\n}\n.dsh-side-chat-annotation-marker { cursor: pointer; }\n.dsh-side-chat-selection-marker { pointer-events: none; }\n.dsh-side-chat-annotation-marker[data-large],\n.dsh-side-chat-selection-marker[data-large] { font-size: 9px; }\n.dsh-side-chat-annotation-marker[data-active] {\n background: var(--side-chat-accent-hover);\n}\n.dsh-side-chat-annotation-marker:focus-visible {\n outline: 2px solid color-mix(in srgb, var(--side-chat-accent) 32%, transparent);\n outline-offset: 1px;\n}\n\n.dsh-side-chat-selection-comment {\n position: fixed;\n z-index: 92;\n display: grid;\n max-width: calc(100vw - 16px);\n box-sizing: border-box;\n gap: 10px;\n padding: 12px;\n border: 1px solid var(--side-chat-border);\n border-radius: 20px;\n background: var(--side-chat-bg);\n box-shadow: 0 5px 18px rgb(0 0 0 / 9%);\n color: var(--side-chat-text);\n}\n.dsh-side-chat-selection-comment textarea {\n width: 100%;\n min-height: 48px;\n max-height: 128px;\n box-sizing: border-box;\n resize: none;\n padding: 6px 8px;\n overflow-y: auto;\n border: 0;\n outline: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n line-height: 1.45;\n}\n.dsh-side-chat-selection-comment-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.dsh-side-chat-selection-comment-actions button {\n min-height: 32px;\n padding: 0 14px;\n border: 1px solid var(--side-chat-border);\n border-radius: 999px;\n background: var(--side-chat-bg);\n color: inherit;\n cursor: pointer;\n font: inherit;\n}\n.dsh-side-chat-selection-comment-actions button:hover { background: var(--dsw-alias-interactive-bg-hover, var(--dsw-surface-subtle, #f3f3f5)); }\n.dsh-side-chat-selection-comment-actions .dsh-side-chat-selection-comment-save {\n border-color: transparent;\n background: var(--side-chat-accent-fill);\n color: var(--side-chat-accent-text);\n}\n.dsh-side-chat-selection-comment-actions .dsh-side-chat-selection-comment-save:hover {\n background: var(--side-chat-accent-hover);\n}\n\n@media (max-width: 900px) {\n .dsh-side-chat-panel {\n right: 12px;\n bottom: 12px;\n width: calc(100vw - 24px);\n max-height: calc(100dvh - 24px);\n border-radius: 24px;\n }\n}\n\n@media (max-width: 720px) {\n .dsh-side-chat-panel {\n right: 8px;\n bottom: 8px;\n width: calc(100vw - 16px);\n height: min(78dvh, 760px);\n max-height: calc(100dvh - 16px);\n border-radius: 22px;\n }\n .dsh-side-chat-transcript { padding: 14px 14px 20px; }\n .dsh-side-chat-header { padding-left: 14px; }\n .dsh-side-chat-error { margin-inline: 12px; }\n}\n\n@media (max-height: 620px) {\n .dsh-side-chat-panel {\n bottom: 8px;\n height: calc(100dvh - 16px);\n max-height: calc(100dvh - 16px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dsh-side-chat-reasoning[data-state=\"running\"] .dsh-side-chat-reasoning-row::after { animation: none; }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .dsh-side-chat-panel { animation: dsh-side-chat-enter 160ms ease-out; }\n @keyframes dsh-side-chat-enter { from { transform: translateY(18px) scale(.985); opacity: .7; } }\n}\n\n@media (forced-colors: active) {\n.dsh-side-chat-panel,\n.dsh-side-chat-selection-actions,\n.dsh-side-chat-selection-comment,\n.dsh-side-chat-annotation-marker { border: 1px solid CanvasText; }\n}\n";
12
+ function referenceDisplayText(label) {
13
+ return `@${label}`;
14
+ }
15
+ /**
16
+ * Resolve one occurrence's occupied draft range across both DSH reference
17
+ * representations: current full `@label` text with `length`, and the legacy
18
+ * one-code-unit U+FFFC placeholder without it.
19
+ */
20
+ function occurrenceRange(snapshot, occurrence, expectedLabel) {
21
+ const start = occurrence.offset;
22
+ if (!Number.isSafeInteger(start) || start < 0 || start > snapshot.draft.length) return;
23
+ if (occurrence.length !== void 0 && Number.isSafeInteger(occurrence.length) && occurrence.length > 0 && start + occurrence.length <= snapshot.draft.length) return {
24
+ start,
25
+ end: start + occurrence.length
26
+ };
27
+ if (snapshot.draft.startsWith("", start)) return {
28
+ start,
29
+ end: start + 1
30
+ };
31
+ const display = referenceDisplayText(expectedLabel);
32
+ if (snapshot.draft.startsWith(display, start)) return {
33
+ start,
34
+ end: start + display.length
35
+ };
36
+ }
37
+ function occurrenceMatchesDraft(snapshot, occurrence, expectedLabel) {
38
+ const range = occurrenceRange(snapshot, occurrence, expectedLabel);
39
+ if (range === void 0) return false;
40
+ const text = snapshot.draft.slice(range.start, range.end);
41
+ return text === "" || text === referenceDisplayText(expectedLabel);
42
+ }
43
+ /** Find the exact occurrence minted by one synchronous insertReference call. */
44
+ function newlyInsertedOccurrence(before, after, source, ref) {
45
+ const previousIds = new Set(before.occurrences.map((occurrence) => occurrence.occurrenceId));
46
+ const inserted = after.occurrences.filter((occurrence) => !previousIds.has(occurrence.occurrenceId) && occurrence.source === source && occurrence.ref === ref);
47
+ return inserted.length === 1 ? inserted[0] : void 0;
48
+ }
49
+ /** Remove one occurrence's display range and, optionally, its separating ASCII gap. */
50
+ function draftWithoutOccurrence(snapshot, occurrence, expectedLabel, options = {}) {
51
+ const range = occurrenceRange(snapshot, occurrence, expectedLabel);
52
+ if (range === void 0) return;
53
+ let { start, end } = range;
54
+ if (options.consumeAdjacentSpace === true) {
55
+ if (snapshot.draft[end] === " ") end += 1;
56
+ else if (start > 0 && snapshot.draft[start - 1] === " ") start -= 1;
57
+ }
58
+ return snapshot.draft.slice(0, start) + snapshot.draft.slice(end);
59
+ }
12
60
  //#endregion
13
61
  //#region src/client/parent-composer/add-to-conversation.ts
14
62
  const SELECTION_REFERENCE_SOURCE = "dsh-side-chat-selection";
15
- const OBJECT_REPLACEMENT_CHARACTER = "";
16
63
  const SELECTION_REFERENCE_LABEL = "__dsh_side_chat_annotations__";
17
64
  function escapeXmlText(value) {
18
65
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
@@ -100,9 +147,6 @@ window.__ModuleLoader__.load({
100
147
  function selectionOccurrences(snapshot) {
101
148
  return snapshot.occurrences.filter((occurrence) => occurrence.source === SELECTION_REFERENCE_SOURCE);
102
149
  }
103
- function occurrenceLength$1(occurrence) {
104
- return occurrence.length !== void 0 && Number.isSafeInteger(occurrence.length) && occurrence.length > 0 ? occurrence.length : 1;
105
- }
106
150
  function storedConversationAnnotations(snapshot) {
107
151
  return selectionOccurrences(snapshot).flatMap((occurrence) => {
108
152
  try {
@@ -131,11 +175,19 @@ window.__ModuleLoader__.load({
131
175
  }
132
176
  function draftWithoutSelectionOccurrences(snapshot) {
133
177
  const occurrences = [...selectionOccurrences(snapshot)].sort((a, b) => b.offset - a.offset);
134
- let draft = snapshot.draft;
135
- for (const occurrence of occurrences) draft = draft.slice(0, occurrence.offset) + draft.slice(occurrence.offset + occurrenceLength$1(occurrence));
178
+ let current = snapshot;
179
+ for (const occurrence of occurrences) {
180
+ const draft = draftWithoutOccurrence(current, occurrence, SELECTION_REFERENCE_LABEL);
181
+ if (draft === void 0) continue;
182
+ current = {
183
+ ...current,
184
+ draft
185
+ };
186
+ }
187
+ let draft = current.draft;
136
188
  if (occurrences.some((occurrence) => occurrence.offset === 0)) {
137
189
  if (draft.startsWith("\n\n")) draft = draft.slice(2);
138
- else if (draft.startsWith("\n")) draft = draft.slice(1);
190
+ else if (draft.startsWith("\n") || draft.startsWith(" ")) draft = draft.slice(1);
139
191
  }
140
192
  return draft;
141
193
  }
@@ -161,42 +213,78 @@ window.__ModuleLoader__.load({
161
213
  }
162
214
  }
163
215
  };
164
- function annotationClipboardText(annotations) {
165
- return annotations.map((annotation) => annotation.text).join("\n\n");
166
- }
167
216
  function writeConversationAnnotations(input, before, annotations) {
168
217
  const draft = draftWithoutSelectionOccurrences(before);
169
- if (selectionOccurrences(before).length > 0) input.setDraft(draft);
170
218
  const insertionState = input.state.getSnapshot();
219
+ if (insertionState.draftRev !== before.draftRev || insertionState.draft !== before.draft) return false;
171
220
  const ref = encodeSelectionReference(annotations);
172
221
  if (!input.insertReference({
173
222
  source: SELECTION_REFERENCE_SOURCE,
174
223
  ref,
175
224
  label: "__dsh_side_chat_annotations__",
176
- clipboardText: annotationClipboardText(annotations)
225
+ clipboardText: annotations.map((annotation) => annotation.text).join("\n\n")
177
226
  }, {
178
227
  start: 0,
179
228
  end: 0,
180
229
  draftRev: insertionState.draftRev
181
230
  })) return false;
182
- const afterInsertion = input.state.getSnapshot();
183
- const occurrence = selectionOccurrences(afterInsertion).find((candidate) => candidate.offset === 0 && candidate.ref === ref);
184
- const displayLength = occurrence === void 0 ? 0 : occurrenceLength$1(occurrence);
185
- if (occurrence === void 0 || displayLength > afterInsertion.draft.length) {
186
- input.setDraft(draft);
231
+ const afterInsert = input.state.getSnapshot();
232
+ const occurrence = newlyInsertedOccurrence(insertionState, afterInsert, SELECTION_REFERENCE_SOURCE, ref);
233
+ const rollbackInsertion = () => {
234
+ const current = input.state.getSnapshot();
235
+ const insertedOccurrence = occurrence === void 0 ? void 0 : current.occurrences.find((candidate) => candidate.occurrenceId === occurrence.occurrenceId);
236
+ if (insertedOccurrence === void 0 || insertedOccurrence.offset !== 0) return;
237
+ const insertedRange = occurrenceRange(current, insertedOccurrence, SELECTION_REFERENCE_LABEL);
238
+ if (insertedRange === void 0 || insertedRange.start !== 0) return;
239
+ let tail = current.draft.slice(insertedRange.end);
240
+ if (tail.startsWith(" ") && tail.slice(1) === before.draft) tail = before.draft;
241
+ else if (tail !== before.draft) return;
242
+ input.setDraft(tail);
243
+ };
244
+ if (occurrence === void 0 || occurrence.offset !== 0) {
245
+ rollbackInsertion();
187
246
  return false;
188
247
  }
189
- const display = afterInsertion.draft.slice(0, displayLength);
190
- input.setDraft(`${display}\n\n${draft}`);
191
- const preserved = selectionOccurrences(input.state.getSnapshot()).some((candidate) => candidate.offset === 0 && candidate.ref === ref);
192
- if (!preserved) input.setDraft(draft);
193
- return preserved;
248
+ const range = occurrenceRange(afterInsert, occurrence, SELECTION_REFERENCE_LABEL);
249
+ if (range === void 0 || range.start !== 0) {
250
+ rollbackInsertion();
251
+ return false;
252
+ }
253
+ let rest = afterInsert.draft.slice(range.end);
254
+ if (rest.startsWith(" ") && rest.slice(1) === before.draft) rest = before.draft;
255
+ else if (rest !== before.draft) {
256
+ rollbackInsertion();
257
+ return false;
258
+ }
259
+ const normalizedDraft = `${afterInsert.draft.slice(range.start, range.end)}\n\n${draft}`;
260
+ const current = input.state.getSnapshot();
261
+ const currentOccurrence = current.occurrences.find((candidate) => candidate.occurrenceId === occurrence.occurrenceId);
262
+ if (current.draftRev !== afterInsert.draftRev || current.draft !== afterInsert.draft || currentOccurrence === void 0 || currentOccurrence.source !== SELECTION_REFERENCE_SOURCE || currentOccurrence.ref !== ref || !occurrenceMatchesDraft(current, currentOccurrence, "__dsh_side_chat_annotations__")) {
263
+ rollbackInsertion();
264
+ return false;
265
+ }
266
+ input.setDraft(normalizedDraft);
267
+ const normalized = input.state.getSnapshot();
268
+ const retained = normalized.occurrences.find((candidate) => candidate.occurrenceId === occurrence.occurrenceId);
269
+ return normalized.draft === normalizedDraft && retained !== void 0 && retained.source === SELECTION_REFERENCE_SOURCE && retained.ref === ref && retained.offset === 0 && occurrenceMatchesDraft(normalized, retained, "__dsh_side_chat_annotations__");
194
270
  }
195
271
  /** Add one passage to the parent composer's aggregated annotation occurrence. */
196
272
  function addSelectionToConversation(input, selection, comment) {
197
273
  const before = input.state.getSnapshot();
198
274
  return writeConversationAnnotations(input, before, [...storedConversationAnnotations(before), annotationFromSelection(selection, comment)]);
199
275
  }
276
+ /** Remove one unsent selected-passage annotation, retaining the aggregate when needed. */
277
+ function removeConversationAnnotation(input, annotationIndex) {
278
+ const before = input.state.getSnapshot();
279
+ const annotations = [...storedConversationAnnotations(before)];
280
+ if (!Number.isSafeInteger(annotationIndex) || annotations[annotationIndex] === void 0) return false;
281
+ annotations.splice(annotationIndex, 1);
282
+ if (annotations.length === 0) {
283
+ input.setDraft(draftWithoutSelectionOccurrences(before));
284
+ return selectionOccurrences(input.state.getSnapshot()).length === 0;
285
+ }
286
+ return writeConversationAnnotations(input, before, annotations);
287
+ }
200
288
  /** Replace the optional comment on one unsent selected-passage annotation. */
201
289
  function updateConversationAnnotation(input, annotationIndex, comment) {
202
290
  const before = input.state.getSnapshot();
@@ -211,10 +299,71 @@ window.__ModuleLoader__.load({
211
299
  };
212
300
  return writeConversationAnnotations(input, before, annotations);
213
301
  }
302
+ function exactClipboardProjection(snapshot, annotationOccurrence, annotationClipboardText) {
303
+ let projected = "";
304
+ let cursor = 0;
305
+ const occurrences = [...snapshot.occurrences].sort((left, right) => left.offset - right.offset);
306
+ for (const occurrence of occurrences) {
307
+ const expectedLabel = occurrence.occurrenceId === annotationOccurrence.occurrenceId ? SELECTION_REFERENCE_LABEL : occurrence.label;
308
+ if (occurrence.length === void 0 && !snapshot.draft.startsWith("", occurrence.offset) && expectedLabel === void 0) return;
309
+ const range = occurrenceRange(snapshot, occurrence, expectedLabel ?? "");
310
+ if (range === void 0 || range.start < cursor) return;
311
+ const clipboardText = occurrence.occurrenceId === annotationOccurrence.occurrenceId ? annotationClipboardText : occurrence.clipboardText;
312
+ if (clipboardText === void 0) return;
313
+ projected += snapshot.draft.slice(cursor, range.start) + clipboardText;
314
+ cursor = range.end;
315
+ }
316
+ return projected + snapshot.draft.slice(cursor);
317
+ }
318
+ function annotationSeparator(draftTail) {
319
+ if (draftTail.startsWith("\n\n")) return "\n\n";
320
+ if (draftTail.startsWith("\n")) return "\n";
321
+ if (draftTail.startsWith(" ")) return " ";
322
+ return "";
323
+ }
324
+ function baseDraftFromMirror(mirrorDraft, clipboardText, expectedBaseDraft) {
325
+ const separators = [
326
+ "\n\n",
327
+ "\n",
328
+ " ",
329
+ ""
330
+ ];
331
+ if (expectedBaseDraft !== void 0) return separators.some((separator) => mirrorDraft === clipboardText + separator + expectedBaseDraft) ? expectedBaseDraft : void 0;
332
+ for (const separator of separators.slice(0, -1)) {
333
+ const prefix = clipboardText + separator;
334
+ if (mirrorDraft.startsWith(prefix)) return mirrorDraft.slice(prefix.length);
335
+ }
336
+ return mirrorDraft === clipboardText ? "" : void 0;
337
+ }
338
+ /** Describe both the display draft and DSH's persisted clipboard projection. */
339
+ function conversationAnnotationRecoveryRecord(snapshot) {
340
+ const occurrence = selectionOccurrences(snapshot).find((candidate) => candidate.offset === 0);
341
+ const ref = conversationAnnotationReference(snapshot);
342
+ if (occurrence === void 0 || ref === void 0) return;
343
+ const range = occurrenceRange(snapshot, occurrence, SELECTION_REFERENCE_LABEL);
344
+ if (range === void 0 || range.start !== 0) return;
345
+ let annotations;
346
+ try {
347
+ annotations = decodeStoredSelectionReference(ref);
348
+ } catch {
349
+ return;
350
+ }
351
+ const clipboardText = annotations.map((annotation) => annotation.text).join("\n\n");
352
+ const mirrorDraft = exactClipboardProjection(snapshot, occurrence, clipboardText);
353
+ if (mirrorDraft === void 0) return;
354
+ const mirrorPrefix = clipboardText + annotationSeparator(snapshot.draft.slice(range.end));
355
+ if (!mirrorDraft.startsWith(mirrorPrefix)) return;
356
+ return {
357
+ ref,
358
+ displayDraft: snapshot.draft,
359
+ mirrorDraft,
360
+ baseDraft: mirrorDraft.slice(mirrorPrefix.length)
361
+ };
362
+ }
214
363
  /** Return the valid aggregated reference currently occupying the leading draft slot. */
215
364
  function conversationAnnotationReference(snapshot) {
216
365
  const occurrence = selectionOccurrences(snapshot).find((candidate) => candidate.offset === 0);
217
- if (occurrence === void 0 || occurrenceLength$1(occurrence) > snapshot.draft.length || occurrence.length === void 0 && !snapshot.draft.startsWith(OBJECT_REPLACEMENT_CHARACTER)) return;
366
+ if (occurrence === void 0 || !occurrenceMatchesDraft(snapshot, occurrence, "__dsh_side_chat_annotations__")) return;
218
367
  try {
219
368
  decodeStoredSelectionReference(occurrence.ref);
220
369
  return occurrence.ref;
@@ -222,34 +371,45 @@ window.__ModuleLoader__.load({
222
371
  return;
223
372
  }
224
373
  }
225
- function draftWithoutOrphanedAnnotationPrefix(snapshot, ref) {
374
+ function draftWithoutOrphanedAnnotationPrefix(snapshot) {
226
375
  if (snapshot.occurrences.some((occurrence) => occurrence.offset === 0)) return;
227
- const prefixes = [`${OBJECT_REPLACEMENT_CHARACTER}\n\n`, `@${SELECTION_REFERENCE_LABEL}\n\n`];
228
- if (ref !== void 0) try {
229
- prefixes.push(`${annotationClipboardText(decodeStoredSelectionReference(ref))}\n\n`);
230
- } catch {}
231
- const prefix = prefixes.find((candidate) => snapshot.draft.startsWith(candidate));
232
- return prefix === void 0 ? void 0 : snapshot.draft.slice(prefix.length);
233
- }
234
- /** Remove a recognized plugin-owned prefix restored without its occurrence. */
376
+ const legacyPrefix = `\n\n`;
377
+ if (snapshot.draft.startsWith(legacyPrefix)) return snapshot.draft.slice(legacyPrefix.length);
378
+ const display = referenceDisplayText(SELECTION_REFERENCE_LABEL);
379
+ if (!snapshot.draft.startsWith(display)) return;
380
+ let rest = snapshot.draft.slice(display.length);
381
+ if (rest.startsWith("\n\n")) rest = rest.slice(2);
382
+ else if (rest.startsWith(" ")) rest = rest.slice(1);
383
+ else return;
384
+ return rest;
385
+ }
386
+ /** Remove the plugin-owned prefix when rc.6 restored its draft without occurrences. */
235
387
  function removeOrphanedConversationAnnotationPlaceholder(input) {
236
388
  const draft = draftWithoutOrphanedAnnotationPrefix(input.state.getSnapshot());
237
389
  if (draft === void 0) return false;
238
390
  input.setDraft(draft);
239
391
  return true;
240
392
  }
241
- /** Rehydrate a lost occurrence only over an exact stored plugin projection. */
242
- function restoreConversationAnnotationReference(input, ref) {
393
+ /** Rehydrate a lost occurrence over either its display draft or exact mirror projection. */
394
+ function restoreConversationAnnotationReference(input, ref, expectedMirrorDraft, expectedBaseDraft) {
243
395
  let annotations;
244
396
  try {
245
397
  annotations = decodeStoredSelectionReference(ref);
246
398
  } catch {
247
399
  return false;
248
400
  }
249
- const draft = draftWithoutOrphanedAnnotationPrefix(input.state.getSnapshot(), ref);
401
+ const snapshot = input.state.getSnapshot();
402
+ const orphanDraft = draftWithoutOrphanedAnnotationPrefix(snapshot);
403
+ const clipboardText = annotations.map((annotation) => annotation.text).join("\n\n");
404
+ const mirrorBaseDraft = expectedMirrorDraft === void 0 ? void 0 : baseDraftFromMirror(expectedMirrorDraft, clipboardText, expectedBaseDraft);
405
+ const draft = orphanDraft ?? (expectedMirrorDraft !== void 0 && snapshot.occurrences.length === 0 && snapshot.draft === expectedMirrorDraft && mirrorBaseDraft !== void 0 ? mirrorBaseDraft : void 0);
250
406
  if (draft === void 0) return false;
251
- input.setDraft(draft);
252
- return writeConversationAnnotations(input, input.state.getSnapshot(), annotations);
407
+ if (draft !== snapshot.draft) input.setDraft(draft);
408
+ const beforeWrite = input.state.getSnapshot();
409
+ if (beforeWrite.draft !== draft || beforeWrite.occurrences.length !== 0) return false;
410
+ if (writeConversationAnnotations(input, beforeWrite, annotations)) return true;
411
+ if (input.state.getSnapshot().occurrences.length === 0 && expectedMirrorDraft !== void 0) input.setDraft(expectedMirrorDraft);
412
+ return false;
253
413
  }
254
414
  function unescapeXmlText(value) {
255
415
  return value.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
@@ -588,23 +748,18 @@ window.__ModuleLoader__.load({
588
748
  }
589
749
  });
590
750
  }
591
- function occurrenceLength(occurrence) {
592
- if (occurrence.length !== void 0 && Number.isSafeInteger(occurrence.length) && occurrence.length > 0) return occurrence.length;
593
- try {
594
- return `@${decodeReferencedConversation(occurrence.ref).title}`.length;
595
- } catch {
596
- return 1;
597
- }
598
- }
599
751
  function removeOccurrence(input, occurrenceId) {
600
752
  const snapshot = input.state.getSnapshot();
601
753
  const occurrence = snapshot.occurrences.find((candidate) => candidate.occurrenceId === occurrenceId);
602
754
  if (occurrence === void 0) return;
603
- let start = occurrence.offset;
604
- let end = occurrence.offset + occurrenceLength(occurrence);
605
- if (snapshot.draft[end] === " ") end += 1;
606
- else if (start > 0 && snapshot.draft[start - 1] === " ") start -= 1;
607
- input.setDraft(snapshot.draft.slice(0, start) + snapshot.draft.slice(end));
755
+ let title;
756
+ try {
757
+ title = decodeReferencedConversation(occurrence.ref).title;
758
+ } catch {
759
+ return;
760
+ }
761
+ const draft = draftWithoutOccurrence(snapshot, occurrence, title, { consumeAdjacentSpace: true });
762
+ if (draft !== void 0) input.setDraft(draft);
608
763
  }
609
764
  /** Insert or refresh one Side Chat label without duplicating an older snapshot. */
610
765
  function addReferencedSideChatToConversation(input, reference) {
@@ -6583,86 +6738,102 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6583
6738
  const edge = 8;
6584
6739
  const width = Math.max(0, size.width);
6585
6740
  const height = Math.max(0, size.height);
6586
- const left = clamp(rect.x + rect.width / 2 - width / 2, edge, viewport.width - width - edge);
6741
+ const viewportLeft = viewport.offsetLeft ?? 0;
6742
+ const viewportTop = viewport.offsetTop ?? 0;
6743
+ const viewportRight = viewportLeft + viewport.width;
6744
+ const viewportBottom = viewportTop + viewport.height;
6745
+ const left = clamp(rect.x + rect.width / 2 - width / 2, viewportLeft + edge, viewportRight - width - edge);
6587
6746
  const belowGap = touch ? 12 : 8;
6588
6747
  const aboveGap = touch ? 64 : 8;
6589
6748
  const below = rect.y + rect.height + belowGap;
6590
6749
  const above = rect.y - height - aboveGap;
6591
- const belowFits = below + height <= viewport.height - edge;
6592
- const aboveFits = above >= edge;
6750
+ const belowFits = below + height <= viewportBottom - edge;
6751
+ const aboveFits = above >= viewportTop + edge;
6593
6752
  let top;
6594
- if (touch && belowFits) top = below;
6753
+ if (belowFits) top = below;
6595
6754
  else if (aboveFits) top = above;
6596
- else if (belowFits) top = below;
6597
- else top = rect.y - aboveGap - edge >= viewport.height - edge - rect.y - rect.height - belowGap ? above : below;
6755
+ else top = rect.y - aboveGap - viewportTop - edge >= viewportBottom - edge - rect.y - rect.height - belowGap ? above : below;
6598
6756
  return {
6599
6757
  left,
6600
- top: clamp(top, edge, viewport.height - height - edge)
6758
+ top: clamp(top, viewportTop + edge, viewportBottom - height - edge)
6601
6759
  };
6602
6760
  }
6603
- function SelectionActions({ selection, touchInteraction = false, askDisabledReason, annotationNumber = 1, annotationEditor, onAddToChat, onMoreDetails, onAskInSideChat, onAnnotationEditorChange, onDismiss }) {
6761
+ function SelectionActions({ selection, touchInteraction = false, askDisabledReason, annotationNumber = 1, annotationEditor, onAddToChat, onMoreDetails, onAskInSideChat, onAnnotationEditorChange, onRemoveAnnotation, onDismiss }) {
6604
6762
  const [editingAnnotation, setEditingAnnotation] = (0, react.useState)(annotationEditor !== void 0);
6605
6763
  const [comment, setComment] = (0, react.useState)(annotationEditor?.initialComment ?? "");
6606
- const [toolbarGeometry, setToolbarGeometry] = (0, react.useState)(() => ({
6764
+ const [toolbarSize, setToolbarSize] = (0, react.useState)(() => ({
6607
6765
  width: 0,
6608
- height: 0,
6609
- viewportWidth: selection.rect.viewportWidth,
6610
- viewportHeight: selection.rect.viewportHeight
6766
+ height: 0
6767
+ }));
6768
+ const [viewport, setViewport] = (0, react.useState)(() => ({
6769
+ width: selection.rect.viewportWidth,
6770
+ height: selection.rect.viewportHeight,
6771
+ offsetLeft: 0,
6772
+ offsetTop: 0
6611
6773
  }));
6612
6774
  const commentRef = (0, react.useRef)(null);
6613
6775
  const toolbarRef = (0, react.useRef)(null);
6614
6776
  const touchActivationRef = (0, react.useRef)(null);
6615
- const toolbarPosition = calculateSelectionActionsPosition(selection.rect, toolbarGeometry, touchInteraction, {
6616
- width: toolbarGeometry.viewportWidth,
6617
- height: toolbarGeometry.viewportHeight
6618
- });
6777
+ const toolbarPosition = calculateSelectionActionsPosition(selection.rect, toolbarSize, touchInteraction, viewport);
6619
6778
  const style = {
6620
6779
  left: toolbarPosition.left,
6621
6780
  top: toolbarPosition.top
6622
6781
  };
6623
- const editorWidth = Math.min(420, selection.rect.viewportWidth - 16);
6624
- const editorAbove = selection.rect.y - 118;
6782
+ const viewportLeft = viewport.offsetLeft;
6783
+ const viewportTop = viewport.offsetTop;
6784
+ const viewportRight = viewportLeft + viewport.width;
6785
+ const viewportBottom = viewportTop + viewport.height;
6786
+ const editorEdge = 8;
6787
+ const editorHeight = 118;
6788
+ const editorWidth = Math.max(0, Math.min(420, viewport.width - 16));
6789
+ const editorAbove = selection.rect.y - editorHeight;
6790
+ const editorBelow = selection.rect.y + selection.rect.height + 12;
6625
6791
  const editorStyle = {
6626
- left: clamp(selection.rect.x + selection.rect.width + 28, 8, selection.rect.viewportWidth - editorWidth - 8),
6627
- top: editorAbove >= 8 ? editorAbove : clamp(selection.rect.y + selection.rect.height + 12, 8, selection.rect.viewportHeight - 126),
6792
+ left: clamp(selection.rect.x + selection.rect.width + 28, viewportLeft + editorEdge, viewportRight - editorWidth - editorEdge),
6793
+ top: clamp(editorAbove >= viewportTop + editorEdge ? editorAbove : editorBelow, viewportTop + editorEdge, viewportBottom - editorHeight - editorEdge),
6628
6794
  width: editorWidth
6629
6795
  };
6796
+ const markerEdge = 4;
6797
+ const markerSize = 22;
6630
6798
  const markerStyle = {
6631
- left: clamp(selection.rect.x + selection.rect.width + 3, 4, selection.rect.viewportWidth - 26),
6632
- top: clamp(selection.rect.y - 12, 4, selection.rect.viewportHeight - 26)
6799
+ left: clamp(selection.rect.x + selection.rect.width + 3, viewportLeft + markerEdge, viewportRight - markerSize - markerEdge),
6800
+ top: clamp(selection.rect.y - 12, viewportTop + markerEdge, viewportBottom - markerSize - markerEdge)
6633
6801
  };
6634
6802
  const markerNumber = annotationNumber > 99 ? "99+" : String(annotationNumber);
6635
6803
  const keepSelection = (event) => {
6636
6804
  event.preventDefault();
6637
6805
  };
6638
6806
  (0, react.useLayoutEffect)(() => {
6639
- if (editingAnnotation) return;
6640
- const toolbar = toolbarRef.current;
6641
- if (toolbar === null) return;
6807
+ const visualViewport = window.visualViewport;
6642
6808
  const measure = () => {
6809
+ const nextViewport = {
6810
+ width: visualViewport?.width ?? window.innerWidth ?? selection.rect.viewportWidth,
6811
+ height: visualViewport?.height ?? window.innerHeight ?? selection.rect.viewportHeight,
6812
+ offsetLeft: visualViewport?.offsetLeft ?? 0,
6813
+ offsetTop: visualViewport?.offsetTop ?? 0
6814
+ };
6815
+ setViewport((current) => current.width === nextViewport.width && current.height === nextViewport.height && current.offsetLeft === nextViewport.offsetLeft && current.offsetTop === nextViewport.offsetTop ? current : nextViewport);
6816
+ const toolbar = toolbarRef.current;
6817
+ if (toolbar === null) return;
6643
6818
  const bounds = toolbar.getBoundingClientRect();
6644
- const visualViewport = window.visualViewport;
6645
- const viewportWidth = visualViewport?.width ?? window.innerWidth ?? selection.rect.viewportWidth;
6646
- const viewportHeight = visualViewport?.height ?? window.innerHeight ?? selection.rect.viewportHeight;
6647
- const next = {
6819
+ const nextSize = {
6648
6820
  width: bounds.width,
6649
- height: bounds.height,
6650
- viewportWidth,
6651
- viewportHeight
6821
+ height: bounds.height
6652
6822
  };
6653
- setToolbarGeometry((current) => current.width === next.width && current.height === next.height && current.viewportWidth === next.viewportWidth && current.viewportHeight === next.viewportHeight ? current : next);
6823
+ setToolbarSize((current) => current.width === nextSize.width && current.height === nextSize.height ? current : nextSize);
6654
6824
  };
6655
6825
  measure();
6826
+ const toolbar = toolbarRef.current;
6656
6827
  const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(measure);
6657
- observer?.observe(toolbar);
6828
+ if (toolbar !== null) observer?.observe(toolbar);
6658
6829
  window.addEventListener("resize", measure);
6659
- window.visualViewport?.addEventListener("resize", measure);
6660
- window.visualViewport?.addEventListener("scroll", measure);
6830
+ visualViewport?.addEventListener("resize", measure);
6831
+ visualViewport?.addEventListener("scroll", measure);
6661
6832
  return () => {
6662
6833
  observer?.disconnect();
6663
6834
  window.removeEventListener("resize", measure);
6664
- window.visualViewport?.removeEventListener("resize", measure);
6665
- window.visualViewport?.removeEventListener("scroll", measure);
6835
+ visualViewport?.removeEventListener("resize", measure);
6836
+ visualViewport?.removeEventListener("scroll", measure);
6666
6837
  };
6667
6838
  }, [
6668
6839
  editingAnnotation,
@@ -6676,7 +6847,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6676
6847
  setEditingAnnotation(false);
6677
6848
  setComment("");
6678
6849
  onAnnotationEditorChange?.(false);
6679
- if (annotationEditor !== void 0) onDismiss();
6850
+ onDismiss();
6680
6851
  };
6681
6852
  const saveAnnotation = () => {
6682
6853
  const trimmed = comment.trim();
@@ -6761,15 +6932,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6761
6932
  onKeyDown: annotationKeyDown
6762
6933
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6763
6934
  className: "dsh-side-chat-selection-comment-actions",
6764
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6765
- type: "button",
6766
- onClick: closeEditor,
6767
- children: "Cancel"
6768
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6769
- type: "submit",
6770
- className: "dsh-side-chat-selection-comment-save",
6771
- children: "Save"
6772
- })]
6935
+ children: [
6936
+ annotationEditor !== void 0 && onRemoveAnnotation !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6937
+ type: "button",
6938
+ onClick: onRemoveAnnotation,
6939
+ children: "Remove"
6940
+ }),
6941
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6942
+ type: "button",
6943
+ onClick: closeEditor,
6944
+ children: "Cancel"
6945
+ }),
6946
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6947
+ type: "submit",
6948
+ className: "dsh-side-chat-selection-comment-save",
6949
+ children: "Save"
6950
+ })
6951
+ ]
6773
6952
  })]
6774
6953
  })] });
6775
6954
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -7833,28 +8012,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7833
8012
  function storageKey(sessionId) {
7834
8013
  return `${STORAGE_PREFIX}${encodeURIComponent(sessionId)}`;
7835
8014
  }
7836
- function clipboardProjection(snapshot) {
7837
- let draft = snapshot.draft;
7838
- const occurrences = [...snapshot.occurrences].sort((a, b) => b.offset - a.offset);
7839
- for (const occurrence of occurrences) {
7840
- const length = occurrence.length ?? 1;
7841
- if (!Number.isSafeInteger(occurrence.offset) || occurrence.offset < 0 || !Number.isSafeInteger(length) || length <= 0 || occurrence.offset + length > draft.length || occurrence.clipboardText === void 0) return;
7842
- draft = draft.slice(0, occurrence.offset) + occurrence.clipboardText + draft.slice(occurrence.offset + length);
7843
- }
7844
- return draft;
7845
- }
7846
8015
  function recordOf(snapshot) {
7847
- const ref = conversationAnnotationReference(snapshot);
7848
- if (ref === void 0) return;
7849
- const projection = clipboardProjection(snapshot);
7850
- return {
7851
- version: 1,
7852
- draft: snapshot.draft,
7853
- ...projection === void 0 ? {} : { projection },
7854
- ref
8016
+ const record = conversationAnnotationRecoveryRecord(snapshot);
8017
+ return record === void 0 ? void 0 : {
8018
+ version: 2,
8019
+ ...record
7855
8020
  };
7856
8021
  }
7857
- /** Tab-scoped recovery for drafts persisted without their runtime occurrences. */
8022
+ function displayDraft(record) {
8023
+ return record.version === 1 ? record.draft : record.displayDraft;
8024
+ }
8025
+ function mirrorDraft(record) {
8026
+ return record.version === 1 ? record.projection : record.mirrorDraft;
8027
+ }
8028
+ /** Tab-scoped recovery for drafts persisted as a reference clipboard projection. */
7858
8029
  var ConversationAnnotationPersistence = class {
7859
8030
  storage;
7860
8031
  observedSessions = /* @__PURE__ */ new Set();
@@ -7871,12 +8042,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7871
8042
  return;
7872
8043
  }
7873
8044
  const stored = this.read(key);
7874
- if (stored !== void 0 && (snapshot.draft === stored.draft || snapshot.draft === stored.projection)) {
7875
- this.observedSessions.add(key);
7876
- if (restoreConversationAnnotationReference(input, stored.ref)) return;
7877
- this.remove(key);
7878
- removeOrphanedConversationAnnotationPlaceholder(input);
7879
- return;
8045
+ if (stored !== void 0) {
8046
+ const exactDisplayDraft = snapshot.draft === displayDraft(stored);
8047
+ const storedMirrorDraft = mirrorDraft(stored);
8048
+ const exactMirrorDraft = storedMirrorDraft !== void 0 && snapshot.draft === storedMirrorDraft;
8049
+ if (exactDisplayDraft || exactMirrorDraft) {
8050
+ this.observedSessions.add(key);
8051
+ if (restoreConversationAnnotationReference(input, stored.ref, storedMirrorDraft, stored.version === 2 ? stored.baseDraft : void 0)) return;
8052
+ if (storedMirrorDraft !== void 0 && input.state.getSnapshot().draft === storedMirrorDraft) return;
8053
+ this.remove(key);
8054
+ removeOrphanedConversationAnnotationPlaceholder(input);
8055
+ return;
8056
+ }
7880
8057
  }
7881
8058
  if (this.observedSessions.has(key)) {
7882
8059
  this.remove(key);
@@ -7893,11 +8070,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7893
8070
  const raw = this.storage.getItem(key);
7894
8071
  if (raw === null) return;
7895
8072
  const value = JSON.parse(raw);
7896
- return value.version === 1 && typeof value.draft === "string" && typeof value.ref === "string" && (value.projection === void 0 || typeof value.projection === "string") ? {
8073
+ if (value.version === 1 && typeof value.draft === "string" && typeof value.ref === "string" && (value.projection === void 0 || typeof value.projection === "string")) return {
7897
8074
  version: 1,
7898
8075
  draft: value.draft,
7899
8076
  ...value.projection === void 0 ? {} : { projection: value.projection },
7900
8077
  ref: value.ref
8078
+ };
8079
+ return value.version === 2 && typeof value.displayDraft === "string" && typeof value.mirrorDraft === "string" && typeof value.baseDraft === "string" && typeof value.ref === "string" ? {
8080
+ version: 2,
8081
+ displayDraft: value.displayDraft,
8082
+ mirrorDraft: value.mirrorDraft,
8083
+ baseDraft: value.baseDraft,
8084
+ ref: value.ref
7901
8085
  } : void 0;
7902
8086
  } catch {
7903
8087
  return;
@@ -8169,6 +8353,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8169
8353
  this.annotationPersistence.reconcile(parentSessionId, input);
8170
8354
  return true;
8171
8355
  }
8356
+ /** Remove one existing unsent annotation from the aggregated occurrence. */
8357
+ removeConversationAnnotation(annotationIndex) {
8358
+ const sessionId = this.currentSessionId();
8359
+ const input = this.currentParentInput();
8360
+ if (sessionId === void 0 || input === void 0 || !removeConversationAnnotation(input, annotationIndex)) return false;
8361
+ this.annotationPersistence.reconcile(sessionId, input);
8362
+ return true;
8363
+ }
8172
8364
  /** Update an existing unsent annotation without adding a duplicate passage. */
8173
8365
  updateConversationAnnotation(annotationIndex, comment) {
8174
8366
  const sessionId = this.currentSessionId();
@@ -8659,6 +8851,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8659
8851
  onAnnotationEditorChange: (open) => {
8660
8852
  annotationEditing.current = open;
8661
8853
  },
8854
+ onRemoveAnnotation: () => {
8855
+ try {
8856
+ if (!sessions.removeConversationAnnotation(editingAnnotation.annotationIndex)) sessions.notify({
8857
+ kind: "warning",
8858
+ text: "Could not remove the annotation."
8859
+ });
8860
+ } catch {
8861
+ sessions.notify({
8862
+ kind: "warning",
8863
+ text: "Could not remove the annotation."
8864
+ });
8865
+ }
8866
+ annotationEditing.current = false;
8867
+ setEditingAnnotation(null);
8868
+ },
8662
8869
  onMoreDetails: () => {},
8663
8870
  onAskInSideChat: () => {},
8664
8871
  onDismiss: () => {