bothouse-dsh 0.1.57 → 0.1.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -2
- package/README.md +3 -2
- package/collab/attachments.mjs +115 -1
- package/collab/cli-member.mjs +69 -11
- package/collab/cloudweb.mjs +4 -1
- package/collab/devices.html +94 -12
- package/collab/dsh-member.mjs +114 -12
- package/collab/instance-lock.mjs +135 -2
- package/collab/local-host.mjs +106 -0
- package/collab/mentions.mjs +626 -12
- package/collab/plugin.mjs +74 -20
- package/collab/roster.mjs +6 -1
- package/collab/store.mjs +9 -1
- package/collab/team-mode-rules.md +1 -1
- package/node_modules/bothouse-dsh-channels/client.js +1213 -169
- package/node_modules/bothouse-dsh-devices/client.js +70 -5
- package/package.json +4 -7
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// dsh-client-source-sha256:
|
|
2
|
-
// dsh-style-sha256:
|
|
1
|
+
// dsh-client-source-sha256: bf3c01b19e0ed8f9d15a02ad7d7e9e9684db4fcbf32f43ba251b220ff57aca56
|
|
2
|
+
// dsh-style-sha256: f6a6de6373a2ead4c3a965e58c158c16626e49a16b91d2d568b35b19fcfdb95a
|
|
3
3
|
window.__ModuleLoader__.load({
|
|
4
4
|
id: "bothouse-dsh-channels",
|
|
5
5
|
factory: (require) => {
|
|
6
6
|
var module = { exports: {} };
|
|
7
7
|
var exports = module.exports;
|
|
8
|
-
(function(){var s=document.createElement('style');s.setAttribute('data-dsh-plugin',"bothouse-dsh-channels");s.textContent=".dshc_page {\n width: 100%;\n height: 100%;\n min-height: 0;\n padding-top: var(--dsh-frame-top-clearance, 0px);\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n grid-template-columns: 220px minmax(0, 1fr) 240px;\n font-size: 14px;\n line-height: 1.5;\n display: grid;\n}\n\n.dshc_notice {\n max-width: 480px;\n color: var(--dsw-alias-label-secondary);\n text-align: center;\n grid-column: 1 / -1;\n place-self: center;\n padding: 24px;\n}\n\n.dshc_rooms, .dshc_members {\n background: var(--dsw-alias-bg-layer-2);\n min-height: 0;\n padding: 0 8px 16px;\n overflow-y: auto;\n}\n\n.dshc_membersHead {\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n flex: none;\n height: 44px;\n margin: 0 0 8px;\n}\n\n.dshc_groupTitle {\n color: var(--dsw-alias-label-tertiary);\n letter-spacing: .02em;\n margin: 12px 8px 6px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_room {\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n display: flex;\n}\n\n.dshc_room:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_room:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_workspace {\n padding-left: 22px;\n font-size: .95em;\n}\n\n.dshc_roomLabel {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_activeCount {\n color: var(--dsw-alias-label-tertiary);\n flex-shrink: 0;\n margin-left: auto;\n font-size: 11px;\n}\n\n.dshc_activeBusy {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_roomActive {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_hash {\n color: var(--dsw-alias-label-tertiary);\n font-size: 16px;\n}\n\n.dshc_chat {\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n display: flex;\n}\n\n.dshc_chatHead {\n -webkit-app-region: drag;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n align-items: center;\n gap: 6px;\n height: 44px;\n padding: 0 16px;\n font-weight: 600;\n display: flex;\n}\n\n.dshc_log {\n flex: 1;\n min-height: 0;\n padding: 8px 0 12px;\n overflow-y: auto;\n}\n\n.dshc_guide {\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n color: var(--dsw-alias-label-secondary);\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n margin: 16px;\n padding: 14px 16px;\n display: flex;\n}\n\n.dshc_empty {\n color: var(--dsw-alias-label-tertiary);\n padding: 24px 16px;\n}\n\n.dshc_message {\n grid-template-columns: 36px minmax(0, 1fr);\n gap: 12px;\n padding: 6px 16px;\n display: grid;\n}\n\n.dshc_system .dshc_text {\n color: var(--dsw-alias-label-tertiary);\n font-style: italic;\n}\n\n.dshc_avatar {\n corner-shape: round;\n color: #fff;\n border-radius: 50%;\n flex: none;\n place-items: center;\n width: 36px;\n height: 36px;\n font-weight: 700;\n display: grid;\n position: relative;\n}\n\n.dshc_avatarImage {\n object-fit: cover;\n border-radius: 50%;\n width: 100%;\n height: 100%;\n position: absolute;\n inset: 0;\n}\n\n.dshc_avatarSmall {\n width: 28px;\n height: 28px;\n font-size: 12px;\n}\n\n.dshc_avatarAnchor {\n align-self: start;\n position: relative;\n}\n\n.dshc_avatarButton {\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 50%;\n padding: 0;\n}\n\n.dshc_avatarMenu {\n z-index: 3;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-base);\n min-width: 140px;\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n flex-direction: column;\n padding: 6px;\n display: flex;\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n}\n\n.dshc_avatarMenuTitle {\n padding: 4px 8px;\n font-weight: 700;\n}\n\n.dshc_avatarMenuItem {\n border-radius: var(--dsw-radius-sm);\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 6px 8px;\n}\n\n.dshc_avatarMenuItem:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_messageBody {\n min-width: 0;\n}\n\n.dshc_messageHead {\n align-items: baseline;\n gap: 6px;\n display: flex;\n}\n\n.dshc_author {\n font-weight: 600;\n}\n\n.dshc_time {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.dshc_tag {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-state-business-primary);\n white-space: nowrap;\n padding: 0 4px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.dshc_edited {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.dshc_ownActions {\n margin-left: auto;\n margin-right: var(--channels-reply-clearance);\n opacity: 0;\n gap: 8px;\n display: inline-flex;\n}\n\n.dshc_message:hover .dshc_ownActions, .dshc_ownActions:focus-within {\n opacity: 1;\n}\n\n@media (hover: none) {\n .dshc_ownActions {\n display: none;\n }\n\n .dshc_message[data-tools-open] .dshc_ownActions {\n opacity: 1;\n display: inline-flex;\n }\n}\n\n.dshc_editBox {\n flex-direction: column;\n gap: 6px;\n margin-top: 4px;\n display: flex;\n}\n\n.dshc_editInput {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-base);\n min-height: 60px;\n color: inherit;\n font: inherit;\n resize: vertical;\n padding: 8px;\n}\n\n.dshc_editBar {\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_editBar .dshc_time {\n margin-right: auto;\n}\n\n.dshc_text {\n white-space: normal;\n overflow-wrap: anywhere;\n}\n\n.dshc_card {\n border-left: 4px solid var(--dsw-alias-state-business-primary);\n border-radius: var(--dsw-radius-sm);\n background: var(--dsw-alias-bg-layer-2);\n margin-top: 4px;\n padding: 6px 10px;\n}\n\n.dshc_card[data-state=\"failed\"] {\n border-left-color: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_card[data-state=\"retrying\"] {\n border-left-color: var(--dsw-alias-state-warning-primary, #faa61a);\n}\n\n.dshc_cardButtons {\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 6px;\n display: flex;\n}\n\n.dshc_cardTitle {\n font-size: 13px;\n font-weight: 600;\n}\n\n.dshc_cardHead {\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_cardViews {\n gap: 4px;\n display: flex;\n}\n\n.dshc_cardViewButton {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n color: inherit;\n cursor: pointer;\n background: none;\n padding: 0 6px;\n font-size: 12px;\n}\n\n.dshc_cardViewButton[aria-pressed=\"true\"] {\n background: var(--dsw-alias-bg-base);\n font-weight: 600;\n}\n\n.dshc_permActions {\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 6px;\n display: flex;\n}\n\n.dshc_permScope {\n color: var(--dsw-alias-text-secondary);\n flex-basis: 100%;\n font-size: 12px;\n}\n\n.dshc_permStatus {\n margin-top: 6px;\n font-size: 13px;\n font-weight: 600;\n}\n\n.dshc_message {\n --channels-reply-clearance: 36px;\n position: relative;\n}\n\n.dshc_quote {\n border-left: 2px solid var(--dsw-alias-label-tertiary);\n min-width: 0;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n text-overflow: ellipsis;\n gap: 6px;\n margin-bottom: 2px;\n padding-left: 8px;\n font-size: 12px;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_quoteDeleted {\n font-style: italic;\n}\n\n.dshc_quoteAuthor {\n flex: none;\n font-weight: 600;\n}\n\n.dshc_replyButton {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n border: 0;\n padding: 0 6px;\n}\n\n.dshc_msgTools {\n z-index: 1;\n border: 1px solid var(--dsw-alias-separator, #7f7f7f40);\n border-radius: var(--dsw-radius-sm, 6px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-primary, #fff));\n opacity: 0;\n pointer-events: none;\n align-items: center;\n gap: 2px;\n padding: 2px;\n display: inline-flex;\n position: absolute;\n top: 2px;\n right: 16px;\n box-shadow: 0 2px 6px #00000014;\n}\n\n.dshc_msgTool {\n border-radius: var(--dsw-radius-sm, 6px);\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 2px 8px;\n font-size: 12px;\n}\n\n.dshc_msgTool:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_msgTool:disabled {\n cursor: default;\n opacity: .6;\n}\n\n.dshc_msgToolNote {\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n padding: 0 6px;\n font-size: 12px;\n}\n\n.dshc_message:hover .dshc_msgTools, .dshc_message:focus-within .dshc_msgTools, .dshc_msgTools:has(.dshc_msgToolNote) {\n opacity: 1;\n pointer-events: auto;\n}\n\n@media (hover: none) {\n .dshc_message {\n -webkit-touch-callout: none;\n }\n\n .dshc_msgTools {\n display: none;\n }\n\n .dshc_message[data-tools-open] {\n background: var(--dsw-alias-interactive-bg-hover);\n }\n\n .dshc_message[data-tools-open] .dshc_msgTools {\n z-index: 3;\n opacity: 1;\n pointer-events: auto;\n display: inline-flex;\n top: -14px;\n right: 12px;\n }\n\n .dshc_message[data-tools-open] .dshc_msgTool {\n width: 40px;\n height: 36px;\n }\n}\n\n.dshc_replyBar {\n border-radius: var(--dsw-radius-sm);\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-2);\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n padding: 4px 8px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_replyBarText {\n white-space: nowrap;\n text-overflow: ellipsis;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n}\n\n.dshc_replyCancel {\n color: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n}\n\n.dshc_working {\n min-height: 20px;\n color: var(--dsw-alias-label-tertiary);\n align-items: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_composer {\n padding: 0 16px 16px;\n position: relative;\n}\n\n.dshc_composerBox {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: var(--dsw-radius-xl);\n background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-layer-2));\n box-shadow: var(--dsw-shadow-lv1-blur);\n flex-direction: column;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_composerBox:focus-within {\n border-color: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_composerToolbar {\n flex: none;\n align-items: center;\n gap: 4px;\n padding: 4px 10px 10px;\n display: flex;\n}\n\n.dshc_composerBox > .dshc_replyBar, .dshc_composerBox > .dshc_attachments {\n margin: 10px 14px 0;\n}\n\n.dshc_mentionButton {\n border-radius: var(--dsw-radius-full, 50%);\n width: 32px;\n height: 32px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n justify-content: center;\n align-items: center;\n padding: 0;\n font-size: 18px;\n display: inline-flex;\n}\n\n.dshc_mentionButton:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_sendButton {\n border-radius: var(--dsw-radius-full, 50%);\n width: 32px;\n height: 32px;\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-label-primary);\n font: inherit;\n cursor: pointer;\n border: 0;\n flex: none;\n justify-content: center;\n align-items: center;\n margin-left: auto;\n padding: 0;\n display: inline-flex;\n}\n\n.dshc_sendButton:disabled {\n opacity: .45;\n cursor: default;\n}\n\n.dshc_sendButton:focus-visible, .dshc_mentionButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_mentionPopup {\n z-index: 2;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-base);\n max-height: 320px;\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n flex-direction: column;\n padding: 6px;\n display: flex;\n position: absolute;\n bottom: calc(100% - 8px);\n left: 16px;\n right: 16px;\n}\n\n.dshc_mentionPopup > [role=\"listbox\"] {\n min-height: 0;\n overflow-y: auto;\n}\n\n.dshc_mentionTitle {\n color: var(--dsw-alias-label-tertiary);\n padding: 4px 8px 6px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_mentionItem {\n border-radius: var(--dsw-radius-sm);\n cursor: pointer;\n align-items: center;\n gap: 10px;\n padding: 6px 8px;\n display: flex;\n}\n\n.dshc_mentionActive {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_mentionName {\n font-weight: 600;\n}\n\n.dshc_mentionMeta {\n color: var(--dsw-alias-label-tertiary);\n margin-left: auto;\n font-size: 12px;\n}\n\n.dshc_mentionHint {\n border-top: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px 2px;\n font-size: 11px;\n}\n\n.dshc_mentionBadge {\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #5865f2) 15%, transparent);\n color: var(--dsw-alias-state-business-primary, #5865f2);\n font-weight: 500;\n line-height: inherit;\n vertical-align: baseline;\n -webkit-box-decoration-break: clone;\n box-decoration-break: clone;\n cursor: pointer;\n border-radius: 3px;\n padding: 0 4px;\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1), color .16s cubic-bezier(.2, 0, 0, 1);\n display: inline-block;\n}\n\n.dshc_mentionBadge:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #5865f2) 28%, transparent);\n}\n\n.dshc_sendError {\n color: var(--dsw-alias-state-error-primary);\n margin-bottom: 6px;\n font-size: 12px;\n}\n\n.dshc_input {\n box-sizing: border-box;\n width: 100%;\n min-height: 52px;\n max-height: 40vh;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n resize: none;\n field-sizing: content;\n background: none;\n border: 0;\n border-radius: 0;\n flex: 1;\n min-width: 0;\n padding: 14px 16px 8px;\n line-height: 1.5;\n}\n\n.dshc_input::placeholder {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n@media (width <= 600px) {\n .dshc_composer {\n padding: 0 8px 8px;\n }\n\n .dshc_composerToolbar {\n padding: 4px 8px 8px;\n }\n\n .dshc_input {\n padding: 12px 14px 8px;\n }\n}\n\n.dshc_input:focus-visible {\n outline: none;\n}\n\n.dshc_member {\n border-radius: var(--dsw-radius-sm);\n min-width: 0;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: 1;\n align-items: center;\n gap: 10px;\n padding: 5px 8px;\n display: flex;\n}\n\n.dshc_member:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_member:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_offline {\n opacity: .5;\n}\n\n.dshc_memberAvatar {\n position: relative;\n}\n\n.dshc_presence {\n border: 2px solid var(--dsw-alias-bg-layer-2);\n corner-shape: round;\n background: #23a55a;\n border-radius: 50%;\n width: 10px;\n height: 10px;\n position: absolute;\n bottom: -2px;\n right: -2px;\n}\n\n.dshc_offline .dshc_presence {\n background: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_memberText {\n flex-direction: column;\n min-width: 0;\n display: flex;\n}\n\n.dshc_memberName {\n align-items: center;\n gap: 6px;\n display: flex;\n}\n\n.dshc_memberStatus {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n@media (width <= 900px) {\n .dshc_page {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .dshc_rooms, .dshc_members {\n display: none;\n }\n}\n\n.dshc_groupHead {\n justify-content: space-between;\n align-items: center;\n padding-right: 4px;\n display: flex;\n}\n\n.dshc_iconButton {\n border-radius: var(--dsw-radius-sm);\n width: 22px;\n height: 22px;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n font-size: 16px;\n line-height: 1;\n}\n\n.dshc_iconButton:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_roomInput, .dshc_fieldInput {\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n font: inherit;\n padding: 6px 8px;\n}\n\n.dshc_roomInput {\n margin: 2px 0 6px;\n}\n\n.dshc_roomInput:focus-visible, .dshc_fieldInput:focus-visible, .dshc_iconButton:focus-visible, .dshc_linkButton:focus-visible, .dshc_addMemberButton:focus-visible, .dshc_primaryButton:focus-visible, .dshc_secondaryButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_addMemberButton {\n box-sizing: border-box;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n margin: 4px 0;\n padding: 7px 8px;\n}\n\n.dshc_addMemberButton:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_settingsDivider {\n border: 0;\n border-top: 1px solid var(--dsw-alias-border-l1);\n width: 100%;\n margin: 8px 0 4px;\n}\n\n.dshc_memberListDivider {\n border: 0;\n border-top: 1px solid var(--dsw-alias-border-l1);\n width: calc(100% - 16px);\n margin: 12px 8px 2px;\n}\n\n.dshc_settingsButton {\n text-align: left;\n border-style: solid;\n border-color: #0000;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_settingsButton svg {\n flex: none;\n}\n\n.dshc_memberRow {\n border-radius: var(--dsw-radius-sm);\n align-items: center;\n display: flex;\n position: relative;\n}\n\n.dshc_memberRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_memberRow .dshc_member:hover {\n background: none;\n}\n\n.dshc_memberModel {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n overflow: hidden;\n}\n\n.dshc_memberActions {\n color: var(--dsw-alias-label-tertiary);\n opacity: 0;\n flex: none;\n gap: 4px;\n padding-right: 6px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_memberRow:hover .dshc_memberActions, .dshc_memberRow:focus-within .dshc_memberActions {\n opacity: 1;\n}\n\n.dshc_linkButton {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 2px 4px;\n font-size: 12px;\n}\n\n.dshc_linkButton:hover {\n color: var(--dsw-alias-label-primary);\n text-decoration: underline;\n}\n\n.dshc_dialogBackdrop {\n z-index: 10;\n background: #00000073;\n place-items: center;\n padding: 16px;\n display: grid;\n position: fixed;\n inset: 0;\n}\n\n.dshc_dialog {\n box-sizing: border-box;\n border-radius: var(--dsw-radius-lg);\n width: min(460px, 100%);\n max-height: calc(100vh - 32px);\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n box-shadow: var(--dsw-elevation-panel, 0 12px 32px #0000004d);\n flex-direction: column;\n gap: 12px;\n padding: 20px;\n display: flex;\n overflow-y: auto;\n}\n\n.dshc_settingsDialog {\n box-sizing: border-box;\n height: min(70vh, 100dvh - 32px);\n max-height: calc(100dvh - 32px);\n overflow: hidden;\n}\n\n.dshc_settingsBody {\n overscroll-behavior: contain;\n flex: auto;\n min-height: 0;\n padding: 2px;\n overflow-y: auto;\n}\n\n.dshc_settingsDialog > :not(.dshc_settingsBody) {\n flex-shrink: 0;\n}\n\n.dshc_settingsFields {\n flex-direction: column;\n gap: 12px;\n display: flex;\n}\n\n.dshc_settingsFeedback {\n overflow-wrap: anywhere;\n max-height: 20%;\n overflow-y: auto;\n}\n\n.dshc_settingsDialog .dshc_dialogActions {\n justify-content: space-between;\n}\n\n@media (width <= 599px) {\n .dshc_settingsDialog {\n height: calc(100dvh - 32px);\n padding: 16px;\n }\n}\n\n.dshc_avatarCropStage {\n background: #000000a6;\n padding: 20px;\n}\n\n.dshc_avatarCropFrame {\n aspect-ratio: 1;\n touch-action: none;\n cursor: grab;\n outline: 2px solid #fff;\n width: min(256px, 100%);\n margin: auto;\n position: relative;\n overflow: hidden;\n}\n\n.dshc_avatarCropFrame:active {\n cursor: grabbing;\n}\n\n.dshc_avatarCropFrame img {\n pointer-events: none;\n user-select: none;\n max-width: none;\n position: absolute;\n}\n\n.dshc_dialogTitle {\n margin: 0;\n font-size: 16px;\n}\n\n.dshc_field {\n color: var(--dsw-alias-label-secondary);\n flex-direction: column;\n gap: 4px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_field textarea {\n resize: vertical;\n}\n\n.dshc_choiceRow {\n flex-wrap: wrap;\n gap: 12px;\n display: flex;\n}\n\n.dshc_choiceColumn {\n flex-direction: column;\n gap: 12px;\n display: flex;\n}\n\n.dshc_choiceColumn .dshc_choice {\n grid-template-columns: max-content minmax(0, 1fr);\n align-items: start;\n display: grid;\n}\n\n.dshc_choice {\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n align-items: center;\n gap: 4px;\n font-size: 13px;\n display: inline-flex;\n}\n\n.dshc_dialogActions {\n justify-content: flex-end;\n gap: 8px;\n display: flex;\n}\n\n.dshc_primaryButton, .dshc_secondaryButton {\n border-radius: var(--dsw-radius-sm);\n font: inherit;\n cursor: pointer;\n padding: 7px 14px;\n}\n\n.dshc_primaryButton {\n color: #fff;\n background: var(--dsw-alias-state-business-primary);\n border: 0;\n}\n\n.dshc_primaryButton:disabled {\n cursor: not-allowed;\n opacity: .5;\n}\n\n.dshc_secondaryButton {\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n background: none;\n}\n\n.dshc_memberFailed .dshc_memberStatus {\n color: var(--dsw-alias-state-error-primary, #ed4245);\n}\n\n.dshc_memberFailed .dshc_presence {\n background: #ed4245;\n}\n\n.dshc_mobileButton, .dshc_drawerBackdrop {\n display: none;\n}\n\n.dshc_panelToggle {\n border-radius: var(--dsw-radius-sm);\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n justify-content: center;\n align-items: center;\n font-size: 16px;\n line-height: 1;\n display: inline-flex;\n}\n\n.dshc_panelToggle:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_panelToggle:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_panelToggleEnd {\n margin-left: auto;\n}\n\n.dshc_capabilityMenu {\n z-index: 4;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n background: var(--dsw-alias-bg-base);\n max-height: 260px;\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n gap: 2px;\n padding: 5px;\n display: grid;\n position: absolute;\n bottom: calc(100% + 4px);\n left: 0;\n right: 0;\n overflow-y: auto;\n}\n\n.dshc_capabilityItem:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_capabilityHelp {\n color: var(--dsw-alias-label-secondary);\n white-space: normal;\n margin: 6px;\n font-size: 12px;\n}\n\n.dshc_capabilityItem:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_capabilityItem {\n text-align: left;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n background: none;\n border: 0;\n padding: 5px 8px;\n font-size: 12px;\n font-weight: 400;\n}\n\n@media (width >= 901px) {\n .dshc_page.dshc_roomsHidden {\n grid-template-columns: minmax(0, 1fr) 240px;\n }\n\n .dshc_page.dshc_membersHidden {\n grid-template-columns: 220px minmax(0, 1fr);\n }\n\n .dshc_page.dshc_roomsHidden.dshc_membersHidden {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .dshc_roomsHidden > .dshc_rooms, .dshc_membersHidden > .dshc_members {\n display: none;\n }\n}\n\n@media (width <= 900px) {\n .dshc_page {\n position: relative;\n overflow: hidden;\n }\n\n .dshc_mobileButton {\n min-width: 40px;\n min-height: 40px;\n color: inherit;\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 6px;\n justify-content: center;\n align-items: center;\n display: inline-flex;\n }\n\n .dshc_mobileButton:last-child {\n margin-left: auto;\n }\n\n .dshc_panelToggle {\n display: none;\n }\n\n .dshc_drawerBackdrop {\n z-index: 4;\n background: #00000073;\n border: 0;\n display: block;\n position: absolute;\n inset: 0;\n }\n\n .dshc_rooms.dshc_drawerOpen, .dshc_members.dshc_drawerOpen {\n z-index: 5;\n box-sizing: border-box;\n width: min(280px, 85%);\n display: block;\n position: absolute;\n top: 0;\n bottom: 0;\n box-shadow: 0 0 24px #0000004d;\n }\n\n .dshc_rooms.dshc_drawerOpen {\n left: 0;\n }\n\n .dshc_members.dshc_drawerOpen {\n right: 0;\n }\n\n .dshc_memberActions {\n opacity: 1;\n }\n\n .dshc_messageHead {\n flex-wrap: wrap;\n }\n}\n\n.dshc_avatarPicker {\n flex-wrap: wrap;\n gap: 8px;\n display: flex;\n}\n\n.dshc_avatarOption {\n cursor: pointer;\n background: none;\n border: 2px solid #0000;\n border-radius: 50%;\n padding: 2px;\n display: grid;\n}\n\n.dshc_avatarOption[aria-checked=\"true\"] {\n border-color: var(--dsw-alias-accent, #5865f2);\n}\n\n.dshc_taskBoardTrigger {\n box-sizing: border-box;\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n height: 34px;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n align-items: center;\n gap: 8px;\n margin: 4px 0;\n padding: 7px 8px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_taskBoardTrigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_taskBoardTrigger:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_taskBoardSummary {\n color: var(--dsw-alias-label-secondary);\n text-align: right;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n overflow: hidden;\n}\n\n.dshc_taskBoardDialog {\n width: min(920px, 100%);\n height: min(70vh, 100dvh - 32px);\n overflow: hidden;\n}\n\n.dshc_taskBoardBody {\n overscroll-behavior: contain;\n flex: auto;\n min-height: 0;\n overflow-y: auto;\n}\n\n.dshc_taskBoardDialog > :not(.dshc_taskBoardBody) {\n flex-shrink: 0;\n}\n\n.dshc_boardTabs {\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n gap: 4px;\n padding-bottom: 8px;\n display: flex;\n overflow-x: auto;\n}\n\n.dshc_boardTabs button {\n border-radius: var(--dsw-radius-sm);\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n padding: 7px 10px;\n font-size: 13px;\n}\n\n.dshc_boardTabs button:hover, .dshc_boardTabs button[aria-selected=\"true\"] {\n color: var(--dsw-alias-state-business-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_boardTabs button:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n outline-offset: -2px;\n}\n\n.dshc_boardTabCount {\n font-variant-numeric: tabular-nums;\n margin-left: 4px;\n}\n\n.dshc_boardCardGrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin: 6px 0 16px;\n padding: 2px;\n list-style: none;\n display: grid;\n}\n\n.dshc_boardTaskCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n border-radius: 12px;\n flex-direction: column;\n gap: 10px;\n min-width: 0;\n padding: 14px;\n transition: transform .14s, box-shadow .14s;\n display: flex;\n}\n\n.dshc_boardTaskCard[data-working=\"true\"] {\n border-color: var(--dsw-alias-state-business-primary);\n}\n\n.dshc_boardTaskCard[data-status=\"done\"] {\n filter: saturate(.25);\n color: var(--dsw-alias-label-secondary);\n}\n\n.dshc_boardTaskCard:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px #00000014;\n}\n\n.dshc_boardTaskHeader {\n align-items: flex-start;\n gap: 8px;\n display: flex;\n}\n\n.dshc_boardTaskTitle {\n overflow-wrap: anywhere;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n flex: 1;\n font-size: 14px;\n font-weight: 600;\n line-height: 1.5;\n display: -webkit-box;\n overflow: hidden;\n}\n\n.dshc_boardTaskHeader .dshc_taskStatus {\n border-radius: 999px;\n padding: 3px 7px;\n font-size: 11px;\n}\n\n.dshc_boardTaskCard .dshc_taskLinks {\n justify-content: flex-end;\n margin-top: auto;\n}\n\n.dshc_boardTabEmpty {\n min-height: 240px;\n color: var(--dsw-alias-label-secondary);\n text-align: center;\n justify-content: center;\n align-items: center;\n padding: 40px 16px;\n display: flex;\n}\n\n@media (width <= 640px) {\n .dshc_boardCardGrid {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_boardTaskCard {\n transition: none;\n }\n\n .dshc_boardTaskCard:hover {\n transform: none;\n }\n}\n\n.dshc_taskBoardScope {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.dshc_taskSection + .dshc_taskSection {\n margin-top: 8px;\n}\n\n.dshc_taskSectionTitle {\n color: var(--dsw-alias-label-secondary);\n margin: 0 0 4px;\n padding: 0 8px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_taskOwnerGroup + .dshc_taskOwnerGroup {\n margin-top: 4px;\n}\n\n.dshc_taskOwnerName {\n color: var(--dsw-alias-label-tertiary);\n padding: 0 8px;\n font-size: 12px;\n}\n\n.dshc_taskList {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.dshc_taskRow {\n border-radius: var(--dsw-radius-sm);\n padding: 6px 8px;\n}\n\n.dshc_taskRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_taskLine {\n align-items: center;\n gap: 6px;\n min-width: 0;\n display: flex;\n}\n\n.dshc_taskTitle {\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n overflow: hidden;\n}\n\n.dshc_taskMeta {\n color: var(--dsw-alias-label-tertiary);\n flex-wrap: wrap;\n gap: 0 8px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_taskLinks {\n gap: 8px;\n display: flex;\n}\n\n.dshc_taskEmpty {\n color: var(--dsw-alias-label-tertiary);\n margin: 0 8px;\n font-size: 12px;\n}\n\n.dshc_taskDone summary {\n cursor: pointer;\n}\n\n.dshc_taskStatus {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-state-business-primary);\n flex: none;\n padding: 0 4px;\n font-size: 10px;\n font-style: normal;\n font-weight: 600;\n}\n\n.dshc_taskStatus[data-status=\"asked\"], .dshc_taskStatus[data-status=\"escalated\"], .dshc_taskStatus[data-status=\"declined\"], .dshc_taskStatus[data-status=\"rework\"] {\n background: var(--dsw-alias-state-warning-primary, var(--dsw-alias-state-error-primary));\n}\n\n.dshc_taskStatus[data-status=\"done\"] {\n background: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_taskCard {\n margin-top: 4px;\n font-style: normal;\n}\n\n.dshc_taskMembers {\n margin: 4px 0 0;\n padding: 0;\n list-style: none;\n}\n\n.dshc_taskMember {\n align-items: center;\n gap: 8px;\n font-size: 13px;\n display: flex;\n}\n\n.dshc_taskMemberName {\n font-weight: 600;\n}\n\n.dshc_taskSub {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_focused {\n background: var(--dsw-alias-interactive-bg-hover);\n outline: 2px solid var(--dsw-alias-state-business-primary);\n}\n\n.dshc_attachments {\n flex-wrap: wrap;\n gap: 6px;\n margin: 4px 0;\n display: flex;\n}\n\n.dshc_attachmentImage {\n border-radius: 4px;\n max-width: min(320px, 100%);\n max-height: 240px;\n display: block;\n}\n\n.dshc_imageAttachment {\n align-content: start;\n max-width: 100%;\n display: grid;\n}\n\n.dshc_imagePreviewTrigger {\n cursor: zoom-in;\n background: none;\n border: 0;\n border-radius: 4px;\n grid-area: 1 / 1;\n padding: 0;\n}\n\n.dshc_imagePreviewTrigger:focus-visible {\n outline: 2px solid var(--dsw-alias-state-business-primary);\n outline-offset: 2px;\n}\n\n.dshc_imagePreviewDialog {\n box-sizing: border-box;\n width: fit-content;\n max-width: 90vw;\n max-height: 90dvh;\n overflow: auto;\n}\n\n.dshc_imagePreviewHeader {\n flex-shrink: 0;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n display: flex;\n}\n\n.dshc_imagePreviewHeader span {\n overflow-wrap: anywhere;\n min-width: 0;\n}\n\n.dshc_imagePreviewHeader button {\n flex-shrink: 0;\n}\n\n.dshc_imagePreviewImage {\n object-fit: contain;\n align-self: center;\n max-width: 100%;\n min-height: 0;\n max-height: 65dvh;\n display: block;\n}\n\n.dshc_imagePreviewActions {\n flex-wrap: wrap;\n flex-shrink: 0;\n gap: 8px;\n display: flex;\n}\n\n.dshc_imagePreviewActions a {\n text-decoration: none;\n}\n\n.dshc_imagePreviewActions button:disabled {\n cursor: wait;\n opacity: .5;\n}\n\n.dshc_attachmentActions {\n border-radius: var(--dsw-radius-sm);\n background: var(--dsw-alias-bg-base);\n opacity: 0;\n pointer-events: none;\n flex-wrap: wrap;\n grid-area: 1 / 1;\n place-self: start end;\n gap: 4px;\n margin: 4px;\n padding: 4px;\n display: flex;\n}\n\n.dshc_attachmentActions a {\n text-decoration: none;\n}\n\n.dshc_attachmentActions button:disabled {\n cursor: wait;\n opacity: .5;\n}\n\n.dshc_imageAttachment:hover .dshc_attachmentActions, .dshc_imageAttachment:focus-within .dshc_attachmentActions, .dshc_imageAttachment[data-copy-state=\"error\"] .dshc_attachmentActions, .dshc_imageAttachment[data-copy-state=\"copying\"] .dshc_attachmentActions, .dshc_imageAttachment[data-copy-state=\"copied\"] .dshc_attachmentActions {\n opacity: 1;\n pointer-events: auto;\n}\n\n@media (hover: none), (pointer: coarse), (width <= 768px) {\n .dshc_attachmentActions {\n opacity: 1;\n pointer-events: auto;\n }\n}\n\n.dshc_attachmentThumb {\n object-fit: cover;\n vertical-align: middle;\n border-radius: 3px;\n width: 32px;\n height: 32px;\n}\n\n.dshc_attachmentFile {\n background: var(--background-secondary, #7f7f7f26);\n color: inherit;\n border-radius: 4px;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n text-decoration: none;\n display: inline-flex;\n}\n\n.dshc_attachmentSize {\n opacity: .6;\n font-size: .85em;\n}\n\n.dshc_attachmentAudio {\n flex-direction: column;\n gap: 2px;\n width: min(320px, 100%);\n display: flex;\n}\n\n.dshc_attachmentAudio audio {\n width: 100%;\n}\n\n.dshc_attachmentAudio a {\n color: inherit;\n text-decoration: none;\n}\n\n.dshc_attachmentFile {\n overflow-wrap: anywhere;\n max-width: 100%;\n}\n\n.dshc_chatHead button, .dshc_chatHead input, .dshc_chatHead select, .dshc_chatHead a, .dshc_chatHead [role=\"menu\"] {\n -webkit-app-region: no-drag;\n}\n\n.dshc_text pre, .dshc_text table {\n max-width: 100%;\n overflow-x: auto;\n}\n\n@media (width >= 901px) {\n .dshc_page {\n grid-template-columns: 232px minmax(0, 1fr) 272px;\n }\n\n .dshc_page.dshc_roomsHidden {\n grid-template-columns: minmax(0, 1fr) 272px;\n }\n\n .dshc_page.dshc_membersHidden {\n grid-template-columns: 232px minmax(0, 1fr);\n }\n\n .dshc_page.dshc_roomsHidden.dshc_membersHidden {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.dshc_rooms {\n padding-top: 4px;\n}\n\n.dshc_room {\n border-radius: var(--dsw-radius-md);\n min-height: 34px;\n padding: 6px 10px;\n transition: background-color .15s, color .15s;\n}\n\n.dshc_workspace {\n min-height: 30px;\n padding-left: 28px;\n font-size: 13px;\n position: relative;\n}\n\n.dshc_workspace:before {\n content: \"\";\n border-left: 1px solid var(--dsw-alias-border-l2);\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n border-bottom-left-radius: 4px;\n width: 7px;\n position: absolute;\n top: 0;\n bottom: 50%;\n left: 15px;\n}\n\n.dshc_workspace .dshc_hash {\n display: none;\n}\n\n.dshc_roomActive {\n color: var(--dsw-alias-label-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent);\n box-shadow: inset 2px 0 0 var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.dshc_activeCount {\n font-variant-numeric: tabular-nums;\n border-radius: 999px;\n margin-left: 0;\n padding: 0 6px;\n font-size: 11px;\n line-height: 18px;\n}\n\n.dshc_activeBusy {\n color: var(--dsw-alias-state-business-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent);\n}\n\n.dshc_roomLabel {\n flex: auto;\n min-width: 0;\n}\n\n.dshc_avatarStack {\n flex: none;\n align-items: center;\n padding-left: 4px;\n display: inline-flex;\n}\n\n.dshc_stackDot {\n border: 2px solid var(--dsw-alias-bg-layer-2);\n background-position: center;\n background-size: cover;\n border-radius: 50%;\n width: 18px;\n height: 18px;\n margin-left: -6px;\n}\n\n.dshc_room .dshc_stackDot {\n width: 16px;\n height: 16px;\n}\n\n.dshc_roomActive .dshc_stackDot {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, var(--dsw-alias-bg-layer-2));\n}\n\n.dshc_chatHead {\n gap: 8px;\n height: 52px;\n}\n\n.dshc_roomName {\n letter-spacing: -.01em;\n white-space: nowrap;\n font-size: 15px;\n}\n\n.dshc_headMeta {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 400;\n overflow: hidden;\n}\n\n.dshc_headActivity {\n color: var(--dsw-alias-label-secondary);\n flex: none;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n font-size: 12px;\n font-weight: 500;\n display: inline-flex;\n}\n\n.dshc_headActivity .dshc_stackDot {\n border-color: var(--dsw-alias-bg-base);\n width: 22px;\n height: 22px;\n}\n\n.dshc_headActivity + .dshc_panelToggleEnd {\n margin-left: 4px;\n}\n\n.dshc_chatHead > .dshc_roomName {\n text-overflow: ellipsis;\n min-width: 0;\n overflow: hidden;\n}\n\n.dshc_chatHead > .dshc_taskBoardTrigger {\n width: auto;\n margin: 0 0 0 auto;\n}\n\n.dshc_chatHead .dshc_taskBoardSummary {\n display: none;\n}\n\n.dshc_headLead {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n border-radius: 999px;\n flex: 0 auto;\n align-items: center;\n gap: 6px;\n min-width: 0;\n padding: 4px 8px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.dshc_headLeadText {\n flex-direction: column;\n min-width: 0;\n display: flex;\n}\n\n.dshc_headLeadName {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_headLeadStatus {\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n font-size: 11px;\n font-weight: 400;\n}\n\n.dshc_headLead + .dshc_panelToggleEnd {\n margin-left: 0;\n}\n\n.dshc_membersHead {\n align-items: center;\n height: 52px;\n padding: 0 4px;\n display: flex;\n}\n\n.dshc_membersHeadTitle {\n color: var(--dsw-alias-label-tertiary);\n letter-spacing: .08em;\n font-size: 11px;\n font-weight: 650;\n}\n\n.dshc_log {\n padding: 12px 0 16px;\n}\n\n.dshc_message {\n grid-template-columns: 36px minmax(0, 1fr);\n gap: 12px;\n padding: 8px 20px 2px;\n transition: background-color .15s;\n}\n\n.dshc_message.dshc_grouped {\n padding-top: 1px;\n}\n\n.dshc_message:hover {\n background: color-mix(in srgb, var(--dsw-alias-interactive-bg-hover) 70%, transparent);\n}\n\n.dshc_messageHead {\n gap: 8px;\n margin-bottom: 2px;\n}\n\n.dshc_messageHeadGrouped {\n margin-bottom: 0;\n}\n\n.dshc_messageHeadGrouped:not(:has(*)) {\n display: none;\n}\n\n.dshc_author {\n font-size: 13px;\n}\n\n.dshc_time, .dshc_edited {\n font-variant-numeric: tabular-nums;\n font-size: 11px;\n}\n\n.dshc_gutterTime {\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n text-align: right;\n opacity: 0;\n align-self: start;\n padding-top: 3px;\n font-size: 10px;\n transition: opacity .15s;\n}\n\n.dshc_message:hover .dshc_gutterTime {\n opacity: 1;\n}\n\n.dshc_text {\n line-height: 1.6;\n}\n\n.dshc_own {\n grid-template-columns: minmax(0, 1fr) 36px;\n}\n\n.dshc_own > :first-child {\n grid-area: 1 / 2;\n}\n\n.dshc_own > .dshc_messageBody {\n flex-direction: column;\n grid-area: 1 / 1;\n align-items: flex-end;\n display: flex;\n}\n\n.dshc_own .dshc_messageHead {\n flex-direction: row-reverse;\n}\n\n.dshc_own .dshc_ownActions {\n margin-left: 0;\n margin-right: auto;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text {\n max-width: min(560px, 100%);\n color: var(--dsw-alias-label-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 16%, var(--dsw-alias-bg-layer-2));\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dsw-alias-state-business-primary) 28%, transparent);\n border-radius: 16px 6px 16px 16px;\n padding: 8px 14px;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_quote {\n align-self: flex-end;\n}\n\n.dshc_own .dshc_msgTools {\n left: 16px;\n right: auto;\n}\n\n.dshc_own.dshc_grouped > .dshc_messageBody > .dshc_text {\n border-radius: 16px 6px 6px 16px;\n}\n\n.dshc_systemRecord {\n flex-direction: column;\n align-items: center;\n padding-bottom: 4px;\n display: flex;\n}\n\n.dshc_systemRecord > .dshc_systemLine {\n align-self: stretch;\n}\n\n.dshc_taskRecord {\n box-sizing: border-box;\n width: min(480px, 100% - 40px);\n margin-top: 0;\n}\n\n.dshc_systemLine {\n color: var(--dsw-alias-label-tertiary);\n align-items: center;\n gap: 12px;\n padding: 10px 32px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_systemLine:before, .dshc_systemLine:after {\n content: \"\";\n background: var(--dsw-alias-border-l2);\n flex: 24px;\n min-width: 24px;\n height: 1px;\n}\n\n.dshc_systemText {\n text-align: center;\n max-width: min(640px, 80%);\n}\n\n.dshc_systemText p {\n margin: 0;\n}\n\n.dshc_errorBar {\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, transparent);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 9%, transparent);\n border-radius: 12px;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px 10px;\n margin: 6px 20px 6px 68px;\n padding: 8px 12px;\n font-size: 13px;\n display: flex;\n}\n\n.dshc_errorBar .dshc_cardButtons {\n margin: 0 0 0 auto;\n}\n\n.dshc_errorBar .dshc_secondaryButton {\n padding: 3px 10px;\n font-size: 12px;\n}\n\n.dshc_errorBar[data-state=\"retried\"] {\n opacity: .65;\n}\n\n.dshc_errorIcon {\n color: #fff;\n background: var(--dsw-alias-state-error-primary);\n border-radius: 50%;\n flex: none;\n place-items: center;\n width: 18px;\n height: 18px;\n font-size: 12px;\n font-weight: 700;\n display: grid;\n}\n\n.dshc_errorText {\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n flex: 200px;\n}\n\n.dshc_errorText p {\n margin: 0;\n}\n\n.dshc_runStrip {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-layer-2));\n border-radius: 12px;\n flex-direction: column;\n max-width: 100%;\n margin-top: 4px;\n padding: 5px 10px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.dshc_runStrip[data-state=\"running\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_runStrip[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_runHead {\n min-width: 0;\n color: var(--dsw-alias-label-secondary);\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_runTitle {\n color: var(--dsw-alias-label-primary);\n flex: none;\n font-weight: 600;\n}\n\n.dshc_runSummary {\n font-variant-numeric: tabular-nums;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_runHead .dshc_cardViews {\n margin-left: 8px;\n}\n\n.dshc_runHead .dshc_cardViewButton {\n color: var(--dsw-alias-label-tertiary);\n border-radius: 999px;\n font-size: 11px;\n}\n\n.dshc_runHead .dshc_cardViewButton[aria-pressed=\"true\"] {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_runIcon {\n border-radius: 50%;\n flex: none;\n width: 12px;\n height: 12px;\n position: relative;\n}\n\n.dshc_runStrip[data-state=\"running\"] .dshc_runIcon {\n border: 2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 25%, transparent);\n border-top-color: var(--dsw-alias-state-business-primary);\n animation: .8s linear infinite dshc_channels-spin;\n}\n\n.dshc_runStrip[data-state=\"completed\"] .dshc_runIcon {\n background: #23a55a;\n}\n\n.dshc_runStrip[data-state=\"completed\"] .dshc_runIcon:after {\n content: \"\";\n border: 1.5px solid #fff;\n border-width: 0 1.5px 1.5px 0;\n width: 3px;\n height: 6px;\n position: absolute;\n top: 2px;\n left: 4px;\n transform: rotate(45deg);\n}\n\n.dshc_runStrip[data-state=\"failed\"] .dshc_runIcon {\n background: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_runSteps {\n color: var(--dsw-alias-label-secondary);\n margin-top: 4px;\n font-size: 12px;\n line-height: 1.55;\n}\n\n.dshc_runSteps p {\n margin: 0;\n}\n\n.dshc_runStrip .dshc_cardButtons {\n margin-top: 6px;\n}\n\n@keyframes dshc_channels-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_runStrip[data-state=\"running\"] .dshc_runIcon {\n animation-duration: 2.4s;\n }\n}\n\n.dshc_card {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n border-left-width: 1px;\n border-radius: 14px;\n margin-top: 6px;\n padding: 10px 12px;\n}\n\n.dshc_card[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, var(--dsw-alias-border-l2));\n}\n\n.dshc_taskAnchor {\n max-width: 560px;\n box-shadow: inset 3px 0 0 var(--channels-accent-warm, #d08a2e);\n}\n\n.dshc_permInline {\n color: var(--dsw-alias-label-secondary);\n flex-wrap: wrap;\n align-items: baseline;\n gap: 4px 8px;\n display: flex;\n}\n\n.dshc_permInline .dshc_text {\n flex: 0 auto;\n min-width: 0;\n}\n\n.dshc_permInline .dshc_text p {\n margin: 0;\n}\n\n.dshc_permLock {\n color: var(--dsw-alias-state-warning-primary, #e9a23b);\n flex: none;\n font-size: 9px;\n}\n\n.dshc_permWaiting {\n color: var(--dsw-alias-state-warning-primary, #e9a23b);\n flex: none;\n font-size: 11px;\n font-weight: 600;\n}\n\n.dshc_permTray {\n flex-direction: column;\n gap: 6px;\n max-height: 40%;\n margin: 0 16px 8px;\n display: flex;\n overflow-y: auto;\n}\n\n.dshc_permTrayItem {\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 55%, var(--dsw-alias-border-l2));\n background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 7%, var(--dsw-alias-bg-layer-2));\n border-radius: 14px;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px 12px;\n padding: 10px 12px;\n display: flex;\n box-shadow: 0 6px 20px -12px #00000059;\n}\n\n.dshc_permTrayText {\n flex: 240px;\n min-width: 0;\n font-size: 13px;\n}\n\n.dshc_permTrayText .dshc_text p {\n margin: 0;\n}\n\n.dshc_permTrayWho {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_permTrayItem .dshc_permActions {\n flex-wrap: nowrap;\n margin: 0;\n}\n\n.dshc_permTrayItem .dshc_permScope {\n display: none;\n}\n\n.dshc_members {\n flex-direction: column;\n padding: 0 10px 16px;\n}\n\n@media (width >= 901px) {\n .dshc_members {\n display: flex;\n }\n\n .dshc_membersHidden > .dshc_members {\n display: none;\n }\n}\n\n.dshc_members.dshc_drawerOpen {\n display: flex;\n}\n\n.dshc_activityNow {\n flex-direction: column;\n gap: 2px;\n margin-bottom: 6px;\n display: flex;\n}\n\n.dshc_activityItem {\n border: 1px solid var(--dsw-alias-border-l2);\n width: 100%;\n color: inherit;\n background: var(--dsw-alias-bg-base);\n font: inherit;\n text-align: left;\n cursor: pointer;\n border-radius: 12px;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n display: flex;\n}\n\n.dshc_activityItem + .dshc_activityItem {\n margin-top: 4px;\n}\n\n.dshc_activityItem:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 40%, var(--dsw-alias-border-l2));\n}\n\n.dshc_activityItem:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_activityText {\n flex-direction: column;\n flex: 1;\n min-width: 0;\n display: flex;\n}\n\n.dshc_activityName {\n font-size: 13px;\n font-weight: 600;\n}\n\n.dshc_activityWhat {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n overflow: hidden;\n}\n\n.dshc_activityPulse {\n background: var(--dsw-alias-state-business-primary);\n border-radius: 50%;\n flex: none;\n width: 8px;\n height: 8px;\n animation: 1.6s ease-in-out infinite dshc_channels-pulse;\n}\n\n@keyframes dshc_channels-pulse {\n 50% {\n box-shadow: 0 0 0 3px #23a55a40;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_activityPulse {\n animation: none;\n }\n}\n\n.dshc_member {\n padding: 6px 8px;\n}\n\n.dshc_memberText {\n flex: 1;\n}\n\n.dshc_memberStatus {\n flex: none;\n margin-left: auto;\n font-size: 11px;\n}\n\n.dshc_memberStatus[data-status=\"busy\"] {\n color: var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.dshc_memberStatus[data-status=\"error\"] {\n color: var(--dsw-alias-state-error-primary);\n font-weight: 600;\n}\n\n.dshc_memberActions {\n background: var(--dsw-alias-bg-layer-2);\n padding-left: 8px;\n position: absolute;\n top: 50%;\n right: 8px;\n transform: translateY(-50%);\n}\n\n.dshc_addMemberButton {\n border-radius: 10px;\n}\n\n@media (width <= 900px) {\n .dshc_headMeta, .dshc_headActivityText {\n display: none;\n }\n\n .dshc_chatHead {\n padding: 0 8px;\n }\n\n .dshc_mobileButton {\n white-space: nowrap;\n }\n\n .dshc_headActivity ~ .dshc_mobileButton:last-child {\n margin-left: 4px;\n }\n\n .dshc_message {\n padding-left: 12px;\n padding-right: 12px;\n }\n\n .dshc_errorBar {\n margin-left: 12px;\n margin-right: 12px;\n }\n\n .dshc_permTray {\n margin: 0 8px 8px;\n }\n\n .dshc_permTrayItem .dshc_permActions {\n flex-wrap: wrap;\n }\n\n .dshc_systemLine {\n padding: 8px 12px;\n }\n\n .dshc_systemText {\n max-width: 90%;\n }\n\n .dshc_memberActions {\n background: none;\n padding-left: 0;\n position: static;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsEnter {\n from {\n opacity: 0;\n transform: translateY(6px);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsEnterOwn {\n from {\n opacity: 0;\n transform: translateY(12px) scale(.985);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsGlow {\n from {\n opacity: 1;\n }\n\n to {\n opacity: 0;\n }\n}\n\n@keyframes dshc_channelsPop {\n from {\n opacity: 0;\n transform: translateY(4px) scale(.98);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsDot {\n 0%, 60%, 100% {\n opacity: .35;\n transform: none;\n }\n\n 30% {\n opacity: 1;\n transform: translateY(-2px);\n }\n}\n\n@keyframes dshc_channelsDrain {\n from {\n transform: scaleX(1);\n }\n\n to {\n transform: scaleX(0);\n }\n}\n\n.dshc_enter {\n animation: .18s cubic-bezier(.2, 0, 0, 1) both dshc_channelsEnter;\n}\n\n.dshc_enterOwn {\n animation: .22s cubic-bezier(.22, 1, .36, 1) both dshc_channelsEnterOwn;\n}\n\n.dshc_enterGlow:before {\n content: \"\";\n pointer-events: none;\n background: linear-gradient(90deg, color-mix(in srgb, var(--dsw-alias-state-business-primary) 10%, transparent), transparent 70%);\n animation: .7s cubic-bezier(.4, 0, .2, 1) both dshc_channelsGlow;\n animation-delay: inherit;\n position: absolute;\n inset: 0;\n}\n\n.dshc_message {\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_room {\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1), color .16s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_card {\n transition: border-color .32s cubic-bezier(.2, 0, 0, 1), background-color .32s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_sendButton, .dshc_primaryButton, .dshc_secondaryButton, .dshc_mentionButton, .dshc_cardViewButton {\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1), opacity .16s cubic-bezier(.2, 0, 0, 1), transform .12s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_sendButton:not(:disabled):active, .dshc_primaryButton:not(:disabled):active, .dshc_secondaryButton:not(:disabled):active, .dshc_mentionButton:not(:disabled):active, .dshc_cardViewButton:active {\n transform: translateY(1px);\n}\n\n.dshc_mentionPopup {\n transform-origin: bottom;\n animation: .16s cubic-bezier(.22, 1, .36, 1) both dshc_channelsPop;\n}\n\n.dshc_typingDots {\n vertical-align: middle;\n gap: 3px;\n margin-right: 6px;\n display: inline-flex;\n}\n\n.dshc_typingDots i {\n background: currentColor;\n border-radius: 50%;\n width: 4px;\n height: 4px;\n animation: 1.2s ease-in-out infinite dshc_channelsDot;\n}\n\n.dshc_typingDots i:nth-child(2) {\n animation-delay: .15s;\n}\n\n.dshc_typingDots i:nth-child(3) {\n animation-delay: .3s;\n}\n\n.dshc_logWrap {\n flex-direction: column;\n flex: 1;\n min-height: 0;\n display: flex;\n position: relative;\n}\n\n.dshc_olderLoading {\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n padding: 8px 16px;\n font-size: 12px;\n}\n\n.dshc_unreadDivider {\n color: var(--dsw-alias-state-error-primary);\n align-items: center;\n gap: 8px;\n margin: 8px 16px;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n}\n\n.dshc_unreadDivider:before, .dshc_unreadDivider:after {\n content: \"\";\n opacity: .35;\n background: currentColor;\n flex: 1;\n height: 1px;\n}\n\n.dshc_withdrawnRow {\n text-align: right;\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 16px;\n font-size: 13px;\n font-style: italic;\n animation: .18s cubic-bezier(.2, 0, 0, 1) both dshc_channelsEnter;\n}\n\n.dshc_jumpLatest {\n z-index: 3;\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #00000029);\n font: inherit;\n cursor: pointer;\n border-radius: 999px;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n font-size: 12px;\n animation: .22s cubic-bezier(.22, 1, .36, 1) both dshc_channelsPop;\n display: inline-flex;\n position: absolute;\n bottom: 12px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.dshc_jumpLatest:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_jumpLatest:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_jumpCount {\n color: var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.dshc_undoToast {\n z-index: 4;\n border-radius: var(--dsw-radius-md);\n width: max-content;\n max-width: calc(100% - 32px);\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-label-primary);\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n align-items: center;\n gap: 12px;\n margin: 0 auto;\n padding: 8px 8px 8px 14px;\n font-size: 13px;\n animation: .22s cubic-bezier(.22, 1, .36, 1) both dshc_channelsPop;\n display: flex;\n position: absolute;\n bottom: 12px;\n left: 0;\n right: 0;\n overflow: hidden;\n}\n\n.dshc_undoButton {\n border-radius: var(--dsw-radius-sm);\n color: inherit;\n background: color-mix(in srgb, currentColor 16%, transparent);\n font: inherit;\n cursor: pointer;\n border: 0;\n padding: 2px 10px;\n font-weight: 600;\n}\n\n.dshc_undoButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_undoBar {\n background: var(--dsw-alias-state-business-primary);\n transform-origin: 0;\n height: 2px;\n animation: linear forwards dshc_channelsDrain;\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n}\n\n.dshc_undoToast ~ .dshc_jumpLatest {\n display: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_enter, .dshc_enterOwn, .dshc_enterGlow:before, .dshc_mentionPopup, .dshc_jumpLatest, .dshc_undoToast, .dshc_withdrawnRow {\n animation: none;\n }\n\n .dshc_typingDots i {\n opacity: .6;\n animation: none;\n }\n\n .dshc_undoBar {\n animation: none;\n }\n\n .dshc_message, .dshc_room, .dshc_card, .dshc_sendButton, .dshc_primaryButton, .dshc_secondaryButton, .dshc_mentionButton, .dshc_mentionBadge, .dshc_cardViewButton {\n transition: none;\n }\n}\n\n.dshc_leadCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n border-radius: 14px;\n align-items: center;\n gap: 10px;\n margin: 8px 8px 4px;\n display: flex;\n}\n\n.dshc_leadCardButton {\n border-radius: inherit;\n min-width: 0;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: auto;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n display: flex;\n}\n\n.dshc_leadCardButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_leadCardButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-alias-border-focus, currentColor);\n outline-offset: -2px;\n}\n\n.dshc_leadCardButton:disabled {\n cursor: default;\n background: none;\n}\n\n.dshc_leadCardText {\n flex-direction: column;\n min-width: 0;\n display: flex;\n}\n\n.dshc_leadCardName {\n color: var(--dsw-alias-label-primary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 650;\n overflow: hidden;\n}\n\n.dshc_leadCardStatus {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n overflow: hidden;\n}\n\n.dshc_message:not(.dshc_own) > .dshc_messageBody > .dshc_text {\n background: var(--dsw-alias-bg-layer-2);\n border-radius: 6px 16px 16px;\n width: fit-content;\n max-width: min(640px, 100%);\n padding: 8px 14px;\n}\n\n.dshc_message.dshc_grouped:not(.dshc_own) > .dshc_messageBody > .dshc_text {\n border-radius: 6px 16px 16px 6px;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text {\n color: var(--dsw-alias-bg-base);\n background: var(--dsw-alias-label-primary);\n box-shadow: none;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text * {\n color: inherit;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text a {\n color: inherit;\n text-decoration: underline;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text code {\n color: inherit;\n background: color-mix(in srgb, var(--dsw-alias-bg-base) 18%, transparent);\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text .dshc_mentionBadge {\n color: var(--dsw-alias-bg-base);\n background: color-mix(in srgb, var(--dsw-alias-bg-base) 24%, transparent);\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text .dshc_mentionBadge:hover {\n background: color-mix(in srgb, var(--dsw-alias-bg-base) 36%, transparent);\n}\n\n.dshc_boardDrawerBackdrop {\n background: #00000040;\n place-items: stretch end;\n padding: 0;\n}\n\n.dshc_boardDrawerBackdrop > .dshc_taskBoardDialog {\n box-sizing: border-box;\n border-radius: 0;\n width: min(440px, 100%);\n height: 100dvh;\n max-height: none;\n animation: .2s ease-out dshc_boardDrawerIn;\n}\n\n@keyframes dshc_boardDrawerIn {\n from {\n opacity: 0;\n transform: translateX(24px);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_boardDrawerBackdrop > .dshc_taskBoardDialog {\n animation: none;\n }\n}\n\n.dshc_schemeToggle {\n border-radius: var(--dsw-radius-sm);\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n justify-content: center;\n align-items: center;\n margin-left: 4px;\n font-size: 16px;\n line-height: 1;\n display: inline-flex;\n}\n\n.dshc_schemeToggle:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_schemeToggle:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_schemeToggle + .dshc_panelToggleEnd {\n margin-left: 4px;\n}\n\n.dshc_progressCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n box-shadow: 0 6px 18px #0000000a, 0 -12px 0 0 var(--dsw-alias-bg-base);\n border-radius: 14px;\n flex: none;\n margin: 8px 16px 4px;\n padding: 8px 14px;\n font-size: 12px;\n position: relative;\n}\n\n.dshc_progressHead {\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_progressTitle {\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n}\n\n.dshc_progressSummary {\n white-space: nowrap;\n text-overflow: ellipsis;\n min-width: 0;\n color: var(--dsw-alias-label-tertiary);\n flex: 1;\n overflow: hidden;\n}\n\n.dshc_progressToggle {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n min-width: 22px;\n height: 24px;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n cursor: pointer;\n background: none;\n flex: none;\n justify-content: center;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n padding: 0 10px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n display: inline-flex;\n}\n\n.dshc_progressChevron {\n font-size: 12px;\n}\n\n.dshc_progressToggle:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_progressToggle:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_progressCard[data-folded=\"true\"] {\n padding: 6px 12px;\n}\n\n.dshc_progressRows {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 0 14px;\n margin: 4px 0 0;\n display: grid;\n}\n\n.dshc_progressRow {\n min-width: 0;\n}\n\n.dshc_progressRow dt {\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n}\n\n.dshc_progressRow dd {\n white-space: nowrap;\n text-overflow: ellipsis;\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n margin: 0;\n overflow: hidden;\n}\n\n.dshc_progressRow[data-empty=\"true\"] dd {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_progressRow[data-row=\"open\"][data-empty=\"false\"] dd, .dshc_progressRow[data-row=\"open\"] .dshc_progressLink {\n color: var(--dsw-alias-state-warning-primary, #b7791f);\n}\n\n.dshc_progressLink {\n max-width: 100%;\n color: inherit;\n font: inherit;\n text-align: left;\n white-space: nowrap;\n text-overflow: ellipsis;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 0;\n overflow: hidden;\n}\n\n.dshc_progressLink:hover {\n text-decoration: underline;\n}\n\n@media (width <= 760px) {\n .dshc_progressCard {\n margin: 6px 10px 2px;\n }\n\n .dshc_progressRows {\n grid-template-columns: 52px minmax(0, 1fr);\n }\n\n .dshc_progressRow {\n display: contents;\n }\n}\n\n.dshc_stepChips {\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 6px;\n display: flex;\n}\n\n.dshc_stepChip {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n max-width: 220px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n border-radius: 999px;\n align-items: center;\n gap: 5px;\n padding: 2px 9px 2px 7px;\n font-size: 11px;\n display: inline-flex;\n}\n\n.dshc_stepChip b {\n color: var(--dsw-alias-label-primary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n overflow: hidden;\n}\n\n.dshc_stepChip:disabled {\n cursor: default;\n}\n\n.dshc_stepChip[aria-expanded=\"true\"] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_stepChip[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_stepChip[data-state=\"failed\"] b {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_stepChip[data-state=\"running\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 55%, var(--dsw-alias-border-l2));\n}\n\n.dshc_stepMark {\n background: var(--dsw-alias-label-tertiary);\n border-radius: 50%;\n flex: none;\n width: 7px;\n height: 7px;\n}\n\n.dshc_stepChip[data-state=\"ok\"] .dshc_stepMark {\n background: #23a55a;\n}\n\n.dshc_stepChip[data-state=\"failed\"] .dshc_stepMark {\n background: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_stepChip[data-state=\"running\"] .dshc_stepMark {\n background: var(--dsw-alias-state-business-primary);\n animation: 1s ease-in-out infinite dshc_channels-blink;\n}\n\n@keyframes dshc_channels-blink {\n 50% {\n opacity: .3;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_stepChip[data-state=\"running\"] .dshc_stepMark {\n animation: none;\n }\n}\n\n.dshc_stepOut {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-base));\n color: var(--dsw-alias-label-secondary);\n border-radius: 8px;\n flex-basis: 100%;\n gap: 4px;\n padding: 8px 10px;\n font-size: 11px;\n line-height: 1.5;\n display: grid;\n}\n\n.dshc_stepOut[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_stepOut div {\n gap: 8px;\n min-width: 0;\n display: flex;\n}\n\n.dshc_stepOut span {\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n}\n\n.dshc_stepOut code {\n overflow-wrap: anywhere;\n min-width: 0;\n font-family: ui-monospace, Menlo, monospace;\n}\n\n.dshc_stepOut[data-state=\"failed\"] > div[data-state=\"failed\"] > div:last-child > code {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_decideTray {\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 55%, var(--dsw-alias-border-l2));\n background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 7%, var(--dsw-alias-bg-layer-2));\n border-radius: 14px;\n flex-direction: column;\n max-height: 45%;\n margin: 0 16px 8px;\n display: flex;\n overflow-y: auto;\n}\n\n.dshc_decideTrayHead {\n width: 100%;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n align-items: baseline;\n gap: 8px;\n padding: 7px 12px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_decideTrayHead strong {\n color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 70%, var(--dsw-alias-label-primary));\n}\n\n.dshc_decideTrayHead small {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_decideTrayHead:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n outline-offset: -2px;\n}\n\n.dshc_decideTrayToggle {\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n margin-left: auto;\n}\n\n.dshc_decideTray .dshc_permTray {\n max-height: none;\n margin: 0;\n padding: 0 8px 8px;\n}\n\n.dshc_askList {\n flex-direction: column;\n display: flex;\n}\n\n.dshc_askItem {\n border-top: 1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 30%, transparent);\n background: var(--dsw-alias-bg-layer-2);\n flex-wrap: wrap;\n align-items: center;\n gap: 6px 10px;\n padding: 8px 12px;\n display: flex;\n}\n\n.dshc_askKind {\n color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 75%, var(--dsw-alias-label-primary));\n background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 15%, transparent);\n border-radius: 5px;\n flex: none;\n padding: 0 6px;\n font-size: 11px;\n}\n\n.dshc_askText {\n flex-direction: column;\n flex: 200px;\n min-width: 0;\n font-size: 13px;\n display: flex;\n}\n\n.dshc_askQuestion {\n overflow-wrap: anywhere;\n}\n\n.dshc_askFrom {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n overflow: hidden;\n}\n\n.dshc_askForm {\n flex: 0 260px;\n align-items: center;\n gap: 6px;\n min-width: 160px;\n display: flex;\n}\n\n.dshc_askInput {\n border: 1px solid var(--dsw-alias-border-l2);\n min-width: 0;\n color: inherit;\n background: var(--dsw-alias-bg-base);\n font: inherit;\n border-radius: 8px;\n flex: 1;\n padding: 4px 8px;\n font-size: 12px;\n}\n\n.dshc_askInput:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_askSend {\n width: 26px;\n height: 26px;\n color: var(--dsw-alias-bg-base);\n background: var(--dsw-alias-label-primary);\n cursor: pointer;\n border: 0;\n border-radius: 50%;\n flex: none;\n justify-content: center;\n align-items: center;\n padding: 0;\n display: inline-flex;\n}\n\n.dshc_askSend:disabled {\n opacity: .3;\n cursor: default;\n}\n\n.dshc_askSend:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n outline-offset: 2px;\n}\n\n.dshc_askDone {\n color: var(--dsw-alias-label-secondary);\n align-items: center;\n gap: 8px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.dshc_askNotice {\n color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 75%, var(--dsw-alias-label-primary));\n flex-basis: 100%;\n font-size: 12px;\n}\n\n@media (width <= 900px) {\n .dshc_decideTray {\n margin: 0 8px 8px;\n }\n\n .dshc_askForm {\n flex-basis: 100%;\n }\n}\n\n.dshc_headTeam {\n flex: none;\n align-items: center;\n margin-left: auto;\n display: inline-flex;\n}\n\n.dshc_mate, .dshc_mateMore {\n border: 2px solid var(--dsw-alias-bg-base);\n background: var(--dsw-alias-bg-base);\n cursor: pointer;\n border-radius: 50%;\n justify-content: center;\n align-items: center;\n margin-left: -6px;\n padding: 0;\n display: inline-flex;\n position: relative;\n}\n\n.dshc_mate:first-child {\n margin-left: 0;\n}\n\n.dshc_mate .dshc_avatar {\n margin: 0;\n}\n\n.dshc_mateMore {\n background: var(--dsw-alias-interactive-bg-hover);\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-weight: 600;\n}\n\n.dshc_mateDot {\n box-sizing: border-box;\n border: 2px solid var(--dsw-alias-bg-base);\n background: var(--dsw-alias-label-tertiary);\n border-radius: 50%;\n width: 9px;\n height: 9px;\n position: absolute;\n bottom: -2px;\n right: -2px;\n}\n\n.dshc_mate[data-state=\"busy\"] .dshc_mateDot {\n background: #23a55a;\n animation: 1.6s ease-in-out infinite dshc_channels-pulse;\n}\n\n.dshc_mate[data-state=\"idle\"] .dshc_mateDot {\n background: var(--dsw-alias-bg-base);\n box-shadow: inset 0 0 0 1.5px var(--dsw-alias-label-tertiary);\n}\n\n.dshc_mate[data-state=\"error\"] .dshc_mateDot {\n background: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_mate[data-state=\"offline\"] {\n opacity: .55;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_mate[data-state=\"busy\"] .dshc_mateDot {\n animation: none;\n }\n}\n\n@media (width <= 760px) {\n .dshc_headTeam {\n display: none;\n }\n}\n\n.dshc_chatHead > .dshc_headTeam + .dshc_taskBoardTrigger {\n margin-left: 12px;\n}\n\n.dshc_sideDock {\n display: none;\n}\n\n@media (width >= 1101px) {\n .dshc_page.dshc_panelOpen {\n grid-template-columns: 220px minmax(0, 1fr) min(420px, 36%) 240px;\n }\n\n .dshc_page.dshc_panelOpen.dshc_roomsHidden {\n grid-template-columns: minmax(0, 1fr) min(420px, 36%) 240px;\n }\n\n .dshc_page.dshc_panelOpen.dshc_membersHidden {\n grid-template-columns: 220px minmax(0, 1fr) min(420px, 36%);\n }\n\n .dshc_page.dshc_panelOpen.dshc_roomsHidden.dshc_membersHidden {\n grid-template-columns: minmax(0, 1fr) min(420px, 36%);\n }\n\n .dshc_page.dshc_panelOpen > .dshc_sideDock {\n border-left: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n min-width: 0;\n min-height: 0;\n display: flex;\n }\n}\n\n.dshc_sidePanel {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n max-height: none;\n box-shadow: none;\n border: 0;\n border-radius: 0;\n margin: 0;\n animation: .2s ease-out dshc_boardDrawerIn;\n position: relative;\n}\n\n.dshc_sidePanel:focus {\n outline: none;\n}\n\n.dshc_sidePanelClose {\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 6px;\n font-size: 18px;\n line-height: 1;\n position: absolute;\n top: 10px;\n right: 10px;\n}\n\n.dshc_sidePanelClose:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_sidePanel {\n animation: none;\n }\n}\n\n.dshc_sidePanel .dshc_boardCardGrid {\n grid-template-columns: minmax(0, 1fr);\n}\n\n.dshc_slashPopup {\n z-index: 6;\n width: min(360px, 100% - 32px);\n animation: none;\n right: auto;\n}\n\n.dshc_slashGroup {\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px 2px;\n font-size: 11px;\n}\n\n.dshc_slashItem {\n border-radius: var(--dsw-radius-sm);\n cursor: pointer;\n align-items: baseline;\n gap: 8px;\n padding: 5px 8px;\n display: flex;\n}\n\n.dshc_slashActive {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_slashName {\n flex: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-weight: 600;\n}\n\n.dshc_slashMeta {\n min-width: 0;\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n overflow: hidden;\n}\n\n.dshc_sendButton.dshc_stopButton {\n background: var(--dsw-alias-label-primary);\n}\n\n.dshc_stopNote + .dshc_sendButton {\n margin-left: 8px;\n}\n\n.dshc_sendButton.dshc_stopButton[data-phase=\"stopping\"] {\n opacity: .6;\n cursor: progress;\n}\n\n.dshc_stopNote {\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n margin-left: auto;\n font-size: 12px;\n}\n\n.dshc_cmdLine {\n color: var(--dsw-alias-label-tertiary);\n flex-wrap: wrap;\n align-items: baseline;\n gap: 6px;\n padding: 1px 16px 1px 72px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_cmdLine code {\n color: var(--dsw-alias-label-secondary);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n\n.dshc_cmdResult {\n min-width: 0;\n max-width: 60ch;\n color: inherit;\n font: inherit;\n text-align: left;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 0;\n overflow: hidden;\n}\n\n.dshc_cmdResult[aria-expanded=\"true\"] {\n white-space: pre-line;\n max-width: none;\n}\n\n.dshc_cmdLine[data-failed=\"true\"] .dshc_cmdResult {\n color: var(--dsw-alias-danger, #d94848);\n}\n\n@media (width <= 720px) {\n .dshc_cmdLine {\n padding-left: 16px;\n }\n}\n\n.dshc_msgTool {\n justify-content: center;\n align-items: center;\n width: 26px;\n height: 24px;\n padding: 0;\n display: inline-flex;\n position: relative;\n}\n\n.dshc_msgTool[data-tip]:after {\n content: attr(data-tip);\n z-index: 3;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary-inverted);\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 11px;\n position: absolute;\n top: calc(100% + 4px);\n left: 50%;\n transform: translateX(-50%);\n}\n\n.dshc_msgTool[data-tip]:hover:after, .dshc_msgTool[data-tip]:focus-visible:after {\n opacity: 1;\n transition: opacity 0s 80ms;\n}\n\n.dshc_permSessionButton {\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n text-underline-offset: 2px;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 4px 6px;\n font-size: 12px;\n text-decoration: underline;\n}\n\n.dshc_permSessionButton:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_permSessionButton:disabled {\n opacity: .5;\n cursor: default;\n}\n\n.dshc_queuedButton {\n border: 1px solid var(--dsw-alias-border-default, #0000001f);\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border-radius: 9px;\n padding: 0 6px;\n font-size: 11px;\n line-height: 16px;\n}\n\n.dshc_queuedButton:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_queuedTag {\n color: #a76a06;\n background: #eaa02824;\n border-radius: 4px;\n padding: 0 5px;\n font-size: 11px;\n line-height: 16px;\n}\n\n[data-queued] .dshc_ownActions {\n opacity: 1;\n}\n\n.dshc_memory {\n margin-top: 4px;\n}\n\n.dshc_memoryBadge {\n background: var(--dsw-alias-fill-secondary, #0000000d);\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n border: 0;\n border-radius: 9px;\n align-items: center;\n gap: 4px;\n padding: 0 6px;\n font-size: 11px;\n line-height: 18px;\n display: inline-flex;\n}\n\n.dshc_memoryBadge:hover, .dshc_memoryBadge[aria-expanded=\"true\"] {\n color: var(--dsw-alias-label-secondary);\n}\n\n.dshc_memoryList {\n border-left: 2px solid var(--dsw-alias-border-default, #0000001f);\n flex-direction: column;\n gap: 4px;\n max-width: 560px;\n margin: 4px 0 0;\n padding: 6px 10px;\n font-size: 12px;\n list-style: none;\n display: flex;\n}\n\n.dshc_memoryItem {\n flex-direction: column;\n display: flex;\n}\n\n.dshc_memoryTitle {\n color: var(--dsw-alias-label-secondary);\n line-height: 1.5;\n}\n\n.dshc_memoryPage {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-family: var(--dsw-font-mono, ui-monospace, monospace);\n}\n\n.dshc_delivery {\n flex-direction: column;\n gap: 6px;\n max-width: 720px;\n margin-top: 8px;\n display: flex;\n}\n\n.dshc_deliveryFrame {\n border: 1px solid var(--background-modifier-accent, #7f7f7f4d);\n background: #fff;\n border-radius: 6px;\n height: 360px;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_deliveryActions {\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n display: flex;\n}\n\n.dshc_deliveryActions a {\n text-decoration: none;\n}\n\n.dshc_deliveryDone {\n color: var(--text-muted, #888);\n font-size: 12px;\n}\n\n.dshc_deliveryFullDialog {\n box-sizing: border-box;\n flex-direction: column;\n gap: 8px;\n width: 94vw;\n max-width: none;\n height: 92dvh;\n display: flex;\n}\n\n.dshc_deliveryFullFrame {\n background: #fff;\n border-radius: 6px;\n flex: 1;\n min-height: 0;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_dmTitle {\n color: var(--text-muted, #888);\n margin: 14px 8px 4px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_dmAvatar {\n margin-right: 6px;\n display: inline-flex;\n}\n\n.dshc_dmRow {\n align-items: center;\n display: flex;\n position: relative;\n}\n\n.dshc_dmRow .dshc_room {\n padding-right: 26px;\n}\n\n.dshc_dmClose {\n border-radius: var(--dsw-radius-sm, 4px);\n width: 20px;\n height: 20px;\n color: var(--dsw-alias-label-tertiary, #888);\n font: inherit;\n cursor: pointer;\n opacity: 0;\n background: none;\n border: 0;\n padding: 0;\n font-size: 15px;\n line-height: 20px;\n position: absolute;\n right: 4px;\n}\n\n.dshc_dmRow:hover .dshc_dmClose, .dshc_dmClose:focus-visible {\n opacity: 1;\n}\n\n.dshc_dmClose:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n@media (hover: none) {\n .dshc_dmClose {\n opacity: 1;\n }\n}\n\n.dshc_dmHead {\n margin-top: 14px;\n}\n\n.dshc_dmPicker {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-2);\n flex-direction: column;\n margin: 0 4px 4px;\n padding: 4px;\n display: flex;\n}\n\n.dshc_dmPickNone {\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px;\n font-size: 12px;\n}\n\n.dshc_dmNewRow {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n}\n\nbutton.dshc_headMeta {\n border-radius: var(--dsw-radius-sm);\n min-width: 0;\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: 0 auto;\n padding: 2px 6px;\n font-size: 12px;\n}\n\nbutton.dshc_headMeta:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_headSpacer {\n flex: auto;\n}\n\n.dshc_roomUnread {\n color: var(--dsw-alias-label-primary);\n font-weight: 650;\n}\n\n.dshc_unreadBadge {\n color: #fff;\n background: var(--dsw-alias-state-danger-primary, #e5484d);\n text-align: center;\n font-variant-numeric: tabular-nums;\n border-radius: 999px;\n flex: none;\n min-width: 18px;\n padding: 0 5px;\n font-size: 11px;\n font-weight: 600;\n line-height: 18px;\n}\n\n.dshc_slashDisabled {\n opacity: .45;\n cursor: not-allowed;\n}\n\n.dshc_leadCardHint {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n overflow: hidden;\n}\n\n.dshc_roomWorking {\n max-width: 96px;\n color: var(--dsw-alias-state-business-primary);\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: none;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 500;\n display: inline-flex;\n overflow: hidden;\n}\n\n.dshc_roomWorkingDot {\n background: currentColor;\n border-radius: 50%;\n flex: none;\n width: 6px;\n height: 6px;\n animation: 1.4s ease-in-out infinite dshc_roomWorkingPulse;\n}\n\n@keyframes dshc_roomWorkingPulse {\n 50% {\n opacity: .35;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_roomWorkingDot {\n animation: none;\n }\n}\n\n.dshc_drawerTools {\n display: none;\n}\n\n@media (width <= 600px) {\n .dshc_drawerTools {\n flex-wrap: wrap;\n gap: 6px;\n margin: 0 8px 8px;\n display: flex;\n }\n\n .dshc_chatHead > .dshc_taskBoardTrigger, .dshc_chatHead > .dshc_schemeToggle, .dshc_chatHead > .dshc_headMeta, .dshc_chatHead > .dshc_headTeam {\n display: none;\n }\n\n .dshc_chatHead > .dshc_roomName {\n flex: auto;\n }\n}\n\n.dshc_panelOpen .dshc_chatHead > .dshc_headTeam, .dshc_panelOpen .dshc_chatHead > .dshc_headMeta {\n display: none;\n}\n\n.dshc_panelOpen .dshc_chatHead > .dshc_roomName {\n flex: 0 auto;\n}\n[data-dc-scheme=\"light\"] {\n --dsw-static-amber-100: #fef5e7;\n --dsw-static-amber-400: #f7ad31;\n --dsw-static-amber-500: #f59e0b;\n --dsw-static-amber-600: #dd8629;\n --dsw-static-amber-900: #27241f;\n --dsw-static-blue-100: #dbeafe;\n --dsw-static-blue-300: #93c5fd;\n --dsw-static-blue-400: #60a5fa;\n --dsw-static-blue-450: #4d93f8;\n --dsw-static-blue-500: #3b82f6;\n --dsw-static-blue-50: #eff6ff;\n --dsw-static-blue-50p: #eaf3ff;\n --dsw-static-blue-600: #2563eb;\n --dsw-static-blue-75: #e5f0ff;\n --dsw-static-blue-800: #1e40af;\n --dsw-static-blue-900: #0e3074;\n --dsw-static-blue-950: #172554;\n --dsw-static-deepseek-100: #e4edfd;\n --dsw-static-deepseek-200: #d3e2ff;\n --dsw-static-deepseek-300: #b7c8fe;\n --dsw-static-deepseek-400: #7aaaff;\n --dsw-static-deepseek-450: #5686fe;\n --dsw-static-deepseek-500: #4176e6;\n --dsw-static-deepseek-50: #edf3fe;\n --dsw-static-deepseek-600: #4868b2;\n --dsw-static-deepseek-700-delete: #2f4c8f;\n --dsw-static-deepseek-800: #34415b;\n --dsw-static-deepseek-900: #283142;\n --dsw-static-green-100: #e6faed;\n --dsw-static-green-400: #4ed17e;\n --dsw-static-green-500: #22c55e;\n --dsw-static-green-500-a08: #22c55e14;\n --dsw-static-green-500-a12: #22c55e1f;\n --dsw-static-green-900: #233c2c;\n --dsw-static-neutral-00: #fff;\n --dsw-static-neutral-1000: #000;\n --dsw-static-neutral-100: #f5f5f5;\n --dsw-static-neutral-150: #ededed;\n --dsw-static-neutral-200: #e5e5e5;\n --dsw-static-neutral-250: #dcdcdc;\n --dsw-static-neutral-300: #d4d4d4;\n --dsw-static-neutral-400: #a2a4a6;\n --dsw-static-neutral-500: #7f8287;\n --dsw-static-neutral-50: #fafafa;\n --dsw-static-neutral-550: #65676b;\n --dsw-static-neutral-600: #545557;\n --dsw-static-neutral-700: #3c3c3d;\n --dsw-static-neutral-800: #292929;\n --dsw-static-neutral-850: #212123;\n --dsw-static-neutral-900: #0f0f0f;\n --dsw-static-neutral-bluish-00: #fff;\n --dsw-static-neutral-bluish-1000: #0f1115;\n --dsw-static-neutral-bluish-100: #ebeef2;\n --dsw-static-neutral-bluish-150: #e9ecf2;\n --dsw-static-neutral-bluish-200: #e1e5ee;\n --dsw-static-neutral-bluish-300: #cfd3d6;\n --dsw-static-neutral-bluish-400: #adb2b8;\n --dsw-static-neutral-bluish-500: #979da6;\n --dsw-static-neutral-bluish-50: #f9fafb;\n --dsw-static-neutral-bluish-600: #81858c;\n --dsw-static-neutral-bluish-60: #f5f6f7;\n --dsw-static-neutral-bluish-700: #61666b;\n --dsw-static-neutral-bluish-750: #43454a;\n --dsw-static-neutral-bluish-75: #f1f3f5;\n --dsw-static-neutral-bluish-800: #353638;\n --dsw-static-neutral-bluish-850: #2c2c2e;\n --dsw-static-neutral-bluish-875: #232324;\n --dsw-static-neutral-bluish-900: #1b1b1c;\n --dsw-static-neutral-bluish-950: #151517;\n --dsw-static-red-100: #fee2e2;\n --dsw-static-red-400: #f25a5a;\n --dsw-static-red-400-a12: #f25a5a1f;\n --dsw-static-red-500: #ef4444;\n --dsw-static-red-50: #fef2f2;\n --dsw-static-red-600: #ec1313;\n --dsw-static-red-600-a08: #ec131314;\n --dsw-static-red-900: #570c0c;\n}\n\n[data-dc-scheme=\"dark\"] {\n --dsw-static-amber-100: #fef5e7;\n --dsw-static-amber-400: #f7ad31;\n --dsw-static-amber-500: #f59e0b;\n --dsw-static-amber-600: #dd8629;\n --dsw-static-amber-900: #27241f;\n --dsw-static-blue-100: #dbeafe;\n --dsw-static-blue-300: #93c5fd;\n --dsw-static-blue-400: #60a5fa;\n --dsw-static-blue-450: #4d93f8;\n --dsw-static-blue-500: #3b82f6;\n --dsw-static-blue-50: #eff6ff;\n --dsw-static-blue-50p: #eaf3ff;\n --dsw-static-blue-600: #2563eb;\n --dsw-static-blue-75: #e5f0ff;\n --dsw-static-blue-800: #1e40af;\n --dsw-static-blue-900: #0e3074;\n --dsw-static-blue-950: #172554;\n --dsw-static-deepseek-100: #e4edfd;\n --dsw-static-deepseek-200: #d3e2ff;\n --dsw-static-deepseek-300: #b7c8fe;\n --dsw-static-deepseek-400: #7aaaff;\n --dsw-static-deepseek-450: #5686fe;\n --dsw-static-deepseek-500: #4176e6;\n --dsw-static-deepseek-50: #edf3fe;\n --dsw-static-deepseek-600: #4868b2;\n --dsw-static-deepseek-700-delete: #2f4c8f;\n --dsw-static-deepseek-800: #34415b;\n --dsw-static-deepseek-900: #283142;\n --dsw-static-green-100: #e6faed;\n --dsw-static-green-400: #4ed17e;\n --dsw-static-green-500: #22c55e;\n --dsw-static-green-500-a08: #22c55e14;\n --dsw-static-green-500-a12: #22c55e1f;\n --dsw-static-green-900: #233c2c;\n --dsw-static-neutral-00: #fff;\n --dsw-static-neutral-1000: #000;\n --dsw-static-neutral-100: #f5f5f5;\n --dsw-static-neutral-150: #ededed;\n --dsw-static-neutral-200: #e5e5e5;\n --dsw-static-neutral-250: #dcdcdc;\n --dsw-static-neutral-300: #d4d4d4;\n --dsw-static-neutral-400: #a2a4a6;\n --dsw-static-neutral-500: #7f8287;\n --dsw-static-neutral-50: #fafafa;\n --dsw-static-neutral-550: #65676b;\n --dsw-static-neutral-600: #545557;\n --dsw-static-neutral-700: #3c3c3d;\n --dsw-static-neutral-800: #292929;\n --dsw-static-neutral-850: #212123;\n --dsw-static-neutral-900: #0f0f0f;\n --dsw-static-neutral-bluish-00: #fff;\n --dsw-static-neutral-bluish-1000: #0f1115;\n --dsw-static-neutral-bluish-100: #ebeef2;\n --dsw-static-neutral-bluish-150: #e9ecf2;\n --dsw-static-neutral-bluish-200: #e1e5ee;\n --dsw-static-neutral-bluish-300: #cfd3d6;\n --dsw-static-neutral-bluish-400: #adb2b8;\n --dsw-static-neutral-bluish-500: #979da6;\n --dsw-static-neutral-bluish-50: #f9fafb;\n --dsw-static-neutral-bluish-600: #81858c;\n --dsw-static-neutral-bluish-60: #f9fafb;\n --dsw-static-neutral-bluish-700: #61666b;\n --dsw-static-neutral-bluish-750: #43454a;\n --dsw-static-neutral-bluish-75: #f1f3f5;\n --dsw-static-neutral-bluish-800: #353638;\n --dsw-static-neutral-bluish-850: #2c2c2e;\n --dsw-static-neutral-bluish-875: #232324;\n --dsw-static-neutral-bluish-900: #1b1b1c;\n --dsw-static-neutral-bluish-950: #151517;\n --dsw-static-red-100: #fee2e2;\n --dsw-static-red-400: #f25a5a;\n --dsw-static-red-400-a12: #f25a5a1f;\n --dsw-static-red-500: #ef4444;\n --dsw-static-red-50: #fef2f2;\n --dsw-static-red-600: #ec1313;\n --dsw-static-red-600-a08: #ec131314;\n --dsw-static-red-900: #570c0c;\n}\n\n[data-dc-scheme=\"light\"] {\n --dsw-alias-bg-base: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-document-preview: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-bg-document-selection: color-mix(in srgb, var(--dsw-static-blue-500) 40%, transparent);\n --dsw-alias-label-document-preview: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-bg-layer-1: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-layer-2: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-layer-3: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-mask-1: #0000003d;\n --dsw-alias-bg-mask-2: #0000001f;\n --dsw-alias-bg-mask-3: #0000007a;\n --dsw-alias-bg-mask-photo: #000000e0;\n --dsw-alias-bg-mask-drop: #ffffffb3;\n --dsw-alias-bg-module-platform: var(--dsw-static-neutral-bluish-60);\n --dsw-alias-bg-multi-select: var(--dsw-static-neutral-bluish-60);\n --dsw-alias-bg-overlay: var(--dsw-static-neutral-bluish-150);\n --dsw-alias-bg-skeleton: #0000000a;\n --dsw-alias-border-inverted2: #0000;\n --dsw-alias-border-inverted: #0000;\n --dsw-alias-border-l1: #0000000a;\n --dsw-alias-border-l2-darkmode-thin: #0000001a;\n --dsw-alias-border-l2: #0000001a;\n --dsw-alias-border-l3: #0000001f;\n --dsw-alias-border-l4: #00000029;\n --dsw-alias-brand-primary-invert: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-brand-primary-new-colorprimary-new-color: #4176e6;\n --dsw-alias-brand-primary: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-brand-text: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-button-contrast-fill: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-button-elevated-fill: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-button-floating-fill: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-button-floating-hover: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-button-ghost-active-border: var(--dsw-static-neutral-bluish-500);\n --dsw-alias-button-ghost-active-fill: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-button-ghost-active-hover: var(--dsw-static-neutral-bluish-150);\n --dsw-alias-button-info-fill: var(--dsw-static-deepseek-500);\n --dsw-alias-button-info-hover: var(--dsw-static-deepseek-400);\n --dsw-alias-button-primary-dimmed: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-button-primary-fill: var(--dsw-alias-brand-primary);\n --dsw-alias-button-primary-hover: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-tool-bar-fill-invisible: #1f1f1f5c;\n --dsw-alias-button-tool-bar-fill: #54555780;\n --dsw-alias-button-tool-bar-hover: #54555799;\n --dsw-alias-interactive-bg-active: #2631481a;\n --dsw-alias-interactive-bg-hover-accent: #26314824;\n --dsw-alias-interactive-bg-hover-danger: #ec13130d;\n --dsw-alias-interactive-bg-hover-solid: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-interactive-bg-hover: #2631480f;\n --dsw-alias-label-caption: var(--dsw-static-neutral-bluish-400);\n --dsw-alias-label-deep-diving: color-mix(in srgb, var(--dsw-static-deepseek-500) 70%, var(--dsw-static-blue-950));\n --dsw-alias-label-deep-diving-shimmer: color-mix(in srgb, var(--dsw-static-deepseek-500) 30%, var(--dsw-static-blue-950));\n --dsw-alias-label-dimmed: var(--dsw-static-neutral-bluish-200);\n --dsw-alias-label-primary-bluish: var(--dsw-static-blue-900);\n --dsw-alias-label-primary-dimmed: var(--dsw-static-neutral-bluish-950);\n --dsw-alias-label-primary-foreground: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-label-primary-inverted: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-label-primary: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-label-secondary: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-label-shimmer: color-mix(in srgb, var(--dsw-static-neutral-1000) 30%, transparent);\n --dsw-alias-label-tertiary: var(--dsw-static-neutral-bluish-600);\n --dsw-alias-menu-icon: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-link: var(--dsw-static-deepseek-500);\n --dsw-alias-markdown-citation: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-markdown-code-block-banner: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-markdown-code-block: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-markdown-code-segment-selected: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-markdown-code-segment-unselected: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-markdown-inline-code: var(--dsw-static-neutral-50);\n --dsw-alias-markdown-placeholder: var(--dsw-static-neutral-bluish-60);\n --dsw-alias-markdown-tag: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-scrollbar-bg-l1: var(--dsw-static-neutral-200);\n --dsw-alias-scrollbar-bg-l2: var(--dsw-static-neutral-200);\n --dsw-alias-scrollbar-hover-l1: var(--dsw-static-neutral-300);\n --dsw-alias-scrollbar-hover-l2: var(--dsw-static-neutral-300);\n --dsw-alias-state-business-primary: var(--dsw-static-deepseek-500);\n --dsw-alias-state-business-tertiary: var(--dsw-static-deepseek-100);\n --dsw-alias-code-diff-added: var(--dsw-static-green-500-a08);\n --dsw-alias-code-diff-deleted: var(--dsw-static-red-600-a08);\n --dsw-alias-file-diff-added-bg: #e6f4e7;\n --dsw-alias-file-diff-added-gutter: #edf7ed;\n --dsw-alias-file-diff-added-marker: #01a241;\n --dsw-alias-file-diff-deleted-bg: #fce6e2;\n --dsw-alias-file-diff-deleted-gutter: #fdece9;\n --dsw-alias-file-diff-deleted-marker: #ba2723;\n --dsw-alias-state-error-primary: var(--dsw-static-red-600);\n --dsw-alias-state-error-secondary: var(--dsw-static-red-400);\n --dsw-alias-state-idle-primary: var(--dsw-static-neutral-300);\n --dsw-alias-state-success-primary: var(--dsw-static-green-500);\n --dsw-alias-state-success-secondary: var(--dsw-static-green-400);\n --dsw-alias-state-success-tertiary: var(--dsw-static-green-100);\n --dsw-alias-state-warn-label: var(--dsw-static-amber-600);\n --dsw-alias-state-warn-primary: var(--dsw-static-amber-500);\n --dsw-alias-state-warn-secondary: var(--dsw-static-amber-400);\n --dsw-alias-state-warn-tertiary: var(--dsw-static-amber-100);\n --dsw-alias-switch-thumb: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-toast-bg: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-toast-label: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-tooltip-bg: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-tooltip-key-bg: color-mix(in srgb, var(--dsw-alias-tooltip-bg), white 18%);\n --dsw-alias-turn-trigger-bg: var(--dsw-alias-markdown-code-block);\n --dsw-alias-turn-trigger-bg-hover: var(--dsw-alias-interactive-bg-hover);\n --dsw-specific-bubble-highlight: var(--dsw-static-deepseek-200);\n --dsw-specific-bubble: var(--dsw-static-deepseek-50);\n --dsw-specific-input-major: var(--dsw-static-neutral-bluish-00);\n --dsw-specific-login-input: var(--dsw-static-neutral-bluish-50);\n --dsw-menu-surface-fill: #f8f9fa94;\n --dsw-alias-menu-group-header-fill: #f8f9faf0;\n --dsw-specific-menu: var(--dsw-menu-surface-fill);\n --dsw-specific-selector: var(--dsw-static-neutral-bluish-60);\n --dsw-specific-sidebar-fill: var(--dsw-static-neutral-bluish-50);\n --dsw-specific-sidebar-nav-item-active-accent: var(--dsw-static-deepseek-100);\n --dsw-specific-sidebar-nav-item-active: var(--dsw-static-neutral-bluish-100);\n --dsw-specific-sidebar-nav-item-hover: var(--dsw-static-neutral-bluish-75);\n --dsw-specific-tip: var(--dsw-static-neutral-bluish-60);\n}\n\n[data-dc-scheme=\"dark\"] {\n --dsw-alias-bg-base: var(--dsw-static-neutral-bluish-950);\n --dsw-alias-bg-document-preview: var(--dsw-static-neutral-bluish-950);\n --dsw-alias-bg-document-selection: color-mix(in srgb, var(--dsw-static-blue-500) 40%, transparent);\n --dsw-alias-label-document-preview: var(--dsw-static-neutral-bluish-300);\n --dsw-alias-bg-layer-1: var(--dsw-static-neutral-bluish-875);\n --dsw-alias-bg-layer-2: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-bg-layer-3: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-bg-mask-1: #00000080;\n --dsw-alias-bg-mask-2: #0003;\n --dsw-alias-bg-mask-3: #0000007a;\n --dsw-alias-bg-mask-photo: #000000e0;\n --dsw-alias-bg-mask-drop: #272730b3;\n --dsw-alias-bg-module-platform: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-bg-multi-select: var(--dsw-static-neutral-850);\n --dsw-alias-bg-overlay: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-bg-skeleton: #ffffff14;\n --dsw-alias-border-inverted2: #ffffff14;\n --dsw-alias-border-inverted: #ffffff0f;\n --dsw-alias-border-l1: #ffffff0f;\n --dsw-alias-border-l2-darkmode-thin: #ffffff0f;\n --dsw-alias-border-l2: #ffffff1f;\n --dsw-alias-border-l3: #ffffff29;\n --dsw-alias-border-l4: #fff3;\n --dsw-alias-brand-primary-invert: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-brand-primary-new-colorprimary-new-color: var(--dsw-static-deepseek-450);\n --dsw-alias-brand-primary: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-brand-text: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-button-contrast-fill: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-button-elevated-fill: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-floating-fill: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-button-floating-hover: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-button-ghost-active-border: var(--dsw-static-neutral-bluish-600);\n --dsw-alias-button-ghost-active-fill: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-ghost-active-hover: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-button-info-fill: var(--dsw-static-deepseek-400);\n --dsw-alias-button-info-hover: var(--dsw-static-deepseek-500);\n --dsw-alias-button-primary-dimmed: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-primary-fill: var(--dsw-alias-brand-primary);\n --dsw-alias-button-primary-hover: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-button-tool-bar-fill-invisible: #1f1f1f5c;\n --dsw-alias-button-tool-bar-fill: #54555780;\n --dsw-alias-button-tool-bar-hover: #54555799;\n --dsw-alias-interactive-bg-active: #ffffff24;\n --dsw-alias-interactive-bg-hover-accent: #ffffff3d;\n --dsw-alias-interactive-bg-hover-danger: #f25a5a26;\n --dsw-alias-interactive-bg-hover-solid: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-interactive-bg-hover: #ffffff14;\n --dsw-alias-label-caption: var(--dsw-static-neutral-bluish-600);\n --dsw-alias-label-deep-diving: color-mix(in srgb, var(--dsw-static-deepseek-450) 55%, var(--dsw-static-neutral-bluish-400));\n --dsw-alias-label-deep-diving-shimmer: color-mix(in srgb, var(--dsw-static-blue-300) 65%, var(--dsw-static-deepseek-400));\n --dsw-alias-label-dimmed: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-label-primary-bluish: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-label-primary-dimmed: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-label-primary-foreground: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-label-primary-inverted: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-label-primary: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-label-secondary: var(--dsw-static-neutral-bluish-300);\n --dsw-alias-label-shimmer: color-mix(in srgb, var(--dsw-static-neutral-00) 45%, transparent);\n --dsw-alias-label-tertiary: var(--dsw-static-neutral-bluish-400);\n --dsw-alias-menu-icon: var(--dsw-alias-label-primary-dimmed);\n --dsw-alias-link: var(--dsw-static-deepseek-400);\n --dsw-alias-markdown-citation: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-markdown-code-block-banner: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-markdown-code-block: var(--dsw-static-neutral-bluish-900);\n --dsw-alias-markdown-code-segment-selected: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-markdown-code-segment-unselected: var(--dsw-static-neutral-bluish-900);\n --dsw-alias-markdown-inline-code: var(--dsw-static-neutral-800);\n --dsw-alias-markdown-placeholder: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-markdown-tag: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-scrollbar-bg-l1: var(--dsw-static-neutral-700);\n --dsw-alias-scrollbar-bg-l2: var(--dsw-static-neutral-600);\n --dsw-alias-scrollbar-hover-l1: var(--dsw-static-neutral-600);\n --dsw-alias-scrollbar-hover-l2: var(--dsw-static-neutral-550);\n --dsw-alias-state-business-primary: var(--dsw-static-deepseek-400);\n --dsw-alias-state-business-tertiary: var(--dsw-static-deepseek-800);\n --dsw-alias-code-diff-added: var(--dsw-static-green-500-a12);\n --dsw-alias-code-diff-deleted: var(--dsw-static-red-400-a12);\n --dsw-alias-file-diff-added-bg: #1f3124;\n --dsw-alias-file-diff-added-gutter: #132016;\n --dsw-alias-file-diff-added-marker: #41c977;\n --dsw-alias-file-diff-deleted-bg: #3c1f1b;\n --dsw-alias-file-diff-deleted-gutter: #28130e;\n --dsw-alias-file-diff-deleted-marker: #fa423e;\n --dsw-alias-state-error-primary: var(--dsw-static-red-400);\n --dsw-alias-state-error-secondary: var(--dsw-static-red-400);\n --dsw-alias-state-idle-primary: var(--dsw-static-neutral-600);\n --dsw-alias-state-success-primary: var(--dsw-static-green-500);\n --dsw-alias-state-success-secondary: var(--dsw-static-green-400);\n --dsw-alias-state-success-tertiary: var(--dsw-static-green-900);\n --dsw-alias-state-warn-label: var(--dsw-static-amber-600);\n --dsw-alias-state-warn-primary: var(--dsw-static-amber-500);\n --dsw-alias-state-warn-secondary: var(--dsw-static-amber-400);\n --dsw-alias-state-warn-tertiary: var(--dsw-static-amber-900);\n --dsw-alias-switch-thumb: var(--dsw-static-neutral-bluish-400);\n --dsw-alias-toast-bg: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-toast-label: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-turn-trigger-bg: var(--dsw-alias-interactive-bg-hover);\n --dsw-alias-turn-trigger-bg-hover: var(--dsw-alias-interactive-bg-active);\n --dsw-alias-tooltip-bg: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-tooltip-key-bg: color-mix(in srgb, var(--dsw-alias-tooltip-bg), white 18%);\n --dsw-specific-bubble-highlight: var(--dsw-static-neutral-bluish-750);\n --dsw-specific-bubble: var(--dsw-static-neutral-bluish-850);\n --dsw-specific-input-major: var(--dsw-static-neutral-bluish-850);\n --dsw-specific-login-input: var(--dsw-static-neutral-bluish-900);\n --dsw-menu-surface-fill: #43454a73;\n --dsw-alias-menu-group-header-fill: #303136f0;\n --dsw-specific-menu: var(--dsw-menu-surface-fill);\n --dsw-specific-selector: var(--dsw-static-neutral-bluish-800);\n --dsw-specific-sidebar-fill: var(--dsw-static-neutral-bluish-900);\n --dsw-specific-sidebar-nav-item-active-accent: var(--dsw-static-neutral-bluish-800);\n --dsw-specific-sidebar-nav-item-active: var(--dsw-static-neutral-bluish-750);\n --dsw-specific-sidebar-nav-item-hover: var(--dsw-static-neutral-bluish-850);\n --dsw-specific-tip: var(--dsw-static-neutral-bluish-800);\n}\n\nhtml[data-platform=\"darwin\"] [data-dc-scheme=\"light\"] {\n --dsw-specific-menu: #f8f9faf0;\n}\n\nhtml[data-platform=\"darwin\"] [data-dc-scheme=\"dark\"] {\n --dsw-specific-menu: #303136f0;\n}\n\n[data-dc-scheme=\"light\"] {\n color-scheme: light;\n}\n\n[data-dc-scheme=\"dark\"] {\n color-scheme: dark;\n}\n.dshc_embed {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n padding-top: var(--dsh-frame-top-clearance, 0px);\n background: var(--dsw-alias-bg-base);\n display: flex;\n}\n\n.dshc_frame {\n background: var(--dsw-alias-bg-base);\n border: 0;\n flex: 1;\n width: 100%;\n height: 100%;\n}\n";document.head.appendChild(s)})();
|
|
8
|
+
(function(){var s=document.createElement('style');s.setAttribute('data-dsh-plugin',"bothouse-dsh-channels");s.textContent=".dshc_page {\n width: 100%;\n height: 100%;\n min-height: 0;\n padding-top: var(--dsh-frame-top-clearance, 0px);\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n grid-template-columns: 220px minmax(0, 1fr) 240px;\n font-size: 14px;\n line-height: 1.5;\n display: grid;\n}\n\n.dshc_notice {\n max-width: 480px;\n color: var(--dsw-alias-label-secondary);\n text-align: center;\n grid-column: 1 / -1;\n place-self: center;\n padding: 24px;\n}\n\n.dshc_rooms, .dshc_members {\n background: var(--dsw-alias-bg-layer-2);\n min-height: 0;\n padding: 0 8px 16px;\n overflow-y: auto;\n}\n\n.dshc_membersHead {\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n flex: none;\n height: 44px;\n margin: 0 0 8px;\n}\n\n.dshc_groupTitle {\n color: var(--dsw-alias-label-tertiary);\n letter-spacing: .02em;\n margin: 12px 8px 6px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_room {\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n display: flex;\n}\n\n.dshc_room:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_room:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_workspace {\n padding-left: 22px;\n font-size: .95em;\n}\n\n.dshc_roomLabel {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_activeCount {\n color: var(--dsw-alias-label-tertiary);\n flex-shrink: 0;\n margin-left: auto;\n font-size: 11px;\n}\n\n.dshc_activeBusy {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_roomActive {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_hash {\n color: var(--dsw-alias-label-tertiary);\n font-size: 16px;\n}\n\n.dshc_chat {\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n display: flex;\n}\n\n.dshc_chatHead {\n -webkit-app-region: drag;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n align-items: center;\n gap: 6px;\n height: 44px;\n padding: 0 16px;\n font-weight: 600;\n display: flex;\n}\n\n.dshc_log {\n flex: 1;\n min-height: 0;\n padding: 8px 0 12px;\n overflow-y: auto;\n}\n\n.dshc_guide {\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n color: var(--dsw-alias-label-secondary);\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n margin: 16px;\n padding: 14px 16px;\n display: flex;\n}\n\n.dshc_empty {\n color: var(--dsw-alias-label-tertiary);\n padding: 24px 16px;\n}\n\n.dshc_message {\n grid-template-columns: 36px minmax(0, 1fr);\n gap: 12px;\n padding: 6px 16px;\n display: grid;\n}\n\n.dshc_system .dshc_text {\n color: var(--dsw-alias-label-tertiary);\n font-style: italic;\n}\n\n.dshc_avatar {\n corner-shape: round;\n color: #fff;\n border-radius: 50%;\n flex: none;\n place-items: center;\n width: 36px;\n height: 36px;\n font-weight: 700;\n display: grid;\n position: relative;\n}\n\n.dshc_avatarImage {\n object-fit: cover;\n border-radius: 50%;\n width: 100%;\n height: 100%;\n position: absolute;\n inset: 0;\n}\n\n.dshc_avatarSmall {\n width: 28px;\n height: 28px;\n font-size: 12px;\n}\n\n.dshc_avatarAnchor {\n align-self: start;\n position: relative;\n}\n\n.dshc_avatarButton {\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 50%;\n padding: 0;\n}\n\n.dshc_avatarMenu {\n z-index: 3;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-base);\n min-width: 140px;\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n flex-direction: column;\n padding: 6px;\n display: flex;\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n}\n\n.dshc_avatarMenuTitle {\n padding: 4px 8px;\n font-weight: 700;\n}\n\n.dshc_avatarMenuItem {\n border-radius: var(--dsw-radius-sm);\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 6px 8px;\n}\n\n.dshc_avatarMenuItem:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_messageBody {\n min-width: 0;\n}\n\n.dshc_messageHead {\n align-items: baseline;\n gap: 6px;\n display: flex;\n}\n\n.dshc_author {\n font-weight: 600;\n}\n\n.dshc_time {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.dshc_tag {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-state-business-primary);\n white-space: nowrap;\n padding: 0 4px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.dshc_edited {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.dshc_ownActions {\n margin-left: auto;\n margin-right: var(--channels-reply-clearance);\n opacity: 0;\n gap: 8px;\n display: inline-flex;\n}\n\n.dshc_message:hover .dshc_ownActions, .dshc_ownActions:focus-within {\n opacity: 1;\n}\n\n@media (hover: none) {\n .dshc_ownActions {\n display: none;\n }\n\n .dshc_message[data-tools-open] .dshc_ownActions {\n opacity: 1;\n display: inline-flex;\n }\n}\n\n.dshc_editBox {\n flex-direction: column;\n gap: 6px;\n margin-top: 4px;\n display: flex;\n}\n\n.dshc_editInput {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-base);\n min-height: 60px;\n color: inherit;\n font: inherit;\n resize: vertical;\n padding: 8px;\n}\n\n.dshc_editBar {\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_editBar .dshc_time {\n margin-right: auto;\n}\n\n.dshc_text {\n white-space: normal;\n overflow-wrap: anywhere;\n}\n\n.dshc_card {\n border-left: 4px solid var(--dsw-alias-state-business-primary);\n border-radius: var(--dsw-radius-sm);\n background: var(--dsw-alias-bg-layer-2);\n margin-top: 4px;\n padding: 6px 10px;\n}\n\n.dshc_card[data-state=\"failed\"] {\n border-left-color: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_card[data-state=\"retrying\"] {\n border-left-color: var(--dsw-alias-state-warning-primary, #faa61a);\n}\n\n.dshc_cardButtons {\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 6px;\n display: flex;\n}\n\n.dshc_cardTitle {\n font-size: 13px;\n font-weight: 600;\n}\n\n.dshc_cardHead {\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_cardViews {\n gap: 4px;\n display: flex;\n}\n\n.dshc_cardViewButton {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n color: inherit;\n cursor: pointer;\n background: none;\n padding: 0 6px;\n font-size: 12px;\n}\n\n.dshc_cardViewButton[aria-pressed=\"true\"] {\n background: var(--dsw-alias-bg-base);\n font-weight: 600;\n}\n\n.dshc_permActions {\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 6px;\n display: flex;\n}\n\n.dshc_permScope {\n color: var(--dsw-alias-text-secondary);\n flex-basis: 100%;\n font-size: 12px;\n}\n\n.dshc_permStatus {\n margin-top: 6px;\n font-size: 13px;\n font-weight: 600;\n}\n\n.dshc_message {\n --channels-reply-clearance: 36px;\n position: relative;\n}\n\n.dshc_quote {\n border-left: 2px solid var(--dsw-alias-label-tertiary);\n min-width: 0;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n text-overflow: ellipsis;\n gap: 6px;\n margin-bottom: 2px;\n padding-left: 8px;\n font-size: 12px;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_quoteDeleted {\n font-style: italic;\n}\n\n.dshc_quoteAuthor {\n flex: none;\n font-weight: 600;\n}\n\n.dshc_replyButton {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n border: 0;\n padding: 0 6px;\n}\n\n.dshc_msgTools {\n z-index: 1;\n border: 1px solid var(--dsw-alias-separator, #7f7f7f40);\n border-radius: var(--dsw-radius-sm, 6px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-primary, #fff));\n opacity: 0;\n pointer-events: none;\n align-items: center;\n gap: 2px;\n padding: 2px;\n display: inline-flex;\n position: absolute;\n top: 2px;\n right: 16px;\n box-shadow: 0 2px 6px #00000014;\n}\n\n.dshc_msgTool {\n border-radius: var(--dsw-radius-sm, 6px);\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 2px 8px;\n font-size: 12px;\n}\n\n.dshc_msgTool:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_msgTool:disabled {\n cursor: default;\n opacity: .6;\n}\n\n.dshc_msgToolNote {\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n padding: 0 6px;\n font-size: 12px;\n}\n\n.dshc_message:hover .dshc_msgTools, .dshc_message:focus-within .dshc_msgTools, .dshc_msgTools:has(.dshc_msgToolNote) {\n opacity: 1;\n pointer-events: auto;\n}\n\n@media (hover: none) {\n .dshc_message {\n -webkit-touch-callout: none;\n }\n\n .dshc_msgTools {\n display: none;\n }\n\n .dshc_message[data-tools-open] {\n background: var(--dsw-alias-interactive-bg-hover);\n }\n\n .dshc_message[data-tools-open] .dshc_msgTools {\n z-index: 3;\n opacity: 1;\n pointer-events: auto;\n display: inline-flex;\n top: -14px;\n right: 12px;\n }\n\n .dshc_message[data-tools-open] .dshc_msgTool {\n width: 40px;\n height: 36px;\n }\n}\n\n.dshc_replyBar {\n border-radius: var(--dsw-radius-sm);\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-2);\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n padding: 4px 8px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_replyBarText {\n white-space: nowrap;\n text-overflow: ellipsis;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n}\n\n.dshc_replyCancel {\n color: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n}\n\n.dshc_working {\n min-height: 20px;\n color: var(--dsw-alias-label-tertiary);\n align-items: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_composer {\n padding: 0 16px 16px;\n position: relative;\n}\n\n.dshc_composerBox {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: var(--dsw-radius-xl);\n background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-layer-2));\n box-shadow: var(--dsw-shadow-lv1-blur);\n flex-direction: column;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_composerBox:focus-within {\n border-color: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_composerToolbar {\n flex: none;\n align-items: center;\n gap: 4px;\n padding: 4px 10px 10px;\n display: flex;\n}\n\n.dshc_composerBox > .dshc_replyBar, .dshc_composerBox > .dshc_attachments {\n margin: 10px 14px 0;\n}\n\n.dshc_mentionButton {\n border-radius: var(--dsw-radius-full, 50%);\n width: 32px;\n height: 32px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n justify-content: center;\n align-items: center;\n padding: 0;\n font-size: 18px;\n display: inline-flex;\n}\n\n.dshc_mentionButton:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_sendButton {\n border-radius: var(--dsw-radius-full, 50%);\n width: 32px;\n height: 32px;\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-label-primary);\n font: inherit;\n cursor: pointer;\n border: 0;\n flex: none;\n justify-content: center;\n align-items: center;\n margin-left: auto;\n padding: 0;\n display: inline-flex;\n}\n\n.dshc_sendButton:disabled {\n opacity: .45;\n cursor: default;\n}\n\n.dshc_sendButton:focus-visible, .dshc_mentionButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_mentionPopup {\n z-index: 2;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-base);\n max-height: 320px;\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n flex-direction: column;\n padding: 6px;\n display: flex;\n position: absolute;\n bottom: calc(100% - 8px);\n left: 16px;\n right: 16px;\n}\n\n.dshc_mentionPopup > [role=\"listbox\"] {\n min-height: 0;\n overflow-y: auto;\n}\n\n.dshc_mentionTitle {\n color: var(--dsw-alias-label-tertiary);\n padding: 4px 8px 6px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_mentionItem {\n border-radius: var(--dsw-radius-sm);\n cursor: pointer;\n align-items: center;\n gap: 10px;\n padding: 6px 8px;\n display: flex;\n}\n\n.dshc_mentionActive {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_mentionName {\n font-weight: 600;\n}\n\n.dshc_mentionMeta {\n color: var(--dsw-alias-label-tertiary);\n margin-left: auto;\n font-size: 12px;\n}\n\n.dshc_mentionHint {\n border-top: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px 2px;\n font-size: 11px;\n}\n\n.dshc_mentionBadge {\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #5865f2) 15%, transparent);\n color: var(--dsw-alias-state-business-primary, #5865f2);\n font-weight: 500;\n line-height: inherit;\n vertical-align: baseline;\n -webkit-box-decoration-break: clone;\n box-decoration-break: clone;\n cursor: pointer;\n border-radius: 3px;\n padding: 0 4px;\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1), color .16s cubic-bezier(.2, 0, 0, 1);\n display: inline-block;\n}\n\n.dshc_mentionBadge:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #5865f2) 28%, transparent);\n}\n\n.dshc_sendError {\n color: var(--dsw-alias-state-error-primary);\n margin-bottom: 6px;\n font-size: 12px;\n}\n\n.dshc_inputSurface {\n min-width: 0;\n display: flex;\n position: relative;\n}\n\n.dshc_input, .dshc_inputHighlights {\n box-sizing: border-box;\n width: 100%;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n white-space: pre-wrap;\n overflow-wrap: break-word;\n background: none;\n border: 0;\n border-radius: 0;\n min-width: 0;\n padding: 14px 16px 8px;\n line-height: 1.5;\n}\n\n.dshc_input {\n resize: none;\n field-sizing: content;\n color: #0000;\n min-width: 0;\n min-height: 52px;\n max-height: 40vh;\n caret-color: var(--dsw-alias-label-primary);\n flex: 1;\n}\n\n.dshc_inputHighlights {\n pointer-events: none;\n height: 100%;\n position: absolute;\n inset: 0;\n overflow: hidden;\n}\n\n.dshc_inputHighlights .dshc_inputMention {\n font-weight: inherit;\n cursor: inherit;\n padding: 0;\n display: inline;\n}\n\n.dshc_inputComposing .dshc_input {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_inputComposing .dshc_inputHighlights {\n visibility: hidden;\n}\n\n@media (forced-colors: active) {\n .dshc_input {\n color: canvastext;\n caret-color: canvastext;\n }\n\n .dshc_inputHighlights {\n display: none;\n }\n}\n\n.dshc_input::placeholder {\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n@media (width <= 600px) {\n .dshc_composer {\n padding: 0 8px 8px;\n }\n\n .dshc_composerToolbar {\n padding: 4px 8px 8px;\n }\n\n .dshc_input, .dshc_inputHighlights {\n padding: 12px 14px 8px;\n }\n}\n\n.dshc_input:focus-visible {\n outline: none;\n}\n\n.dshc_member {\n border-radius: var(--dsw-radius-sm);\n min-width: 0;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: 1;\n align-items: center;\n gap: 10px;\n padding: 5px 8px;\n display: flex;\n}\n\n.dshc_member:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_member:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_offline {\n opacity: .5;\n}\n\n.dshc_memberAvatar {\n position: relative;\n}\n\n.dshc_presence {\n border: 2px solid var(--dsw-alias-bg-layer-2);\n corner-shape: round;\n background: #23a55a;\n border-radius: 50%;\n width: 10px;\n height: 10px;\n position: absolute;\n bottom: -2px;\n right: -2px;\n}\n\n.dshc_offline .dshc_presence {\n background: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_memberText {\n flex-direction: column;\n min-width: 0;\n display: flex;\n}\n\n.dshc_memberName {\n align-items: center;\n gap: 6px;\n display: flex;\n}\n\n.dshc_memberStatus {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n@media (width <= 900px) {\n .dshc_page {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .dshc_rooms, .dshc_members {\n display: none;\n }\n}\n\n.dshc_groupHead {\n justify-content: space-between;\n align-items: center;\n padding-right: 4px;\n display: flex;\n}\n\n.dshc_iconButton {\n border-radius: var(--dsw-radius-sm);\n width: 22px;\n height: 22px;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n font-size: 16px;\n line-height: 1;\n}\n\n.dshc_iconButton:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_roomInput, .dshc_fieldInput {\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n font: inherit;\n padding: 6px 8px;\n}\n\n.dshc_roomInput {\n margin: 2px 0 6px;\n}\n\n.dshc_roomInput:focus-visible, .dshc_fieldInput:focus-visible, .dshc_iconButton:focus-visible, .dshc_linkButton:focus-visible, .dshc_addMemberButton:focus-visible, .dshc_primaryButton:focus-visible, .dshc_secondaryButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_addMemberButton {\n box-sizing: border-box;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n margin: 4px 0;\n padding: 7px 8px;\n}\n\n.dshc_addMemberButton:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_settingsDivider {\n border: 0;\n border-top: 1px solid var(--dsw-alias-border-l1);\n width: 100%;\n margin: 8px 0 4px;\n}\n\n.dshc_memberListDivider {\n border: 0;\n border-top: 1px solid var(--dsw-alias-border-l1);\n width: calc(100% - 16px);\n margin: 12px 8px 2px;\n}\n\n.dshc_settingsButton {\n text-align: left;\n border-style: solid;\n border-color: #0000;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_settingsButton svg {\n flex: none;\n}\n\n.dshc_memberRow {\n border-radius: var(--dsw-radius-sm);\n align-items: center;\n display: flex;\n position: relative;\n}\n\n.dshc_memberRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_memberRow .dshc_member:hover {\n background: none;\n}\n\n.dshc_memberModel {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n overflow: hidden;\n}\n\n.dshc_memberActions {\n color: var(--dsw-alias-label-tertiary);\n opacity: 0;\n flex: none;\n gap: 4px;\n padding-right: 6px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_memberRow:hover .dshc_memberActions, .dshc_memberRow:focus-within .dshc_memberActions {\n opacity: 1;\n}\n\n.dshc_linkButton {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 2px 4px;\n font-size: 12px;\n}\n\n.dshc_linkButton:hover {\n color: var(--dsw-alias-label-primary);\n text-decoration: underline;\n}\n\n.dshc_dialogBackdrop {\n z-index: 10;\n background: #00000073;\n place-items: center;\n padding: 16px;\n display: grid;\n position: fixed;\n inset: 0;\n}\n\n.dshc_dialog {\n box-sizing: border-box;\n border-radius: var(--dsw-radius-lg);\n width: min(460px, 100%);\n max-height: calc(100vh - 32px);\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n box-shadow: var(--dsw-elevation-panel, 0 12px 32px #0000004d);\n flex-direction: column;\n gap: 12px;\n padding: 20px;\n display: flex;\n overflow-y: auto;\n}\n\n.dshc_settingsDialog {\n box-sizing: border-box;\n height: min(70vh, 100dvh - 32px);\n max-height: calc(100dvh - 32px);\n overflow: hidden;\n}\n\n.dshc_settingsBody {\n overscroll-behavior: contain;\n flex: auto;\n min-height: 0;\n padding: 2px;\n overflow-y: auto;\n}\n\n.dshc_settingsDialog > :not(.dshc_settingsBody) {\n flex-shrink: 0;\n}\n\n.dshc_settingsFields {\n flex-direction: column;\n gap: 12px;\n display: flex;\n}\n\n.dshc_settingsFeedback {\n overflow-wrap: anywhere;\n max-height: 20%;\n overflow-y: auto;\n}\n\n.dshc_settingsDialog .dshc_dialogActions {\n justify-content: space-between;\n}\n\n@media (width <= 599px) {\n .dshc_settingsDialog {\n height: calc(100dvh - 32px);\n padding: 16px;\n }\n}\n\n.dshc_avatarCropStage {\n background: #000000a6;\n padding: 20px;\n}\n\n.dshc_avatarCropFrame {\n aspect-ratio: 1;\n touch-action: none;\n cursor: grab;\n outline: 2px solid #fff;\n width: min(256px, 100%);\n margin: auto;\n position: relative;\n overflow: hidden;\n}\n\n.dshc_avatarCropFrame:active {\n cursor: grabbing;\n}\n\n.dshc_avatarCropFrame img {\n pointer-events: none;\n user-select: none;\n max-width: none;\n position: absolute;\n}\n\n.dshc_dialogTitle {\n margin: 0;\n font-size: 16px;\n}\n\n.dshc_field {\n color: var(--dsw-alias-label-secondary);\n flex-direction: column;\n gap: 4px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_field textarea {\n resize: vertical;\n}\n\n.dshc_choiceRow {\n flex-wrap: wrap;\n gap: 12px;\n display: flex;\n}\n\n.dshc_choiceColumn {\n flex-direction: column;\n gap: 12px;\n display: flex;\n}\n\n.dshc_choiceColumn .dshc_choice {\n grid-template-columns: max-content minmax(0, 1fr);\n align-items: start;\n display: grid;\n}\n\n.dshc_choice {\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n align-items: center;\n gap: 4px;\n font-size: 13px;\n display: inline-flex;\n}\n\n.dshc_dialogActions {\n justify-content: flex-end;\n gap: 8px;\n display: flex;\n}\n\n.dshc_primaryButton, .dshc_secondaryButton {\n border-radius: var(--dsw-radius-sm);\n font: inherit;\n cursor: pointer;\n padding: 7px 14px;\n}\n\n.dshc_primaryButton {\n color: #fff;\n background: var(--dsw-alias-state-business-primary);\n border: 0;\n}\n\n.dshc_primaryButton:disabled {\n cursor: not-allowed;\n opacity: .5;\n}\n\n.dshc_secondaryButton {\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n background: none;\n}\n\n.dshc_memberFailed .dshc_memberStatus {\n color: var(--dsw-alias-state-error-primary, #ed4245);\n}\n\n.dshc_memberFailed .dshc_presence {\n background: #ed4245;\n}\n\n.dshc_mobileButton, .dshc_drawerBackdrop {\n display: none;\n}\n\n.dshc_panelToggle {\n border-radius: var(--dsw-radius-sm);\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n justify-content: center;\n align-items: center;\n font-size: 16px;\n line-height: 1;\n display: inline-flex;\n}\n\n.dshc_panelToggle:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_panelToggle:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_panelToggleEnd {\n margin-left: auto;\n}\n\n.dshc_capabilityMenu {\n z-index: 4;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n background: var(--dsw-alias-bg-base);\n max-height: 260px;\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n gap: 2px;\n padding: 5px;\n display: grid;\n position: absolute;\n bottom: calc(100% + 4px);\n left: 0;\n right: 0;\n overflow-y: auto;\n}\n\n.dshc_capabilityItem:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_capabilityHelp {\n color: var(--dsw-alias-label-secondary);\n white-space: normal;\n margin: 6px;\n font-size: 12px;\n}\n\n.dshc_capabilityItem:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_capabilityItem {\n text-align: left;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n background: none;\n border: 0;\n padding: 5px 8px;\n font-size: 12px;\n font-weight: 400;\n}\n\n@media (width >= 901px) {\n .dshc_page.dshc_roomsHidden {\n grid-template-columns: minmax(0, 1fr) 240px;\n }\n\n .dshc_page.dshc_membersHidden {\n grid-template-columns: 220px minmax(0, 1fr);\n }\n\n .dshc_page.dshc_roomsHidden.dshc_membersHidden {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .dshc_roomsHidden > .dshc_rooms, .dshc_membersHidden > .dshc_members {\n display: none;\n }\n}\n\n@media (width <= 900px) {\n .dshc_page {\n position: relative;\n overflow: hidden;\n }\n\n .dshc_mobileButton {\n min-width: 40px;\n min-height: 40px;\n color: inherit;\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 6px;\n justify-content: center;\n align-items: center;\n display: inline-flex;\n }\n\n .dshc_mobileButton:last-child {\n margin-left: auto;\n }\n\n .dshc_panelToggle {\n display: none;\n }\n\n .dshc_drawerBackdrop {\n z-index: 4;\n background: #00000073;\n border: 0;\n display: block;\n position: absolute;\n inset: 0;\n }\n\n .dshc_rooms.dshc_drawerOpen, .dshc_members.dshc_drawerOpen {\n z-index: 5;\n box-sizing: border-box;\n width: min(280px, 85%);\n display: block;\n position: absolute;\n top: 0;\n bottom: 0;\n box-shadow: 0 0 24px #0000004d;\n }\n\n .dshc_rooms.dshc_drawerOpen {\n left: 0;\n }\n\n .dshc_members.dshc_drawerOpen {\n right: 0;\n }\n\n .dshc_memberActions {\n opacity: 1;\n }\n\n .dshc_messageHead {\n flex-wrap: wrap;\n }\n}\n\n.dshc_avatarPicker {\n flex-wrap: wrap;\n gap: 8px;\n display: flex;\n}\n\n.dshc_avatarOption {\n cursor: pointer;\n background: none;\n border: 2px solid #0000;\n border-radius: 50%;\n padding: 2px;\n display: grid;\n}\n\n.dshc_avatarOption[aria-checked=\"true\"] {\n border-color: var(--dsw-alias-accent, #5865f2);\n}\n\n.dshc_taskBoardTrigger {\n box-sizing: border-box;\n border-radius: var(--dsw-radius-sm);\n width: 100%;\n height: 34px;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n align-items: center;\n gap: 8px;\n margin: 4px 0;\n padding: 7px 8px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_taskBoardTrigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_taskBoardTrigger:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_taskBoardSummary {\n color: var(--dsw-alias-label-secondary);\n text-align: right;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n overflow: hidden;\n}\n\n.dshc_taskBoardDialog {\n width: min(920px, 100%);\n height: min(70vh, 100dvh - 32px);\n overflow: hidden;\n}\n\n.dshc_taskBoardBody {\n overscroll-behavior: contain;\n flex: auto;\n min-height: 0;\n overflow-y: auto;\n}\n\n.dshc_taskBoardDialog > :not(.dshc_taskBoardBody) {\n flex-shrink: 0;\n}\n\n.dshc_boardTabs {\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n gap: 4px;\n padding-bottom: 8px;\n display: flex;\n overflow-x: auto;\n}\n\n.dshc_boardTabs button {\n border-radius: var(--dsw-radius-sm);\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n padding: 7px 10px;\n font-size: 13px;\n}\n\n.dshc_boardTabs button:hover, .dshc_boardTabs button[aria-selected=\"true\"] {\n color: var(--dsw-alias-state-business-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_boardTabs button:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n outline-offset: -2px;\n}\n\n.dshc_boardTabCount {\n font-variant-numeric: tabular-nums;\n margin-left: 4px;\n}\n\n.dshc_boardCardGrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin: 6px 0 16px;\n padding: 2px;\n list-style: none;\n display: grid;\n}\n\n.dshc_boardTaskCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n border-radius: 12px;\n flex-direction: column;\n gap: 10px;\n min-width: 0;\n padding: 14px;\n transition: transform .14s, box-shadow .14s;\n display: flex;\n}\n\n.dshc_boardTaskCard[data-working=\"true\"] {\n border-color: var(--dsw-alias-state-business-primary);\n}\n\n.dshc_boardTaskCard[data-status=\"done\"] {\n filter: saturate(.25);\n color: var(--dsw-alias-label-secondary);\n}\n\n.dshc_boardTaskCard:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px #00000014;\n}\n\n.dshc_boardTaskHeader {\n align-items: flex-start;\n gap: 8px;\n display: flex;\n}\n\n.dshc_boardTaskTitle {\n overflow-wrap: anywhere;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n flex: 1;\n font-size: 14px;\n font-weight: 600;\n line-height: 1.5;\n display: -webkit-box;\n overflow: hidden;\n}\n\n.dshc_boardTaskHeader .dshc_taskStatus {\n border-radius: 999px;\n padding: 3px 7px;\n font-size: 11px;\n}\n\n.dshc_boardTaskCard .dshc_taskLinks {\n justify-content: flex-end;\n margin-top: auto;\n}\n\n.dshc_boardTabEmpty {\n min-height: 240px;\n color: var(--dsw-alias-label-secondary);\n text-align: center;\n justify-content: center;\n align-items: center;\n padding: 40px 16px;\n display: flex;\n}\n\n@media (width <= 640px) {\n .dshc_boardCardGrid {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_boardTaskCard {\n transition: none;\n }\n\n .dshc_boardTaskCard:hover {\n transform: none;\n }\n}\n\n.dshc_taskBoardScope {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.dshc_taskSection + .dshc_taskSection {\n margin-top: 8px;\n}\n\n.dshc_taskSectionTitle {\n color: var(--dsw-alias-label-secondary);\n margin: 0 0 4px;\n padding: 0 8px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_taskOwnerGroup + .dshc_taskOwnerGroup {\n margin-top: 4px;\n}\n\n.dshc_taskOwnerName {\n color: var(--dsw-alias-label-tertiary);\n padding: 0 8px;\n font-size: 12px;\n}\n\n.dshc_taskList {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.dshc_taskRow {\n border-radius: var(--dsw-radius-sm);\n padding: 6px 8px;\n}\n\n.dshc_taskRow:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_taskLine {\n align-items: center;\n gap: 6px;\n min-width: 0;\n display: flex;\n}\n\n.dshc_taskTitle {\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n overflow: hidden;\n}\n\n.dshc_taskMeta {\n color: var(--dsw-alias-label-tertiary);\n flex-wrap: wrap;\n gap: 0 8px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_taskLinks {\n gap: 8px;\n display: flex;\n}\n\n.dshc_taskEmpty {\n color: var(--dsw-alias-label-tertiary);\n margin: 0 8px;\n font-size: 12px;\n}\n\n.dshc_taskDone summary {\n cursor: pointer;\n}\n\n.dshc_taskStatus {\n border-radius: var(--dsw-radius-xs);\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-state-business-primary);\n flex: none;\n padding: 0 4px;\n font-size: 10px;\n font-style: normal;\n font-weight: 600;\n}\n\n.dshc_taskStatus[data-status=\"asked\"], .dshc_taskStatus[data-status=\"escalated\"], .dshc_taskStatus[data-status=\"declined\"], .dshc_taskStatus[data-status=\"rework\"] {\n background: var(--dsw-alias-state-warning-primary, var(--dsw-alias-state-error-primary));\n}\n\n.dshc_taskStatus[data-status=\"done\"] {\n background: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_taskCard {\n margin-top: 4px;\n font-style: normal;\n}\n\n.dshc_taskMembers {\n margin: 4px 0 0;\n padding: 0;\n list-style: none;\n}\n\n.dshc_taskMember {\n align-items: center;\n gap: 8px;\n font-size: 13px;\n display: flex;\n}\n\n.dshc_taskMemberName {\n font-weight: 600;\n}\n\n.dshc_taskSub {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_focused {\n background: var(--dsw-alias-interactive-bg-hover);\n outline: 2px solid var(--dsw-alias-state-business-primary);\n}\n\n.dshc_attachments {\n flex-wrap: wrap;\n gap: 6px;\n margin: 4px 0;\n display: flex;\n}\n\n.dshc_attachmentImage {\n border-radius: 4px;\n max-width: min(320px, 100%);\n max-height: 240px;\n display: block;\n}\n\n.dshc_imageAttachment {\n align-content: start;\n max-width: 100%;\n display: grid;\n}\n\n.dshc_imagePreviewTrigger {\n cursor: zoom-in;\n background: none;\n border: 0;\n border-radius: 4px;\n grid-area: 1 / 1;\n padding: 0;\n}\n\n.dshc_imagePreviewTrigger:focus-visible {\n outline: 2px solid var(--dsw-alias-state-business-primary);\n outline-offset: 2px;\n}\n\n.dshc_imagePreviewDialog {\n box-sizing: border-box;\n width: fit-content;\n max-width: 90vw;\n max-height: 90dvh;\n overflow: auto;\n}\n\n.dshc_imagePreviewHeader {\n flex-shrink: 0;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n display: flex;\n}\n\n.dshc_imagePreviewHeader span {\n overflow-wrap: anywhere;\n min-width: 0;\n}\n\n.dshc_imagePreviewHeader button {\n flex-shrink: 0;\n}\n\n.dshc_imagePreviewImage {\n object-fit: contain;\n align-self: center;\n max-width: 100%;\n min-height: 0;\n max-height: 65dvh;\n display: block;\n}\n\n.dshc_imagePreviewActions {\n flex-wrap: wrap;\n flex-shrink: 0;\n gap: 8px;\n display: flex;\n}\n\n.dshc_imagePreviewActions a {\n text-decoration: none;\n}\n\n.dshc_imagePreviewActions button:disabled {\n cursor: wait;\n opacity: .5;\n}\n\n.dshc_attachmentActions {\n border-radius: var(--dsw-radius-sm);\n background: var(--dsw-alias-bg-base);\n opacity: 0;\n pointer-events: none;\n flex-wrap: wrap;\n grid-area: 1 / 1;\n place-self: start end;\n gap: 4px;\n margin: 4px;\n padding: 4px;\n display: flex;\n}\n\n.dshc_attachmentActions a {\n text-decoration: none;\n}\n\n.dshc_attachmentActions button:disabled {\n cursor: wait;\n opacity: .5;\n}\n\n.dshc_imageAttachment:hover .dshc_attachmentActions, .dshc_imageAttachment:focus-within .dshc_attachmentActions, .dshc_imageAttachment[data-copy-state=\"error\"] .dshc_attachmentActions, .dshc_imageAttachment[data-copy-state=\"copying\"] .dshc_attachmentActions, .dshc_imageAttachment[data-copy-state=\"copied\"] .dshc_attachmentActions {\n opacity: 1;\n pointer-events: auto;\n}\n\n@media (hover: none), (pointer: coarse), (width <= 768px) {\n .dshc_attachmentActions {\n opacity: 1;\n pointer-events: auto;\n }\n}\n\n.dshc_attachmentThumb {\n object-fit: cover;\n vertical-align: middle;\n border-radius: 3px;\n width: 32px;\n height: 32px;\n}\n\n.dshc_attachmentFile {\n background: var(--background-secondary, #7f7f7f26);\n color: inherit;\n border-radius: 4px;\n align-items: center;\n gap: 4px;\n padding: 4px 8px;\n text-decoration: none;\n display: inline-flex;\n}\n\n.dshc_attachmentSize {\n opacity: .6;\n font-size: .85em;\n}\n\n.dshc_attachmentAudio {\n flex-direction: column;\n gap: 2px;\n width: min(320px, 100%);\n display: flex;\n}\n\n.dshc_attachmentAudio audio {\n width: 100%;\n}\n\n.dshc_attachmentAudio a {\n color: inherit;\n text-decoration: none;\n}\n\n.dshc_attachmentFile {\n overflow-wrap: anywhere;\n max-width: 100%;\n}\n\n.dshc_uploadBar {\n background: var(--dsw-alias-border-l2);\n border-radius: 2px;\n flex: 80px;\n min-width: 60px;\n height: 4px;\n overflow: hidden;\n}\n\n.dshc_uploadFill {\n background: var(--dsw-alias-state-business-primary);\n height: 100%;\n transition: width .12s linear;\n display: block;\n}\n\n.dshc_uploadState {\n opacity: .7;\n white-space: nowrap;\n font-size: .85em;\n}\n\n.dshc_attachmentFile[data-state=\"failed\"] {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_attachmentFile[data-state=\"failed\"] .dshc_uploadState {\n opacity: 1;\n}\n\n.dshc_chatHead button, .dshc_chatHead input, .dshc_chatHead select, .dshc_chatHead a, .dshc_chatHead [role=\"menu\"] {\n -webkit-app-region: no-drag;\n}\n\n.dshc_text pre, .dshc_text table {\n max-width: 100%;\n overflow-x: auto;\n}\n\n@media (width >= 901px) {\n .dshc_page {\n grid-template-columns: 232px minmax(0, 1fr) 272px;\n }\n\n .dshc_page.dshc_roomsHidden {\n grid-template-columns: minmax(0, 1fr) 272px;\n }\n\n .dshc_page.dshc_membersHidden {\n grid-template-columns: 232px minmax(0, 1fr);\n }\n\n .dshc_page.dshc_roomsHidden.dshc_membersHidden {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.dshc_rooms {\n padding-top: 4px;\n}\n\n.dshc_room {\n border-radius: var(--dsw-radius-md);\n min-height: 34px;\n padding: 6px 10px;\n transition: background-color .15s, color .15s;\n}\n\n.dshc_workspace {\n min-height: 30px;\n padding-left: 28px;\n font-size: 13px;\n position: relative;\n}\n\n.dshc_workspace:before {\n content: \"\";\n border-left: 1px solid var(--dsw-alias-border-l2);\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n border-bottom-left-radius: 4px;\n width: 7px;\n position: absolute;\n top: 0;\n bottom: 50%;\n left: 15px;\n}\n\n.dshc_workspace .dshc_hash {\n display: none;\n}\n\n.dshc_roomActive {\n color: var(--dsw-alias-label-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent);\n box-shadow: inset 2px 0 0 var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.dshc_activeCount {\n font-variant-numeric: tabular-nums;\n border-radius: 999px;\n margin-left: 0;\n padding: 0 6px;\n font-size: 11px;\n line-height: 18px;\n}\n\n.dshc_activeBusy {\n color: var(--dsw-alias-state-business-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent);\n}\n\n.dshc_roomLabel {\n flex: auto;\n min-width: 0;\n}\n\n.dshc_avatarStack {\n flex: none;\n align-items: center;\n padding-left: 4px;\n display: inline-flex;\n}\n\n.dshc_stackDot {\n border: 2px solid var(--dsw-alias-bg-layer-2);\n background-position: center;\n background-size: cover;\n border-radius: 50%;\n width: 18px;\n height: 18px;\n margin-left: -6px;\n}\n\n.dshc_room .dshc_stackDot {\n width: 16px;\n height: 16px;\n}\n\n.dshc_roomActive .dshc_stackDot {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, var(--dsw-alias-bg-layer-2));\n}\n\n.dshc_chatHead {\n gap: 8px;\n height: 52px;\n}\n\n.dshc_roomName {\n letter-spacing: -.01em;\n white-space: nowrap;\n font-size: 15px;\n}\n\n.dshc_headMeta {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 400;\n overflow: hidden;\n}\n\n.dshc_headActivity {\n color: var(--dsw-alias-label-secondary);\n flex: none;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n font-size: 12px;\n font-weight: 500;\n display: inline-flex;\n}\n\n.dshc_headActivity .dshc_stackDot {\n border-color: var(--dsw-alias-bg-base);\n width: 22px;\n height: 22px;\n}\n\n.dshc_headActivity + .dshc_panelToggleEnd {\n margin-left: 4px;\n}\n\n.dshc_chatHead > .dshc_roomName {\n text-overflow: ellipsis;\n min-width: 0;\n overflow: hidden;\n}\n\n.dshc_chatHead > .dshc_taskBoardTrigger {\n width: auto;\n margin: 0 0 0 auto;\n}\n\n.dshc_chatHead .dshc_taskBoardSummary {\n display: none;\n}\n\n.dshc_headLead {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n border-radius: 999px;\n flex: 0 auto;\n align-items: center;\n gap: 6px;\n min-width: 0;\n padding: 4px 8px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.dshc_headLeadText {\n flex-direction: column;\n min-width: 0;\n display: flex;\n}\n\n.dshc_headLeadName {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_headLeadStatus {\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n font-size: 11px;\n font-weight: 400;\n}\n\n.dshc_headLead + .dshc_panelToggleEnd {\n margin-left: 0;\n}\n\n.dshc_membersHead {\n align-items: center;\n height: 52px;\n padding: 0 4px;\n display: flex;\n}\n\n.dshc_membersHeadTitle {\n color: var(--dsw-alias-label-tertiary);\n letter-spacing: .08em;\n font-size: 11px;\n font-weight: 650;\n}\n\n.dshc_log {\n padding: 12px 0 16px;\n}\n\n.dshc_message {\n grid-template-columns: 36px minmax(0, 1fr);\n gap: 12px;\n padding: 8px 20px 2px;\n transition: background-color .15s;\n}\n\n.dshc_message.dshc_grouped {\n padding-top: 1px;\n}\n\n.dshc_message:hover {\n background: color-mix(in srgb, var(--dsw-alias-interactive-bg-hover) 70%, transparent);\n}\n\n.dshc_messageHead {\n gap: 8px;\n margin-bottom: 2px;\n}\n\n.dshc_messageHeadGrouped {\n margin-bottom: 0;\n}\n\n.dshc_messageHeadGrouped:not(:has(*)) {\n display: none;\n}\n\n.dshc_author {\n font-size: 13px;\n}\n\n.dshc_time, .dshc_edited {\n font-variant-numeric: tabular-nums;\n font-size: 11px;\n}\n\n.dshc_gutterTime {\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n text-align: right;\n opacity: 0;\n align-self: start;\n padding-top: 3px;\n font-size: 10px;\n transition: opacity .15s;\n}\n\n.dshc_message:hover .dshc_gutterTime {\n opacity: 1;\n}\n\n.dshc_text {\n line-height: 1.6;\n}\n\n.dshc_own {\n grid-template-columns: minmax(0, 1fr) 36px;\n}\n\n.dshc_own > :first-child {\n grid-area: 1 / 2;\n}\n\n.dshc_own > .dshc_messageBody {\n flex-direction: column;\n grid-area: 1 / 1;\n align-items: flex-end;\n display: flex;\n}\n\n.dshc_own .dshc_messageHead {\n flex-direction: row-reverse;\n}\n\n.dshc_own .dshc_ownActions {\n margin-left: 0;\n margin-right: auto;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text {\n max-width: min(560px, 100%);\n color: var(--dsw-alias-label-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 16%, var(--dsw-alias-bg-layer-2));\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dsw-alias-state-business-primary) 28%, transparent);\n border-radius: 16px 6px 16px 16px;\n padding: 8px 14px;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_quote {\n align-self: flex-end;\n}\n\n.dshc_own .dshc_msgTools {\n left: 16px;\n right: auto;\n}\n\n.dshc_own.dshc_grouped > .dshc_messageBody > .dshc_text {\n border-radius: 16px 6px 6px 16px;\n}\n\n.dshc_systemRecord {\n flex-direction: column;\n align-items: center;\n padding-bottom: 4px;\n display: flex;\n}\n\n.dshc_systemRecord > .dshc_systemLine {\n align-self: stretch;\n}\n\n.dshc_taskRecord {\n box-sizing: border-box;\n width: min(480px, 100% - 40px);\n margin-top: 0;\n}\n\n.dshc_systemLine {\n color: var(--dsw-alias-label-tertiary);\n align-items: center;\n gap: 12px;\n padding: 10px 32px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_systemLine:before, .dshc_systemLine:after {\n content: \"\";\n background: var(--dsw-alias-border-l2);\n flex: 24px;\n min-width: 24px;\n height: 1px;\n}\n\n.dshc_systemText {\n text-align: center;\n max-width: min(640px, 80%);\n}\n\n.dshc_systemText p {\n margin: 0;\n}\n\n.dshc_errorBar {\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, transparent);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 9%, transparent);\n border-radius: 12px;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px 10px;\n margin: 6px 20px 6px 68px;\n padding: 8px 12px;\n font-size: 13px;\n display: flex;\n}\n\n.dshc_errorBar .dshc_cardButtons {\n margin: 0 0 0 auto;\n}\n\n.dshc_errorBar .dshc_secondaryButton {\n padding: 3px 10px;\n font-size: 12px;\n}\n\n.dshc_errorBar[data-state=\"retried\"] {\n opacity: .65;\n}\n\n.dshc_errorIcon {\n color: #fff;\n background: var(--dsw-alias-state-error-primary);\n border-radius: 50%;\n flex: none;\n place-items: center;\n width: 18px;\n height: 18px;\n font-size: 12px;\n font-weight: 700;\n display: grid;\n}\n\n.dshc_errorText {\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n flex: 200px;\n}\n\n.dshc_errorText p {\n margin: 0;\n}\n\n.dshc_runStrip {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-layer-2));\n border-radius: 12px;\n flex-direction: column;\n max-width: 100%;\n margin-top: 4px;\n padding: 5px 10px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.dshc_runStrip[data-state=\"running\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_runStrip[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_runHead {\n min-width: 0;\n color: var(--dsw-alias-label-secondary);\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_runTitle {\n color: var(--dsw-alias-label-primary);\n flex: none;\n font-weight: 600;\n}\n\n.dshc_runSummary {\n font-variant-numeric: tabular-nums;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_runHead .dshc_cardViews {\n margin-left: 8px;\n}\n\n.dshc_runHead .dshc_cardViewButton {\n color: var(--dsw-alias-label-tertiary);\n border-radius: 999px;\n font-size: 11px;\n}\n\n.dshc_runHead .dshc_cardViewButton[aria-pressed=\"true\"] {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_runIcon {\n border-radius: 50%;\n flex: none;\n width: 12px;\n height: 12px;\n position: relative;\n}\n\n.dshc_runStrip[data-state=\"running\"] .dshc_runIcon {\n border: 2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 25%, transparent);\n border-top-color: var(--dsw-alias-state-business-primary);\n animation: .8s linear infinite dshc_channels-spin;\n}\n\n.dshc_runStrip[data-state=\"completed\"] .dshc_runIcon {\n background: #23a55a;\n}\n\n.dshc_runStrip[data-state=\"completed\"] .dshc_runIcon:after {\n content: \"\";\n border: 1.5px solid #fff;\n border-width: 0 1.5px 1.5px 0;\n width: 3px;\n height: 6px;\n position: absolute;\n top: 2px;\n left: 4px;\n transform: rotate(45deg);\n}\n\n.dshc_runStrip[data-state=\"failed\"] .dshc_runIcon {\n background: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_runSteps {\n color: var(--dsw-alias-label-secondary);\n margin-top: 4px;\n font-size: 12px;\n line-height: 1.55;\n}\n\n.dshc_runSteps p {\n margin: 0;\n}\n\n.dshc_runStrip .dshc_cardButtons {\n margin-top: 6px;\n}\n\n@keyframes dshc_channels-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_runStrip[data-state=\"running\"] .dshc_runIcon {\n animation-duration: 2.4s;\n }\n}\n\n.dshc_card {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n border-left-width: 1px;\n border-radius: 14px;\n margin-top: 6px;\n padding: 10px 12px;\n}\n\n.dshc_card[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, var(--dsw-alias-border-l2));\n}\n\n.dshc_taskAnchor {\n max-width: 560px;\n box-shadow: inset 3px 0 0 var(--channels-accent-warm, #d08a2e);\n}\n\n.dshc_permInline {\n color: var(--dsw-alias-label-secondary);\n flex-wrap: wrap;\n align-items: baseline;\n gap: 4px 8px;\n display: flex;\n}\n\n.dshc_permInline .dshc_text {\n flex: 0 auto;\n min-width: 0;\n}\n\n.dshc_permInline .dshc_text p {\n margin: 0;\n}\n\n.dshc_permLock {\n color: var(--dsw-alias-state-warning-primary, #e9a23b);\n flex: none;\n font-size: 9px;\n}\n\n.dshc_permWaiting {\n color: var(--dsw-alias-state-warning-primary, #e9a23b);\n flex: none;\n font-size: 11px;\n font-weight: 600;\n}\n\n.dshc_permTray {\n flex-direction: column;\n gap: 6px;\n max-height: 40%;\n margin: 0 16px 8px;\n display: flex;\n overflow-y: auto;\n}\n\n.dshc_permTrayItem {\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 55%, var(--dsw-alias-border-l2));\n background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 7%, var(--dsw-alias-bg-layer-2));\n border-radius: 14px;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px 12px;\n padding: 10px 12px;\n display: flex;\n box-shadow: 0 6px 20px -12px #00000059;\n}\n\n.dshc_permTrayText {\n flex: 240px;\n min-width: 0;\n font-size: 13px;\n}\n\n.dshc_permTrayText .dshc_text p {\n margin: 0;\n}\n\n.dshc_permTrayWho {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_permTrayItem .dshc_permActions {\n flex-wrap: nowrap;\n margin: 0;\n}\n\n.dshc_permTrayItem .dshc_permScope {\n display: none;\n}\n\n.dshc_members {\n flex-direction: column;\n padding: 0 10px 16px;\n}\n\n@media (width >= 901px) {\n .dshc_members {\n display: flex;\n }\n\n .dshc_membersHidden > .dshc_members {\n display: none;\n }\n}\n\n.dshc_members.dshc_drawerOpen {\n display: flex;\n}\n\n.dshc_activityNow {\n flex-direction: column;\n gap: 2px;\n margin-bottom: 6px;\n display: flex;\n}\n\n.dshc_activityItem {\n border: 1px solid var(--dsw-alias-border-l2);\n width: 100%;\n color: inherit;\n background: var(--dsw-alias-bg-base);\n font: inherit;\n text-align: left;\n cursor: pointer;\n border-radius: 12px;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n display: flex;\n}\n\n.dshc_activityItem + .dshc_activityItem {\n margin-top: 4px;\n}\n\n.dshc_activityItem:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 40%, var(--dsw-alias-border-l2));\n}\n\n.dshc_activityItem:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_activityText {\n flex-direction: column;\n flex: 1;\n min-width: 0;\n display: flex;\n}\n\n.dshc_activityName {\n font-size: 13px;\n font-weight: 600;\n}\n\n.dshc_activityWhat {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n overflow: hidden;\n}\n\n.dshc_activityPulse {\n background: var(--dsw-alias-state-business-primary);\n border-radius: 50%;\n flex: none;\n width: 8px;\n height: 8px;\n animation: 1.6s ease-in-out infinite dshc_channels-pulse;\n}\n\n@keyframes dshc_channels-pulse {\n 50% {\n box-shadow: 0 0 0 3px #23a55a40;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_activityPulse {\n animation: none;\n }\n}\n\n.dshc_member {\n padding: 6px 8px;\n}\n\n.dshc_memberText {\n flex: 1;\n}\n\n.dshc_memberStatus {\n flex: none;\n margin-left: auto;\n font-size: 11px;\n}\n\n.dshc_memberStatus[data-status=\"busy\"] {\n color: var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.dshc_memberStatus[data-status=\"error\"] {\n color: var(--dsw-alias-state-error-primary);\n font-weight: 600;\n}\n\n.dshc_memberActions {\n background: var(--dsw-alias-bg-layer-2);\n padding-left: 8px;\n position: absolute;\n top: 50%;\n right: 8px;\n transform: translateY(-50%);\n}\n\n.dshc_addMemberButton {\n border-radius: 10px;\n}\n\n@media (width <= 900px) {\n .dshc_headMeta, .dshc_headActivityText {\n display: none;\n }\n\n .dshc_chatHead {\n padding: 0 8px;\n }\n\n .dshc_mobileButton {\n white-space: nowrap;\n }\n\n .dshc_headActivity ~ .dshc_mobileButton:last-child {\n margin-left: 4px;\n }\n\n .dshc_message {\n padding-left: 12px;\n padding-right: 12px;\n }\n\n .dshc_errorBar {\n margin-left: 12px;\n margin-right: 12px;\n }\n\n .dshc_permTray {\n margin: 0 8px 8px;\n }\n\n .dshc_permTrayItem .dshc_permActions {\n flex-wrap: wrap;\n }\n\n .dshc_systemLine {\n padding: 8px 12px;\n }\n\n .dshc_systemText {\n max-width: 90%;\n }\n\n .dshc_memberActions {\n background: none;\n padding-left: 0;\n position: static;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsEnter {\n from {\n opacity: 0;\n transform: translateY(6px);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsEnterOwn {\n from {\n opacity: 0;\n transform: translateY(12px) scale(.985);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsGlow {\n from {\n opacity: 1;\n }\n\n to {\n opacity: 0;\n }\n}\n\n@keyframes dshc_channelsPop {\n from {\n opacity: 0;\n transform: translateY(4px) scale(.98);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@keyframes dshc_channelsDot {\n 0%, 60%, 100% {\n opacity: .35;\n transform: none;\n }\n\n 30% {\n opacity: 1;\n transform: translateY(-2px);\n }\n}\n\n@keyframes dshc_channelsDrain {\n from {\n transform: scaleX(1);\n }\n\n to {\n transform: scaleX(0);\n }\n}\n\n.dshc_enter {\n animation: .18s cubic-bezier(.2, 0, 0, 1) both dshc_channelsEnter;\n}\n\n.dshc_enterOwn {\n animation: .22s cubic-bezier(.22, 1, .36, 1) both dshc_channelsEnterOwn;\n}\n\n.dshc_enterGlow:before {\n content: \"\";\n pointer-events: none;\n background: linear-gradient(90deg, color-mix(in srgb, var(--dsw-alias-state-business-primary) 10%, transparent), transparent 70%);\n animation: .7s cubic-bezier(.4, 0, .2, 1) both dshc_channelsGlow;\n animation-delay: inherit;\n position: absolute;\n inset: 0;\n}\n\n.dshc_message {\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_room {\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1), color .16s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_card {\n transition: border-color .32s cubic-bezier(.2, 0, 0, 1), background-color .32s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_sendButton, .dshc_primaryButton, .dshc_secondaryButton, .dshc_mentionButton, .dshc_cardViewButton {\n transition: background-color .16s cubic-bezier(.2, 0, 0, 1), opacity .16s cubic-bezier(.2, 0, 0, 1), transform .12s cubic-bezier(.2, 0, 0, 1);\n}\n\n.dshc_sendButton:not(:disabled):active, .dshc_primaryButton:not(:disabled):active, .dshc_secondaryButton:not(:disabled):active, .dshc_mentionButton:not(:disabled):active, .dshc_cardViewButton:active {\n transform: translateY(1px);\n}\n\n.dshc_mentionPopup {\n transform-origin: bottom;\n animation: .16s cubic-bezier(.22, 1, .36, 1) both dshc_channelsPop;\n}\n\n.dshc_typingDots {\n vertical-align: middle;\n gap: 3px;\n margin-right: 6px;\n display: inline-flex;\n}\n\n.dshc_typingDots i {\n background: currentColor;\n border-radius: 50%;\n width: 4px;\n height: 4px;\n animation: 1.2s ease-in-out infinite dshc_channelsDot;\n}\n\n.dshc_typingDots i:nth-child(2) {\n animation-delay: .15s;\n}\n\n.dshc_typingDots i:nth-child(3) {\n animation-delay: .3s;\n}\n\n.dshc_logWrap {\n flex-direction: column;\n flex: 1;\n min-height: 0;\n display: flex;\n position: relative;\n}\n\n.dshc_olderLoading {\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n padding: 8px 16px;\n font-size: 12px;\n}\n\n.dshc_unreadDivider {\n color: var(--dsw-alias-state-error-primary);\n align-items: center;\n gap: 8px;\n margin: 8px 16px;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n}\n\n.dshc_unreadDivider:before, .dshc_unreadDivider:after {\n content: \"\";\n opacity: .35;\n background: currentColor;\n flex: 1;\n height: 1px;\n}\n\n.dshc_withdrawnRow {\n text-align: right;\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 16px;\n font-size: 13px;\n font-style: italic;\n animation: .18s cubic-bezier(.2, 0, 0, 1) both dshc_channelsEnter;\n}\n\n.dshc_jumpLatest {\n z-index: 3;\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #00000029);\n font: inherit;\n cursor: pointer;\n border-radius: 999px;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n font-size: 12px;\n animation: .22s cubic-bezier(.22, 1, .36, 1) both dshc_channelsPop;\n display: inline-flex;\n position: absolute;\n bottom: 12px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.dshc_jumpLatest:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_jumpLatest:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_jumpCount {\n color: var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.dshc_undoToast {\n z-index: 4;\n border-radius: var(--dsw-radius-md);\n width: max-content;\n max-width: calc(100% - 32px);\n color: var(--dsw-alias-label-primary-inverted);\n background: var(--dsw-alias-label-primary);\n box-shadow: var(--dsw-elevation-panel, 0 8px 24px #0000003d);\n align-items: center;\n gap: 12px;\n margin: 0 auto;\n padding: 8px 8px 8px 14px;\n font-size: 13px;\n animation: .22s cubic-bezier(.22, 1, .36, 1) both dshc_channelsPop;\n display: flex;\n position: absolute;\n bottom: 12px;\n left: 0;\n right: 0;\n overflow: hidden;\n}\n\n.dshc_undoButton {\n border-radius: var(--dsw-radius-sm);\n color: inherit;\n background: color-mix(in srgb, currentColor 16%, transparent);\n font: inherit;\n cursor: pointer;\n border: 0;\n padding: 2px 10px;\n font-weight: 600;\n}\n\n.dshc_undoButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_undoBar {\n background: var(--dsw-alias-state-business-primary);\n transform-origin: 0;\n height: 2px;\n animation: linear forwards dshc_channelsDrain;\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n}\n\n.dshc_undoToast ~ .dshc_jumpLatest {\n display: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_enter, .dshc_enterOwn, .dshc_enterGlow:before, .dshc_mentionPopup, .dshc_jumpLatest, .dshc_undoToast, .dshc_withdrawnRow {\n animation: none;\n }\n\n .dshc_typingDots i {\n opacity: .6;\n animation: none;\n }\n\n .dshc_undoBar {\n animation: none;\n }\n\n .dshc_message, .dshc_room, .dshc_card, .dshc_sendButton, .dshc_primaryButton, .dshc_secondaryButton, .dshc_mentionButton, .dshc_mentionBadge, .dshc_cardViewButton {\n transition: none;\n }\n}\n\n.dshc_leadCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n border-radius: 14px;\n align-items: center;\n gap: 10px;\n margin: 8px 8px 4px;\n display: flex;\n}\n\n.dshc_leadCardButton {\n border-radius: inherit;\n min-width: 0;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: auto;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n display: flex;\n}\n\n.dshc_leadCardButton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_leadCardButton:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-alias-border-focus, currentColor);\n outline-offset: -2px;\n}\n\n.dshc_leadCardButton:disabled {\n cursor: default;\n background: none;\n}\n\n.dshc_leadCardText {\n flex-direction: column;\n min-width: 0;\n display: flex;\n}\n\n.dshc_leadCardName {\n color: var(--dsw-alias-label-primary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 650;\n overflow: hidden;\n}\n\n.dshc_leadCardStatus {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n overflow: hidden;\n}\n\n.dshc_message:not(.dshc_own) > .dshc_messageBody > .dshc_text {\n background: var(--dsw-alias-bg-layer-2);\n border-radius: 6px 16px 16px;\n width: fit-content;\n max-width: min(640px, 100%);\n padding: 8px 14px;\n}\n\n.dshc_message.dshc_grouped:not(.dshc_own) > .dshc_messageBody > .dshc_text {\n border-radius: 6px 16px 16px 6px;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text {\n color: var(--dsw-alias-bg-base);\n background: var(--dsw-alias-label-primary);\n box-shadow: none;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text * {\n color: inherit;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text a {\n color: inherit;\n text-decoration: underline;\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text code {\n color: inherit;\n background: color-mix(in srgb, var(--dsw-alias-bg-base) 18%, transparent);\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text .dshc_mentionBadge {\n color: var(--dsw-alias-bg-base);\n background: color-mix(in srgb, var(--dsw-alias-bg-base) 24%, transparent);\n}\n\n.dshc_own > .dshc_messageBody > .dshc_text .dshc_mentionBadge:hover {\n background: color-mix(in srgb, var(--dsw-alias-bg-base) 36%, transparent);\n}\n\n.dshc_boardDrawerBackdrop {\n background: #00000040;\n place-items: stretch end;\n padding: 0;\n}\n\n.dshc_boardDrawerBackdrop > .dshc_taskBoardDialog {\n box-sizing: border-box;\n border-radius: 0;\n width: min(440px, 100%);\n height: 100dvh;\n max-height: none;\n animation: .2s ease-out dshc_boardDrawerIn;\n}\n\n@keyframes dshc_boardDrawerIn {\n from {\n opacity: 0;\n transform: translateX(24px);\n }\n\n to {\n opacity: 1;\n transform: none;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_boardDrawerBackdrop > .dshc_taskBoardDialog {\n animation: none;\n }\n}\n\n.dshc_schemeToggle {\n border-radius: var(--dsw-radius-sm);\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n justify-content: center;\n align-items: center;\n margin-left: 4px;\n font-size: 16px;\n line-height: 1;\n display: inline-flex;\n}\n\n.dshc_schemeToggle:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_schemeToggle:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_schemeToggle + .dshc_panelToggleEnd {\n margin-left: 4px;\n}\n\n.dshc_progressCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n box-shadow: 0 6px 18px #0000000a, 0 -12px 0 0 var(--dsw-alias-bg-base);\n border-radius: 14px;\n flex: none;\n margin: 8px 16px 4px;\n padding: 8px 14px;\n font-size: 12px;\n position: relative;\n}\n\n.dshc_progressHead {\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.dshc_progressTitle {\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n}\n\n.dshc_progressSummary {\n white-space: nowrap;\n text-overflow: ellipsis;\n min-width: 0;\n color: var(--dsw-alias-label-tertiary);\n flex: 1;\n overflow: hidden;\n}\n\n.dshc_progressToggle {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-sm);\n min-width: 22px;\n height: 24px;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n cursor: pointer;\n background: none;\n flex: none;\n justify-content: center;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n padding: 0 10px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n display: inline-flex;\n}\n\n.dshc_progressChevron {\n font-size: 12px;\n}\n\n.dshc_progressToggle:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_progressToggle:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_progressCard[data-folded=\"true\"] {\n padding: 6px 12px;\n}\n\n.dshc_progressRows {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 0 14px;\n margin: 4px 0 0;\n display: grid;\n}\n\n.dshc_progressRow {\n min-width: 0;\n}\n\n.dshc_progressRow dt {\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n}\n\n.dshc_progressRow dd {\n white-space: nowrap;\n text-overflow: ellipsis;\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n margin: 0;\n overflow: hidden;\n}\n\n.dshc_progressRow[data-empty=\"true\"] dd {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dshc_progressRow[data-row=\"open\"][data-empty=\"false\"] dd, .dshc_progressRow[data-row=\"open\"] .dshc_progressLink {\n color: var(--dsw-alias-state-warning-primary, #b7791f);\n}\n\n.dshc_progressLink {\n max-width: 100%;\n color: inherit;\n font: inherit;\n text-align: left;\n white-space: nowrap;\n text-overflow: ellipsis;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 0;\n overflow: hidden;\n}\n\n.dshc_progressLink:hover {\n text-decoration: underline;\n}\n\n@media (width <= 760px) {\n .dshc_progressCard {\n margin: 6px 10px 2px;\n }\n\n .dshc_progressRows {\n grid-template-columns: 52px minmax(0, 1fr);\n }\n\n .dshc_progressRow {\n display: contents;\n }\n}\n\n.dshc_stepChips {\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 6px;\n display: flex;\n}\n\n.dshc_stepChip {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n max-width: 220px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n border-radius: 999px;\n align-items: center;\n gap: 5px;\n padding: 2px 9px 2px 7px;\n font-size: 11px;\n display: inline-flex;\n}\n\n.dshc_stepChip b {\n color: var(--dsw-alias-label-primary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n overflow: hidden;\n}\n\n.dshc_stepChip:disabled {\n cursor: default;\n}\n\n.dshc_stepChip[aria-expanded=\"true\"] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_stepChip[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_stepChip[data-state=\"failed\"] b {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_stepChip[data-state=\"running\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary) 55%, var(--dsw-alias-border-l2));\n}\n\n.dshc_stepMark {\n background: var(--dsw-alias-label-tertiary);\n border-radius: 50%;\n flex: none;\n width: 7px;\n height: 7px;\n}\n\n.dshc_stepChip[data-state=\"ok\"] .dshc_stepMark {\n background: #23a55a;\n}\n\n.dshc_stepChip[data-state=\"failed\"] .dshc_stepMark {\n background: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_stepChip[data-state=\"running\"] .dshc_stepMark {\n background: var(--dsw-alias-state-business-primary);\n animation: 1s ease-in-out infinite dshc_channels-blink;\n}\n\n@keyframes dshc_channels-blink {\n 50% {\n opacity: .3;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_stepChip[data-state=\"running\"] .dshc_stepMark {\n animation: none;\n }\n}\n\n.dshc_stepOut {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-base));\n color: var(--dsw-alias-label-secondary);\n border-radius: 8px;\n flex-basis: 100%;\n gap: 4px;\n padding: 8px 10px;\n font-size: 11px;\n line-height: 1.5;\n display: grid;\n}\n\n.dshc_stepOut[data-state=\"failed\"] {\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, var(--dsw-alias-border-l2));\n}\n\n.dshc_stepOut div {\n gap: 8px;\n min-width: 0;\n display: flex;\n}\n\n.dshc_stepOut span {\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n}\n\n.dshc_stepOut code {\n overflow-wrap: anywhere;\n min-width: 0;\n font-family: ui-monospace, Menlo, monospace;\n}\n\n.dshc_stepOut[data-state=\"failed\"] > div[data-state=\"failed\"] > div:last-child > code {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_decideTray {\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 55%, var(--dsw-alias-border-l2));\n background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 7%, var(--dsw-alias-bg-layer-2));\n border-radius: 14px;\n flex-direction: column;\n max-height: 45%;\n margin: 0 16px 8px;\n display: flex;\n overflow-y: auto;\n}\n\n.dshc_decideTrayHead {\n width: 100%;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n flex: none;\n align-items: baseline;\n gap: 8px;\n padding: 7px 12px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_decideTrayHead strong {\n color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 70%, var(--dsw-alias-label-primary));\n}\n\n.dshc_decideTrayHead small {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.dshc_decideTrayHead:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n outline-offset: -2px;\n}\n\n.dshc_decideTrayToggle {\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n margin-left: auto;\n}\n\n.dshc_decideTray .dshc_permTray {\n max-height: none;\n margin: 0;\n padding: 0 8px 8px;\n}\n\n.dshc_askList {\n flex-direction: column;\n display: flex;\n}\n\n.dshc_askItem {\n border-top: 1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 30%, transparent);\n background: var(--dsw-alias-bg-layer-2);\n flex-wrap: wrap;\n align-items: center;\n gap: 6px 10px;\n padding: 8px 12px;\n display: flex;\n}\n\n.dshc_askKind {\n color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 75%, var(--dsw-alias-label-primary));\n background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 15%, transparent);\n border-radius: 5px;\n flex: none;\n padding: 0 6px;\n font-size: 11px;\n}\n\n.dshc_askText {\n flex-direction: column;\n flex: 200px;\n min-width: 0;\n font-size: 13px;\n display: flex;\n}\n\n.dshc_askQuestion {\n overflow-wrap: anywhere;\n}\n\n.dshc_askFrom {\n color: var(--dsw-alias-label-secondary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n overflow: hidden;\n}\n\n.dshc_askForm {\n flex: 0 260px;\n align-items: center;\n gap: 6px;\n min-width: 160px;\n display: flex;\n}\n\n.dshc_askInput {\n border: 1px solid var(--dsw-alias-border-l2);\n min-width: 0;\n color: inherit;\n background: var(--dsw-alias-bg-base);\n font: inherit;\n border-radius: 8px;\n flex: 1;\n padding: 4px 8px;\n font-size: 12px;\n}\n\n.dshc_askInput:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n}\n\n.dshc_askSend {\n width: 26px;\n height: 26px;\n color: var(--dsw-alias-bg-base);\n background: var(--dsw-alias-label-primary);\n cursor: pointer;\n border: 0;\n border-radius: 50%;\n flex: none;\n justify-content: center;\n align-items: center;\n padding: 0;\n display: inline-flex;\n}\n\n.dshc_askSend:disabled {\n opacity: .3;\n cursor: default;\n}\n\n.dshc_askSend:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color);\n outline-offset: 2px;\n}\n\n.dshc_askDone {\n color: var(--dsw-alias-label-secondary);\n align-items: center;\n gap: 8px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.dshc_askNotice {\n color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #e9a23b) 75%, var(--dsw-alias-label-primary));\n flex-basis: 100%;\n font-size: 12px;\n}\n\n@media (width <= 900px) {\n .dshc_decideTray {\n margin: 0 8px 8px;\n }\n\n .dshc_askForm {\n flex-basis: 100%;\n }\n}\n\n.dshc_headTeam {\n flex: none;\n align-items: center;\n margin-left: auto;\n display: inline-flex;\n}\n\n.dshc_mate, .dshc_mateMore {\n border: 2px solid var(--dsw-alias-bg-base);\n background: var(--dsw-alias-bg-base);\n cursor: pointer;\n border-radius: 50%;\n justify-content: center;\n align-items: center;\n margin-left: -6px;\n padding: 0;\n display: inline-flex;\n position: relative;\n}\n\n.dshc_mate:first-child {\n margin-left: 0;\n}\n\n.dshc_mate .dshc_avatar {\n margin: 0;\n}\n\n.dshc_mateMore {\n background: var(--dsw-alias-interactive-bg-hover);\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-weight: 600;\n}\n\n.dshc_mateDot {\n box-sizing: border-box;\n border: 2px solid var(--dsw-alias-bg-base);\n background: var(--dsw-alias-label-tertiary);\n border-radius: 50%;\n width: 9px;\n height: 9px;\n position: absolute;\n bottom: -2px;\n right: -2px;\n}\n\n.dshc_mate[data-state=\"busy\"] .dshc_mateDot {\n background: #23a55a;\n animation: 1.6s ease-in-out infinite dshc_channels-pulse;\n}\n\n.dshc_mate[data-state=\"idle\"] .dshc_mateDot {\n background: var(--dsw-alias-bg-base);\n box-shadow: inset 0 0 0 1.5px var(--dsw-alias-label-tertiary);\n}\n\n.dshc_mate[data-state=\"error\"] .dshc_mateDot {\n background: var(--dsw-alias-state-error-primary);\n}\n\n.dshc_mate[data-state=\"offline\"] {\n opacity: .55;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_mate[data-state=\"busy\"] .dshc_mateDot {\n animation: none;\n }\n}\n\n@media (width <= 760px) {\n .dshc_headTeam {\n display: none;\n }\n}\n\n.dshc_chatHead > .dshc_headTeam + .dshc_taskBoardTrigger {\n margin-left: 12px;\n}\n\n.dshc_sideDock {\n display: none;\n}\n\n@media (width >= 1101px) {\n .dshc_page.dshc_panelOpen {\n grid-template-columns: 220px minmax(0, 1fr) min(420px, 36%) 240px;\n }\n\n .dshc_page.dshc_panelOpen.dshc_roomsHidden {\n grid-template-columns: minmax(0, 1fr) min(420px, 36%) 240px;\n }\n\n .dshc_page.dshc_panelOpen.dshc_membersHidden {\n grid-template-columns: 220px minmax(0, 1fr) min(420px, 36%);\n }\n\n .dshc_page.dshc_panelOpen.dshc_roomsHidden.dshc_membersHidden {\n grid-template-columns: minmax(0, 1fr) min(420px, 36%);\n }\n\n .dshc_page.dshc_panelOpen > .dshc_sideDock {\n border-left: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-base);\n min-width: 0;\n min-height: 0;\n display: flex;\n }\n}\n\n.dshc_sidePanel {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n max-height: none;\n box-shadow: none;\n border: 0;\n border-radius: 0;\n margin: 0;\n animation: .2s ease-out dshc_boardDrawerIn;\n position: relative;\n}\n\n.dshc_sidePanel:focus {\n outline: none;\n}\n\n.dshc_sidePanelClose {\n width: 28px;\n height: 28px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 6px;\n font-size: 18px;\n line-height: 1;\n position: absolute;\n top: 10px;\n right: 10px;\n}\n\n.dshc_sidePanelClose:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_sidePanel {\n animation: none;\n }\n}\n\n.dshc_sidePanel .dshc_boardCardGrid {\n grid-template-columns: minmax(0, 1fr);\n}\n\n.dshc_slashPopup {\n z-index: 6;\n width: min(360px, 100% - 32px);\n animation: none;\n right: auto;\n}\n\n.dshc_slashGroup {\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px 2px;\n font-size: 11px;\n}\n\n.dshc_slashItem {\n border-radius: var(--dsw-radius-sm);\n cursor: pointer;\n align-items: baseline;\n gap: 8px;\n padding: 5px 8px;\n display: flex;\n}\n\n.dshc_slashActive {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_slashName {\n flex: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-weight: 600;\n}\n\n.dshc_slashMeta {\n min-width: 0;\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n overflow: hidden;\n}\n\n.dshc_sendButton.dshc_stopButton {\n background: var(--dsw-alias-label-primary);\n}\n\n.dshc_stopNote + .dshc_sendButton {\n margin-left: 8px;\n}\n\n.dshc_sendButton.dshc_stopButton[data-phase=\"stopping\"] {\n opacity: .6;\n cursor: progress;\n}\n\n.dshc_stopNote {\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n margin-left: auto;\n font-size: 12px;\n}\n\n.dshc_cmdLine {\n color: var(--dsw-alias-label-tertiary);\n flex-wrap: wrap;\n align-items: baseline;\n gap: 6px;\n padding: 1px 16px 1px 72px;\n font-size: 12px;\n display: flex;\n}\n\n.dshc_cmdLine code {\n color: var(--dsw-alias-label-secondary);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n\n.dshc_cmdResult {\n min-width: 0;\n max-width: 60ch;\n color: inherit;\n font: inherit;\n text-align: left;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 0;\n overflow: hidden;\n}\n\n.dshc_cmdResult[aria-expanded=\"true\"] {\n white-space: pre-line;\n max-width: none;\n}\n\n.dshc_cmdLine[data-failed=\"true\"] .dshc_cmdResult {\n color: var(--dsw-alias-danger, #d94848);\n}\n\n@media (width <= 720px) {\n .dshc_cmdLine {\n padding-left: 16px;\n }\n}\n\n.dshc_msgTool {\n justify-content: center;\n align-items: center;\n width: 26px;\n height: 24px;\n padding: 0;\n display: inline-flex;\n position: relative;\n}\n\n.dshc_msgTool[data-tip]:after {\n content: attr(data-tip);\n z-index: 3;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary-inverted);\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 11px;\n position: absolute;\n top: calc(100% + 4px);\n left: 50%;\n transform: translateX(-50%);\n}\n\n.dshc_msgTool[data-tip]:hover:after, .dshc_msgTool[data-tip]:focus-visible:after {\n opacity: 1;\n transition: opacity 0s 80ms;\n}\n\n.dshc_permSessionButton {\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n text-underline-offset: 2px;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 4px 6px;\n font-size: 12px;\n text-decoration: underline;\n}\n\n.dshc_permSessionButton:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_permSessionButton:disabled {\n opacity: .5;\n cursor: default;\n}\n\n.dshc_queuedButton {\n border: 1px solid var(--dsw-alias-border-default, #0000001f);\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n cursor: pointer;\n background: none;\n border-radius: 9px;\n padding: 0 6px;\n font-size: 11px;\n line-height: 16px;\n}\n\n.dshc_queuedButton:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshc_queuedTag {\n color: #a76a06;\n background: #eaa02824;\n border-radius: 4px;\n padding: 0 5px;\n font-size: 11px;\n line-height: 16px;\n}\n\n[data-queued] .dshc_ownActions {\n opacity: 1;\n}\n\n.dshc_memory {\n margin-top: 4px;\n}\n\n.dshc_memoryBadge {\n background: var(--dsw-alias-fill-secondary, #0000000d);\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n cursor: pointer;\n border: 0;\n border-radius: 9px;\n align-items: center;\n gap: 4px;\n padding: 0 6px;\n font-size: 11px;\n line-height: 18px;\n display: inline-flex;\n}\n\n.dshc_memoryBadge:hover, .dshc_memoryBadge[aria-expanded=\"true\"] {\n color: var(--dsw-alias-label-secondary);\n}\n\n.dshc_memoryList {\n border-left: 2px solid var(--dsw-alias-border-default, #0000001f);\n flex-direction: column;\n gap: 4px;\n max-width: 560px;\n margin: 4px 0 0;\n padding: 6px 10px;\n font-size: 12px;\n list-style: none;\n display: flex;\n}\n\n.dshc_memoryItem {\n flex-direction: column;\n display: flex;\n}\n\n.dshc_memoryTitle {\n color: var(--dsw-alias-label-secondary);\n line-height: 1.5;\n}\n\n.dshc_memoryPage {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-family: var(--dsw-font-mono, ui-monospace, monospace);\n}\n\n.dshc_delivery {\n flex-direction: column;\n gap: 6px;\n max-width: 720px;\n margin-top: 8px;\n display: flex;\n}\n\n.dshc_deliveryFrame {\n border: 1px solid var(--background-modifier-accent, #7f7f7f4d);\n background: #fff;\n border-radius: 6px;\n height: 360px;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_deliveryActions {\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n display: flex;\n}\n\n.dshc_deliveryActions a {\n text-decoration: none;\n}\n\n.dshc_deliveryDone {\n color: var(--text-muted, #888);\n font-size: 12px;\n}\n\n.dshc_deliveryFullDialog {\n box-sizing: border-box;\n flex-direction: column;\n gap: 8px;\n width: 94vw;\n max-width: none;\n height: 92dvh;\n display: flex;\n}\n\n.dshc_deliveryFullFrame {\n background: #fff;\n border-radius: 6px;\n flex: 1;\n min-height: 0;\n display: flex;\n overflow: hidden;\n}\n\n.dshc_dmTitle {\n color: var(--text-muted, #888);\n margin: 14px 8px 4px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.dshc_dmAvatar {\n margin-right: 6px;\n display: inline-flex;\n}\n\n.dshc_dmRow {\n align-items: center;\n display: flex;\n position: relative;\n}\n\n.dshc_dmRow .dshc_room {\n padding-right: 26px;\n}\n\n.dshc_dmClose {\n border-radius: var(--dsw-radius-sm, 4px);\n width: 20px;\n height: 20px;\n color: var(--dsw-alias-label-tertiary, #888);\n font: inherit;\n cursor: pointer;\n opacity: 0;\n background: none;\n border: 0;\n padding: 0;\n font-size: 15px;\n line-height: 20px;\n position: absolute;\n right: 4px;\n}\n\n.dshc_dmRow:hover .dshc_dmClose, .dshc_dmClose:focus-visible {\n opacity: 1;\n}\n\n.dshc_dmClose:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n@media (hover: none) {\n .dshc_dmClose {\n opacity: 1;\n }\n}\n\n.dshc_dmHead {\n margin-top: 14px;\n}\n\n.dshc_dmPicker {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: var(--dsw-alias-bg-layer-2);\n flex-direction: column;\n margin: 0 4px 4px;\n padding: 4px;\n display: flex;\n}\n\n.dshc_dmPickNone {\n color: var(--dsw-alias-label-tertiary);\n padding: 6px 8px;\n font-size: 12px;\n}\n\n.dshc_dmNewRow {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n}\n\nbutton.dshc_headMeta {\n border-radius: var(--dsw-radius-sm);\n min-width: 0;\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n flex: 0 auto;\n padding: 2px 6px;\n font-size: 12px;\n}\n\nbutton.dshc_headMeta:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dshc_headSpacer {\n flex: auto;\n}\n\n.dshc_roomUnread {\n color: var(--dsw-alias-label-primary);\n font-weight: 650;\n}\n\n.dshc_unreadBadge {\n color: #fff;\n background: var(--dsw-alias-state-danger-primary, #e5484d);\n text-align: center;\n font-variant-numeric: tabular-nums;\n border-radius: 999px;\n flex: none;\n min-width: 18px;\n padding: 0 5px;\n font-size: 11px;\n font-weight: 600;\n line-height: 18px;\n}\n\n.dshc_slashDisabled {\n opacity: .45;\n cursor: not-allowed;\n}\n\n.dshc_leadCardHint {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n overflow: hidden;\n}\n\n.dshc_roomWorking {\n max-width: 96px;\n color: var(--dsw-alias-state-business-primary);\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: none;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 500;\n display: inline-flex;\n overflow: hidden;\n}\n\n.dshc_roomWorkingDot {\n background: currentColor;\n border-radius: 50%;\n flex: none;\n width: 6px;\n height: 6px;\n animation: 1.4s ease-in-out infinite dshc_roomWorkingPulse;\n}\n\n@keyframes dshc_roomWorkingPulse {\n 50% {\n opacity: .35;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshc_roomWorkingDot {\n animation: none;\n }\n}\n\n.dshc_drawerTools {\n display: none;\n}\n\n@media (width <= 600px) {\n .dshc_drawerTools {\n flex-wrap: wrap;\n gap: 6px;\n margin: 0 8px 8px;\n display: flex;\n }\n\n .dshc_chatHead > .dshc_taskBoardTrigger, .dshc_chatHead > .dshc_schemeToggle, .dshc_chatHead > .dshc_headMeta, .dshc_chatHead > .dshc_headTeam {\n display: none;\n }\n\n .dshc_chatHead > .dshc_roomName {\n flex: auto;\n }\n}\n\n.dshc_panelOpen .dshc_chatHead > .dshc_headTeam, .dshc_panelOpen .dshc_chatHead > .dshc_headMeta {\n display: none;\n}\n\n.dshc_panelOpen .dshc_chatHead > .dshc_roomName {\n flex: 0 auto;\n}\n[data-dc-scheme=\"light\"] {\n --dsw-static-amber-100: #fef5e7;\n --dsw-static-amber-400: #f7ad31;\n --dsw-static-amber-500: #f59e0b;\n --dsw-static-amber-600: #dd8629;\n --dsw-static-amber-900: #27241f;\n --dsw-static-blue-100: #dbeafe;\n --dsw-static-blue-300: #93c5fd;\n --dsw-static-blue-400: #60a5fa;\n --dsw-static-blue-450: #4d93f8;\n --dsw-static-blue-500: #3b82f6;\n --dsw-static-blue-50: #eff6ff;\n --dsw-static-blue-50p: #eaf3ff;\n --dsw-static-blue-600: #2563eb;\n --dsw-static-blue-75: #e5f0ff;\n --dsw-static-blue-800: #1e40af;\n --dsw-static-blue-900: #0e3074;\n --dsw-static-blue-950: #172554;\n --dsw-static-deepseek-100: #e4edfd;\n --dsw-static-deepseek-200: #d3e2ff;\n --dsw-static-deepseek-300: #b7c8fe;\n --dsw-static-deepseek-400: #7aaaff;\n --dsw-static-deepseek-450: #5686fe;\n --dsw-static-deepseek-500: #4176e6;\n --dsw-static-deepseek-50: #edf3fe;\n --dsw-static-deepseek-600: #4868b2;\n --dsw-static-deepseek-700-delete: #2f4c8f;\n --dsw-static-deepseek-800: #34415b;\n --dsw-static-deepseek-900: #283142;\n --dsw-static-green-100: #e6faed;\n --dsw-static-green-400: #4ed17e;\n --dsw-static-green-500: #22c55e;\n --dsw-static-green-500-a08: #22c55e14;\n --dsw-static-green-500-a12: #22c55e1f;\n --dsw-static-green-900: #233c2c;\n --dsw-static-neutral-00: #fff;\n --dsw-static-neutral-1000: #000;\n --dsw-static-neutral-100: #f5f5f5;\n --dsw-static-neutral-150: #ededed;\n --dsw-static-neutral-200: #e5e5e5;\n --dsw-static-neutral-250: #dcdcdc;\n --dsw-static-neutral-300: #d4d4d4;\n --dsw-static-neutral-400: #a2a4a6;\n --dsw-static-neutral-500: #7f8287;\n --dsw-static-neutral-50: #fafafa;\n --dsw-static-neutral-550: #65676b;\n --dsw-static-neutral-600: #545557;\n --dsw-static-neutral-700: #3c3c3d;\n --dsw-static-neutral-800: #292929;\n --dsw-static-neutral-850: #212123;\n --dsw-static-neutral-900: #0f0f0f;\n --dsw-static-neutral-bluish-00: #fff;\n --dsw-static-neutral-bluish-1000: #0f1115;\n --dsw-static-neutral-bluish-100: #ebeef2;\n --dsw-static-neutral-bluish-150: #e9ecf2;\n --dsw-static-neutral-bluish-200: #e1e5ee;\n --dsw-static-neutral-bluish-300: #cfd3d6;\n --dsw-static-neutral-bluish-400: #adb2b8;\n --dsw-static-neutral-bluish-500: #979da6;\n --dsw-static-neutral-bluish-50: #f9fafb;\n --dsw-static-neutral-bluish-600: #81858c;\n --dsw-static-neutral-bluish-60: #f5f6f7;\n --dsw-static-neutral-bluish-700: #61666b;\n --dsw-static-neutral-bluish-750: #43454a;\n --dsw-static-neutral-bluish-75: #f1f3f5;\n --dsw-static-neutral-bluish-800: #353638;\n --dsw-static-neutral-bluish-850: #2c2c2e;\n --dsw-static-neutral-bluish-875: #232324;\n --dsw-static-neutral-bluish-900: #1b1b1c;\n --dsw-static-neutral-bluish-950: #151517;\n --dsw-static-red-100: #fee2e2;\n --dsw-static-red-400: #f25a5a;\n --dsw-static-red-400-a12: #f25a5a1f;\n --dsw-static-red-500: #ef4444;\n --dsw-static-red-50: #fef2f2;\n --dsw-static-red-600: #ec1313;\n --dsw-static-red-600-a08: #ec131314;\n --dsw-static-red-900: #570c0c;\n}\n\n[data-dc-scheme=\"dark\"] {\n --dsw-static-amber-100: #fef5e7;\n --dsw-static-amber-400: #f7ad31;\n --dsw-static-amber-500: #f59e0b;\n --dsw-static-amber-600: #dd8629;\n --dsw-static-amber-900: #27241f;\n --dsw-static-blue-100: #dbeafe;\n --dsw-static-blue-300: #93c5fd;\n --dsw-static-blue-400: #60a5fa;\n --dsw-static-blue-450: #4d93f8;\n --dsw-static-blue-500: #3b82f6;\n --dsw-static-blue-50: #eff6ff;\n --dsw-static-blue-50p: #eaf3ff;\n --dsw-static-blue-600: #2563eb;\n --dsw-static-blue-75: #e5f0ff;\n --dsw-static-blue-800: #1e40af;\n --dsw-static-blue-900: #0e3074;\n --dsw-static-blue-950: #172554;\n --dsw-static-deepseek-100: #e4edfd;\n --dsw-static-deepseek-200: #d3e2ff;\n --dsw-static-deepseek-300: #b7c8fe;\n --dsw-static-deepseek-400: #7aaaff;\n --dsw-static-deepseek-450: #5686fe;\n --dsw-static-deepseek-500: #4176e6;\n --dsw-static-deepseek-50: #edf3fe;\n --dsw-static-deepseek-600: #4868b2;\n --dsw-static-deepseek-700-delete: #2f4c8f;\n --dsw-static-deepseek-800: #34415b;\n --dsw-static-deepseek-900: #283142;\n --dsw-static-green-100: #e6faed;\n --dsw-static-green-400: #4ed17e;\n --dsw-static-green-500: #22c55e;\n --dsw-static-green-500-a08: #22c55e14;\n --dsw-static-green-500-a12: #22c55e1f;\n --dsw-static-green-900: #233c2c;\n --dsw-static-neutral-00: #fff;\n --dsw-static-neutral-1000: #000;\n --dsw-static-neutral-100: #f5f5f5;\n --dsw-static-neutral-150: #ededed;\n --dsw-static-neutral-200: #e5e5e5;\n --dsw-static-neutral-250: #dcdcdc;\n --dsw-static-neutral-300: #d4d4d4;\n --dsw-static-neutral-400: #a2a4a6;\n --dsw-static-neutral-500: #7f8287;\n --dsw-static-neutral-50: #fafafa;\n --dsw-static-neutral-550: #65676b;\n --dsw-static-neutral-600: #545557;\n --dsw-static-neutral-700: #3c3c3d;\n --dsw-static-neutral-800: #292929;\n --dsw-static-neutral-850: #212123;\n --dsw-static-neutral-900: #0f0f0f;\n --dsw-static-neutral-bluish-00: #fff;\n --dsw-static-neutral-bluish-1000: #0f1115;\n --dsw-static-neutral-bluish-100: #ebeef2;\n --dsw-static-neutral-bluish-150: #e9ecf2;\n --dsw-static-neutral-bluish-200: #e1e5ee;\n --dsw-static-neutral-bluish-300: #cfd3d6;\n --dsw-static-neutral-bluish-400: #adb2b8;\n --dsw-static-neutral-bluish-500: #979da6;\n --dsw-static-neutral-bluish-50: #f9fafb;\n --dsw-static-neutral-bluish-600: #81858c;\n --dsw-static-neutral-bluish-60: #f9fafb;\n --dsw-static-neutral-bluish-700: #61666b;\n --dsw-static-neutral-bluish-750: #43454a;\n --dsw-static-neutral-bluish-75: #f1f3f5;\n --dsw-static-neutral-bluish-800: #353638;\n --dsw-static-neutral-bluish-850: #2c2c2e;\n --dsw-static-neutral-bluish-875: #232324;\n --dsw-static-neutral-bluish-900: #1b1b1c;\n --dsw-static-neutral-bluish-950: #151517;\n --dsw-static-red-100: #fee2e2;\n --dsw-static-red-400: #f25a5a;\n --dsw-static-red-400-a12: #f25a5a1f;\n --dsw-static-red-500: #ef4444;\n --dsw-static-red-50: #fef2f2;\n --dsw-static-red-600: #ec1313;\n --dsw-static-red-600-a08: #ec131314;\n --dsw-static-red-900: #570c0c;\n}\n\n[data-dc-scheme=\"light\"] {\n --dsw-alias-bg-base: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-document-preview: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-bg-document-selection: color-mix(in srgb, var(--dsw-static-blue-500) 40%, transparent);\n --dsw-alias-label-document-preview: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-bg-layer-1: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-layer-2: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-layer-3: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-bg-mask-1: #0000003d;\n --dsw-alias-bg-mask-2: #0000001f;\n --dsw-alias-bg-mask-3: #0000007a;\n --dsw-alias-bg-mask-photo: #000000e0;\n --dsw-alias-bg-mask-drop: #ffffffb3;\n --dsw-alias-bg-module-platform: var(--dsw-static-neutral-bluish-60);\n --dsw-alias-bg-multi-select: var(--dsw-static-neutral-bluish-60);\n --dsw-alias-bg-overlay: var(--dsw-static-neutral-bluish-150);\n --dsw-alias-bg-skeleton: #0000000a;\n --dsw-alias-border-inverted2: #0000;\n --dsw-alias-border-inverted: #0000;\n --dsw-alias-border-l1: #0000000a;\n --dsw-alias-border-l2-darkmode-thin: #0000001a;\n --dsw-alias-border-l2: #0000001a;\n --dsw-alias-border-l3: #0000001f;\n --dsw-alias-border-l4: #00000029;\n --dsw-alias-brand-primary-invert: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-brand-primary-new-colorprimary-new-color: #4176e6;\n --dsw-alias-brand-primary: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-brand-text: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-button-contrast-fill: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-button-elevated-fill: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-button-floating-fill: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-button-floating-hover: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-button-ghost-active-border: var(--dsw-static-neutral-bluish-500);\n --dsw-alias-button-ghost-active-fill: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-button-ghost-active-hover: var(--dsw-static-neutral-bluish-150);\n --dsw-alias-button-info-fill: var(--dsw-static-deepseek-500);\n --dsw-alias-button-info-hover: var(--dsw-static-deepseek-400);\n --dsw-alias-button-primary-dimmed: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-button-primary-fill: var(--dsw-alias-brand-primary);\n --dsw-alias-button-primary-hover: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-tool-bar-fill-invisible: #1f1f1f5c;\n --dsw-alias-button-tool-bar-fill: #54555780;\n --dsw-alias-button-tool-bar-hover: #54555799;\n --dsw-alias-interactive-bg-active: #2631481a;\n --dsw-alias-interactive-bg-hover-accent: #26314824;\n --dsw-alias-interactive-bg-hover-danger: #ec13130d;\n --dsw-alias-interactive-bg-hover-solid: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-interactive-bg-hover: #2631480f;\n --dsw-alias-label-caption: var(--dsw-static-neutral-bluish-400);\n --dsw-alias-label-deep-diving: color-mix(in srgb, var(--dsw-static-deepseek-500) 70%, var(--dsw-static-blue-950));\n --dsw-alias-label-deep-diving-shimmer: color-mix(in srgb, var(--dsw-static-deepseek-500) 30%, var(--dsw-static-blue-950));\n --dsw-alias-label-dimmed: var(--dsw-static-neutral-bluish-200);\n --dsw-alias-label-primary-bluish: var(--dsw-static-blue-900);\n --dsw-alias-label-primary-dimmed: var(--dsw-static-neutral-bluish-950);\n --dsw-alias-label-primary-foreground: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-label-primary-inverted: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-label-primary: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-label-secondary: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-label-shimmer: color-mix(in srgb, var(--dsw-static-neutral-1000) 30%, transparent);\n --dsw-alias-label-tertiary: var(--dsw-static-neutral-bluish-600);\n --dsw-alias-menu-icon: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-link: var(--dsw-static-deepseek-500);\n --dsw-alias-markdown-citation: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-markdown-code-block-banner: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-markdown-code-block: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-markdown-code-segment-selected: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-markdown-code-segment-unselected: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-markdown-inline-code: var(--dsw-static-neutral-50);\n --dsw-alias-markdown-placeholder: var(--dsw-static-neutral-bluish-60);\n --dsw-alias-markdown-tag: var(--dsw-static-neutral-bluish-75);\n --dsw-alias-scrollbar-bg-l1: var(--dsw-static-neutral-200);\n --dsw-alias-scrollbar-bg-l2: var(--dsw-static-neutral-200);\n --dsw-alias-scrollbar-hover-l1: var(--dsw-static-neutral-300);\n --dsw-alias-scrollbar-hover-l2: var(--dsw-static-neutral-300);\n --dsw-alias-state-business-primary: var(--dsw-static-deepseek-500);\n --dsw-alias-state-business-tertiary: var(--dsw-static-deepseek-100);\n --dsw-alias-code-diff-added: var(--dsw-static-green-500-a08);\n --dsw-alias-code-diff-deleted: var(--dsw-static-red-600-a08);\n --dsw-alias-file-diff-added-bg: #e6f4e7;\n --dsw-alias-file-diff-added-gutter: #edf7ed;\n --dsw-alias-file-diff-added-marker: #01a241;\n --dsw-alias-file-diff-deleted-bg: #fce6e2;\n --dsw-alias-file-diff-deleted-gutter: #fdece9;\n --dsw-alias-file-diff-deleted-marker: #ba2723;\n --dsw-alias-state-error-primary: var(--dsw-static-red-600);\n --dsw-alias-state-error-secondary: var(--dsw-static-red-400);\n --dsw-alias-state-idle-primary: var(--dsw-static-neutral-300);\n --dsw-alias-state-success-primary: var(--dsw-static-green-500);\n --dsw-alias-state-success-secondary: var(--dsw-static-green-400);\n --dsw-alias-state-success-tertiary: var(--dsw-static-green-100);\n --dsw-alias-state-warn-label: var(--dsw-static-amber-600);\n --dsw-alias-state-warn-primary: var(--dsw-static-amber-500);\n --dsw-alias-state-warn-secondary: var(--dsw-static-amber-400);\n --dsw-alias-state-warn-tertiary: var(--dsw-static-amber-100);\n --dsw-alias-switch-thumb: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-toast-bg: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-toast-label: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-tooltip-bg: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-tooltip-key-bg: color-mix(in srgb, var(--dsw-alias-tooltip-bg), white 18%);\n --dsw-alias-turn-trigger-bg: var(--dsw-alias-markdown-code-block);\n --dsw-alias-turn-trigger-bg-hover: var(--dsw-alias-interactive-bg-hover);\n --dsw-specific-bubble-highlight: var(--dsw-static-deepseek-200);\n --dsw-specific-bubble: var(--dsw-static-deepseek-50);\n --dsw-specific-input-major: var(--dsw-static-neutral-bluish-00);\n --dsw-specific-login-input: var(--dsw-static-neutral-bluish-50);\n --dsw-menu-surface-fill: #f8f9fa94;\n --dsw-alias-menu-group-header-fill: #f8f9faf0;\n --dsw-specific-menu: var(--dsw-menu-surface-fill);\n --dsw-specific-selector: var(--dsw-static-neutral-bluish-60);\n --dsw-specific-sidebar-fill: var(--dsw-static-neutral-bluish-50);\n --dsw-specific-sidebar-nav-item-active-accent: var(--dsw-static-deepseek-100);\n --dsw-specific-sidebar-nav-item-active: var(--dsw-static-neutral-bluish-100);\n --dsw-specific-sidebar-nav-item-hover: var(--dsw-static-neutral-bluish-75);\n --dsw-specific-tip: var(--dsw-static-neutral-bluish-60);\n}\n\n[data-dc-scheme=\"dark\"] {\n --dsw-alias-bg-base: var(--dsw-static-neutral-bluish-950);\n --dsw-alias-bg-document-preview: var(--dsw-static-neutral-bluish-950);\n --dsw-alias-bg-document-selection: color-mix(in srgb, var(--dsw-static-blue-500) 40%, transparent);\n --dsw-alias-label-document-preview: var(--dsw-static-neutral-bluish-300);\n --dsw-alias-bg-layer-1: var(--dsw-static-neutral-bluish-875);\n --dsw-alias-bg-layer-2: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-bg-layer-3: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-bg-mask-1: #00000080;\n --dsw-alias-bg-mask-2: #0003;\n --dsw-alias-bg-mask-3: #0000007a;\n --dsw-alias-bg-mask-photo: #000000e0;\n --dsw-alias-bg-mask-drop: #272730b3;\n --dsw-alias-bg-module-platform: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-bg-multi-select: var(--dsw-static-neutral-850);\n --dsw-alias-bg-overlay: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-bg-skeleton: #ffffff14;\n --dsw-alias-border-inverted2: #ffffff14;\n --dsw-alias-border-inverted: #ffffff0f;\n --dsw-alias-border-l1: #ffffff0f;\n --dsw-alias-border-l2-darkmode-thin: #ffffff0f;\n --dsw-alias-border-l2: #ffffff1f;\n --dsw-alias-border-l3: #ffffff29;\n --dsw-alias-border-l4: #fff3;\n --dsw-alias-brand-primary-invert: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-brand-primary-new-colorprimary-new-color: var(--dsw-static-deepseek-450);\n --dsw-alias-brand-primary: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-brand-text: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-button-contrast-fill: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-button-elevated-fill: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-floating-fill: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-button-floating-hover: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-button-ghost-active-border: var(--dsw-static-neutral-bluish-600);\n --dsw-alias-button-ghost-active-fill: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-ghost-active-hover: var(--dsw-static-neutral-bluish-700);\n --dsw-alias-button-info-fill: var(--dsw-static-deepseek-400);\n --dsw-alias-button-info-hover: var(--dsw-static-deepseek-500);\n --dsw-alias-button-primary-dimmed: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-button-primary-fill: var(--dsw-alias-brand-primary);\n --dsw-alias-button-primary-hover: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-button-tool-bar-fill-invisible: #1f1f1f5c;\n --dsw-alias-button-tool-bar-fill: #54555780;\n --dsw-alias-button-tool-bar-hover: #54555799;\n --dsw-alias-interactive-bg-active: #ffffff24;\n --dsw-alias-interactive-bg-hover-accent: #ffffff3d;\n --dsw-alias-interactive-bg-hover-danger: #f25a5a26;\n --dsw-alias-interactive-bg-hover-solid: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-interactive-bg-hover: #ffffff14;\n --dsw-alias-label-caption: var(--dsw-static-neutral-bluish-600);\n --dsw-alias-label-deep-diving: color-mix(in srgb, var(--dsw-static-deepseek-450) 55%, var(--dsw-static-neutral-bluish-400));\n --dsw-alias-label-deep-diving-shimmer: color-mix(in srgb, var(--dsw-static-blue-300) 65%, var(--dsw-static-deepseek-400));\n --dsw-alias-label-dimmed: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-label-primary-bluish: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-label-primary-dimmed: var(--dsw-static-neutral-bluish-100);\n --dsw-alias-label-primary-foreground: var(--dsw-static-neutral-bluish-1000);\n --dsw-alias-label-primary-inverted: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-label-primary: var(--dsw-static-neutral-bluish-50);\n --dsw-alias-label-secondary: var(--dsw-static-neutral-bluish-300);\n --dsw-alias-label-shimmer: color-mix(in srgb, var(--dsw-static-neutral-00) 45%, transparent);\n --dsw-alias-label-tertiary: var(--dsw-static-neutral-bluish-400);\n --dsw-alias-menu-icon: var(--dsw-alias-label-primary-dimmed);\n --dsw-alias-link: var(--dsw-static-deepseek-400);\n --dsw-alias-markdown-citation: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-markdown-code-block-banner: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-markdown-code-block: var(--dsw-static-neutral-bluish-900);\n --dsw-alias-markdown-code-segment-selected: var(--dsw-static-neutral-bluish-800);\n --dsw-alias-markdown-code-segment-unselected: var(--dsw-static-neutral-bluish-900);\n --dsw-alias-markdown-inline-code: var(--dsw-static-neutral-800);\n --dsw-alias-markdown-placeholder: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-markdown-tag: var(--dsw-static-neutral-bluish-850);\n --dsw-alias-scrollbar-bg-l1: var(--dsw-static-neutral-700);\n --dsw-alias-scrollbar-bg-l2: var(--dsw-static-neutral-600);\n --dsw-alias-scrollbar-hover-l1: var(--dsw-static-neutral-600);\n --dsw-alias-scrollbar-hover-l2: var(--dsw-static-neutral-550);\n --dsw-alias-state-business-primary: var(--dsw-static-deepseek-400);\n --dsw-alias-state-business-tertiary: var(--dsw-static-deepseek-800);\n --dsw-alias-code-diff-added: var(--dsw-static-green-500-a12);\n --dsw-alias-code-diff-deleted: var(--dsw-static-red-400-a12);\n --dsw-alias-file-diff-added-bg: #1f3124;\n --dsw-alias-file-diff-added-gutter: #132016;\n --dsw-alias-file-diff-added-marker: #41c977;\n --dsw-alias-file-diff-deleted-bg: #3c1f1b;\n --dsw-alias-file-diff-deleted-gutter: #28130e;\n --dsw-alias-file-diff-deleted-marker: #fa423e;\n --dsw-alias-state-error-primary: var(--dsw-static-red-400);\n --dsw-alias-state-error-secondary: var(--dsw-static-red-400);\n --dsw-alias-state-idle-primary: var(--dsw-static-neutral-600);\n --dsw-alias-state-success-primary: var(--dsw-static-green-500);\n --dsw-alias-state-success-secondary: var(--dsw-static-green-400);\n --dsw-alias-state-success-tertiary: var(--dsw-static-green-900);\n --dsw-alias-state-warn-label: var(--dsw-static-amber-600);\n --dsw-alias-state-warn-primary: var(--dsw-static-amber-500);\n --dsw-alias-state-warn-secondary: var(--dsw-static-amber-400);\n --dsw-alias-state-warn-tertiary: var(--dsw-static-amber-900);\n --dsw-alias-switch-thumb: var(--dsw-static-neutral-bluish-400);\n --dsw-alias-toast-bg: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-toast-label: var(--dsw-static-neutral-bluish-00);\n --dsw-alias-turn-trigger-bg: var(--dsw-alias-interactive-bg-hover);\n --dsw-alias-turn-trigger-bg-hover: var(--dsw-alias-interactive-bg-active);\n --dsw-alias-tooltip-bg: var(--dsw-static-neutral-bluish-750);\n --dsw-alias-tooltip-key-bg: color-mix(in srgb, var(--dsw-alias-tooltip-bg), white 18%);\n --dsw-specific-bubble-highlight: var(--dsw-static-neutral-bluish-750);\n --dsw-specific-bubble: var(--dsw-static-neutral-bluish-850);\n --dsw-specific-input-major: var(--dsw-static-neutral-bluish-850);\n --dsw-specific-login-input: var(--dsw-static-neutral-bluish-900);\n --dsw-menu-surface-fill: #43454a73;\n --dsw-alias-menu-group-header-fill: #303136f0;\n --dsw-specific-menu: var(--dsw-menu-surface-fill);\n --dsw-specific-selector: var(--dsw-static-neutral-bluish-800);\n --dsw-specific-sidebar-fill: var(--dsw-static-neutral-bluish-900);\n --dsw-specific-sidebar-nav-item-active-accent: var(--dsw-static-neutral-bluish-800);\n --dsw-specific-sidebar-nav-item-active: var(--dsw-static-neutral-bluish-750);\n --dsw-specific-sidebar-nav-item-hover: var(--dsw-static-neutral-bluish-850);\n --dsw-specific-tip: var(--dsw-static-neutral-bluish-800);\n}\n\nhtml[data-platform=\"darwin\"] [data-dc-scheme=\"light\"] {\n --dsw-specific-menu: #f8f9faf0;\n}\n\nhtml[data-platform=\"darwin\"] [data-dc-scheme=\"dark\"] {\n --dsw-specific-menu: #303136f0;\n}\n\n[data-dc-scheme=\"light\"] {\n color-scheme: light;\n}\n\n[data-dc-scheme=\"dark\"] {\n color-scheme: dark;\n}\n.dshc_embed {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n padding-top: var(--dsh-frame-top-clearance, 0px);\n background: var(--dsw-alias-bg-base);\n display: flex;\n}\n\n.dshc_frame {\n background: var(--dsw-alias-bg-base);\n border: 0;\n flex: 1;\n width: 100%;\n height: 100%;\n}\n\n.dshc_remote {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n padding-top: var(--dsh-frame-top-clearance, 0px);\n background: var(--dsw-alias-bg-base);\n flex-direction: column;\n display: flex;\n}\n\n.dshc_remoteBar {\n border-bottom: 1px solid var(--dsw-alias-border-subtle, #7f7f7f40);\n color: var(--dsw-alias-label-secondary);\n align-items: center;\n gap: 12px;\n padding: 6px 12px;\n font-size: 13px;\n display: flex;\n}\n\n.dshc_remoteName {\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n}\n\n.dshc_remoteBack {\n border: 1px solid var(--dsw-alias-border-subtle, #7f7f7f59);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n cursor: pointer;\n background: none;\n border-radius: 6px;\n flex: none;\n padding: 2px 10px;\n}\n\n.dshc_remoteBody {\n flex: 1;\n min-height: 0;\n}\n";document.head.appendChild(s)})();
|
|
9
9
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
10
10
|
// \0rolldown/runtime.js
|
|
11
11
|
var __create = Object.create;
|
|
@@ -63,8 +63,9 @@ function randomUUID() {
|
|
|
63
63
|
// src/client/channel-source.ts
|
|
64
64
|
/** Relative URL of a stored attachment (same base as the other collab requests). */
|
|
65
65
|
const attachmentUrl = (id) => `collab/api/attachments/${encodeURIComponent(id)}`;
|
|
66
|
-
/** Read a browser File into the POST form. */
|
|
67
66
|
const MAX_MESSAGE_BODY = 8388608;
|
|
67
|
+
/** #197: fallback when the service does not report its cap (`max_upload_bytes`); matches collab's DEFAULT_MAX_UPLOAD_BYTES. */
|
|
68
|
+
const DEFAULT_MAX_UPLOAD_BYTES = 1073741824;
|
|
68
69
|
/** Whether a message with these attachments would exceed the server's body cap (checked before sending). */
|
|
69
70
|
const messageTooLarge = (text, attachments) => new TextEncoder().encode(JSON.stringify({
|
|
70
71
|
text,
|
|
@@ -72,16 +73,14 @@ const messageTooLarge = (text, attachments) => new TextEncoder().encode(JSON.str
|
|
|
72
73
|
reply_to: Number.MAX_SAFE_INTEGER,
|
|
73
74
|
attachments
|
|
74
75
|
})).length > MAX_MESSAGE_BODY;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
name: file.name || "pasted",
|
|
81
|
-
mime: file.type || "application/octet-stream",
|
|
82
|
-
data: btoa(binary)
|
|
83
|
-
};
|
|
76
|
+
/** Bytes as a limit reads in the UI: 1 GB, 500 MB, 12.5 MB. */
|
|
77
|
+
const formatBytes = (bytes) => {
|
|
78
|
+
if (bytes >= 1024 ** 3) return `${Number((bytes / 1024 ** 3).toFixed(2))} GB`;
|
|
79
|
+
if (bytes >= 1024 ** 2) return `${Number((bytes / 1024 ** 2).toFixed(1))} MB`;
|
|
80
|
+
return `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
84
81
|
};
|
|
82
|
+
/** The upload route's path for one file: a header cannot carry 中文, so name and type ride the query. */
|
|
83
|
+
const uploadUrl = (file) => `collab/api/attachments?name=${encodeURIComponent(file.name || "pasted")}&mime=${encodeURIComponent(file.type || "application/octet-stream")}`;
|
|
85
84
|
/** #1023: the private chats, one per agent member, in id order. */
|
|
86
85
|
const dmRooms = (rooms) => rooms.filter((r) => r.dm_member != null);
|
|
87
86
|
/**
|
|
@@ -121,7 +120,8 @@ const INITIAL = {
|
|
|
121
120
|
tasks: [],
|
|
122
121
|
taskChat: void 0,
|
|
123
122
|
hasMoreOlder: false,
|
|
124
|
-
outbox: []
|
|
123
|
+
outbox: [],
|
|
124
|
+
maxUploadBytes: DEFAULT_MAX_UPLOAD_BYTES
|
|
125
125
|
};
|
|
126
126
|
/** Position of message `id` in rows ascending by id, or -1. */
|
|
127
127
|
function indexOfId(rows, id) {
|
|
@@ -160,15 +160,77 @@ const rememberedRoom = () => {
|
|
|
160
160
|
};
|
|
161
161
|
/** Messages requested per catch-up read after a reconnect. */
|
|
162
162
|
const CATCH_UP_LIMIT = 200;
|
|
163
|
+
/** #197: one file the composer holds beyond the service's cap, named so the message can say which. */
|
|
164
|
+
var UploadTooLarge = class extends Error {
|
|
165
|
+
fileName;
|
|
166
|
+
size;
|
|
167
|
+
limit;
|
|
168
|
+
constructor(fileName, size, limit) {
|
|
169
|
+
super(`${fileName}:${formatBytes(size)} > ${formatBytes(limit)}`);
|
|
170
|
+
this.fileName = fileName;
|
|
171
|
+
this.size = size;
|
|
172
|
+
this.limit = limit;
|
|
173
|
+
this.name = "UploadTooLarge";
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* #216: where the instance that lost the lock publishes its holder. It is an exact route the
|
|
178
|
+
* refused plugin registers before throwing, so it answers while every other `/collab/api/*`
|
|
179
|
+
* path is a bare 404.
|
|
180
|
+
*/
|
|
181
|
+
const LOCK_HOLDER_PATH = "collab/api/lock";
|
|
163
182
|
/** Status-coded service failure so the panel can tell "not enabled" from "not signed in". */
|
|
164
183
|
var ServiceError = class extends Error {
|
|
165
184
|
status;
|
|
166
|
-
|
|
185
|
+
code;
|
|
186
|
+
/**
|
|
187
|
+
* @param status - HTTP status the service answered with.
|
|
188
|
+
* @param message - the service's own sentence, kept for logs and the fallback line.
|
|
189
|
+
* @param code - the machine code the service sends beside it (`conflict`, `invalid`, …), if any.
|
|
190
|
+
*/
|
|
191
|
+
constructor(status, message, code) {
|
|
167
192
|
super(message);
|
|
168
193
|
this.status = status;
|
|
194
|
+
this.code = code;
|
|
169
195
|
this.name = "ServiceError";
|
|
170
196
|
}
|
|
171
197
|
};
|
|
198
|
+
/** 工作区<n> is how a workspace is addressed, so the store refuses that shape for a chat channel (collab/store.mjs WORKSPACE_PREFIX). */
|
|
199
|
+
const WORKSPACE_NAME = /^工作区\d+$/;
|
|
200
|
+
/** The cap the service puts on a member name; source: collab/roster.mjs `MAX_NAME = 32`. */
|
|
201
|
+
const MEMBER_NAME_MAX = 32;
|
|
202
|
+
/** The one member name the service accepts with a space; source: collab/roster.mjs `validate` (`/^Claude Code(?:-\d+)?$/`). */
|
|
203
|
+
const MEMBER_NAME_SPACED = /^Claude Code(?:-\d+)?$/;
|
|
204
|
+
/**
|
|
205
|
+
* The member-name shape the service refuses, copied from collab/roster.mjs `validate` so a name it
|
|
206
|
+
* accepted (say `Claude Code`) is never blamed for an unrelated `invalid` such as a missing model.
|
|
207
|
+
* @param name - the name after the service's own trim.
|
|
208
|
+
* @returns whether the service would word this as a bad name.
|
|
209
|
+
*/
|
|
210
|
+
function memberNameRefused(name) {
|
|
211
|
+
return name.length > MEMBER_NAME_MAX || /[@<>\r\n\t]/.test(name) || /\s/.test(name) && !MEMBER_NAME_SPACED.test(name);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* #268: the collab service answers a refusal with `code` plus an English sentence, and the panel
|
|
215
|
+
* used to show that sentence as-is. Word the failures a person can act on from the `code` alone —
|
|
216
|
+
* never by matching the English text — and keep the service sentence only inside the last resort.
|
|
217
|
+
* @param error - whatever the action rejected with.
|
|
218
|
+
* @param t - the panel's dictionary, so an English UI stays English.
|
|
219
|
+
* @param place - the field being edited when the action failed.
|
|
220
|
+
* @param typed - the name the person typed, for the copy that names it.
|
|
221
|
+
* @returns one line to show under the form.
|
|
222
|
+
*/
|
|
223
|
+
function errorText(error, t, place = "other", typed = "") {
|
|
224
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
225
|
+
if (!(error instanceof ServiceError)) return message;
|
|
226
|
+
const code = error.code;
|
|
227
|
+
const name = place === "room" ? typed.trim().replace(/^#/, "") : typed.trim();
|
|
228
|
+
if (code === "conflict" && place === "room") return WORKSPACE_NAME.test(name) ? t("channelNameReserved", { name }) : t("channelNameTaken", { name });
|
|
229
|
+
if (code === "invalid" && place === "room") return t("channelNameInvalid");
|
|
230
|
+
if (code === "conflict" && place === "member") return t("memberNameTaken", { name });
|
|
231
|
+
if (code === "invalid" && place === "member" && memberNameRefused(name)) return t("memberNameInvalid");
|
|
232
|
+
return t("actionFailed", { message });
|
|
233
|
+
}
|
|
172
234
|
function json(method, body) {
|
|
173
235
|
return {
|
|
174
236
|
method,
|
|
@@ -186,6 +248,37 @@ function browserTransport() {
|
|
|
186
248
|
credentials: "same-origin",
|
|
187
249
|
...init
|
|
188
250
|
}),
|
|
251
|
+
upload: (url, body, onProgress, signal) => new Promise((resolve, reject) => {
|
|
252
|
+
const xhr = new XMLHttpRequest();
|
|
253
|
+
xhr.open("POST", url);
|
|
254
|
+
xhr.withCredentials = true;
|
|
255
|
+
xhr.setRequestHeader("content-type", body.type || "application/octet-stream");
|
|
256
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
257
|
+
if (event.lengthComputable) onProgress(event.loaded, event.total);
|
|
258
|
+
});
|
|
259
|
+
xhr.addEventListener("load", () => {
|
|
260
|
+
let parsed;
|
|
261
|
+
try {
|
|
262
|
+
parsed = JSON.parse(xhr.responseText);
|
|
263
|
+
} catch {
|
|
264
|
+
parsed = void 0;
|
|
265
|
+
}
|
|
266
|
+
resolve({
|
|
267
|
+
status: xhr.status,
|
|
268
|
+
body: parsed
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
xhr.addEventListener("error", () => {
|
|
272
|
+
reject(/* @__PURE__ */ new Error("Upload failed: the connection dropped"));
|
|
273
|
+
});
|
|
274
|
+
xhr.addEventListener("abort", () => {
|
|
275
|
+
reject(/* @__PURE__ */ new Error("Upload cancelled"));
|
|
276
|
+
});
|
|
277
|
+
signal?.addEventListener("abort", () => {
|
|
278
|
+
xhr.abort();
|
|
279
|
+
}, { once: true });
|
|
280
|
+
xhr.send(body);
|
|
281
|
+
}),
|
|
189
282
|
openEvents: (url) => new EventSource(url, { withCredentials: true }),
|
|
190
283
|
url: (path) => new URL(path, document.baseURI).toString(),
|
|
191
284
|
delay: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
@@ -208,6 +301,8 @@ function createChannelSource(transport) {
|
|
|
208
301
|
/** Identifies the scheduled retry, so a stop() or a newer retry cancels it. */
|
|
209
302
|
let reconnectToken;
|
|
210
303
|
/** Unconfirmed messages for every room; the snapshot exposes the selected room's. */
|
|
304
|
+
/** #197: how big one attachment may be, as the service reported it (state) or the collab default. */
|
|
305
|
+
let uploadLimit = DEFAULT_MAX_UPLOAD_BYTES;
|
|
211
306
|
let outbox = [];
|
|
212
307
|
/**
|
|
213
308
|
* Page-back request in flight per room, tagged with the ticket and the epoch
|
|
@@ -250,17 +345,54 @@ function createChannelSource(transport) {
|
|
|
250
345
|
const response = await transport.fetch(transport.url(path), init);
|
|
251
346
|
if (!response.ok) {
|
|
252
347
|
const body = await response.json().catch(() => ({}));
|
|
253
|
-
throw new ServiceError(response.status, body.error ?? response.statusText);
|
|
348
|
+
throw new ServiceError(response.status, body.error ?? response.statusText, body.code);
|
|
254
349
|
}
|
|
255
350
|
return await response.json();
|
|
256
351
|
};
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
352
|
+
/**
|
|
353
|
+
* #216: a 404 from the channel service means one of two things — no service is configured,
|
|
354
|
+
* or another DSH on this machine already holds the channel data and its plugin refused to
|
|
355
|
+
* start. Only the refusing instance knows which, and it publishes `busy` at
|
|
356
|
+
* `collab/api/lock` before it gives up; a host without that route answers 404 there too, so
|
|
357
|
+
* the plain "not configured" reading stands.
|
|
358
|
+
*/
|
|
359
|
+
const readLockHolder = async () => {
|
|
360
|
+
try {
|
|
361
|
+
const body = await request(LOCK_HOLDER_PATH);
|
|
362
|
+
if (body.busy !== true) return body.free === true ? { free: true } : void 0;
|
|
363
|
+
return {
|
|
364
|
+
holder: body.owner ?? void 0,
|
|
365
|
+
dataDir: body.dataDir,
|
|
366
|
+
lock: body.lock,
|
|
367
|
+
free: false
|
|
368
|
+
};
|
|
369
|
+
} catch (error) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
const fail = async (error, current = () => true) => {
|
|
374
|
+
if (error instanceof ServiceError && error.status === 404) {
|
|
375
|
+
const lock = await readLockHolder();
|
|
376
|
+
if (!current()) return;
|
|
377
|
+
if (lock !== void 0) set({
|
|
378
|
+
phase: "locked",
|
|
379
|
+
lockHolder: lock.holder,
|
|
380
|
+
lockDataDir: lock.dataDir,
|
|
381
|
+
lockPath: lock.lock,
|
|
382
|
+
lockFree: lock.free,
|
|
383
|
+
error: void 0
|
|
384
|
+
});
|
|
385
|
+
else set({
|
|
386
|
+
phase: "unavailable",
|
|
387
|
+
lockHolder: void 0,
|
|
388
|
+
lockDataDir: void 0,
|
|
389
|
+
lockPath: void 0,
|
|
390
|
+
lockFree: void 0,
|
|
391
|
+
error: void 0
|
|
392
|
+
});
|
|
393
|
+
} else if (error instanceof ServiceError && error.status === 401) set({
|
|
263
394
|
phase: "unauthorized",
|
|
395
|
+
lockHolder: void 0,
|
|
264
396
|
error: void 0
|
|
265
397
|
});
|
|
266
398
|
else set({
|
|
@@ -429,6 +561,7 @@ function createChannelSource(transport) {
|
|
|
429
561
|
try {
|
|
430
562
|
const state = await request("collab/api/state", { signal: read.controller.signal });
|
|
431
563
|
if (!current()) return;
|
|
564
|
+
if (Number.isSafeInteger(state.max_upload_bytes) && (state.max_upload_bytes ?? 0) > 0) uploadLimit = state.max_upload_bytes;
|
|
432
565
|
const remembered = previous.roomId === void 0 ? rememberedRoom() : void 0;
|
|
433
566
|
const roomId = state.rooms.some((r) => r.id === previous.roomId) ? previous.roomId : state.rooms.some((r) => r.id === remembered) ? remembered : state.rooms[0]?.id;
|
|
434
567
|
const rows = [];
|
|
@@ -483,6 +616,10 @@ function createChannelSource(transport) {
|
|
|
483
616
|
let next = {
|
|
484
617
|
phase: "ready",
|
|
485
618
|
error: void 0,
|
|
619
|
+
lockHolder: void 0,
|
|
620
|
+
lockDataDir: void 0,
|
|
621
|
+
lockPath: void 0,
|
|
622
|
+
lockFree: void 0,
|
|
486
623
|
rooms: state.rooms,
|
|
487
624
|
roomId,
|
|
488
625
|
members: state.members,
|
|
@@ -493,6 +630,7 @@ function createChannelSource(transport) {
|
|
|
493
630
|
hasMoreNewer,
|
|
494
631
|
leadId: state.lead_id ?? null,
|
|
495
632
|
effectiveLeadId: state.effective_lead_id ?? state.lead_id ?? null,
|
|
633
|
+
maxUploadBytes: uploadLimit,
|
|
496
634
|
tasks: keepBoard ? previous.tasks : [],
|
|
497
635
|
taskChat: keepBoard ? previous.taskChat : void 0,
|
|
498
636
|
outbox: outbox.filter((entry) => entry.roomId === roomId)
|
|
@@ -509,8 +647,8 @@ function createChannelSource(transport) {
|
|
|
509
647
|
if (roomId !== void 0) loadBoard(owner, roomId);
|
|
510
648
|
} catch (error) {
|
|
511
649
|
if (!current()) return;
|
|
512
|
-
|
|
513
|
-
|
|
650
|
+
await fail(error, current);
|
|
651
|
+
if (pending === read) pending = void 0;
|
|
514
652
|
}
|
|
515
653
|
};
|
|
516
654
|
const selectRoom = async (roomId) => {
|
|
@@ -573,6 +711,7 @@ function createChannelSource(transport) {
|
|
|
573
711
|
pending?.controller.abort();
|
|
574
712
|
pending = void 0;
|
|
575
713
|
retry(owner);
|
|
714
|
+
refresh(true);
|
|
576
715
|
};
|
|
577
716
|
events.onmessage = (event) => {
|
|
578
717
|
if (lifetime !== owner || stream !== events) return;
|
|
@@ -588,7 +727,7 @@ function createChannelSource(transport) {
|
|
|
588
727
|
reconnectToken = token;
|
|
589
728
|
const wait = backoffMs(attempt);
|
|
590
729
|
attempt += 1;
|
|
591
|
-
set({
|
|
730
|
+
if (snapshot.phase !== "locked" && snapshot.phase !== "unavailable" && snapshot.phase !== "unauthorized") set({
|
|
592
731
|
phase: "reconnecting",
|
|
593
732
|
error: void 0
|
|
594
733
|
});
|
|
@@ -685,9 +824,33 @@ function createChannelSource(transport) {
|
|
|
685
824
|
throw error;
|
|
686
825
|
}
|
|
687
826
|
};
|
|
827
|
+
/**
|
|
828
|
+
* #197: stream one file to the service and return the metadata the message names.
|
|
829
|
+
* The bytes never pass through the message JSON, so the size cap is the service's
|
|
830
|
+
* `maxUploadBytes` instead of MAX_MESSAGE_BODY.
|
|
831
|
+
*/
|
|
832
|
+
const upload = async (file, onProgress, signal) => {
|
|
833
|
+
if (file.size > uploadLimit) throw new UploadTooLarge(file.name, file.size, uploadLimit);
|
|
834
|
+
if (transport.upload === void 0) throw new Error("This browser cannot stream uploads");
|
|
835
|
+
const { status, body } = await transport.upload(transport.url(uploadUrl(file)), file, onProgress ?? (() => {}), signal);
|
|
836
|
+
const value = body;
|
|
837
|
+
if (status === 413) {
|
|
838
|
+
uploadLimit = Number(value?.max_upload_bytes ?? uploadLimit);
|
|
839
|
+
throw new UploadTooLarge(file.name, file.size, uploadLimit);
|
|
840
|
+
}
|
|
841
|
+
const stored = value?.attachment;
|
|
842
|
+
if (status >= 400 || stored?.id === void 0) throw new ServiceError(status, value?.error ?? "Upload failed");
|
|
843
|
+
return {
|
|
844
|
+
id: stored.id,
|
|
845
|
+
name: stored.name ?? file.name,
|
|
846
|
+
mime: stored.mime ?? file.type,
|
|
847
|
+
size: stored.size ?? file.size
|
|
848
|
+
};
|
|
849
|
+
};
|
|
688
850
|
return {
|
|
689
851
|
source,
|
|
690
852
|
actions: {
|
|
853
|
+
uploadAttachment: upload,
|
|
691
854
|
async reviewTask(id, verdict, note) {
|
|
692
855
|
const { task } = await request(`collab/api/tasks/${id}/review`, json("POST", {
|
|
693
856
|
verdict,
|
|
@@ -706,8 +869,8 @@ function createChannelSource(transport) {
|
|
|
706
869
|
set({ tasks: [...(snapshot.tasks ?? []).filter((row) => row.id !== task.id), task] });
|
|
707
870
|
},
|
|
708
871
|
selectRoom,
|
|
709
|
-
async send(text, replyTo, attachments) {
|
|
710
|
-
const roomId = snapshot.roomId;
|
|
872
|
+
async send(text, replyTo, attachments, targetRoomId) {
|
|
873
|
+
const roomId = targetRoomId ?? snapshot.roomId;
|
|
711
874
|
if (roomId === void 0) throw new Error("No channel is open");
|
|
712
875
|
await post(roomId, randomUUID(), text, replyTo, attachments);
|
|
713
876
|
},
|
|
@@ -16648,6 +16811,44 @@ function softBreaksToHard(text) {
|
|
|
16648
16811
|
for (let k = breaks.length - 1; k >= 0; k--) out = out.slice(0, breaks[k]) + " " + out.slice(breaks[k]);
|
|
16649
16812
|
return out;
|
|
16650
16813
|
}
|
|
16814
|
+
// src/client/collab-url.ts
|
|
16815
|
+
/**
|
|
16816
|
+
* #236: which DSH a channels panel talks to. The local panel uses the collab paths as they are
|
|
16817
|
+
* (`collab/api/…`, relative to document.baseURI); a panel opened from 其他电脑 on a paired
|
|
16818
|
+
* computer sends the same paths through this DSH's forwarder `collab/remote/<link>/…`
|
|
16819
|
+
* (dsh/collab/pairing.mjs remoteProxyRoute), so mentions, avatars, replies, attachments and the
|
|
16820
|
+
* task board all read and write the other computer and never this one.
|
|
16821
|
+
*/
|
|
16822
|
+
/** Local panel: paths stay as they are. */
|
|
16823
|
+
const localCollabUrl = (path) => path;
|
|
16824
|
+
/** A paired link id, as dsh/collab/pairing.mjs PairClient issues them (randomUUID). */
|
|
16825
|
+
const LINK_ID = /^[0-9a-f-]{36}$/;
|
|
16826
|
+
/**
|
|
16827
|
+
* Paths of a paired computer, through this DSH's remote forwarder.
|
|
16828
|
+
* @param linkId - link id from `GET collab/api/remote/links`.
|
|
16829
|
+
* @returns the path mapper; non-collab paths (http links) are left alone.
|
|
16830
|
+
*/
|
|
16831
|
+
function remoteCollabUrl(linkId) {
|
|
16832
|
+
if (!LINK_ID.test(linkId)) throw new Error(`bad remote link id: ${linkId}`);
|
|
16833
|
+
const mount = `collab/remote/${linkId}/`;
|
|
16834
|
+
return (path) => path.startsWith("collab/") ? mount + path.slice(7) : path;
|
|
16835
|
+
}
|
|
16836
|
+
/**
|
|
16837
|
+
* Same transport with its paths sent to a paired computer.
|
|
16838
|
+
* @param base - transport bound to this page (browserTransport).
|
|
16839
|
+
* @param map - path mapper from remoteCollabUrl.
|
|
16840
|
+
* @returns the remote transport.
|
|
16841
|
+
*/
|
|
16842
|
+
function mappedTransport(base, map) {
|
|
16843
|
+
return {
|
|
16844
|
+
...base,
|
|
16845
|
+
url: (path) => base.url(map(path))
|
|
16846
|
+
};
|
|
16847
|
+
}
|
|
16848
|
+
/** The mapper of the panel a component renders in; the local panel when none is provided. */
|
|
16849
|
+
const CollabUrlContext = (0, react.createContext)(localCollabUrl);
|
|
16850
|
+
/** Read the mapper of the surrounding channels panel. */
|
|
16851
|
+
const useCollabUrl = () => (0, react.useContext)(CollabUrlContext);
|
|
16651
16852
|
// src/client/ChannelsPage.module.css
|
|
16652
16853
|
var ChannelsPage_module_default = {
|
|
16653
16854
|
"activeBusy": "dshc_activeBusy",
|
|
@@ -16789,6 +16990,10 @@ var ChannelsPage_module_default = {
|
|
|
16789
16990
|
"imagePreviewImage": "dshc_imagePreviewImage",
|
|
16790
16991
|
"imagePreviewTrigger": "dshc_imagePreviewTrigger",
|
|
16791
16992
|
"input": "dshc_input",
|
|
16993
|
+
"inputComposing": "dshc_inputComposing",
|
|
16994
|
+
"inputHighlights": "dshc_inputHighlights",
|
|
16995
|
+
"inputMention": "dshc_inputMention",
|
|
16996
|
+
"inputSurface": "dshc_inputSurface",
|
|
16792
16997
|
"jumpCount": "dshc_jumpCount",
|
|
16793
16998
|
"jumpLatest": "dshc_jumpLatest",
|
|
16794
16999
|
"leadCard": "dshc_leadCard",
|
|
@@ -16962,6 +17167,9 @@ var ChannelsPage_module_default = {
|
|
|
16962
17167
|
"undoToast": "dshc_undoToast",
|
|
16963
17168
|
"unreadBadge": "dshc_unreadBadge",
|
|
16964
17169
|
"unreadDivider": "dshc_unreadDivider",
|
|
17170
|
+
"uploadBar": "dshc_uploadBar",
|
|
17171
|
+
"uploadFill": "dshc_uploadFill",
|
|
17172
|
+
"uploadState": "dshc_uploadState",
|
|
16965
17173
|
"withdrawnRow": "dshc_withdrawnRow",
|
|
16966
17174
|
"working": "dshc_working",
|
|
16967
17175
|
"workspace": "dshc_workspace"
|
|
@@ -17027,8 +17235,10 @@ function useScheme() {
|
|
|
17027
17235
|
// src/client/Attachments.tsx
|
|
17028
17236
|
const isImage = (mime) => /^image\/(png|jpeg|gif|webp)$/.test(mime);
|
|
17029
17237
|
const isAudio = (mime) => mime.startsWith("audio/");
|
|
17030
|
-
const size =
|
|
17238
|
+
const size = formatBytes;
|
|
17031
17239
|
function ImageAttachment({ attachment: a, t }) {
|
|
17240
|
+
const url = useCollabUrl();
|
|
17241
|
+
const attachmentUrl$1 = (id) => url(attachmentUrl(id));
|
|
17032
17242
|
const [opened, setOpened] = (0, react.useState)(false);
|
|
17033
17243
|
const trigger = (0, react.useRef)(null);
|
|
17034
17244
|
const dialog = (0, react.useRef)(null);
|
|
@@ -17060,7 +17270,7 @@ function ImageAttachment({ attachment: a, t }) {
|
|
|
17060
17270
|
setCopyState("error");
|
|
17061
17271
|
return;
|
|
17062
17272
|
}
|
|
17063
|
-
const response = await fetch(attachmentUrl(a.id));
|
|
17273
|
+
const response = await fetch(attachmentUrl$1(a.id));
|
|
17064
17274
|
if (!response.ok) throw new Error("Image download failed");
|
|
17065
17275
|
const blob = await response.blob();
|
|
17066
17276
|
await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
|
|
@@ -17072,7 +17282,7 @@ function ImageAttachment({ attachment: a, t }) {
|
|
|
17072
17282
|
const copyLabel = t(copyState === "copied" ? "attachmentCopied" : copyState === "copying" ? "attachmentCopying" : "attachmentCopy");
|
|
17073
17283
|
const actions = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
17074
17284
|
className: copyState === "error" ? ChannelsPage_module_default.primaryButton : ChannelsPage_module_default.secondaryButton,
|
|
17075
|
-
href: attachmentUrl(a.id),
|
|
17285
|
+
href: attachmentUrl$1(a.id),
|
|
17076
17286
|
download: a.name,
|
|
17077
17287
|
target: "_blank",
|
|
17078
17288
|
rel: "noreferrer",
|
|
@@ -17108,7 +17318,7 @@ function ImageAttachment({ attachment: a, t }) {
|
|
|
17108
17318
|
onClick: () => setOpened(true),
|
|
17109
17319
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
17110
17320
|
className: ChannelsPage_module_default.attachmentImage,
|
|
17111
|
-
src: attachmentUrl(a.id),
|
|
17321
|
+
src: attachmentUrl$1(a.id),
|
|
17112
17322
|
alt: a.name,
|
|
17113
17323
|
loading: "lazy"
|
|
17114
17324
|
})
|
|
@@ -17164,7 +17374,7 @@ function ImageAttachment({ attachment: a, t }) {
|
|
|
17164
17374
|
}),
|
|
17165
17375
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
17166
17376
|
className: ChannelsPage_module_default.imagePreviewImage,
|
|
17167
|
-
src: attachmentUrl(a.id),
|
|
17377
|
+
src: attachmentUrl$1(a.id),
|
|
17168
17378
|
alt: a.name
|
|
17169
17379
|
}),
|
|
17170
17380
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -17180,6 +17390,8 @@ function ImageAttachment({ attachment: a, t }) {
|
|
|
17180
17390
|
}
|
|
17181
17391
|
/** Thumbnails for images, a download card for other files. */
|
|
17182
17392
|
function AttachmentList({ attachments, t }) {
|
|
17393
|
+
const url = useCollabUrl();
|
|
17394
|
+
const attachmentUrl$2 = (id) => url(attachmentUrl(id));
|
|
17183
17395
|
if (attachments === void 0 || attachments.length === 0) return null;
|
|
17184
17396
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
17185
17397
|
className: ChannelsPage_module_default.attachments,
|
|
@@ -17191,11 +17403,11 @@ function AttachmentList({ attachments, t }) {
|
|
|
17191
17403
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
|
|
17192
17404
|
controls: true,
|
|
17193
17405
|
preload: "metadata",
|
|
17194
|
-
src: attachmentUrl(a.id),
|
|
17406
|
+
src: attachmentUrl$2(a.id),
|
|
17195
17407
|
title: a.name
|
|
17196
17408
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
17197
17409
|
className: ChannelsPage_module_default.attachmentSize,
|
|
17198
|
-
href: attachmentUrl(a.id),
|
|
17410
|
+
href: attachmentUrl$2(a.id),
|
|
17199
17411
|
download: a.name,
|
|
17200
17412
|
children: [
|
|
17201
17413
|
a.name,
|
|
@@ -17205,7 +17417,7 @@ function AttachmentList({ attachments, t }) {
|
|
|
17205
17417
|
})]
|
|
17206
17418
|
}, a.id) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
17207
17419
|
className: ChannelsPage_module_default.attachmentFile,
|
|
17208
|
-
href: attachmentUrl(a.id),
|
|
17420
|
+
href: attachmentUrl$2(a.id),
|
|
17209
17421
|
download: a.name,
|
|
17210
17422
|
children: [
|
|
17211
17423
|
"📎 ",
|
|
@@ -17225,13 +17437,11 @@ const eventFiles = (list) => list === null ? [] : Array.from(list.files);
|
|
|
17225
17437
|
* Composer helpers: an attach button with its hidden file input, the pending
|
|
17226
17438
|
* list with remove buttons, and paste/drop handlers for the textarea.
|
|
17227
17439
|
*/
|
|
17228
|
-
function useComposerAttachments(add
|
|
17440
|
+
function useComposerAttachments(add) {
|
|
17229
17441
|
const inputRef = (0, react.useRef)(null);
|
|
17230
17442
|
const take = (files) => {
|
|
17231
17443
|
if (files.length === 0) return;
|
|
17232
|
-
|
|
17233
|
-
onError(error instanceof Error ? error.message : String(error));
|
|
17234
|
-
});
|
|
17444
|
+
add(files);
|
|
17235
17445
|
};
|
|
17236
17446
|
return {
|
|
17237
17447
|
onPaste(event) {
|
|
@@ -17281,37 +17491,67 @@ function useComposerAttachments(add, onError) {
|
|
|
17281
17491
|
}
|
|
17282
17492
|
};
|
|
17283
17493
|
}
|
|
17284
|
-
/** Files waiting to be sent with the next message. */
|
|
17285
|
-
function PendingAttachments({ files, onRemove, removeLabel }) {
|
|
17494
|
+
/** Files waiting to be sent with the next message, each with its upload progress. */
|
|
17495
|
+
function PendingAttachments({ files, onRemove, removeLabel, labels }) {
|
|
17286
17496
|
if (files.length === 0) return null;
|
|
17287
17497
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
17288
17498
|
className: ChannelsPage_module_default.attachments,
|
|
17289
|
-
children: files.map((f
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
|
|
17297
|
-
|
|
17298
|
-
|
|
17299
|
-
|
|
17300
|
-
|
|
17301
|
-
|
|
17302
|
-
|
|
17303
|
-
"
|
|
17304
|
-
|
|
17305
|
-
|
|
17306
|
-
|
|
17307
|
-
|
|
17308
|
-
|
|
17499
|
+
children: files.map((f) => {
|
|
17500
|
+
const percent = Math.round(f.progress * 100);
|
|
17501
|
+
const state = f.error !== void 0 ? f.error : f.attachment !== void 0 ? "" : percent > 0 ? labels.uploading : labels.queued;
|
|
17502
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
17503
|
+
className: ChannelsPage_module_default.attachmentFile,
|
|
17504
|
+
"data-state": f.error !== void 0 ? "failed" : f.attachment !== void 0 ? "done" : "uploading",
|
|
17505
|
+
children: [
|
|
17506
|
+
f.preview !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
17507
|
+
className: ChannelsPage_module_default.attachmentThumb,
|
|
17508
|
+
src: f.preview,
|
|
17509
|
+
alt: f.name
|
|
17510
|
+
}) : "📎",
|
|
17511
|
+
" ",
|
|
17512
|
+
f.name,
|
|
17513
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
17514
|
+
className: ChannelsPage_module_default.attachmentSize,
|
|
17515
|
+
children: size(f.size)
|
|
17516
|
+
}),
|
|
17517
|
+
f.attachment === void 0 && f.error === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
17518
|
+
className: ChannelsPage_module_default.uploadBar,
|
|
17519
|
+
role: "progressbar",
|
|
17520
|
+
"aria-valuenow": percent,
|
|
17521
|
+
"aria-valuemin": 0,
|
|
17522
|
+
"aria-valuemax": 100,
|
|
17523
|
+
"aria-label": `${f.name} ${percent}%`,
|
|
17524
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
17525
|
+
className: ChannelsPage_module_default.uploadFill,
|
|
17526
|
+
style: { width: `${percent}%` }
|
|
17527
|
+
})
|
|
17528
|
+
}),
|
|
17529
|
+
state !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
17530
|
+
className: ChannelsPage_module_default.uploadState,
|
|
17531
|
+
children: state
|
|
17532
|
+
}),
|
|
17533
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
17534
|
+
type: "button",
|
|
17535
|
+
className: ChannelsPage_module_default.replyCancel,
|
|
17536
|
+
title: removeLabel,
|
|
17537
|
+
"aria-label": removeLabel,
|
|
17538
|
+
onClick: () => onRemove(f.key),
|
|
17539
|
+
children: "×"
|
|
17540
|
+
})
|
|
17541
|
+
]
|
|
17542
|
+
}, f.key);
|
|
17543
|
+
})
|
|
17309
17544
|
});
|
|
17310
17545
|
}
|
|
17311
17546
|
// src/client/PanelPages.module.css
|
|
17312
17547
|
var PanelPages_module_default = {
|
|
17313
17548
|
"embed": "dshc_embed",
|
|
17314
|
-
"frame": "dshc_frame"
|
|
17549
|
+
"frame": "dshc_frame",
|
|
17550
|
+
"remote": "dshc_remote",
|
|
17551
|
+
"remoteBack": "dshc_remoteBack",
|
|
17552
|
+
"remoteBar": "dshc_remoteBar",
|
|
17553
|
+
"remoteBody": "dshc_remoteBody",
|
|
17554
|
+
"remoteName": "dshc_remoteName"
|
|
17315
17555
|
};
|
|
17316
17556
|
// src/client/EmbedPage.tsx
|
|
17317
17557
|
/**
|
|
@@ -17344,10 +17584,10 @@ function firstWebLink(text) {
|
|
|
17344
17584
|
return /https?:\/\/[^\s<>()\]"'`]+/i.exec(text)?.[0].replace(/[.,;:!?。,;:!?]+$/, "");
|
|
17345
17585
|
}
|
|
17346
17586
|
/** The web page this message delivers: an .html upload first, else the first link. */
|
|
17347
|
-
function deliverableOf(message) {
|
|
17587
|
+
function deliverableOf(message, url = (path) => path) {
|
|
17348
17588
|
const page = message.attachments?.find(isHtmlAttachment);
|
|
17349
17589
|
if (page !== void 0) return {
|
|
17350
|
-
src: `${attachmentUrl(page.id)}?view=page`,
|
|
17590
|
+
src: `${url(attachmentUrl(page.id))}?view=page`,
|
|
17351
17591
|
title: page.name,
|
|
17352
17592
|
sameOrigin: true
|
|
17353
17593
|
};
|
|
@@ -18495,7 +18735,8 @@ const GROUP_TITLE = {
|
|
|
18495
18735
|
model: "slashGroupModel",
|
|
18496
18736
|
view: "slashGroupView"
|
|
18497
18737
|
};
|
|
18498
|
-
function SlashMenu({ commands, selected, target, dmName, disabled = () => false, grouped, t, onPick, onHover }) {
|
|
18738
|
+
function SlashMenu({ commands, selected, target, leadName, dmName, disabled = () => false, grouped, t, onPick, onHover }) {
|
|
18739
|
+
const noCmd = dmName ?? leadName;
|
|
18499
18740
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
18500
18741
|
className: `${ChannelsPage_module_default.mentionPopup} ${ChannelsPage_module_default.slashPopup}`,
|
|
18501
18742
|
role: "region",
|
|
@@ -18503,7 +18744,7 @@ function SlashMenu({ commands, selected, target, dmName, disabled = () => false,
|
|
|
18503
18744
|
children: [
|
|
18504
18745
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
18505
18746
|
className: ChannelsPage_module_default.mentionTitle,
|
|
18506
|
-
children: target !== void 0 ? t("slashTitleLead", { name: target }) :
|
|
18747
|
+
children: target !== void 0 ? t("slashTitleLead", { name: target }) : noCmd !== void 0 ? t("slashTitleDmNoCmd", { name: noCmd }) : t("slashTitleNoLead")
|
|
18507
18748
|
}),
|
|
18508
18749
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
18509
18750
|
role: "listbox",
|
|
@@ -18810,10 +19051,248 @@ function insertMention(text, caret, member) {
|
|
|
18810
19051
|
* Highlights valid channel members/roles as pill badges (Discord-style),
|
|
18811
19052
|
* cleanly separated from regular text, without false positives in code or emails.
|
|
18812
19053
|
*/
|
|
19054
|
+
const FENCE = "```";
|
|
18813
19055
|
function escapeRegex(s) {
|
|
18814
19056
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18815
19057
|
}
|
|
18816
19058
|
/**
|
|
19059
|
+
* True when the text holds a blank line: two line endings with only spaces or tabs between.
|
|
19060
|
+
* A `code span` cannot cross one — the blank line ends the paragraph, so the renderer closes the
|
|
19061
|
+
* span before it (#245: `@肥鱼` + blank line + `@G哥` inside one pair of backticks is plain text
|
|
19062
|
+
* on the page, and must be plain text on the backend too).
|
|
19063
|
+
*/
|
|
19064
|
+
function hasBlankLine(text) {
|
|
19065
|
+
return /\n[ \t]*\n/.test(text.replace(/\r\n?/g, "\n"));
|
|
19066
|
+
}
|
|
19067
|
+
const HTML_BLOCK_TAGS = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul";
|
|
19068
|
+
const HTML_BLOCK_TAG = new RegExp(`^ {0,3}</?(?:${HTML_BLOCK_TAGS})(?:[ \\t]|/?>|$)`, "i");
|
|
19069
|
+
const HTML_ANY_TAG = /^ {0,3}<\/?[A-Za-z][A-Za-z0-9-]*(?:[ \t]+[^<>]*)?[ \t]*\/?>[ \t]*$/;
|
|
19070
|
+
const LIST_MARKER = /^ {0,3}(?:[-*+]|\d{1,9}[.)])(?=[ \t]|$)/;
|
|
19071
|
+
const FENCE_LINE = /^ {0,3}(?:`{3,}|~{3,})/;
|
|
19072
|
+
/** Columns the leading indent of `text` covers, starting from column `base`. */
|
|
19073
|
+
function indentWidth(text, base = 0) {
|
|
19074
|
+
let col = base;
|
|
19075
|
+
for (const ch of text) if (ch === " ") col += 1;
|
|
19076
|
+
else if (ch === " ") col += 4 - col % 4;
|
|
19077
|
+
else break;
|
|
19078
|
+
return col - base;
|
|
19079
|
+
}
|
|
19080
|
+
/**
|
|
19081
|
+
* The blockquote prefix at `line[i]` (three spaces at most, then `>` and one optional blank), read
|
|
19082
|
+
* from column `col`; `{ next, col }` after it, or undefined when the line has none there.
|
|
19083
|
+
*/
|
|
19084
|
+
function quotePrefixAt(line, from, col) {
|
|
19085
|
+
let i = from;
|
|
19086
|
+
let at = col;
|
|
19087
|
+
let spaces = 0;
|
|
19088
|
+
while (spaces < 4 && line[i] === " ") {
|
|
19089
|
+
i++;
|
|
19090
|
+
at++;
|
|
19091
|
+
spaces++;
|
|
19092
|
+
}
|
|
19093
|
+
if (spaces > 3 || line[i] !== ">") return void 0;
|
|
19094
|
+
i++;
|
|
19095
|
+
at++;
|
|
19096
|
+
if (line[i] === " " || line[i] === " ") {
|
|
19097
|
+
at += line[i] === " " ? 4 - at % 4 : 1;
|
|
19098
|
+
i++;
|
|
19099
|
+
}
|
|
19100
|
+
return {
|
|
19101
|
+
next: i,
|
|
19102
|
+
col: at
|
|
19103
|
+
};
|
|
19104
|
+
}
|
|
19105
|
+
/** Strips every blockquote marker of a line, so its content can be measured from column 0. */
|
|
19106
|
+
function stripQuotes(line) {
|
|
19107
|
+
let i = 0;
|
|
19108
|
+
let col = 0;
|
|
19109
|
+
let depth = 0;
|
|
19110
|
+
for (;;) {
|
|
19111
|
+
const prefix = quotePrefixAt(line, i, col);
|
|
19112
|
+
if (!prefix) return {
|
|
19113
|
+
rest: line.slice(i),
|
|
19114
|
+
depth,
|
|
19115
|
+
col
|
|
19116
|
+
};
|
|
19117
|
+
i = prefix.next;
|
|
19118
|
+
col = prefix.col;
|
|
19119
|
+
depth++;
|
|
19120
|
+
}
|
|
19121
|
+
}
|
|
19122
|
+
/**
|
|
19123
|
+
* Column where a list item's content begins, or undefined when the line is not a list item. An
|
|
19124
|
+
* empty marker (`1.` alone on its line) cannot open a list inside a paragraph, so it counts as
|
|
19125
|
+
* neither.
|
|
19126
|
+
*/
|
|
19127
|
+
function itemContentIndent(line) {
|
|
19128
|
+
const { rest, col } = stripQuotes(line);
|
|
19129
|
+
const lead = indentWidth(rest, col);
|
|
19130
|
+
if (lead >= 4) return void 0;
|
|
19131
|
+
const body = rest.slice(/^[ \t]*/.exec(rest)[0].length);
|
|
19132
|
+
const marker = LIST_MARKER.exec(body);
|
|
19133
|
+
if (!marker) return void 0;
|
|
19134
|
+
if (body.slice(marker[0].length).trim() === "") return void 0;
|
|
19135
|
+
const gap = /^[ \t]*/.exec(body.slice(marker[0].length))[0];
|
|
19136
|
+
const padding = gap === "" ? 1 : Math.min(indentWidth(gap, col + lead + marker[0].length), 4);
|
|
19137
|
+
return lead + marker[0].length + padding;
|
|
19138
|
+
}
|
|
19139
|
+
/**
|
|
19140
|
+
* True when a line whose content starts with `content` opens one of the block-level structures
|
|
19141
|
+
* this rule knows, ending the paragraph before it: ATX headings, setext underlines, thematic
|
|
19142
|
+
* breaks, backtick fences, list items, HTML blocks and a further quote marker. Tilde fences and
|
|
19143
|
+
* math blocks also end a paragraph in CommonMark, but the backend reads neither, so treating them
|
|
19144
|
+
* as spans keeps a different bug out of this change (#259 stays on the six families the report
|
|
19145
|
+
* names; the deviation list above splitMentions() records both). A footnote definition is handled
|
|
19146
|
+
* by footnoteRanges(), not here: it ends a body, not the paragraph a code span sits in (#271).
|
|
19147
|
+
* `container` is set for a line that lazily continues an outer container: there a bare list
|
|
19148
|
+
* marker or a lone HTML tag already opens a block the paragraph cannot hold.
|
|
19149
|
+
*/
|
|
19150
|
+
function startsBlock(content, setext = true, container = false) {
|
|
19151
|
+
if (indentWidth(content) >= 4) return false;
|
|
19152
|
+
if (container && /^ {0,3}(?:[-*+]|\d{1,9}[.)])(?:[ \t]|$)/.test(content)) return true;
|
|
19153
|
+
if (container && HTML_ANY_TAG.test(content)) return true;
|
|
19154
|
+
return /^ {0,3}#{1,6}(?:[ \t]|$)/.test(content) || setext && /^ {0,3}(?:=+|-+)[ \t]*$/.test(content) || /^ {0,3}(?:[*\-_][ \t]*){3,}$/.test(content) || /^ {0,3}`{3,}[^`]*$/.test(content) || /^ {0,3}>/.test(content) || /^ {0,3}[-*+][ \t]+\S/.test(content) || /^ {0,3}1[.)][ \t]+\S/.test(content) || /^ {0,3}<(?:script|pre|style|textarea)(?:[ \t>]|$)/i.test(content) || /^ {0,3}<!--/.test(content) || /^ {0,3}<\?/.test(content) || /^ {0,3}<![A-Za-z]/.test(content) || /^ {0,3}<!\[CDATA\[/.test(content) || HTML_BLOCK_TAG.test(content);
|
|
19155
|
+
}
|
|
19156
|
+
/**
|
|
19157
|
+
* [start, end) offsets of every physical line; CRLF, CR and LF all end one. Read once per
|
|
19158
|
+
* codeSpanRanges() call and handed to lineIndex(), not rescanned per backtick run (#271: doing
|
|
19159
|
+
* that cost O(runs × lines) — 128 KiB of one-backtick lines took 54 s inside hub.post's
|
|
19160
|
+
* transaction).
|
|
19161
|
+
*/
|
|
19162
|
+
function splitLines(text) {
|
|
19163
|
+
const lines = [];
|
|
19164
|
+
for (let start = 0; start <= text.length;) {
|
|
19165
|
+
let end = start;
|
|
19166
|
+
while (end < text.length && text[end] !== "\n" && text[end] !== "\r") end++;
|
|
19167
|
+
lines.push([start, end]);
|
|
19168
|
+
if (end >= text.length) break;
|
|
19169
|
+
start = text[end] === "\r" && text[end + 1] === "\n" ? end + 2 : end + 1;
|
|
19170
|
+
}
|
|
19171
|
+
return lines;
|
|
19172
|
+
}
|
|
19173
|
+
/**
|
|
19174
|
+
* The line holding `offset`: the last line that starts at or before it. `splitLines` covers the
|
|
19175
|
+
* whole text contiguously, so that is the line `offset` sits on, or the one that ends there.
|
|
19176
|
+
*/
|
|
19177
|
+
function lineAt(lines, offset) {
|
|
19178
|
+
let lo = 0;
|
|
19179
|
+
let hi = lines.length - 1;
|
|
19180
|
+
while (lo < hi) {
|
|
19181
|
+
const mid = lo + hi + 1 >> 1;
|
|
19182
|
+
if (lines[mid][0] <= offset) lo = mid;
|
|
19183
|
+
else hi = mid - 1;
|
|
19184
|
+
}
|
|
19185
|
+
return lo;
|
|
19186
|
+
}
|
|
19187
|
+
function lineFacts(text, lines, i) {
|
|
19188
|
+
const line = text.slice(lines[i][0], lines[i][1]);
|
|
19189
|
+
const { rest, depth, col } = stripQuotes(line);
|
|
19190
|
+
return {
|
|
19191
|
+
blank: line.trim() === "",
|
|
19192
|
+
fence: FENCE_LINE.test(line),
|
|
19193
|
+
itemIndent: itemContentIndent(line),
|
|
19194
|
+
rest,
|
|
19195
|
+
depth,
|
|
19196
|
+
col,
|
|
19197
|
+
atDepth: startsBlock(rest, false, true)
|
|
19198
|
+
};
|
|
19199
|
+
}
|
|
19200
|
+
function lineIndex(text) {
|
|
19201
|
+
const lines = splitLines(text);
|
|
19202
|
+
const facts = lines.map((_, i) => lineFacts(text, lines, i));
|
|
19203
|
+
const contained = new Array(lines.length);
|
|
19204
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19205
|
+
const f = facts[i];
|
|
19206
|
+
if (f.blank || f.fence) {
|
|
19207
|
+
contained[i] = {
|
|
19208
|
+
baseIndent: 0,
|
|
19209
|
+
depth: 0
|
|
19210
|
+
};
|
|
19211
|
+
continue;
|
|
19212
|
+
}
|
|
19213
|
+
const run = i > 0 && !facts[i - 1].blank && !facts[i - 1].fence ? contained[i - 1] : void 0;
|
|
19214
|
+
contained[i] = {
|
|
19215
|
+
baseIndent: f.itemIndent !== void 0 ? f.itemIndent : run ? run.baseIndent : 0,
|
|
19216
|
+
depth: Math.max(f.depth, run ? run.depth : 0)
|
|
19217
|
+
};
|
|
19218
|
+
}
|
|
19219
|
+
return {
|
|
19220
|
+
lines,
|
|
19221
|
+
facts,
|
|
19222
|
+
contained
|
|
19223
|
+
};
|
|
19224
|
+
}
|
|
19225
|
+
/**
|
|
19226
|
+
* True when the text between an opening and a closing backtick run holds something that ends the
|
|
19227
|
+
* paragraph, so the run cannot close the span (#259): a blank line, or a line that opens a
|
|
19228
|
+
* block-level structure. The renderer decides this inside the containers the span sits in, so the
|
|
19229
|
+
* list item's content indent and the blockquote depth of the opening line are carried along, and
|
|
19230
|
+
* a line that lazily continues an outer container is judged by what it would start there.
|
|
19231
|
+
*
|
|
19232
|
+
* `index` is the lineIndex(text) of the whole text, shared by every pair (#271): rebuilt per pair
|
|
19233
|
+
* it cost O(runs × lines), 128 KiB of one-backtick lines taking 54 s.
|
|
19234
|
+
*/
|
|
19235
|
+
function endsParagraph(text, from, to, index) {
|
|
19236
|
+
if (hasBlankLine(text.slice(from, to))) return true;
|
|
19237
|
+
const { lines, facts, contained } = index;
|
|
19238
|
+
const openLine = lineAt(lines, from);
|
|
19239
|
+
const { baseIndent, depth: openDepth } = contained[openLine];
|
|
19240
|
+
for (let i = openLine + 1; i < lines.length && lines[i][0] < to; i++) {
|
|
19241
|
+
const f = facts[i];
|
|
19242
|
+
if (f.rest.trim() === "") return true;
|
|
19243
|
+
if (f.depth > openDepth) return true;
|
|
19244
|
+
if (f.depth < openDepth) {
|
|
19245
|
+
if (f.atDepth) return true;
|
|
19246
|
+
continue;
|
|
19247
|
+
}
|
|
19248
|
+
const indent = indentWidth(f.rest, f.col) - baseIndent;
|
|
19249
|
+
if (startsBlock(" ".repeat(Math.max(0, indent)) + f.rest.trimStart(), indent >= 0, indent < 0)) return true;
|
|
19250
|
+
}
|
|
19251
|
+
return false;
|
|
19252
|
+
}
|
|
19253
|
+
/**
|
|
19254
|
+
* Offsets of Markdown inline code spans, [start, end) each, matching what the markdown renderer
|
|
19255
|
+
* turns into <code> and the backend skips (dsh/collab/mentions.mjs codeSpanRanges — keep them in
|
|
19256
|
+
* step). A span is a run of backticks closed by a run of the same length inside the same
|
|
19257
|
+
* paragraph; it may hold single line breaks but nothing that ends the paragraph (a blank line
|
|
19258
|
+
* #245, or a block-level structure #259). A lone backtick stays literal text, and runs of three
|
|
19259
|
+
* or more are fences, left to fencedRanges().
|
|
19260
|
+
*/
|
|
19261
|
+
function codeSpanRanges(text) {
|
|
19262
|
+
const runs = [];
|
|
19263
|
+
for (let i = 0; i < text.length;) {
|
|
19264
|
+
if (text[i] !== "`") {
|
|
19265
|
+
i++;
|
|
19266
|
+
continue;
|
|
19267
|
+
}
|
|
19268
|
+
let j = i;
|
|
19269
|
+
while (j < text.length && text[j] === "`") j++;
|
|
19270
|
+
runs.push({
|
|
19271
|
+
start: i,
|
|
19272
|
+
len: j - i
|
|
19273
|
+
});
|
|
19274
|
+
i = j;
|
|
19275
|
+
}
|
|
19276
|
+
const ranges = [];
|
|
19277
|
+
let index;
|
|
19278
|
+
for (let k = 0; k < runs.length; k++) {
|
|
19279
|
+
const { start, len } = runs[k];
|
|
19280
|
+
if (len >= 3) continue;
|
|
19281
|
+
for (let c = k + 1; c < runs.length; c++) {
|
|
19282
|
+
if (runs[c].len !== len) continue;
|
|
19283
|
+
if (endsParagraph(text, start + len, runs[c].start, index ??= lineIndex(text))) break;
|
|
19284
|
+
ranges.push([start + len, runs[c].start]);
|
|
19285
|
+
k = c;
|
|
19286
|
+
break;
|
|
19287
|
+
}
|
|
19288
|
+
}
|
|
19289
|
+
return ranges;
|
|
19290
|
+
}
|
|
19291
|
+
/** True when [index, index + len) overlaps any excluded range. */
|
|
19292
|
+
function overlaps(ranges, index, len) {
|
|
19293
|
+
return ranges.some(([start, end]) => index < end && index + len > start);
|
|
19294
|
+
}
|
|
19295
|
+
/**
|
|
18817
19296
|
* Builds a regex matcher and lookup map for channel member mention aliases.
|
|
18818
19297
|
* Longest aliases are prioritized so e.g. "@阿柚子" matches before "@阿柚".
|
|
18819
19298
|
*/
|
|
@@ -18846,7 +19325,287 @@ function buildMentionMatcher(members) {
|
|
|
18846
19325
|
};
|
|
18847
19326
|
}
|
|
18848
19327
|
/**
|
|
18849
|
-
*
|
|
19328
|
+
* Offsets of ``` fenced blocks, [start, end) each, the way the backend reads them
|
|
19329
|
+
* (dsh/collab/mentions.mjs split(): text.split('```') and odd parts are inside a fence).
|
|
19330
|
+
* An unterminated fence runs to the end of the text, as it does there.
|
|
19331
|
+
*/
|
|
19332
|
+
function fencedRanges(text) {
|
|
19333
|
+
const ranges = [];
|
|
19334
|
+
let open = -1;
|
|
19335
|
+
let at = text.indexOf(FENCE);
|
|
19336
|
+
while (at >= 0) {
|
|
19337
|
+
if (open < 0) open = at + 3;
|
|
19338
|
+
else {
|
|
19339
|
+
ranges.push([open, at]);
|
|
19340
|
+
open = -1;
|
|
19341
|
+
}
|
|
19342
|
+
at = text.indexOf(FENCE, at + 3);
|
|
19343
|
+
}
|
|
19344
|
+
if (open >= 0) ranges.push([open, text.length]);
|
|
19345
|
+
return ranges;
|
|
19346
|
+
}
|
|
19347
|
+
/** A definition line: `[^label]:` at 0-3 columns, the label holding no space or bracket. */
|
|
19348
|
+
const FOOTNOTE_DEF = /^ {0,3}\[\^([^\]\s]+)\]:/;
|
|
19349
|
+
/** A line that opens an HTML block (CommonMark types 1-6): its lines are raw until a blank one. */
|
|
19350
|
+
const HTML_OPEN = new RegExp(`^ {0,3}(?:<(?:script|pre|style|textarea)(?:[ \\t>]|$)|<!--|<\\?|<![A-Za-z]|<!\\[CDATA\\[|</?(?:${HTML_BLOCK_TAGS})(?:[ \\t>]|/?>|$))`, "i");
|
|
19351
|
+
/**
|
|
19352
|
+
* True when a line at 0-3 columns opens a block, so it ends a footnote body before it. A setext
|
|
19353
|
+
* underline (`===`, `--`) is absorbed by the definition's own paragraph and does not end it, while
|
|
19354
|
+
* a bare `-` or `1.` does, because it opens a list. A tilde fence is left out on purpose: it ends a
|
|
19355
|
+
* body in CommonMark, but the backend reads only ``` fences (#259), and the footnote rule stays on
|
|
19356
|
+
* the same side of that gap.
|
|
19357
|
+
*/
|
|
19358
|
+
function endsDefinitionBody(rest) {
|
|
19359
|
+
if (indentWidth(rest) >= 4) return false;
|
|
19360
|
+
const c = rest.replace(/^[ \t]*/, "");
|
|
19361
|
+
if (/^(?:[*\-_][ \t]*){3,}$/.test(c)) return true;
|
|
19362
|
+
if (/^(?:[-*+]|\d{1,9}[.)])(?:[ \t]|$)/.test(c)) return true;
|
|
19363
|
+
if (/^(?:=+|-+)[ \t]*$/.test(c)) return false;
|
|
19364
|
+
return /^#{1,6}(?:[ \t]|$)/.test(c) || /^\$\$/.test(c) || /^ {0,3}`{3,}[^`]*$/.test(c) || /^>/.test(c) || /^ {0,3}<(?:script|pre|style|textarea)(?:[ \t>]|$)/i.test(c) || /^ {0,3}<!--/.test(c) || /^ {0,3}<\?/.test(c) || /^ {0,3}<![A-Za-z]/.test(c) || /^ {0,3}<!\[CDATA\[/.test(c) || HTML_BLOCK_TAG.test(c) || HTML_ANY_TAG.test(c);
|
|
19365
|
+
}
|
|
19366
|
+
/**
|
|
19367
|
+
* Ranges whose lines the renderer does not read as Markdown at all, so a `[^x]:` inside one is
|
|
19368
|
+
* plain text and not a definition: backtick fences, `~~~` fences and `$$` math blocks.
|
|
19369
|
+
*/
|
|
19370
|
+
function footnoteOpaqueRanges(text) {
|
|
19371
|
+
const ranges = [...fencedRanges(text)];
|
|
19372
|
+
for (const open of [/^ {0,3}~{3,}/, /^ {0,3}\$\$/]) {
|
|
19373
|
+
let start = -1;
|
|
19374
|
+
let at = 0;
|
|
19375
|
+
for (const line of text.split("\n")) {
|
|
19376
|
+
if (open.test(line)) {
|
|
19377
|
+
if (start < 0) start = at;
|
|
19378
|
+
else {
|
|
19379
|
+
ranges.push([start, at + line.length]);
|
|
19380
|
+
start = -1;
|
|
19381
|
+
}
|
|
19382
|
+
}
|
|
19383
|
+
at += line.length + 1;
|
|
19384
|
+
}
|
|
19385
|
+
if (start >= 0) ranges.push([start, text.length]);
|
|
19386
|
+
}
|
|
19387
|
+
return ranges;
|
|
19388
|
+
}
|
|
19389
|
+
/** Lines of an HTML block: from its opening line to the line before the next blank one. */
|
|
19390
|
+
function htmlBlockRegions(text, lines) {
|
|
19391
|
+
const regions = [];
|
|
19392
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19393
|
+
if (!HTML_OPEN.test(text.slice(lines[i][0], lines[i][1]))) continue;
|
|
19394
|
+
let end = i;
|
|
19395
|
+
while (end + 1 < lines.length && text.slice(lines[end + 1][0], lines[end + 1][1]).trim() !== "") end++;
|
|
19396
|
+
regions.push([lines[i][0], lines[end][1]]);
|
|
19397
|
+
i = end;
|
|
19398
|
+
}
|
|
19399
|
+
return regions;
|
|
19400
|
+
}
|
|
19401
|
+
/**
|
|
19402
|
+
* Every footnote definition in the text with the container it sits in: `depth` quote markers and
|
|
19403
|
+
* `base` the content indent of the list item that holds it (0 at the top level). Read from the
|
|
19404
|
+
* first line only, which is enough to walk the body.
|
|
19405
|
+
*
|
|
19406
|
+
* `marker` is where the `[^label]:` marker of the line ends. Only that prefix is invisible on the
|
|
19407
|
+
* page; the text after it is the definition's own body and shows up in the footnote section, so a
|
|
19408
|
+
* reference there is a reference a reader can see (#271).
|
|
19409
|
+
*/
|
|
19410
|
+
function footnoteDefs(text, lines) {
|
|
19411
|
+
const defs = [];
|
|
19412
|
+
const opaque = [...footnoteOpaqueRanges(text), ...htmlBlockRegions(text, lines)];
|
|
19413
|
+
const inside = (offset) => opaque.some(([start, end]) => offset >= start && offset < end);
|
|
19414
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19415
|
+
if (inside(lines[i][0])) continue;
|
|
19416
|
+
const { rest, depth, col } = stripQuotes(text.slice(lines[i][0], lines[i][1]));
|
|
19417
|
+
let m = FOOTNOTE_DEF.exec(rest);
|
|
19418
|
+
if (m) {
|
|
19419
|
+
defs.push({
|
|
19420
|
+
line: i,
|
|
19421
|
+
label: m[1],
|
|
19422
|
+
depth,
|
|
19423
|
+
base: 0,
|
|
19424
|
+
marker: lines[i][1] - rest.length + m[0].length
|
|
19425
|
+
});
|
|
19426
|
+
continue;
|
|
19427
|
+
}
|
|
19428
|
+
const marker = LIST_MARKER.exec(rest);
|
|
19429
|
+
if (!marker) continue;
|
|
19430
|
+
const after = rest.slice(marker[0].length);
|
|
19431
|
+
const gap = /^[ \t]*/.exec(after)[0];
|
|
19432
|
+
if (gap === "") continue;
|
|
19433
|
+
const indent = indentWidth(gap, col + marker[0].length);
|
|
19434
|
+
const content = after.slice(gap.length);
|
|
19435
|
+
const pad = content.length < 4 ? Math.min(indent, 3) : 0;
|
|
19436
|
+
m = FOOTNOTE_DEF.exec(" ".repeat(pad) + content);
|
|
19437
|
+
if (m && indent < 4) defs.push({
|
|
19438
|
+
line: i,
|
|
19439
|
+
label: m[1],
|
|
19440
|
+
depth,
|
|
19441
|
+
base: indent,
|
|
19442
|
+
marker: lines[i][1] - content.length + m[0].length - pad
|
|
19443
|
+
});
|
|
19444
|
+
}
|
|
19445
|
+
return defs;
|
|
19446
|
+
}
|
|
19447
|
+
/**
|
|
19448
|
+
* Last line of the body of `def`, walking the lines its paragraph covers.
|
|
19449
|
+
*
|
|
19450
|
+
* Two collected definitions can never sit inside one body: mdast nests a second definition inside
|
|
19451
|
+
* the first only when its line is indented four or more columns past the first one's content
|
|
19452
|
+
* column, and such a line is never collected as a definition (footnoteDefs() reads `[^x]:` at 0-3
|
|
19453
|
+
* columns only, like every other list marker). A line it does collect therefore opens a block of
|
|
19454
|
+
* its own, so the walk stops there with the last line it really covered — no shortcut needed, and
|
|
19455
|
+
* no re-walking either: both bodies end before the next definition line, which keeps the whole
|
|
19456
|
+
* pass linear in the text. This says nothing about a *nested* definition the page still renders:
|
|
19457
|
+
* that one is not collected here at all, and its page semantics stay a known deviation below.
|
|
19458
|
+
*/
|
|
19459
|
+
function definitionBodyEnd(text, lines, def, next) {
|
|
19460
|
+
let last = def.line;
|
|
19461
|
+
let open = text.slice(def.marker, lines[def.line][1]).trim() !== "";
|
|
19462
|
+
for (let j = def.line + 1; j < lines.length; j++) {
|
|
19463
|
+
if (j === next) break;
|
|
19464
|
+
const { rest, depth, col } = stripQuotes(text.slice(lines[j][0], lines[j][1]));
|
|
19465
|
+
if (rest.trim() === "") {
|
|
19466
|
+
let k = j;
|
|
19467
|
+
while (k < lines.length) {
|
|
19468
|
+
if (stripQuotes(text.slice(lines[k][0], lines[k][1])).rest.trim() !== "") break;
|
|
19469
|
+
k++;
|
|
19470
|
+
}
|
|
19471
|
+
if (k < lines.length) {
|
|
19472
|
+
const r = stripQuotes(text.slice(lines[k][0], lines[k][1]));
|
|
19473
|
+
if (r.depth >= def.depth && indentWidth(r.rest, r.col) - def.base >= 4) {
|
|
19474
|
+
j = k - 1;
|
|
19475
|
+
last = k - 1;
|
|
19476
|
+
open = true;
|
|
19477
|
+
continue;
|
|
19478
|
+
}
|
|
19479
|
+
}
|
|
19480
|
+
break;
|
|
19481
|
+
}
|
|
19482
|
+
if (depth > def.depth) break;
|
|
19483
|
+
if (depth < def.depth) {
|
|
19484
|
+
if (!open || endsDefinitionBody(rest)) break;
|
|
19485
|
+
last = j;
|
|
19486
|
+
continue;
|
|
19487
|
+
}
|
|
19488
|
+
if (indentWidth(rest, col) - def.base >= 4) {
|
|
19489
|
+
last = j;
|
|
19490
|
+
open = true;
|
|
19491
|
+
continue;
|
|
19492
|
+
}
|
|
19493
|
+
if (endsDefinitionBody(rest)) break;
|
|
19494
|
+
if (!open) break;
|
|
19495
|
+
last = j;
|
|
19496
|
+
}
|
|
19497
|
+
return last;
|
|
19498
|
+
}
|
|
19499
|
+
/** Ranges sorted and merged, so overlapping and stacked ranges cost one pass. */
|
|
19500
|
+
function mergeRanges(ranges) {
|
|
19501
|
+
const sorted = [...ranges].sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
19502
|
+
const out = [];
|
|
19503
|
+
for (const [start, end] of sorted) {
|
|
19504
|
+
const last = out[out.length - 1];
|
|
19505
|
+
if (last && start <= last[1]) last[1] = Math.max(last[1], end);
|
|
19506
|
+
else out.push([start, end]);
|
|
19507
|
+
}
|
|
19508
|
+
return out;
|
|
19509
|
+
}
|
|
19510
|
+
/** `text` with every `[start, end)` blanked out, line endings kept so offsets stay put. */
|
|
19511
|
+
function maskText(text, ranges) {
|
|
19512
|
+
const chars = text.split("");
|
|
19513
|
+
for (const [start, end] of mergeRanges(ranges)) for (let i = start; i < end && i < chars.length; i++) if (chars[i] !== "\n" && chars[i] !== "\r") chars[i] = " ";
|
|
19514
|
+
return chars.join("");
|
|
19515
|
+
}
|
|
19516
|
+
/**
|
|
19517
|
+
* [start, end) of every hidden footnote definition: the definition line and the lines that
|
|
19518
|
+
* continue it, for each label nothing readable refers to. A repeated label keeps only its first
|
|
19519
|
+
* definition (the renderer shows that one and drops the rest), and a reference counts only where a
|
|
19520
|
+
* reader can see it — not inside a fence, an inline code span, an HTML comment, the `[^x]:` marker
|
|
19521
|
+
* of a definition line or another hidden body. The text after the marker of a definition line is
|
|
19522
|
+
* the body the page prints in the footnote section, so a reference there does count. The backend
|
|
19523
|
+
* applies the same rule in mentions(), so the page and the gate agree on what was called.
|
|
19524
|
+
*
|
|
19525
|
+
* The early return keeps the cost off ordinary text: without `[^` nothing is scanned at all.
|
|
19526
|
+
*/
|
|
19527
|
+
function footnoteRanges(text, codeSpans) {
|
|
19528
|
+
if (!text.includes("[^")) return [];
|
|
19529
|
+
const spans = codeSpans ?? codeSpanRanges(text);
|
|
19530
|
+
const lines = splitLines(text);
|
|
19531
|
+
const defs = footnoteDefs(text, lines);
|
|
19532
|
+
if (defs.length === 0) return [];
|
|
19533
|
+
const ends = defs.map((def, k) => definitionBodyEnd(text, lines, def, k + 1 < defs.length ? defs[k + 1].line : void 0));
|
|
19534
|
+
const bodies = defs.map((def, k) => [lines[def.line][0], lines[ends[k]][1]]);
|
|
19535
|
+
const blind = [
|
|
19536
|
+
...spans,
|
|
19537
|
+
...footnoteOpaqueRanges(text),
|
|
19538
|
+
...htmlBlockRegions(text, lines)
|
|
19539
|
+
];
|
|
19540
|
+
for (const def of defs) blind.push([lines[def.line][0], def.marker]);
|
|
19541
|
+
for (const m of text.matchAll(/<!--[\s\S]*?-->/g)) blind.push([m.index, m.index + m[0].length]);
|
|
19542
|
+
const searchable = maskText(text, blind);
|
|
19543
|
+
const first = /* @__PURE__ */ new Map();
|
|
19544
|
+
defs.forEach((def, i) => {
|
|
19545
|
+
const key = def.label.toLowerCase();
|
|
19546
|
+
if (!first.has(key)) first.set(key, i);
|
|
19547
|
+
});
|
|
19548
|
+
const calledOutside = /* @__PURE__ */ new Set();
|
|
19549
|
+
const insideBody = defs.map(() => /* @__PURE__ */ new Set());
|
|
19550
|
+
let body = 0;
|
|
19551
|
+
for (const m of searchable.matchAll(/\[\^([^\]\s]+)\]/g)) {
|
|
19552
|
+
let slashes = 0;
|
|
19553
|
+
for (let i = m.index - 1; i >= 0 && searchable[i] === "\\"; i--) slashes++;
|
|
19554
|
+
if (slashes % 2 !== 0) continue;
|
|
19555
|
+
while (body < bodies.length && bodies[body][1] <= m.index) body++;
|
|
19556
|
+
const owner = body < bodies.length && m.index >= bodies[body][0] ? body : -1;
|
|
19557
|
+
(owner < 0 ? calledOutside : insideBody[owner]).add(m[1].toLowerCase());
|
|
19558
|
+
}
|
|
19559
|
+
const shown = /* @__PURE__ */ new Set();
|
|
19560
|
+
const pending = [];
|
|
19561
|
+
const open = (labels) => {
|
|
19562
|
+
for (const label of labels) {
|
|
19563
|
+
const i = first.get(label);
|
|
19564
|
+
if (i === void 0 || shown.has(i)) continue;
|
|
19565
|
+
shown.add(i);
|
|
19566
|
+
pending.push(i);
|
|
19567
|
+
}
|
|
19568
|
+
};
|
|
19569
|
+
open(calledOutside);
|
|
19570
|
+
while (pending.length > 0) open(insideBody[pending.pop()]);
|
|
19571
|
+
const hidden = [];
|
|
19572
|
+
defs.forEach((def, i) => {
|
|
19573
|
+
if (!shown.has(i)) hidden.push([lines[def.line][0], lines[ends[i]][1]]);
|
|
19574
|
+
});
|
|
19575
|
+
return mergeRanges(hidden);
|
|
19576
|
+
}
|
|
19577
|
+
/**
|
|
19578
|
+
* Known deviations from the page, kept on purpose (#259, #271). Each one showed the same answer
|
|
19579
|
+
* before this code was written, so the list is the honest edge of this rule, not a regression:
|
|
19580
|
+
*
|
|
19581
|
+
* - `~~~` fences: the page renders them as code, the backend reads only ```, so a name inside one
|
|
19582
|
+
* is coloured nowhere and called there.
|
|
19583
|
+
* - `$$` math blocks: the page ends a paragraph at one, the backend does not, so a code span that
|
|
19584
|
+
* crosses `$$` holds there and breaks here (nobody called, page colours both names).
|
|
19585
|
+
* - A GFM table delimiter row (`|---|`): same shape — the page ends the paragraph, the backend
|
|
19586
|
+
* keeps the span, so nobody is called while the page colours both names.
|
|
19587
|
+
* - A four-column indented code block: the page renders it as code and colours nothing, the
|
|
19588
|
+
* backend has no indented-code reader and calls the name. The footnote rule below shares it: a
|
|
19589
|
+
* `[^x]:` line indented four columns is a code block on the page and hidden there, and it is
|
|
19590
|
+
* not a definition here either, so a name on that line is a call there. Likewise a `[^2]` written
|
|
19591
|
+
* only on a four-column line is not a reference on the page, while this backend reads it as one
|
|
19592
|
+
* and shows the `[^2]:` definition the page keeps hidden.
|
|
19593
|
+
* - A nested definition — a `[^x]:` line indented four or more columns past the outer body's
|
|
19594
|
+
* content column. The page nests it under the outer footnote and shows it only when its own
|
|
19595
|
+
* label is called; this splitter collects definitions at 0-3 columns only, so that line is never
|
|
19596
|
+
* a definition here. Both matchers then give the same answer, and it is not the page's, in both
|
|
19597
|
+
* directions: `[^1]: a` / four-column `[^2]: 注 @name` / `见 [^1]` colours the name here while
|
|
19598
|
+
* the page renders the outer footnote as `a` with no badge, and with `见 [^2]` nothing is
|
|
19599
|
+
* coloured here while the page does show the badge. Same on bdd67b4 — not a regression of #271,
|
|
19600
|
+
* and #323 corrected the notes instead of widening the implementation.
|
|
19601
|
+
* - A code span opened on a block line (`# ` + backtick + name): the paragraph already ended
|
|
19602
|
+
* before the opener, so the page never makes it a span and colours the names; endsParagraph()
|
|
19603
|
+
* only reads the lines between the runs, so the span still holds here.
|
|
19604
|
+
*
|
|
19605
|
+
* Splits plain text into non-mention and mention segments. An @ inside a ``` fence, an inline
|
|
19606
|
+
* code span or a hidden footnote definition is left as text: the backend does not call anyone
|
|
19607
|
+
* there (dsh/collab/mentions.mjs), so colouring it would promise a call that never happens (#237,
|
|
19608
|
+
* #271).
|
|
18850
19609
|
*/
|
|
18851
19610
|
function splitMentions(text, members) {
|
|
18852
19611
|
if (!text) return [];
|
|
@@ -18854,10 +19613,17 @@ function splitMentions(text, members) {
|
|
|
18854
19613
|
if (!matcher) return [{ text }];
|
|
18855
19614
|
const { regex, aliasMap } = matcher;
|
|
18856
19615
|
const segments = [];
|
|
19616
|
+
const codeSpans = codeSpanRanges(text);
|
|
19617
|
+
const skip = [
|
|
19618
|
+
...fencedRanges(text),
|
|
19619
|
+
...codeSpans,
|
|
19620
|
+
...footnoteRanges(text, codeSpans)
|
|
19621
|
+
];
|
|
18857
19622
|
let lastIndex = 0;
|
|
18858
19623
|
for (const match of text.matchAll(regex)) {
|
|
18859
19624
|
const matchText = match[0];
|
|
18860
19625
|
const startIndex = match.index ?? 0;
|
|
19626
|
+
if (overlaps(skip, startIndex, matchText.length)) continue;
|
|
18861
19627
|
if (startIndex > lastIndex) segments.push({ text: text.slice(lastIndex, startIndex) });
|
|
18862
19628
|
const member = aliasMap.get(matchText);
|
|
18863
19629
|
segments.push({
|
|
@@ -18999,13 +19765,14 @@ const AVATARS = [
|
|
|
18999
19765
|
/**
|
|
19000
19766
|
* Image source for a member avatar.
|
|
19001
19767
|
* @param avatar - built-in slug, http(s) URL announced by a bot, or empty.
|
|
19768
|
+
* @param url - #236 path mapper of the panel (a paired computer's panel reads its avatars).
|
|
19002
19769
|
* @returns a URL relative to the document base, the absolute URL, or undefined for the initial badge.
|
|
19003
19770
|
*/
|
|
19004
|
-
function avatarSrc(avatar) {
|
|
19771
|
+
function avatarSrc(avatar, url = (path) => path) {
|
|
19005
19772
|
if (avatar === void 0 || avatar === "") return void 0;
|
|
19006
|
-
if (/^upload:[a-z0-9-]+\.png$/.test(avatar)) return `collab/avatars/uploads/${avatar.slice(7)}
|
|
19773
|
+
if (/^upload:[a-z0-9-]+\.png$/.test(avatar)) return url(`collab/avatars/uploads/${avatar.slice(7)}`);
|
|
19007
19774
|
if (/^https?:\/\//.test(avatar)) return avatar;
|
|
19008
|
-
return AVATARS.includes(avatar) ? `collab/avatars/${avatar}.png` : void 0;
|
|
19775
|
+
return AVATARS.includes(avatar) ? url(`collab/avatars/${avatar}.png`) : void 0;
|
|
19009
19776
|
}
|
|
19010
19777
|
// src/client/Avatar.tsx
|
|
19011
19778
|
/** Member portrait: a built-in or bot-announced image over a colored initial badge. */
|
|
@@ -19026,7 +19793,7 @@ function color(id) {
|
|
|
19026
19793
|
}
|
|
19027
19794
|
/** Member portrait, or a colored initial badge when there is none or the image fails to load. */
|
|
19028
19795
|
function Avatar({ id, name, avatar, avatarVersion, small }) {
|
|
19029
|
-
const base = avatarSrc(avatar);
|
|
19796
|
+
const base = avatarSrc(avatar, useCollabUrl());
|
|
19030
19797
|
const src = base && avatar?.startsWith("upload:") && avatarVersion ? `${base}?v=${avatarVersion}` : base;
|
|
19031
19798
|
const [broken, setBroken] = (0, react.useState)(void 0);
|
|
19032
19799
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -19116,8 +19883,8 @@ function avatarPng(image, rect) {
|
|
|
19116
19883
|
if (bytes > 262144) throw new AvatarOutputSizeError(bytes);
|
|
19117
19884
|
return data;
|
|
19118
19885
|
}
|
|
19119
|
-
async function uploadAvatar(id, png) {
|
|
19120
|
-
const response = await fetch(`collab/api/members/${encodeURIComponent(id)}/avatar
|
|
19886
|
+
async function uploadAvatar(id, png, url = (path) => path) {
|
|
19887
|
+
const response = await fetch(url(`collab/api/members/${encodeURIComponent(id)}/avatar`), {
|
|
19121
19888
|
method: png === void 0 ? "DELETE" : "POST",
|
|
19122
19889
|
headers: { "content-type": "application/json" },
|
|
19123
19890
|
...png === void 0 ? {} : { body: JSON.stringify({ png }) }
|
|
@@ -19336,6 +20103,7 @@ const DEFAULT_PRESET = "workspace-write";
|
|
|
19336
20103
|
* @returns a modal form.
|
|
19337
20104
|
*/
|
|
19338
20105
|
function MemberDialog({ member, members = NO_MEMBERS, listModels, listClis, listPersonas, onSubmit, onClose, t }) {
|
|
20106
|
+
const collabUrl = useCollabUrl();
|
|
19339
20107
|
const [name, setName] = (0, react.useState)(member?.name ?? "DSH");
|
|
19340
20108
|
const [nameCustomized, setNameCustomized] = (0, react.useState)(member === void 0 ? false : member.nameCustomized);
|
|
19341
20109
|
const [keepName, setKeepName] = (0, react.useState)(true);
|
|
@@ -19375,7 +20143,7 @@ function MemberDialog({ member, members = NO_MEMBERS, listModels, listClis, list
|
|
|
19375
20143
|
setError(void 0);
|
|
19376
20144
|
try {
|
|
19377
20145
|
if (member) {
|
|
19378
|
-
const updated = await uploadAvatar(member.id, png);
|
|
20146
|
+
const updated = await uploadAvatar(member.id, png, collabUrl);
|
|
19379
20147
|
setAvatar(updated.avatar ?? "");
|
|
19380
20148
|
} else {
|
|
19381
20149
|
setPendingPng(png);
|
|
@@ -19384,7 +20152,7 @@ function MemberDialog({ member, members = NO_MEMBERS, listModels, listClis, list
|
|
|
19384
20152
|
setPreview(png ? `data:image/png;base64,${png}` : void 0);
|
|
19385
20153
|
} catch (reason) {
|
|
19386
20154
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
19387
|
-
setError(message === "avatar-png" ? t("memberAvatarPngError") : message === "avatar-size" ? t("memberAvatarSizeError") : message === "avatar-convert" ? t("memberAvatarConvertError") :
|
|
20155
|
+
setError(message === "avatar-png" ? t("memberAvatarPngError") : message === "avatar-size" ? t("memberAvatarSizeError") : message === "avatar-convert" ? t("memberAvatarConvertError") : errorText(reason, t));
|
|
19388
20156
|
} finally {
|
|
19389
20157
|
setBusy(false);
|
|
19390
20158
|
}
|
|
@@ -19417,7 +20185,7 @@ function MemberDialog({ member, members = NO_MEMBERS, listModels, listClis, list
|
|
|
19417
20185
|
}).catch((reason) => {
|
|
19418
20186
|
if (live) {
|
|
19419
20187
|
setRoutes([]);
|
|
19420
|
-
setError(reason
|
|
20188
|
+
setError(errorText(reason, t));
|
|
19421
20189
|
}
|
|
19422
20190
|
});
|
|
19423
20191
|
return () => {
|
|
@@ -19435,7 +20203,7 @@ function MemberDialog({ member, members = NO_MEMBERS, listModels, listClis, list
|
|
|
19435
20203
|
}).catch((reason) => {
|
|
19436
20204
|
if (live) {
|
|
19437
20205
|
setClis([]);
|
|
19438
|
-
setError(reason
|
|
20206
|
+
setError(errorText(reason, t));
|
|
19439
20207
|
}
|
|
19440
20208
|
});
|
|
19441
20209
|
return () => {
|
|
@@ -19449,7 +20217,7 @@ function MemberDialog({ member, members = NO_MEMBERS, listModels, listClis, list
|
|
|
19449
20217
|
setBusy(true);
|
|
19450
20218
|
setError(void 0);
|
|
19451
20219
|
const identity = nameCustomized === void 0 ? {} : { nameCustomized };
|
|
19452
|
-
|
|
20220
|
+
const input = type === "cli" ? {
|
|
19453
20221
|
name: name.trim(),
|
|
19454
20222
|
...identity,
|
|
19455
20223
|
persona,
|
|
@@ -19467,8 +20235,9 @@ function MemberDialog({ member, members = NO_MEMBERS, listModels, listClis, list
|
|
|
19467
20235
|
provider,
|
|
19468
20236
|
model
|
|
19469
20237
|
} : null
|
|
19470
|
-
}
|
|
19471
|
-
|
|
20238
|
+
};
|
|
20239
|
+
onSubmit(input).then(onClose).catch((reason) => {
|
|
20240
|
+
setError(errorText(reason, t, "member", input.name));
|
|
19472
20241
|
}).finally(() => {
|
|
19473
20242
|
setBusy(false);
|
|
19474
20243
|
});
|
|
@@ -19865,7 +20634,7 @@ function ChannelSettingsDialog({ room, members, effectiveLead, renameRoom, setDi
|
|
|
19865
20634
|
} catch (error) {
|
|
19866
20635
|
setErrors((previous) => ({
|
|
19867
20636
|
...previous,
|
|
19868
|
-
[target]: error
|
|
20637
|
+
[target]: errorText(error, t, target === "basic" ? "room" : "other", name)
|
|
19869
20638
|
}));
|
|
19870
20639
|
} finally {
|
|
19871
20640
|
setSaving((previous) => ({
|
|
@@ -20087,7 +20856,7 @@ function ChannelSettingsDialog({ room, members, effectiveLead, renameRoom, setDi
|
|
|
20087
20856
|
}).catch((error) => {
|
|
20088
20857
|
setErrors((previous) => ({
|
|
20089
20858
|
...previous,
|
|
20090
|
-
workspace: error
|
|
20859
|
+
workspace: errorText(error, t)
|
|
20091
20860
|
}));
|
|
20092
20861
|
}).finally(() => {
|
|
20093
20862
|
setRenewing(false);
|
|
@@ -20602,6 +21371,20 @@ function KindTag({ kind, cli, name, t }) {
|
|
|
20602
21371
|
function isImeEnter(event) {
|
|
20603
21372
|
return Reflect.get(event.nativeEvent, "isComposing") === true || Reflect.get(event.nativeEvent, "keyCode") === 229;
|
|
20604
21373
|
}
|
|
21374
|
+
/**
|
|
21375
|
+
* #197: an object URL for an image the composer is about to send, so no base64 copy is
|
|
21376
|
+
* held for the thumbnail. A browser or test environment without createObjectURL simply
|
|
21377
|
+
* shows the 📎 instead of a preview.
|
|
21378
|
+
*/
|
|
21379
|
+
function previewOf(file) {
|
|
21380
|
+
if (!/^image\/(png|jpeg|gif|webp)$/.test(file.type) || file.size > 8388608) return void 0;
|
|
21381
|
+
if (typeof URL.createObjectURL !== "function") return void 0;
|
|
21382
|
+
try {
|
|
21383
|
+
return URL.createObjectURL(file);
|
|
21384
|
+
} catch {
|
|
21385
|
+
return;
|
|
21386
|
+
}
|
|
21387
|
+
}
|
|
20605
21388
|
/** Touch or narrow screens get the short composer placeholder (the long one wraps beside the send button). */
|
|
20606
21389
|
function compactComposer() {
|
|
20607
21390
|
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(hover: none), (max-width: 600px)").matches;
|
|
@@ -20698,6 +21481,26 @@ function clockOf(seconds) {
|
|
|
20698
21481
|
minute: "2-digit"
|
|
20699
21482
|
});
|
|
20700
21483
|
}
|
|
21484
|
+
/**
|
|
21485
|
+
* #216: fill the "held by another DSH" notice from the holder record. A record written by an
|
|
21486
|
+
* older build may miss a field, so each one falls back to its own unknown placeholder rather
|
|
21487
|
+
* than printing `undefined`. `dataDir` and `lock` come from the refused instance too: a holder
|
|
21488
|
+
* that wrote no record still leaves the page able to name the contended directory and file.
|
|
21489
|
+
*/
|
|
21490
|
+
function lockHolderText(holder, dataDir, lockPath, t) {
|
|
21491
|
+
const unknown = t("lockedUnknown");
|
|
21492
|
+
return {
|
|
21493
|
+
pid: holder?.pid ?? unknown,
|
|
21494
|
+
started: holder?.startedAt === void 0 ? unknown : new Date(holder.startedAt).toLocaleString([], {
|
|
21495
|
+
month: "numeric",
|
|
21496
|
+
day: "numeric",
|
|
21497
|
+
hour: "2-digit",
|
|
21498
|
+
minute: "2-digit"
|
|
21499
|
+
}),
|
|
21500
|
+
dataDir: holder?.dataDir ?? dataDir ?? unknown,
|
|
21501
|
+
lock: lockPath ?? unknown
|
|
21502
|
+
};
|
|
21503
|
+
}
|
|
20701
21504
|
/** #1020: the memory entries a reply used — a small badge that opens to their titles. */
|
|
20702
21505
|
function MemoryBadge({ items, t }) {
|
|
20703
21506
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -20740,6 +21543,7 @@ function MemoryBadge({ items, t }) {
|
|
|
20740
21543
|
}
|
|
20741
21544
|
const LONG_PRESS_MS = 450;
|
|
20742
21545
|
function MessageRow({ message, member, members, t, onConfigure, menuOpen, onAvatar, onCloseMenu, onMention, onDm, onReview, onDecide, onReply, onTask, onRetry, retrying, tasks, focused, cardView, onCardView, own, grouped, enter, queuedFor, taskCardShown = true, toolsOpen = false, onLongPress }) {
|
|
21546
|
+
const collabUrl = useCollabUrl();
|
|
20743
21547
|
const labels = (0, react.useMemo)(() => ({
|
|
20744
21548
|
code: {
|
|
20745
21549
|
copyLabel: t("copyCode"),
|
|
@@ -20899,7 +21703,7 @@ function MessageRow({ message, member, members, t, onConfigure, menuOpen, onAvat
|
|
|
20899
21703
|
const taskCards = anchored.filter((task) => task.parent_task === null || !anchoredIds.has(task.parent_task));
|
|
20900
21704
|
const progressLines = role === "progress" ? shown.split("\n") : [];
|
|
20901
21705
|
const deliveredFor = deliveredTasks(message, tasks);
|
|
20902
|
-
const deliveredPage = deliveredFor.length > 0 ? deliverableOf(message) : void 0;
|
|
21706
|
+
const deliveredPage = deliveredFor.length > 0 ? deliverableOf(message, collabUrl) : void 0;
|
|
20903
21707
|
const delivered = deliveredPage === void 0 ? void 0 : {
|
|
20904
21708
|
page: deliveredPage,
|
|
20905
21709
|
task: deliveredFor.find((task) => task.status === "returned") ?? deliveredFor[0]
|
|
@@ -21465,7 +22269,7 @@ function MemberGroup({ title, members, busy, onPick, onEdit, onRemove, onDm, t }
|
|
|
21465
22269
|
* @param props - framework, injected, and locale shares.
|
|
21466
22270
|
* @returns the three-column channel view.
|
|
21467
22271
|
*/
|
|
21468
|
-
function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask, openDm, closeDm, createRoom, renameRoom, listModels, listClis, listPersonas, addMember, updateMember, removeMember, setLead, setWorkspacePath, renewRoom, setDispatchMode, setMaxRelay, decidePermission, retryMessage, editMessage, withdrawMessage, loadOlder, loadNewer, keepLatest, showLatest, openModelSettings, answerTask, t }) {
|
|
22272
|
+
function ChannelsPage({ useChannels, selectRoom, send, uploadAttachment, reviewTask, dispatchTask, openDm, closeDm, createRoom, renameRoom, listModels, listClis, listPersonas, addMember, updateMember, removeMember, setLead, setWorkspacePath, renewRoom, setDispatchMode, setMaxRelay, decidePermission, retryMessage, editMessage, withdrawMessage, loadOlder, loadNewer, keepLatest, showLatest, openModelSettings, answerTask, t }) {
|
|
21469
22273
|
const scheme = useScheme();
|
|
21470
22274
|
const state = useChannels((snapshot) => snapshot);
|
|
21471
22275
|
const [editing, setEditing] = (0, react.useState)(void 0);
|
|
@@ -21476,28 +22280,92 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
21476
22280
|
const [sending, setSending] = (0, react.useState)({});
|
|
21477
22281
|
const [replies, setReplies] = (0, react.useState)({});
|
|
21478
22282
|
const [pendingFiles, setPendingFiles] = (0, react.useState)({});
|
|
21479
|
-
const
|
|
21480
|
-
|
|
21481
|
-
|
|
21482
|
-
|
|
21483
|
-
|
|
21484
|
-
|
|
21485
|
-
|
|
21486
|
-
|
|
21487
|
-
}));
|
|
21488
|
-
return;
|
|
21489
|
-
}
|
|
21490
|
-
setPendingFiles((previous) => ({
|
|
22283
|
+
const pendingRef = (0, react.useRef)(pendingFiles);
|
|
22284
|
+
const updateFiles = (update) => {
|
|
22285
|
+
pendingRef.current = update(pendingRef.current);
|
|
22286
|
+
setPendingFiles(pendingRef.current);
|
|
22287
|
+
};
|
|
22288
|
+
const uploading = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
22289
|
+
const patchFile = (roomId, key, change) => {
|
|
22290
|
+
updateFiles((previous) => ({
|
|
21491
22291
|
...previous,
|
|
21492
|
-
[roomId]:
|
|
22292
|
+
[roomId]: (previous[roomId] ?? []).map((file) => file.key === key ? change(file) : file)
|
|
21493
22293
|
}));
|
|
21494
|
-
}
|
|
22294
|
+
};
|
|
22295
|
+
const limit = state.maxUploadBytes ?? 1073741824;
|
|
22296
|
+
const attach = useComposerAttachments((picked) => {
|
|
21495
22297
|
const roomId = state.roomId;
|
|
21496
|
-
if (roomId
|
|
22298
|
+
if (roomId === void 0) return;
|
|
22299
|
+
const added = picked.map((file) => {
|
|
22300
|
+
const preview = previewOf(file);
|
|
22301
|
+
return {
|
|
22302
|
+
key: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
|
|
22303
|
+
name: file.name || "pasted",
|
|
22304
|
+
mime: file.type || "application/octet-stream",
|
|
22305
|
+
size: file.size,
|
|
22306
|
+
progress: 0,
|
|
22307
|
+
...preview === void 0 ? {} : { preview },
|
|
22308
|
+
...uploadAttachment === void 0 ? { error: t("uploadUnsupported") } : file.size > limit ? { error: t("attachmentTooLarge", {
|
|
22309
|
+
name: file.name || "pasted",
|
|
22310
|
+
size: formatBytes(file.size),
|
|
22311
|
+
limit: formatBytes(limit)
|
|
22312
|
+
}) } : {}
|
|
22313
|
+
};
|
|
22314
|
+
});
|
|
22315
|
+
updateFiles((previous) => ({
|
|
21497
22316
|
...previous,
|
|
21498
|
-
[roomId]:
|
|
22317
|
+
[roomId]: [...previous[roomId] ?? [], ...added]
|
|
21499
22318
|
}));
|
|
22319
|
+
picked.forEach((file, index) => {
|
|
22320
|
+
const entry = added[index];
|
|
22321
|
+
if (entry.error !== void 0 || uploadAttachment === void 0) return;
|
|
22322
|
+
const controller = new AbortController();
|
|
22323
|
+
const done = uploadAttachment(file, (sent, total) => {
|
|
22324
|
+
patchFile(roomId, entry.key, (current) => ({
|
|
22325
|
+
...current,
|
|
22326
|
+
progress: total === 0 ? 1 : Math.min(1, sent / total)
|
|
22327
|
+
}));
|
|
22328
|
+
}, controller.signal).then((attachment) => {
|
|
22329
|
+
patchFile(roomId, entry.key, (current) => ({
|
|
22330
|
+
...current,
|
|
22331
|
+
progress: 1,
|
|
22332
|
+
attachment
|
|
22333
|
+
}));
|
|
22334
|
+
}).catch((error) => {
|
|
22335
|
+
patchFile(roomId, entry.key, (current) => ({
|
|
22336
|
+
...current,
|
|
22337
|
+
error: error instanceof UploadTooLarge ? t("attachmentTooLarge", {
|
|
22338
|
+
name: error.fileName,
|
|
22339
|
+
size: formatBytes(error.size),
|
|
22340
|
+
limit: formatBytes(error.limit)
|
|
22341
|
+
}) : t("uploadFailed", {
|
|
22342
|
+
name: current.name,
|
|
22343
|
+
message: error instanceof Error ? error.message : String(error)
|
|
22344
|
+
})
|
|
22345
|
+
}));
|
|
22346
|
+
}).finally(() => {
|
|
22347
|
+
uploading.current.delete(entry.key);
|
|
22348
|
+
});
|
|
22349
|
+
uploading.current.set(entry.key, {
|
|
22350
|
+
done,
|
|
22351
|
+
controller
|
|
22352
|
+
});
|
|
22353
|
+
});
|
|
21500
22354
|
});
|
|
22355
|
+
const removeFile = (roomId, key) => {
|
|
22356
|
+
const gone = (pendingRef.current[roomId] ?? []).find((file) => file.key === key);
|
|
22357
|
+
if (gone?.preview !== void 0) URL.revokeObjectURL(gone.preview);
|
|
22358
|
+
uploading.current.get(key)?.controller.abort();
|
|
22359
|
+
uploading.current.delete(key);
|
|
22360
|
+
updateFiles((previous) => ({
|
|
22361
|
+
...previous,
|
|
22362
|
+
[roomId]: (previous[roomId] ?? []).filter((file) => file.key !== key)
|
|
22363
|
+
}));
|
|
22364
|
+
};
|
|
22365
|
+
(0, react.useEffect)(() => () => {
|
|
22366
|
+
for (const job of uploading.current.values()) job.controller.abort();
|
|
22367
|
+
for (const room of Object.values(pendingRef.current)) for (const file of room) if (file.preview !== void 0) URL.revokeObjectURL(file.preview);
|
|
22368
|
+
}, []);
|
|
21501
22369
|
const pendingRooms = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
21502
22370
|
const draft = state.roomId === void 0 ? "" : drafts[state.roomId] ?? "";
|
|
21503
22371
|
const sendError = state.roomId === void 0 ? void 0 : errors[state.roomId];
|
|
@@ -21519,6 +22387,24 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
21519
22387
|
pendingWithdrawRef.current?.commit();
|
|
21520
22388
|
}, []);
|
|
21521
22389
|
const inputRef = (0, react.useRef)(null);
|
|
22390
|
+
const inputHighlightsRef = (0, react.useRef)(null);
|
|
22391
|
+
const [composing, setComposing] = (0, react.useState)(false);
|
|
22392
|
+
const syncInputHighlights = () => {
|
|
22393
|
+
const input = inputRef.current;
|
|
22394
|
+
const highlights = inputHighlightsRef.current;
|
|
22395
|
+
if (!input || !highlights) return;
|
|
22396
|
+
highlights.style.width = `${input.getBoundingClientRect().width - (input.offsetWidth - input.clientWidth)}px`;
|
|
22397
|
+
highlights.scrollTop = input.scrollTop;
|
|
22398
|
+
highlights.scrollLeft = input.scrollLeft;
|
|
22399
|
+
};
|
|
22400
|
+
(0, react.useLayoutEffect)(() => {
|
|
22401
|
+
syncInputHighlights();
|
|
22402
|
+
const input = inputRef.current;
|
|
22403
|
+
if (!input || typeof ResizeObserver === "undefined") return;
|
|
22404
|
+
const observer = new ResizeObserver(syncInputHighlights);
|
|
22405
|
+
observer.observe(input);
|
|
22406
|
+
return () => observer.disconnect();
|
|
22407
|
+
}, [draft, state.roomId]);
|
|
21522
22408
|
const [caret, setCaret] = (0, react.useState)(0);
|
|
21523
22409
|
const [dismissedAt, setDismissedAt] = (0, react.useState)(void 0);
|
|
21524
22410
|
const [highlight, setHighlight] = (0, react.useState)(0);
|
|
@@ -21871,7 +22757,7 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
21871
22757
|
};
|
|
21872
22758
|
}, [stopping, stillAnswering]);
|
|
21873
22759
|
if (state.phase !== "ready") {
|
|
21874
|
-
const notice = state.phase === "unavailable" ? t("unavailable") : state.phase === "unauthorized" ? t("unauthorized") : state.phase === "error" ? t("error", { message: state.error ?? "" }) : t("loading");
|
|
22760
|
+
const notice = state.phase === "locked" ? state.lockFree === true ? t("lockedFree") : state.lockHolder === void 0 ? t("lockedUnnamed", lockHolderText(void 0, state.lockDataDir, state.lockPath, t)) : t("locked", lockHolderText(state.lockHolder, state.lockDataDir, state.lockPath, t)) : state.phase === "unavailable" ? t("unavailable") : state.phase === "unauthorized" ? t("unauthorized") : state.phase === "error" ? t("error", { message: state.error ?? "" }) : t("loading");
|
|
21875
22761
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
21876
22762
|
className: ChannelsPage_module_default.page,
|
|
21877
22763
|
"data-dc-scheme": scheme,
|
|
@@ -21936,7 +22822,7 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
21936
22822
|
const slashCommands = slash === void 0 || capabilitiesDismissed || popupOpen ? [] : matchCommands(slash, (key) => t(key)).filter((c) => dmWith === void 0 || c.name !== "stop all" && c.name !== "board");
|
|
21937
22823
|
const slashOpen = slashCommands.length > 0;
|
|
21938
22824
|
const slashSelected = Math.min(slashHighlight, Math.max(slashCommands.length - 1, 0));
|
|
21939
|
-
const workingBots = (state.roomId === void 0 ? [] : state.thinking[String(state.roomId)] ?? []).map((id) => byId.get(id)).filter((m) => m !== void 0 && m.kind === "bot");
|
|
22825
|
+
const workingBots = (state.roomId === void 0 ? [] : state.thinking[String(state.roomId)] ?? []).map((id) => byId.get(id)).filter((m) => m !== void 0 && (m.kind === "bot" || m.kind === "dsh"));
|
|
21940
22826
|
const stopTarget = workingBots.find((m) => m.id === commandTarget?.id) ?? workingBots[0];
|
|
21941
22827
|
const roomFiles = state.roomId === void 0 ? [] : pendingFiles[state.roomId] ?? [];
|
|
21942
22828
|
const stopMode = (stopTarget !== void 0 || stopping?.phase === "stopping") && draft.trim() === "" && roomFiles.length === 0;
|
|
@@ -22007,7 +22893,7 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
22007
22893
|
createRoom(name).then(() => {
|
|
22008
22894
|
setNewRoom(void 0);
|
|
22009
22895
|
}).catch((error) => {
|
|
22010
|
-
setRoomError(error
|
|
22896
|
+
setRoomError(errorText(error, t, "room", name));
|
|
22011
22897
|
});
|
|
22012
22898
|
};
|
|
22013
22899
|
const ownActions = (m) => {
|
|
@@ -22082,7 +22968,7 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
22082
22968
|
if (!window.confirm(t("removeConfirm", { name: member.name }))) return;
|
|
22083
22969
|
setMemberError(void 0);
|
|
22084
22970
|
removeMember(member.id).catch((error) => {
|
|
22085
|
-
setMemberError(error
|
|
22971
|
+
setMemberError(errorText(error, t, "member", member.name));
|
|
22086
22972
|
});
|
|
22087
22973
|
};
|
|
22088
22974
|
const openPicker = () => {
|
|
@@ -22192,12 +23078,20 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
22192
23078
|
const submit = () => {
|
|
22193
23079
|
const roomId = state.roomId;
|
|
22194
23080
|
const text = addressCommand(draft.trim(), commandTarget?.name);
|
|
22195
|
-
const
|
|
22196
|
-
if (!text &&
|
|
22197
|
-
|
|
23081
|
+
const held = roomId === void 0 ? [] : pendingRef.current[roomId] ?? [];
|
|
23082
|
+
if (!text && held.length === 0 || roomId === void 0 || pendingRooms.current.has(roomId)) return;
|
|
23083
|
+
const refused = held.filter((file) => file.error !== void 0);
|
|
23084
|
+
if (refused.length > 0) {
|
|
23085
|
+
setErrors((previous) => ({
|
|
23086
|
+
...previous,
|
|
23087
|
+
[roomId]: refused[0]?.error
|
|
23088
|
+
}));
|
|
23089
|
+
return;
|
|
23090
|
+
}
|
|
23091
|
+
if (messageTooLarge(text, [])) {
|
|
22198
23092
|
setErrors((previous) => ({
|
|
22199
23093
|
...previous,
|
|
22200
|
-
[roomId]: t("
|
|
23094
|
+
[roomId]: t("messageTooLarge", { limit: formatBytes(MAX_MESSAGE_BODY) })
|
|
22201
23095
|
}));
|
|
22202
23096
|
return;
|
|
22203
23097
|
}
|
|
@@ -22214,31 +23108,54 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
22214
23108
|
if (state.hasMoreNewer === true) showLatest?.();
|
|
22215
23109
|
setUnread(void 0);
|
|
22216
23110
|
const replyTo = replies[roomId]?.id;
|
|
22217
|
-
|
|
22218
|
-
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
|
|
22224
|
-
|
|
22225
|
-
|
|
22226
|
-
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
|
|
23111
|
+
/** Post the message; a file that failed while it streamed is reported instead of sent. */
|
|
23112
|
+
const go = () => {
|
|
23113
|
+
const keys = new Set(held.map((file) => file.key));
|
|
23114
|
+
const ready = (pendingRef.current[roomId] ?? []).filter((file) => keys.has(file.key));
|
|
23115
|
+
const broken = ready.find((file) => file.error !== void 0);
|
|
23116
|
+
if (broken?.error !== void 0) {
|
|
23117
|
+
setErrors((previous) => ({
|
|
23118
|
+
...previous,
|
|
23119
|
+
[roomId]: broken.error
|
|
23120
|
+
}));
|
|
23121
|
+
return Promise.resolve();
|
|
23122
|
+
}
|
|
23123
|
+
const files = ready.map((file) => file.attachment).filter((a) => a !== void 0);
|
|
23124
|
+
if (!text && ready.length === 0) return Promise.resolve();
|
|
23125
|
+
return send(text, replyTo, files.length > 0 ? files : void 0, roomId).then(() => {
|
|
23126
|
+
for (const file of ready) if (file.preview !== void 0) URL.revokeObjectURL(file.preview);
|
|
23127
|
+
setDrafts((previous) => ({
|
|
23128
|
+
...previous,
|
|
23129
|
+
[roomId]: ""
|
|
23130
|
+
}));
|
|
23131
|
+
updateFiles((previous) => ({
|
|
23132
|
+
...previous,
|
|
23133
|
+
[roomId]: (previous[roomId] ?? []).filter((file) => !keys.has(file.key))
|
|
23134
|
+
}));
|
|
23135
|
+
setReplies((previous) => ({
|
|
23136
|
+
...previous,
|
|
23137
|
+
[roomId]: void 0
|
|
23138
|
+
}));
|
|
23139
|
+
}).catch((error) => {
|
|
23140
|
+
setErrors((previous) => ({
|
|
23141
|
+
...previous,
|
|
23142
|
+
[roomId]: t("sendFailed", { message: error instanceof Error ? error.message : String(error) })
|
|
23143
|
+
}));
|
|
23144
|
+
});
|
|
23145
|
+
};
|
|
23146
|
+
const settle = () => {
|
|
22236
23147
|
pendingRooms.current.delete(roomId);
|
|
22237
23148
|
setSending((previous) => ({
|
|
22238
23149
|
...previous,
|
|
22239
23150
|
[roomId]: false
|
|
22240
23151
|
}));
|
|
22241
|
-
}
|
|
23152
|
+
};
|
|
23153
|
+
const waits = held.map((file) => uploading.current.get(file.key)?.done).filter((job) => job !== void 0);
|
|
23154
|
+
if (waits.length === 0) {
|
|
23155
|
+
go().finally(settle);
|
|
23156
|
+
return;
|
|
23157
|
+
}
|
|
23158
|
+
Promise.all(waits).then(go).finally(settle);
|
|
22242
23159
|
};
|
|
22243
23160
|
const startReply = (message) => {
|
|
22244
23161
|
const roomId = state.roomId;
|
|
@@ -22876,6 +23793,7 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
22876
23793
|
commands: slashCommands,
|
|
22877
23794
|
selected: slashSelected,
|
|
22878
23795
|
target: commandTarget?.name,
|
|
23796
|
+
leadName: dmWith === void 0 && commandTarget === void 0 ? lead?.name : void 0,
|
|
22879
23797
|
dmName: dmWith === void 0 ? void 0 : dmPeer?.name ?? room?.name,
|
|
22880
23798
|
disabled: (c) => commandTarget === void 0 && needsTarget(c),
|
|
22881
23799
|
grouped: slash === "",
|
|
@@ -22972,47 +23890,61 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
22972
23890
|
state.roomId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PendingAttachments, {
|
|
22973
23891
|
files: pendingFiles[state.roomId] ?? [],
|
|
22974
23892
|
removeLabel: t("removeAttachment"),
|
|
22975
|
-
|
|
22976
|
-
|
|
22977
|
-
|
|
22978
|
-
...previous,
|
|
22979
|
-
[roomId]: (previous[roomId] ?? []).filter((_, i) => i !== index)
|
|
22980
|
-
}));
|
|
22981
|
-
}
|
|
22982
|
-
}),
|
|
22983
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
22984
|
-
ref: inputRef,
|
|
22985
|
-
className: ChannelsPage_module_default.input,
|
|
22986
|
-
rows: 1,
|
|
22987
|
-
enterKeyHint: "send",
|
|
22988
|
-
value: draft,
|
|
22989
|
-
disabled: state.roomId === void 0 || sending[state.roomId] === true,
|
|
22990
|
-
placeholder,
|
|
22991
|
-
role: "combobox",
|
|
22992
|
-
"aria-expanded": popupOpen || slashOpen,
|
|
22993
|
-
"aria-controls": slashOpen ? "channel-slash-list" : "channel-mention-list",
|
|
22994
|
-
"aria-autocomplete": "list",
|
|
22995
|
-
"aria-activedescendant": popupOpen ? `channel-mention-${candidates[selected].id}` : slashOpen ? `channel-slash-${slashSelected}` : void 0,
|
|
22996
|
-
onChange: (event) => {
|
|
22997
|
-
const roomId = state.roomId;
|
|
22998
|
-
const value = event.target.value;
|
|
22999
|
-
if (roomId !== void 0) setDrafts((previous) => ({
|
|
23000
|
-
...previous,
|
|
23001
|
-
[roomId]: value
|
|
23002
|
-
}));
|
|
23003
|
-
setCapabilitiesDismissed(false);
|
|
23004
|
-
setHelpOpen(false);
|
|
23005
|
-
setCaret(event.target.selectionStart);
|
|
23006
|
-
setHighlight(0);
|
|
23007
|
-
setSlashHighlight(0);
|
|
23893
|
+
labels: {
|
|
23894
|
+
uploading: t("attachmentUploading"),
|
|
23895
|
+
queued: t("attachmentQueued")
|
|
23008
23896
|
},
|
|
23009
|
-
|
|
23010
|
-
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
|
|
23897
|
+
onRemove: (key) => removeFile(state.roomId, key)
|
|
23898
|
+
}),
|
|
23899
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
23900
|
+
className: composing ? `${ChannelsPage_module_default.inputSurface} ${ChannelsPage_module_default.inputComposing}` : ChannelsPage_module_default.inputSurface,
|
|
23901
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
23902
|
+
ref: inputRef,
|
|
23903
|
+
className: ChannelsPage_module_default.input,
|
|
23904
|
+
rows: 1,
|
|
23905
|
+
enterKeyHint: "send",
|
|
23906
|
+
value: draft,
|
|
23907
|
+
disabled: state.roomId === void 0 || sending[state.roomId] === true,
|
|
23908
|
+
placeholder,
|
|
23909
|
+
role: "combobox",
|
|
23910
|
+
"aria-expanded": popupOpen || slashOpen,
|
|
23911
|
+
"aria-controls": slashOpen ? "channel-slash-list" : "channel-mention-list",
|
|
23912
|
+
"aria-autocomplete": "list",
|
|
23913
|
+
"aria-activedescendant": popupOpen ? `channel-mention-${candidates[selected].id}` : slashOpen ? `channel-slash-${slashSelected}` : void 0,
|
|
23914
|
+
onChange: (event) => {
|
|
23915
|
+
const roomId = state.roomId;
|
|
23916
|
+
const value = event.target.value;
|
|
23917
|
+
if (roomId !== void 0) setDrafts((previous) => ({
|
|
23918
|
+
...previous,
|
|
23919
|
+
[roomId]: value
|
|
23920
|
+
}));
|
|
23921
|
+
setCapabilitiesDismissed(false);
|
|
23922
|
+
setHelpOpen(false);
|
|
23923
|
+
setCaret(event.target.selectionStart);
|
|
23924
|
+
setHighlight(0);
|
|
23925
|
+
setSlashHighlight(0);
|
|
23926
|
+
},
|
|
23927
|
+
onSelect: (event) => {
|
|
23928
|
+
setCaret(event.currentTarget.selectionStart);
|
|
23929
|
+
},
|
|
23930
|
+
onScroll: syncInputHighlights,
|
|
23931
|
+
onCompositionStart: () => setComposing(true),
|
|
23932
|
+
onCompositionEnd: () => setComposing(false),
|
|
23933
|
+
onKeyDown,
|
|
23934
|
+
onPaste: attach.onPaste,
|
|
23935
|
+
onDragOver: attach.onDragOver,
|
|
23936
|
+
onDrop: attach.onDrop
|
|
23937
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
23938
|
+
ref: inputHighlightsRef,
|
|
23939
|
+
className: ChannelsPage_module_default.inputHighlights,
|
|
23940
|
+
"data-composer-highlights": true,
|
|
23941
|
+
"aria-hidden": "true",
|
|
23942
|
+
children: [splitMentions(draft, visibleMembers).map((segment, index) => segment.member ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
23943
|
+
className: `${ChannelsPage_module_default.mentionBadge} ${ChannelsPage_module_default.inputMention}`,
|
|
23944
|
+
"data-mention": segment.member.id,
|
|
23945
|
+
children: segment.text
|
|
23946
|
+
}, index) : segment.text), "\n"]
|
|
23947
|
+
})]
|
|
23016
23948
|
}),
|
|
23017
23949
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
23018
23950
|
className: ChannelsPage_module_default.composerToolbar,
|
|
@@ -23279,6 +24211,34 @@ function ChannelsPage({ useChannels, selectRoom, send, reviewTask, dispatchTask,
|
|
|
23279
24211
|
]
|
|
23280
24212
|
});
|
|
23281
24213
|
}
|
|
24214
|
+
// src/client/RemoteChannelsPage.tsx
|
|
24215
|
+
const BELOW_BAR = { "--dsh-frame-top-clearance": "0px" };
|
|
24216
|
+
function RemoteChannelsPage(props) {
|
|
24217
|
+
const { remoteName, collabUrl, back, t } = props;
|
|
24218
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollabUrlContext.Provider, {
|
|
24219
|
+
value: collabUrl,
|
|
24220
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
24221
|
+
className: PanelPages_module_default.remote,
|
|
24222
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
24223
|
+
className: PanelPages_module_default.remoteBar,
|
|
24224
|
+
role: "status",
|
|
24225
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
24226
|
+
className: PanelPages_module_default.remoteName,
|
|
24227
|
+
children: t("remoteViewing", { name: remoteName })
|
|
24228
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
24229
|
+
type: "button",
|
|
24230
|
+
className: PanelPages_module_default.remoteBack,
|
|
24231
|
+
onClick: back,
|
|
24232
|
+
children: t("remoteBack")
|
|
24233
|
+
})]
|
|
24234
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
24235
|
+
className: PanelPages_module_default.remoteBody,
|
|
24236
|
+
style: BELOW_BAR,
|
|
24237
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelsPage, { ...props })
|
|
24238
|
+
})]
|
|
24239
|
+
})
|
|
24240
|
+
});
|
|
24241
|
+
}
|
|
23282
24242
|
// src/client/locales.ts
|
|
23283
24243
|
/** Channel panel dictionaries. */
|
|
23284
24244
|
/** Locale namespace owned by the channel panel. */
|
|
@@ -23351,6 +24311,8 @@ const zh = {
|
|
|
23351
24311
|
panelGalaxy: "Wiki 星河",
|
|
23352
24312
|
panelBothouse: "bothouse",
|
|
23353
24313
|
panelDevices: "其他电脑",
|
|
24314
|
+
remoteViewing: "正在用「{name}」的频道(那台电脑上的成员和消息)",
|
|
24315
|
+
remoteBack: "返回其他电脑",
|
|
23354
24316
|
channelSettings: "频道设置",
|
|
23355
24317
|
settingsBasic: "基本",
|
|
23356
24318
|
settingsDispatch: "派活",
|
|
@@ -23369,6 +24331,12 @@ const zh = {
|
|
|
23369
24331
|
channelName: "频道名称",
|
|
23370
24332
|
channelNameHint: "1–32 个字符,不能含空白。",
|
|
23371
24333
|
workspaceNameFixed: "工作区名称由系统分配,不能修改。",
|
|
24334
|
+
channelNameReserved: "「{name}」是工作区占用的名字,换个频道名吧。",
|
|
24335
|
+
channelNameTaken: "已经有频道叫「{name}」了,换个名字吧。",
|
|
24336
|
+
channelNameInvalid: "频道名要 1–32 个字符,不能含空白。",
|
|
24337
|
+
memberNameTaken: "已经有成员叫「{name}」了,换个名字吧。",
|
|
24338
|
+
memberNameInvalid: "成员名要 1–32 个字符,不能含空白、@ 或 <>(Claude Code 除外)。",
|
|
24339
|
+
actionFailed: "没成功:{message}",
|
|
23372
24340
|
slashTitle: "命令",
|
|
23373
24341
|
slashTitleLead: "发给 {name} 的命令",
|
|
23374
24342
|
slashTitleNoLead: "命令 · 先在频道设置里选主角色,才能用这些命令",
|
|
@@ -23534,6 +24502,10 @@ const zh = {
|
|
|
23534
24502
|
placeholder: "发消息到 #{room},@成员 派活(回车发送,Shift+回车换行)",
|
|
23535
24503
|
loading: "正在连接频道服务…",
|
|
23536
24504
|
unavailable: "频道服务未启用:在 collab.yml 中配置服务后,用 --patch collab.yml 启动 DSH。",
|
|
24505
|
+
locked: "频道服务没有启动:这台电脑上已经有另一个 DSH 在用同一份频道数据(进程 {pid},{started} 打开,数据目录 {dataDir})。直接用已经打开的那个就行;要换成本页这个 DSH,先把那个关掉,再把本页这个 DSH 关掉重开——只刷新页面不会把频道接上。",
|
|
24506
|
+
lockedUnnamed: "频道服务没有启动:这台电脑上已经有另一个 DSH 在用同一份频道数据(数据目录 {dataDir},进程 {pid})——它没留下是哪个进程的记录,通常是因为那个 DSH 是旧版本,或者刚打开还没写记录。别去改配置:先看谁占着这个文件:macOS 或 Linux 上用 lsof {lock},Windows 上在资源监视器的磁盘活动里搜这个文件名;找到那个 DSH 后关掉它,再关掉并重开本页这个 DSH,频道就能接上(只刷新页面不行)。",
|
|
24507
|
+
lockedFree: "频道服务还是没有启动:刚才占着频道数据的那个 DSH 已经关掉了,锁是空的;但本页的频道插件是在它占着的时候启动失败的,不会自己回来。把本页这个 DSH 关掉重开,频道就能接上。",
|
|
24508
|
+
lockedUnknown: "未知",
|
|
23537
24509
|
unauthorized: "还没有登录 DSH,请刷新页面。",
|
|
23538
24510
|
error: "频道服务出错:{message}",
|
|
23539
24511
|
empty: "这里还没有消息。@一个成员开始吧。",
|
|
@@ -23547,6 +24519,12 @@ const zh = {
|
|
|
23547
24519
|
attachButton: "添加图片或文件",
|
|
23548
24520
|
removeAttachment: "移除附件",
|
|
23549
24521
|
attachmentsTooLarge: "附件太大:一条消息合计不能超过 {limit}",
|
|
24522
|
+
messageTooLarge: "消息太长:一条消息不能超过 {limit}",
|
|
24523
|
+
attachmentTooLarge: "「{name}」{size},超过单个文件上限 {limit}",
|
|
24524
|
+
attachmentUploading: "上传中",
|
|
24525
|
+
attachmentQueued: "等待上传",
|
|
24526
|
+
uploadFailed: "「{name}」上传失败:{message}",
|
|
24527
|
+
uploadUnsupported: "这个页面不能上传文件,请刷新后再试",
|
|
23550
24528
|
mentionPick: "在输入框里 @{name}",
|
|
23551
24529
|
memberMenu: "{name} 的成员操作",
|
|
23552
24530
|
avatarMention: "@ TA",
|
|
@@ -23746,6 +24724,8 @@ const en = {
|
|
|
23746
24724
|
panelGalaxy: "Wiki galaxy",
|
|
23747
24725
|
panelBothouse: "bothouse",
|
|
23748
24726
|
panelDevices: "Other computers",
|
|
24727
|
+
remoteViewing: "Channels on “{name}” (members and messages of that computer)",
|
|
24728
|
+
remoteBack: "Back to other computers",
|
|
23749
24729
|
channelSettings: "Channel settings",
|
|
23750
24730
|
settingsBasic: "Basic",
|
|
23751
24731
|
settingsDispatch: "Dispatch",
|
|
@@ -23764,6 +24744,12 @@ const en = {
|
|
|
23764
24744
|
channelName: "Channel name",
|
|
23765
24745
|
channelNameHint: "1–32 characters, without whitespace.",
|
|
23766
24746
|
workspaceNameFixed: "Workspace names are assigned by the system and cannot be changed.",
|
|
24747
|
+
channelNameReserved: "\"{name}\" is a workspace name; pick another channel name.",
|
|
24748
|
+
channelNameTaken: "A channel named \"{name}\" already exists; pick another name.",
|
|
24749
|
+
channelNameInvalid: "A channel name is 1–32 characters and contains no whitespace.",
|
|
24750
|
+
memberNameTaken: "A member named \"{name}\" already exists; pick another name.",
|
|
24751
|
+
memberNameInvalid: "A member name is 1–32 characters, without whitespace, @ or <> (Claude Code excepted).",
|
|
24752
|
+
actionFailed: "That did not work: {message}",
|
|
23767
24753
|
slashTitle: "Commands",
|
|
23768
24754
|
slashTitleLead: "Commands to {name}",
|
|
23769
24755
|
slashTitleNoLead: "Commands · pick a primary role in channel settings to use these",
|
|
@@ -23884,6 +24870,10 @@ const en = {
|
|
|
23884
24870
|
placeholder: "Message #{room}; @ a member to put it to work (Enter sends, Shift+Enter for a new line)",
|
|
23885
24871
|
loading: "Connecting to the channel service…",
|
|
23886
24872
|
unavailable: "The channel service is off: configure it in collab.yml, then start DSH with --patch collab.yml.",
|
|
24873
|
+
locked: "The channel service did not start: another DSH on this machine is already using the same channel data (process {pid}, opened {started}, data directory {dataDir}). Keep using the DSH you already have open; to switch to this one, quit that one, then quit and reopen this DSH — reloading the page will not reconnect the channel.",
|
|
24874
|
+
lockedUnnamed: "The channel service did not start: another DSH on this machine is already using the same channel data (data directory {dataDir}, process {pid}) and left no record of which process it is — usually because that DSH is an older build, or has only just started. Do not touch the configuration: first see what holds the lock file — lsof {lock} on macOS or Linux, or search that file name in Resource Monitor on Windows; then quit that DSH, quit and reopen this one, and the channel reconnects (reloading the page will not).",
|
|
24875
|
+
lockedFree: "The channel service still did not start: the DSH that held the channel data has quit and the lock is free, but this page's channel plugin refused at startup and does not come back by itself. Quit and reopen this DSH to reconnect the channel.",
|
|
24876
|
+
lockedUnknown: "unknown",
|
|
23887
24877
|
unauthorized: "Not signed in to DSH; reload the page.",
|
|
23888
24878
|
error: "Channel service error: {message}",
|
|
23889
24879
|
empty: "No messages yet. @ a member to start.",
|
|
@@ -23897,6 +24887,12 @@ const en = {
|
|
|
23897
24887
|
attachButton: "Attach images or files",
|
|
23898
24888
|
removeAttachment: "Remove attachment",
|
|
23899
24889
|
attachmentsTooLarge: "Attachments too large: one message can carry at most {limit} in total",
|
|
24890
|
+
messageTooLarge: "Message too long: one message can hold at most {limit}",
|
|
24891
|
+
attachmentTooLarge: "\"{name}\" is {size}, over the per-file limit of {limit}",
|
|
24892
|
+
attachmentUploading: "Uploading",
|
|
24893
|
+
attachmentQueued: "Waiting to upload",
|
|
24894
|
+
uploadFailed: "\"{name}\" failed to upload: {message}",
|
|
24895
|
+
uploadUnsupported: "This page cannot upload files; reload and try again",
|
|
23900
24896
|
mentionPick: "Mention {name} in the message box",
|
|
23901
24897
|
memberMenu: "Member actions for {name}",
|
|
23902
24898
|
avatarMention: "Mention",
|
|
@@ -23985,6 +24981,10 @@ const en = {
|
|
|
23985
24981
|
};
|
|
23986
24982
|
// src/client/index.ts
|
|
23987
24983
|
const PANEL_ID = "channels";
|
|
24984
|
+
const REMOTE_PANEL_ID = "remote-channels";
|
|
24985
|
+
const DEVICES_PANEL_ID = "collab-devices";
|
|
24986
|
+
/** Global the 其他电脑 page (same origin, in an iframe) calls to open a link's channels here. */
|
|
24987
|
+
const OPEN_REMOTE = "__bothouseOpenRemoteChannels";
|
|
23988
24988
|
/** Required browser services. */
|
|
23989
24989
|
const inject = [
|
|
23990
24990
|
"slots",
|
|
@@ -24003,11 +25003,13 @@ function apply(ctx) {
|
|
|
24003
25003
|
const t = ctx.locale.bind(NS);
|
|
24004
25004
|
const channels = createChannelSource(browserTransport());
|
|
24005
25005
|
ctx.effect(() => channels.dispose, "client-ui-discord: channel stream");
|
|
24006
|
-
const injected = (openModelSettings) => (
|
|
25006
|
+
const injected = (openModelSettings) => injectedFor(channels, openModelSettings);
|
|
25007
|
+
const injectedFor = (channels, openModelSettings) => ({
|
|
24007
25008
|
openModelSettings,
|
|
24008
25009
|
hooks: { channels: channels.source },
|
|
24009
25010
|
selectRoom: channels.actions.selectRoom,
|
|
24010
25011
|
send: channels.actions.send,
|
|
25012
|
+
uploadAttachment: channels.actions.uploadAttachment,
|
|
24011
25013
|
reviewTask: channels.actions.reviewTask,
|
|
24012
25014
|
dispatchTask: channels.actions.dispatchTask,
|
|
24013
25015
|
openDm: channels.actions.openDm,
|
|
@@ -24068,6 +25070,47 @@ function apply(ctx) {
|
|
|
24068
25070
|
disposePanel?.();
|
|
24069
25071
|
};
|
|
24070
25072
|
});
|
|
25073
|
+
ctx.slots.inject("main", () => {
|
|
25074
|
+
let remote;
|
|
25075
|
+
const close = () => {
|
|
25076
|
+
remote?.dispose();
|
|
25077
|
+
remote = void 0;
|
|
25078
|
+
};
|
|
25079
|
+
const open = (link, name) => {
|
|
25080
|
+
const url = remoteCollabUrl(link);
|
|
25081
|
+
if (remote?.link !== link) {
|
|
25082
|
+
close();
|
|
25083
|
+
const source = createChannelSource(mappedTransport(browserTransport(), url));
|
|
25084
|
+
const unregister = ctx.slots.register({
|
|
25085
|
+
name: "main",
|
|
25086
|
+
key: REMOTE_PANEL_ID,
|
|
25087
|
+
locale: NS,
|
|
25088
|
+
inject: () => ({
|
|
25089
|
+
...injectedFor(source, () => false),
|
|
25090
|
+
remoteName: name,
|
|
25091
|
+
collabUrl: url,
|
|
25092
|
+
back: () => {
|
|
25093
|
+
ctx.layout.selectPanel(DEVICES_PANEL_ID);
|
|
25094
|
+
}
|
|
25095
|
+
})
|
|
25096
|
+
}, RemoteChannelsPage);
|
|
25097
|
+
remote = {
|
|
25098
|
+
link,
|
|
25099
|
+
dispose: () => {
|
|
25100
|
+
unregister();
|
|
25101
|
+
source.dispose();
|
|
25102
|
+
}
|
|
25103
|
+
};
|
|
25104
|
+
}
|
|
25105
|
+
ctx.layout.selectPanel(REMOTE_PANEL_ID);
|
|
25106
|
+
return true;
|
|
25107
|
+
};
|
|
25108
|
+
Reflect.set(globalThis, OPEN_REMOTE, open);
|
|
25109
|
+
return () => {
|
|
25110
|
+
if (Reflect.get(globalThis, "__bothouseOpenRemoteChannels") === open) Reflect.deleteProperty(globalThis, OPEN_REMOTE);
|
|
25111
|
+
close();
|
|
25112
|
+
};
|
|
25113
|
+
});
|
|
24071
25114
|
if (typeof ctx.inject === "function") ctx.inject(["theme"], (themed) => {
|
|
24072
25115
|
const theme = Reflect.get(themed, "theme");
|
|
24073
25116
|
if (theme === void 0) return;
|
|
@@ -24088,6 +25131,7 @@ function apply(ctx) {
|
|
|
24088
25131
|
label: () => t("panel")
|
|
24089
25132
|
}, ChannelsIcon));
|
|
24090
25133
|
}
|
|
25134
|
+
exports.OPEN_REMOTE = OPEN_REMOTE;
|
|
24091
25135
|
exports.apply = apply;
|
|
24092
25136
|
exports.inject = inject;
|
|
24093
25137
|
|