@ahggg/dsh-side-chat 0.7.0 → 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 (36) hide show
  1. package/README.md +0 -2
  2. package/README.zh-CN.md +0 -2
  3. package/lib/client/parent-composer/add-to-conversation.d.ts +14 -50
  4. package/lib/client/parent-composer/add-to-conversation.d.ts.map +1 -1
  5. package/lib/client/parent-composer/add-to-conversation.js +217 -22
  6. package/lib/client/parent-composer/add-to-conversation.js.map +1 -1
  7. package/lib/client/parent-composer/annotation-persistence.d.ts +1 -1
  8. package/lib/client/parent-composer/annotation-persistence.d.ts.map +1 -1
  9. package/lib/client/parent-composer/annotation-persistence.js +50 -12
  10. package/lib/client/parent-composer/annotation-persistence.js.map +1 -1
  11. package/lib/client/parent-composer/composer-reference.d.ts +72 -0
  12. package/lib/client/parent-composer/composer-reference.d.ts.map +1 -0
  13. package/lib/client/parent-composer/composer-reference.js +57 -0
  14. package/lib/client/parent-composer/composer-reference.js.map +1 -0
  15. package/lib/client/parent-composer/referenced-conversation.d.ts +1 -1
  16. package/lib/client/parent-composer/referenced-conversation.d.ts.map +1 -1
  17. package/lib/client/parent-composer/referenced-conversation.js +11 -18
  18. package/lib/client/parent-composer/referenced-conversation.js.map +1 -1
  19. package/lib/client/rc6/Rc6SideChatOverlay.d.ts.map +1 -1
  20. package/lib/client/rc6/Rc6SideChatOverlay.js +12 -1
  21. package/lib/client/rc6/Rc6SideChatOverlay.js.map +1 -1
  22. package/lib/client/rc6/sessions-adapter.d.ts +2 -0
  23. package/lib/client/rc6/sessions-adapter.d.ts.map +1 -1
  24. package/lib/client/rc6/sessions-adapter.js +12 -1
  25. package/lib/client/rc6/sessions-adapter.js.map +1 -1
  26. package/lib/client/selection/SelectionActions.d.ts +5 -1
  27. package/lib/client/selection/SelectionActions.d.ts.map +1 -1
  28. package/lib/client/selection/SelectionActions.js +66 -49
  29. package/lib/client/selection/SelectionActions.js.map +1 -1
  30. package/lib/client.js +334 -91
  31. package/lib/client.js.map +1 -1
  32. package/lib/shared/constants.d.ts +1 -1
  33. package/lib/shared/constants.d.ts.map +1 -1
  34. package/lib/shared/constants.js +1 -1
  35. package/lib/shared/constants.js.map +1 -1
  36. package/package.json +36 -29
package/lib/client.js CHANGED
@@ -9,10 +9,58 @@ 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 = "";
63
+ const SELECTION_REFERENCE_LABEL = "__dsh_side_chat_annotations__";
16
64
  function escapeXmlText(value) {
17
65
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
18
66
  }
@@ -127,11 +175,19 @@ window.__ModuleLoader__.load({
127
175
  }
128
176
  function draftWithoutSelectionOccurrences(snapshot) {
129
177
  const occurrences = [...selectionOccurrences(snapshot)].sort((a, b) => b.offset - a.offset);
130
- let draft = snapshot.draft;
131
- for (const occurrence of occurrences) draft = draft.slice(0, occurrence.offset) + draft.slice(occurrence.offset + 1);
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;
132
188
  if (occurrences.some((occurrence) => occurrence.offset === 0)) {
133
189
  if (draft.startsWith("\n\n")) draft = draft.slice(2);
134
- else if (draft.startsWith("\n")) draft = draft.slice(1);
190
+ else if (draft.startsWith("\n") || draft.startsWith(" ")) draft = draft.slice(1);
135
191
  }
136
192
  return draft;
137
193
  }
@@ -159,11 +215,12 @@ window.__ModuleLoader__.load({
159
215
  };
160
216
  function writeConversationAnnotations(input, before, annotations) {
161
217
  const draft = draftWithoutSelectionOccurrences(before);
162
- if (selectionOccurrences(before).length > 0) input.setDraft(draft);
163
218
  const insertionState = input.state.getSnapshot();
219
+ if (insertionState.draftRev !== before.draftRev || insertionState.draft !== before.draft) return false;
220
+ const ref = encodeSelectionReference(annotations);
164
221
  if (!input.insertReference({
165
222
  source: SELECTION_REFERENCE_SOURCE,
166
- ref: encodeSelectionReference(annotations),
223
+ ref,
167
224
  label: "__dsh_side_chat_annotations__",
168
225
  clipboardText: annotations.map((annotation) => annotation.text).join("\n\n")
169
226
  }, {
@@ -171,14 +228,63 @@ window.__ModuleLoader__.load({
171
228
  end: 0,
172
229
  draftRev: insertionState.draftRev
173
230
  })) return false;
174
- input.setDraft(`${OBJECT_REPLACEMENT_CHARACTER}\n\n${draft}`);
175
- return true;
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();
246
+ return false;
247
+ }
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__");
176
270
  }
177
271
  /** Add one passage to the parent composer's aggregated annotation occurrence. */
178
272
  function addSelectionToConversation(input, selection, comment) {
179
273
  const before = input.state.getSnapshot();
180
274
  return writeConversationAnnotations(input, before, [...storedConversationAnnotations(before), annotationFromSelection(selection, comment)]);
181
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
+ }
182
288
  /** Replace the optional comment on one unsent selected-passage annotation. */
183
289
  function updateConversationAnnotation(input, annotationIndex, comment) {
184
290
  const before = input.state.getSnapshot();
@@ -193,10 +299,71 @@ window.__ModuleLoader__.load({
193
299
  };
194
300
  return writeConversationAnnotations(input, before, annotations);
195
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
+ }
196
363
  /** Return the valid aggregated reference currently occupying the leading draft slot. */
197
364
  function conversationAnnotationReference(snapshot) {
198
365
  const occurrence = selectionOccurrences(snapshot).find((candidate) => candidate.offset === 0);
199
- if (occurrence === void 0 || !snapshot.draft.startsWith(OBJECT_REPLACEMENT_CHARACTER)) return;
366
+ if (occurrence === void 0 || !occurrenceMatchesDraft(snapshot, occurrence, "__dsh_side_chat_annotations__")) return;
200
367
  try {
201
368
  decodeStoredSelectionReference(occurrence.ref);
202
369
  return occurrence.ref;
@@ -205,9 +372,16 @@ window.__ModuleLoader__.load({
205
372
  }
206
373
  }
207
374
  function draftWithoutOrphanedAnnotationPrefix(snapshot) {
208
- const prefix = `${OBJECT_REPLACEMENT_CHARACTER}\n\n`;
209
- if (!snapshot.draft.startsWith(prefix) || snapshot.occurrences.some((occurrence) => occurrence.offset === 0)) return;
210
- return snapshot.draft.slice(prefix.length);
375
+ if (snapshot.occurrences.some((occurrence) => occurrence.offset === 0)) return;
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;
211
385
  }
212
386
  /** Remove the plugin-owned prefix when rc.6 restored its draft without occurrences. */
213
387
  function removeOrphanedConversationAnnotationPlaceholder(input) {
@@ -216,18 +390,26 @@ window.__ModuleLoader__.load({
216
390
  input.setDraft(draft);
217
391
  return true;
218
392
  }
219
- /** Rehydrate a lost rc.6 occurrence only over the exact plugin-owned orphan prefix. */
220
- 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) {
221
395
  let annotations;
222
396
  try {
223
397
  annotations = decodeStoredSelectionReference(ref);
224
398
  } catch {
225
399
  return false;
226
400
  }
227
- const draft = draftWithoutOrphanedAnnotationPrefix(input.state.getSnapshot());
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);
228
406
  if (draft === void 0) return false;
229
- input.setDraft(draft);
230
- 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;
231
413
  }
232
414
  function unescapeXmlText(value) {
233
415
  return value.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
@@ -566,23 +748,18 @@ window.__ModuleLoader__.load({
566
748
  }
567
749
  });
568
750
  }
569
- function occurrenceLength(occurrence) {
570
- if (occurrence.length !== void 0 && Number.isSafeInteger(occurrence.length) && occurrence.length > 0) return occurrence.length;
571
- try {
572
- return `@${decodeReferencedConversation(occurrence.ref).title}`.length;
573
- } catch {
574
- return 1;
575
- }
576
- }
577
751
  function removeOccurrence(input, occurrenceId) {
578
752
  const snapshot = input.state.getSnapshot();
579
753
  const occurrence = snapshot.occurrences.find((candidate) => candidate.occurrenceId === occurrenceId);
580
754
  if (occurrence === void 0) return;
581
- let start = occurrence.offset;
582
- let end = occurrence.offset + occurrenceLength(occurrence);
583
- if (snapshot.draft[end] === " ") end += 1;
584
- else if (start > 0 && snapshot.draft[start - 1] === " ") start -= 1;
585
- 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);
586
763
  }
587
764
  /** Insert or refresh one Side Chat label without duplicating an older snapshot. */
588
765
  function addReferencedSideChatToConversation(input, reference) {
@@ -6561,86 +6738,102 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6561
6738
  const edge = 8;
6562
6739
  const width = Math.max(0, size.width);
6563
6740
  const height = Math.max(0, size.height);
6564
- 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);
6565
6746
  const belowGap = touch ? 12 : 8;
6566
6747
  const aboveGap = touch ? 64 : 8;
6567
6748
  const below = rect.y + rect.height + belowGap;
6568
6749
  const above = rect.y - height - aboveGap;
6569
- const belowFits = below + height <= viewport.height - edge;
6570
- const aboveFits = above >= edge;
6750
+ const belowFits = below + height <= viewportBottom - edge;
6751
+ const aboveFits = above >= viewportTop + edge;
6571
6752
  let top;
6572
- if (touch && belowFits) top = below;
6753
+ if (belowFits) top = below;
6573
6754
  else if (aboveFits) top = above;
6574
- else if (belowFits) top = below;
6575
- 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;
6576
6756
  return {
6577
6757
  left,
6578
- top: clamp(top, edge, viewport.height - height - edge)
6758
+ top: clamp(top, viewportTop + edge, viewportBottom - height - edge)
6579
6759
  };
6580
6760
  }
6581
- 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 }) {
6582
6762
  const [editingAnnotation, setEditingAnnotation] = (0, react.useState)(annotationEditor !== void 0);
6583
6763
  const [comment, setComment] = (0, react.useState)(annotationEditor?.initialComment ?? "");
6584
- const [toolbarGeometry, setToolbarGeometry] = (0, react.useState)(() => ({
6764
+ const [toolbarSize, setToolbarSize] = (0, react.useState)(() => ({
6585
6765
  width: 0,
6586
- height: 0,
6587
- viewportWidth: selection.rect.viewportWidth,
6588
- 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
6589
6773
  }));
6590
6774
  const commentRef = (0, react.useRef)(null);
6591
6775
  const toolbarRef = (0, react.useRef)(null);
6592
6776
  const touchActivationRef = (0, react.useRef)(null);
6593
- const toolbarPosition = calculateSelectionActionsPosition(selection.rect, toolbarGeometry, touchInteraction, {
6594
- width: toolbarGeometry.viewportWidth,
6595
- height: toolbarGeometry.viewportHeight
6596
- });
6777
+ const toolbarPosition = calculateSelectionActionsPosition(selection.rect, toolbarSize, touchInteraction, viewport);
6597
6778
  const style = {
6598
6779
  left: toolbarPosition.left,
6599
6780
  top: toolbarPosition.top
6600
6781
  };
6601
- const editorWidth = Math.min(420, selection.rect.viewportWidth - 16);
6602
- 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;
6603
6791
  const editorStyle = {
6604
- left: clamp(selection.rect.x + selection.rect.width + 28, 8, selection.rect.viewportWidth - editorWidth - 8),
6605
- 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),
6606
6794
  width: editorWidth
6607
6795
  };
6796
+ const markerEdge = 4;
6797
+ const markerSize = 22;
6608
6798
  const markerStyle = {
6609
- left: clamp(selection.rect.x + selection.rect.width + 3, 4, selection.rect.viewportWidth - 26),
6610
- 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)
6611
6801
  };
6612
6802
  const markerNumber = annotationNumber > 99 ? "99+" : String(annotationNumber);
6613
6803
  const keepSelection = (event) => {
6614
6804
  event.preventDefault();
6615
6805
  };
6616
6806
  (0, react.useLayoutEffect)(() => {
6617
- if (editingAnnotation) return;
6618
- const toolbar = toolbarRef.current;
6619
- if (toolbar === null) return;
6807
+ const visualViewport = window.visualViewport;
6620
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;
6621
6818
  const bounds = toolbar.getBoundingClientRect();
6622
- const visualViewport = window.visualViewport;
6623
- const viewportWidth = visualViewport?.width ?? window.innerWidth ?? selection.rect.viewportWidth;
6624
- const viewportHeight = visualViewport?.height ?? window.innerHeight ?? selection.rect.viewportHeight;
6625
- const next = {
6819
+ const nextSize = {
6626
6820
  width: bounds.width,
6627
- height: bounds.height,
6628
- viewportWidth,
6629
- viewportHeight
6821
+ height: bounds.height
6630
6822
  };
6631
- 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);
6632
6824
  };
6633
6825
  measure();
6826
+ const toolbar = toolbarRef.current;
6634
6827
  const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(measure);
6635
- observer?.observe(toolbar);
6828
+ if (toolbar !== null) observer?.observe(toolbar);
6636
6829
  window.addEventListener("resize", measure);
6637
- window.visualViewport?.addEventListener("resize", measure);
6638
- window.visualViewport?.addEventListener("scroll", measure);
6830
+ visualViewport?.addEventListener("resize", measure);
6831
+ visualViewport?.addEventListener("scroll", measure);
6639
6832
  return () => {
6640
6833
  observer?.disconnect();
6641
6834
  window.removeEventListener("resize", measure);
6642
- window.visualViewport?.removeEventListener("resize", measure);
6643
- window.visualViewport?.removeEventListener("scroll", measure);
6835
+ visualViewport?.removeEventListener("resize", measure);
6836
+ visualViewport?.removeEventListener("scroll", measure);
6644
6837
  };
6645
6838
  }, [
6646
6839
  editingAnnotation,
@@ -6654,7 +6847,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6654
6847
  setEditingAnnotation(false);
6655
6848
  setComment("");
6656
6849
  onAnnotationEditorChange?.(false);
6657
- if (annotationEditor !== void 0) onDismiss();
6850
+ onDismiss();
6658
6851
  };
6659
6852
  const saveAnnotation = () => {
6660
6853
  const trimmed = comment.trim();
@@ -6739,15 +6932,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6739
6932
  onKeyDown: annotationKeyDown
6740
6933
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6741
6934
  className: "dsh-side-chat-selection-comment-actions",
6742
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6743
- type: "button",
6744
- onClick: closeEditor,
6745
- children: "Cancel"
6746
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6747
- type: "submit",
6748
- className: "dsh-side-chat-selection-comment-save",
6749
- children: "Save"
6750
- })]
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
+ ]
6751
6952
  })]
6752
6953
  })] });
6753
6954
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -7812,14 +8013,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7812
8013
  return `${STORAGE_PREFIX}${encodeURIComponent(sessionId)}`;
7813
8014
  }
7814
8015
  function recordOf(snapshot) {
7815
- const ref = conversationAnnotationReference(snapshot);
7816
- return ref === void 0 ? void 0 : {
7817
- version: 1,
7818
- draft: snapshot.draft,
7819
- ref
8016
+ const record = conversationAnnotationRecoveryRecord(snapshot);
8017
+ return record === void 0 ? void 0 : {
8018
+ version: 2,
8019
+ ...record
7820
8020
  };
7821
8021
  }
7822
- /** Tab-scoped recovery for rc.6 drafts that persist U+FFFC without 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. */
7823
8029
  var ConversationAnnotationPersistence = class {
7824
8030
  storage;
7825
8031
  observedSessions = /* @__PURE__ */ new Set();
@@ -7836,12 +8042,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7836
8042
  return;
7837
8043
  }
7838
8044
  const stored = this.read(key);
7839
- if (stored !== void 0 && snapshot.draft === stored.draft) {
7840
- this.observedSessions.add(key);
7841
- if (restoreConversationAnnotationReference(input, stored.ref)) return;
7842
- this.remove(key);
7843
- removeOrphanedConversationAnnotationPlaceholder(input);
7844
- 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
+ }
7845
8057
  }
7846
8058
  if (this.observedSessions.has(key)) {
7847
8059
  this.remove(key);
@@ -7858,9 +8070,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7858
8070
  const raw = this.storage.getItem(key);
7859
8071
  if (raw === null) return;
7860
8072
  const value = JSON.parse(raw);
7861
- return value.version === 1 && typeof value.draft === "string" && typeof value.ref === "string" ? {
8073
+ if (value.version === 1 && typeof value.draft === "string" && typeof value.ref === "string" && (value.projection === void 0 || typeof value.projection === "string")) return {
7862
8074
  version: 1,
7863
8075
  draft: value.draft,
8076
+ ...value.projection === void 0 ? {} : { projection: value.projection },
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,
7864
8084
  ref: value.ref
7865
8085
  } : void 0;
7866
8086
  } catch {
@@ -8133,6 +8353,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8133
8353
  this.annotationPersistence.reconcile(parentSessionId, input);
8134
8354
  return true;
8135
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
+ }
8136
8364
  /** Update an existing unsent annotation without adding a duplicate passage. */
8137
8365
  updateConversationAnnotation(annotationIndex, comment) {
8138
8366
  const sessionId = this.currentSessionId();
@@ -8623,6 +8851,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8623
8851
  onAnnotationEditorChange: (open) => {
8624
8852
  annotationEditing.current = open;
8625
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
+ },
8626
8869
  onMoreDetails: () => {},
8627
8870
  onAskInSideChat: () => {},
8628
8871
  onDismiss: () => {