bothouse-dsh 0.1.42 → 0.1.43
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/client/client.js +177 -16
- package/collab/dsh-member.mjs +57 -4
- package/collab/handoff-rules.mjs +2 -0
- package/collab/hub.mjs +2 -2
- package/collab/store.mjs +4 -4
- package/collab/team-exit.mjs +8 -2
- package/package.json +1 -1
package/client/client.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// dsh-client-source-sha256:
|
|
2
|
-
// dsh-style-sha256:
|
|
1
|
+
// dsh-client-source-sha256: 79a9d7898c26fd8a4dbb904f053c9288ca8683a349abf407d1ba53a8ec300f85
|
|
2
|
+
// dsh-style-sha256: 8de64f320b70b31c6dba515e74187a98284317258d9cb57df3dc16f2eb345641
|
|
3
3
|
window.__ModuleLoader__.load({
|
|
4
4
|
id: "bothouse-dsh",
|
|
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");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_message:hover {\n background: var(--dsw-alias-interactive-bg-hover);\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 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 opacity: 1;\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 opacity: 0;\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 position: absolute;\n top: 4px;\n right: 16px;\n}\n\n.dshc_message:hover .dshc_replyButton, .dshc_message:focus-within .dshc_replyButton, .dshc_replyButton:focus-visible {\n opacity: 1;\n}\n\n@media (hover: none) {\n .dshc_replyButton {\n opacity: 1;\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 padding: 0 16px;\n font-size: 12px;\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_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 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_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";document.head.appendChild(s)})();
|
|
8
|
+
(function(){var s=document.createElement('style');s.setAttribute('data-dsh-plugin',"bothouse-dsh");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_message:hover {\n background: var(--dsw-alias-interactive-bg-hover);\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 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 opacity: 1;\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 opacity: 0;\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 position: absolute;\n top: 4px;\n right: 16px;\n}\n\n.dshc_message:hover .dshc_replyButton, .dshc_message:focus-within .dshc_replyButton, .dshc_replyButton:focus-visible {\n opacity: 1;\n}\n\n@media (hover: none) {\n .dshc_replyButton {\n opacity: 1;\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 padding: 0 16px;\n font-size: 12px;\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_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 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";document.head.appendChild(s)})();
|
|
9
9
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
10
10
|
// \0rolldown/runtime.js
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
@@ -16413,6 +16413,7 @@ var ChannelsPage_module_default = {
|
|
|
16413
16413
|
"activeBusy": "dshc_activeBusy",
|
|
16414
16414
|
"activeCount": "dshc_activeCount",
|
|
16415
16415
|
"addMemberButton": "dshc_addMemberButton",
|
|
16416
|
+
"attachmentActions": "dshc_attachmentActions",
|
|
16416
16417
|
"attachmentAudio": "dshc_attachmentAudio",
|
|
16417
16418
|
"attachmentFile": "dshc_attachmentFile",
|
|
16418
16419
|
"attachmentImage": "dshc_attachmentImage",
|
|
@@ -16475,6 +16476,12 @@ var ChannelsPage_module_default = {
|
|
|
16475
16476
|
"guide": "dshc_guide",
|
|
16476
16477
|
"hash": "dshc_hash",
|
|
16477
16478
|
"iconButton": "dshc_iconButton",
|
|
16479
|
+
"imageAttachment": "dshc_imageAttachment",
|
|
16480
|
+
"imagePreviewActions": "dshc_imagePreviewActions",
|
|
16481
|
+
"imagePreviewDialog": "dshc_imagePreviewDialog",
|
|
16482
|
+
"imagePreviewHeader": "dshc_imagePreviewHeader",
|
|
16483
|
+
"imagePreviewImage": "dshc_imagePreviewImage",
|
|
16484
|
+
"imagePreviewTrigger": "dshc_imagePreviewTrigger",
|
|
16478
16485
|
"input": "dshc_input",
|
|
16479
16486
|
"linkButton": "dshc_linkButton",
|
|
16480
16487
|
"log": "dshc_log",
|
|
@@ -16570,22 +16577,163 @@ var ChannelsPage_module_default = {
|
|
|
16570
16577
|
const isImage = (mime) => /^image\/(png|jpeg|gif|webp)$/.test(mime);
|
|
16571
16578
|
const isAudio = (mime) => mime.startsWith("audio/");
|
|
16572
16579
|
const size = (bytes) => bytes < 1024 ? `${bytes} B` : bytes < 1048576 ? `${(bytes / 1024).toFixed(1)} KB` : `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
16580
|
+
function ImageAttachment({ attachment: a, t }) {
|
|
16581
|
+
const [opened, setOpened] = (0, react.useState)(false);
|
|
16582
|
+
const trigger = (0, react.useRef)(null);
|
|
16583
|
+
const dialog = (0, react.useRef)(null);
|
|
16584
|
+
const closeButton = (0, react.useRef)(null);
|
|
16585
|
+
(0, react.useEffect)(() => {
|
|
16586
|
+
if (!opened) return;
|
|
16587
|
+
const origin = trigger.current;
|
|
16588
|
+
closeButton.current?.focus();
|
|
16589
|
+
const containFocus = (event) => {
|
|
16590
|
+
if (!dialog.current?.contains(event.target)) closeButton.current?.focus();
|
|
16591
|
+
};
|
|
16592
|
+
document.addEventListener("focusin", containFocus);
|
|
16593
|
+
return () => {
|
|
16594
|
+
document.removeEventListener("focusin", containFocus);
|
|
16595
|
+
origin?.focus();
|
|
16596
|
+
};
|
|
16597
|
+
}, [opened]);
|
|
16598
|
+
const [copyState, setCopyState] = (0, react.useState)("idle");
|
|
16599
|
+
(0, react.useEffect)(() => {
|
|
16600
|
+
if (copyState !== "copied") return;
|
|
16601
|
+
const timer = setTimeout(() => setCopyState("idle"), 2e3);
|
|
16602
|
+
return () => clearTimeout(timer);
|
|
16603
|
+
}, [copyState]);
|
|
16604
|
+
const copy = async () => {
|
|
16605
|
+
if (copyState === "copying") return;
|
|
16606
|
+
setCopyState("copying");
|
|
16607
|
+
try {
|
|
16608
|
+
if (typeof navigator.clipboard?.write !== "function" || typeof ClipboardItem === "undefined") {
|
|
16609
|
+
setCopyState("error");
|
|
16610
|
+
return;
|
|
16611
|
+
}
|
|
16612
|
+
const response = await fetch(attachmentUrl(a.id));
|
|
16613
|
+
if (!response.ok) throw new Error("Image download failed");
|
|
16614
|
+
const blob = await response.blob();
|
|
16615
|
+
await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
|
|
16616
|
+
setCopyState("copied");
|
|
16617
|
+
} catch {
|
|
16618
|
+
setCopyState("error");
|
|
16619
|
+
}
|
|
16620
|
+
};
|
|
16621
|
+
const copyLabel = t(copyState === "copied" ? "attachmentCopied" : copyState === "copying" ? "attachmentCopying" : "attachmentCopy");
|
|
16622
|
+
const actions = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
16623
|
+
className: copyState === "error" ? ChannelsPage_module_default.primaryButton : ChannelsPage_module_default.secondaryButton,
|
|
16624
|
+
href: attachmentUrl(a.id),
|
|
16625
|
+
download: a.name,
|
|
16626
|
+
target: "_blank",
|
|
16627
|
+
rel: "noreferrer",
|
|
16628
|
+
"aria-label": t("attachmentDownload"),
|
|
16629
|
+
children: t("attachmentDownload")
|
|
16630
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16631
|
+
type: "button",
|
|
16632
|
+
className: ChannelsPage_module_default.secondaryButton,
|
|
16633
|
+
onClick: () => {
|
|
16634
|
+
copy();
|
|
16635
|
+
},
|
|
16636
|
+
disabled: copyState === "copying",
|
|
16637
|
+
"aria-label": copyLabel,
|
|
16638
|
+
"aria-live": "polite",
|
|
16639
|
+
children: copyLabel
|
|
16640
|
+
})] });
|
|
16641
|
+
const error = copyState === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16642
|
+
className: ChannelsPage_module_default.sendError,
|
|
16643
|
+
role: "alert",
|
|
16644
|
+
children: t("attachmentCopyError")
|
|
16645
|
+
});
|
|
16646
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16647
|
+
className: ChannelsPage_module_default.imageAttachment,
|
|
16648
|
+
"data-copy-state": copyState,
|
|
16649
|
+
children: [
|
|
16650
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16651
|
+
ref: trigger,
|
|
16652
|
+
type: "button",
|
|
16653
|
+
className: ChannelsPage_module_default.imagePreviewTrigger,
|
|
16654
|
+
title: a.name,
|
|
16655
|
+
"aria-haspopup": "dialog",
|
|
16656
|
+
"aria-expanded": opened,
|
|
16657
|
+
onClick: () => setOpened(true),
|
|
16658
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
16659
|
+
className: ChannelsPage_module_default.attachmentImage,
|
|
16660
|
+
src: attachmentUrl(a.id),
|
|
16661
|
+
alt: a.name,
|
|
16662
|
+
loading: "lazy"
|
|
16663
|
+
})
|
|
16664
|
+
}),
|
|
16665
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16666
|
+
className: ChannelsPage_module_default.attachmentActions,
|
|
16667
|
+
children: actions
|
|
16668
|
+
}),
|
|
16669
|
+
!opened && error,
|
|
16670
|
+
opened && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16671
|
+
className: ChannelsPage_module_default.dialogBackdrop,
|
|
16672
|
+
onClick: (event) => {
|
|
16673
|
+
if (event.target === event.currentTarget) setOpened(false);
|
|
16674
|
+
},
|
|
16675
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
16676
|
+
ref: dialog,
|
|
16677
|
+
className: `${ChannelsPage_module_default.dialog} ${ChannelsPage_module_default.imagePreviewDialog}`,
|
|
16678
|
+
role: "dialog",
|
|
16679
|
+
"aria-modal": "true",
|
|
16680
|
+
"aria-label": a.name,
|
|
16681
|
+
tabIndex: -1,
|
|
16682
|
+
onKeyDown: (event) => {
|
|
16683
|
+
if (event.key === "Escape") {
|
|
16684
|
+
event.preventDefault();
|
|
16685
|
+
event.stopPropagation();
|
|
16686
|
+
setOpened(false);
|
|
16687
|
+
}
|
|
16688
|
+
if (event.key === "Tab") {
|
|
16689
|
+
const controls = Array.from(event.currentTarget.querySelectorAll("button:not(:disabled), a[href]"));
|
|
16690
|
+
const first = controls[0];
|
|
16691
|
+
const last = controls[controls.length - 1];
|
|
16692
|
+
if (event.shiftKey && (document.activeElement === first || document.activeElement === dialog.current)) {
|
|
16693
|
+
event.preventDefault();
|
|
16694
|
+
last?.focus();
|
|
16695
|
+
} else if (!event.shiftKey && (document.activeElement === last || document.activeElement === dialog.current)) {
|
|
16696
|
+
event.preventDefault();
|
|
16697
|
+
first?.focus();
|
|
16698
|
+
}
|
|
16699
|
+
}
|
|
16700
|
+
},
|
|
16701
|
+
children: [
|
|
16702
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16703
|
+
className: ChannelsPage_module_default.imagePreviewHeader,
|
|
16704
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: a.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16705
|
+
ref: closeButton,
|
|
16706
|
+
type: "button",
|
|
16707
|
+
className: ChannelsPage_module_default.secondaryButton,
|
|
16708
|
+
"aria-label": t("close"),
|
|
16709
|
+
onClick: () => setOpened(false),
|
|
16710
|
+
children: "×"
|
|
16711
|
+
})]
|
|
16712
|
+
}),
|
|
16713
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
16714
|
+
className: ChannelsPage_module_default.imagePreviewImage,
|
|
16715
|
+
src: attachmentUrl(a.id),
|
|
16716
|
+
alt: a.name
|
|
16717
|
+
}),
|
|
16718
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16719
|
+
className: ChannelsPage_module_default.imagePreviewActions,
|
|
16720
|
+
children: actions
|
|
16721
|
+
}),
|
|
16722
|
+
error
|
|
16723
|
+
]
|
|
16724
|
+
})
|
|
16725
|
+
}), document.body)
|
|
16726
|
+
]
|
|
16727
|
+
});
|
|
16728
|
+
}
|
|
16573
16729
|
/** Thumbnails for images, a download card for other files. */
|
|
16574
|
-
function AttachmentList({ attachments }) {
|
|
16730
|
+
function AttachmentList({ attachments, t }) {
|
|
16575
16731
|
if (attachments === void 0 || attachments.length === 0) return null;
|
|
16576
16732
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16577
16733
|
className: ChannelsPage_module_default.attachments,
|
|
16578
|
-
children: attachments.map((a) => isImage(a.mime) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
16579
|
-
|
|
16580
|
-
|
|
16581
|
-
rel: "noreferrer",
|
|
16582
|
-
title: a.name,
|
|
16583
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
16584
|
-
className: ChannelsPage_module_default.attachmentImage,
|
|
16585
|
-
src: attachmentUrl(a.id),
|
|
16586
|
-
alt: a.name,
|
|
16587
|
-
loading: "lazy"
|
|
16588
|
-
})
|
|
16734
|
+
children: attachments.map((a) => isImage(a.mime) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageAttachment, {
|
|
16735
|
+
attachment: a,
|
|
16736
|
+
t
|
|
16589
16737
|
}, a.id) : isAudio(a.mime) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16590
16738
|
className: ChannelsPage_module_default.attachmentAudio,
|
|
16591
16739
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
|
|
@@ -18744,7 +18892,10 @@ function MessageRow({ message, member, t, onConfigure, menuOpen, onAvatar, onClo
|
|
|
18744
18892
|
text: markdown,
|
|
18745
18893
|
labels
|
|
18746
18894
|
})
|
|
18747
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentList, {
|
|
18895
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentList, {
|
|
18896
|
+
attachments: message.attachments,
|
|
18897
|
+
t
|
|
18898
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
18748
18899
|
className: ChannelsPage_module_default.card,
|
|
18749
18900
|
"data-state": card.state,
|
|
18750
18901
|
children: [
|
|
@@ -19803,6 +19954,11 @@ const NS = "discord-channels";
|
|
|
19803
19954
|
const zh = {
|
|
19804
19955
|
copyCode: "复制代码",
|
|
19805
19956
|
copiedCode: "已复制",
|
|
19957
|
+
attachmentDownload: "下载",
|
|
19958
|
+
attachmentCopy: "复制图片",
|
|
19959
|
+
attachmentCopying: "复制中…",
|
|
19960
|
+
attachmentCopied: "已复制",
|
|
19961
|
+
attachmentCopyError: "复制失败,请下载图片后保存或复制。",
|
|
19806
19962
|
footnotes: "脚注",
|
|
19807
19963
|
panel: "频道",
|
|
19808
19964
|
channelSettings: "频道设置",
|
|
@@ -20008,6 +20164,11 @@ const en = {
|
|
|
20008
20164
|
dispatchMixed: "Mixed — choose models and members for each task",
|
|
20009
20165
|
copyCode: "Copy code",
|
|
20010
20166
|
copiedCode: "Copied",
|
|
20167
|
+
attachmentDownload: "Download",
|
|
20168
|
+
attachmentCopy: "Copy image",
|
|
20169
|
+
attachmentCopying: "Copying…",
|
|
20170
|
+
attachmentCopied: "Copied",
|
|
20171
|
+
attachmentCopyError: "Could not copy. Download the image to save or copy it.",
|
|
20011
20172
|
footnotes: "Footnotes",
|
|
20012
20173
|
taskTabLabel: "Filter tasks by status",
|
|
20013
20174
|
taskTabAll: "All",
|
package/collab/dsh-member.mjs
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// one that was consumed is settled from the Session log. Nothing is re-sent,
|
|
8
8
|
// so a prompt is executed at most once.
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
|
-
import { readFileSync } from 'node:fs';
|
|
10
|
+
import { readFileSync, realpathSync, statSync } from 'node:fs';
|
|
11
|
+
import { basename, isAbsolute, relative, sep } from 'node:path';
|
|
11
12
|
import { exitTarget } from './team-exit.mjs';
|
|
12
13
|
import { DEFAULT_DISPATCH_MODE, MAX_TEXT } from './store.mjs';
|
|
13
14
|
import { ChannelApprovals, DEFAULT_ACTIONS } from './approvals.mjs';
|
|
@@ -62,6 +63,54 @@ import { attachmentBlocks, namedAttachmentBlocks, nativeAttachmentBlocks, undeli
|
|
|
62
63
|
|
|
63
64
|
const CONTEXT_LIMIT = 50;
|
|
64
65
|
const CLOSE_TIMEOUT_MS = 5_000;
|
|
66
|
+
const MAX_LOCAL_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
67
|
+
const MAX_LOCAL_IMAGES = 10;
|
|
68
|
+
const IMAGE_MIME = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp' };
|
|
69
|
+
// Markdown targets may contain spaces; bare paths end at whitespace or punctuation.
|
|
70
|
+
const LOCAL_IMAGE = /!\[([^\]\n]*)\]\((\/[^)\n]+?\.(?:png|jpe?g|gif|webp))\)|(?<![\w/])\/[^\s<>"'`()[\]{},。;,;]+?\.(?:png|jpe?g|gif|webp)(?![\w/])/gi;
|
|
71
|
+
|
|
72
|
+
/** Only files physically inside this room's working directory may leave the machine. */
|
|
73
|
+
export function localImageReply(text, workspacePath, files, logger) {
|
|
74
|
+
if (!files || !workspacePath || !LOCAL_IMAGE.test(text)) return { text, attachments: [] };
|
|
75
|
+
LOCAL_IMAGE.lastIndex = 0;
|
|
76
|
+
let root;
|
|
77
|
+
try { root = realpathSync(workspacePath); } catch { return { text, attachments: [] }; }
|
|
78
|
+
const attachments = [];
|
|
79
|
+
const result = text.replace(LOCAL_IMAGE, (match, alt, markdownPath) => {
|
|
80
|
+
const path = markdownPath ?? match;
|
|
81
|
+
if (!isAbsolute(path)) return match;
|
|
82
|
+
let target;
|
|
83
|
+
try { target = realpathSync(path); } catch (error) {
|
|
84
|
+
logger?.warn('collab: local image skipped (%s): %s', path, error.message);
|
|
85
|
+
return match;
|
|
86
|
+
}
|
|
87
|
+
const within = relative(root, target);
|
|
88
|
+
if (within === '..' || within.startsWith(`..${sep}`) || isAbsolute(within)) return match;
|
|
89
|
+
if (attachments.length >= MAX_LOCAL_IMAGES) {
|
|
90
|
+
logger?.warn('collab: local image skipped (%s): attachment limit %d', path, MAX_LOCAL_IMAGES);
|
|
91
|
+
return match;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const size = statSync(target).size;
|
|
95
|
+
if (size > MAX_LOCAL_IMAGE_BYTES) {
|
|
96
|
+
logger?.warn('collab: local image skipped (%s): exceeds %d bytes', path, MAX_LOCAL_IMAGE_BYTES);
|
|
97
|
+
return match;
|
|
98
|
+
}
|
|
99
|
+
const data = readFileSync(target);
|
|
100
|
+
if (data.length > MAX_LOCAL_IMAGE_BYTES) {
|
|
101
|
+
logger?.warn('collab: local image skipped (%s): exceeds %d bytes', path, MAX_LOCAL_IMAGE_BYTES);
|
|
102
|
+
return match;
|
|
103
|
+
}
|
|
104
|
+
const mime = IMAGE_MIME[path.slice(path.lastIndexOf('.') + 1).toLowerCase()];
|
|
105
|
+
attachments.push(...files.save([{ name: basename(path), mime, data: data.toString('base64') }]));
|
|
106
|
+
return alt ?? '';
|
|
107
|
+
} catch (error) {
|
|
108
|
+
logger?.warn('collab: local image skipped (%s): %s', path, error.message);
|
|
109
|
+
return match;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
return { text: result, attachments };
|
|
113
|
+
}
|
|
65
114
|
// Agent-to-agent relay cap: after this many agent messages in a row without a
|
|
66
115
|
// human message, agent mentions stop starting DSH turns (prevents loops with bots).
|
|
67
116
|
export const DEFAULT_MAX_RELAY = 6;
|
|
@@ -659,8 +708,9 @@ export class DshMembers {
|
|
|
659
708
|
if (!text) { this.fail(job.id, `${member.name} 这一轮没有给出回复(${member.cli} 没有输出文字)`); return; }
|
|
660
709
|
latest = '';
|
|
661
710
|
const reply = this.returnToLead(job, member, text);
|
|
662
|
-
const
|
|
663
|
-
const
|
|
711
|
+
const converted = localImageReply(reply, cwd, this.rt.files, this.rt.logger);
|
|
712
|
+
const committed = draft === undefined ? undefined : this.hub.commit(draft.id, 'role', member.id, converted.text, undefined, converted.attachments);
|
|
713
|
+
const message = committed ?? this.hub.post(job.room_id, { authorKind: 'role', author: member.id, name: member.name, clientKey, text: converted.text, attachments: converted.attachments }).message;
|
|
664
714
|
done = true;
|
|
665
715
|
this.store.finishJob(job.id, 'done', message.id);
|
|
666
716
|
} catch (error) {
|
|
@@ -745,9 +795,12 @@ export class DshMembers {
|
|
|
745
795
|
}
|
|
746
796
|
const kind = reason?.kind;
|
|
747
797
|
const complete = kind === undefined || kind === 'completed' || kind === 'max-tokens';
|
|
798
|
+
const reply = this.returnToLead(job, member, complete ? text : `${text}\n\n(这一轮没有正常结束:${describeReason(reason)})`);
|
|
799
|
+
const cwd = this.store.workspacePath(job.room_id, this.rt.workspacePath);
|
|
800
|
+
const converted = localImageReply(reply, cwd, this.rt.files, this.rt.logger);
|
|
748
801
|
const { message } = this.hub.post(job.room_id, {
|
|
749
802
|
authorKind: 'role', author: member.id, name: member.name, clientKey: `dsh-job-${job.id}`,
|
|
750
|
-
text:
|
|
803
|
+
text: converted.text, attachments: converted.attachments,
|
|
751
804
|
});
|
|
752
805
|
this.store.finishJob(job.id, 'done', message.id);
|
|
753
806
|
return complete;
|
package/collab/handoff-rules.mjs
CHANGED
|
@@ -63,6 +63,8 @@ function run(c, lines, lead, key = 'msg') {
|
|
|
63
63
|
case 'kinds': {
|
|
64
64
|
const found = captures(sections(lines, c.section, lead), new RegExp(c.line_re), lead);
|
|
65
65
|
const got = new Set(found);
|
|
66
|
+
const mx = c.max, n = mx ? found.filter(k => k === mx.kind).length : 0;
|
|
67
|
+
if (mx && n > mx.n) return mx.msg.replace('{n}', String(n));
|
|
66
68
|
const miss = c.need.filter(k => !got.has(k));
|
|
67
69
|
return miss.length ? missing(c, miss) : null;
|
|
68
70
|
}
|
package/collab/hub.mjs
CHANGED
|
@@ -162,10 +162,10 @@ export class Hub {
|
|
|
162
162
|
return r;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
commit(id, authorKind, author, text, fromTurn) {
|
|
165
|
+
commit(id, authorKind, author, text, fromTurn, attachments) {
|
|
166
166
|
const after = [];
|
|
167
167
|
const r = this.store.transaction(() => {
|
|
168
|
-
const c = this.store.commit(id, authorKind, author, text, fromTurn);
|
|
168
|
+
const c = this.store.commit(id, authorKind, author, text, fromTurn, attachments);
|
|
169
169
|
if (c?.first) for (const plan of this.planners) { const next = plan(c.message); if (next) after.push(next); }
|
|
170
170
|
return c;
|
|
171
171
|
});
|
package/collab/store.mjs
CHANGED
|
@@ -92,7 +92,7 @@ export class Store {
|
|
|
92
92
|
byKey: this.db.prepare('SELECT * FROM messages WHERE client_key = ?'),
|
|
93
93
|
byId: this.db.prepare('SELECT * FROM messages WHERE id = ? AND deleted = 0'),
|
|
94
94
|
edit: this.db.prepare('UPDATE messages SET text = ?, card = ?, changed_at = ? WHERE id = ? AND author_kind = ? AND author = ? AND deleted = 0 RETURNING *'),
|
|
95
|
-
commit: this.db.prepare('UPDATE messages SET text = ?, from_turn = ?, draft = 0, changed_at = ? WHERE id = ? AND author_kind = ? AND author = ? AND deleted = 0 AND card IS NULL AND draft = 1 RETURNING *'),
|
|
95
|
+
commit: this.db.prepare('UPDATE messages SET text = ?, from_turn = ?, attachments = ?, draft = 0, changed_at = ? WHERE id = ? AND author_kind = ? AND author = ? AND deleted = 0 AND card IS NULL AND draft = 1 RETURNING *'),
|
|
96
96
|
editUser: this.db.prepare(`UPDATE messages SET text = ?, edited_at = ? WHERE id = ? AND author_kind = 'user' AND author = ?
|
|
97
97
|
AND deleted = 0 AND draft = 0 AND card IS NULL RETURNING *`),
|
|
98
98
|
del: this.db.prepare('UPDATE messages SET deleted = 1 WHERE id = ? AND author_kind = ? AND author = ? AND deleted = 0 RETURNING *'),
|
|
@@ -664,9 +664,9 @@ export class Store {
|
|
|
664
664
|
* into a reply (the caller fans out once). A row already committed gets the
|
|
665
665
|
* text and first = false. undefined if it is not that author's live text row.
|
|
666
666
|
*/
|
|
667
|
-
commit(id, authorKind, author, text, fromTurn, now = Date.now() / 1000) {
|
|
668
|
-
if (!text.trim() || text.length > MAX_TEXT) return undefined;
|
|
669
|
-
const row = this.q.commit.get(text, fromTurn == null ? null : (fromTurn ? 1 : 0), now, id, authorKind, author);
|
|
667
|
+
commit(id, authorKind, author, text, fromTurn, attachments, now = Date.now() / 1000) {
|
|
668
|
+
if ((!text.trim() && !attachments?.length) || text.length > MAX_TEXT) return undefined;
|
|
669
|
+
const row = this.q.commit.get(text, fromTurn == null ? null : (fromTurn ? 1 : 0), attachments?.length ? JSON.stringify(attachments) : null, now, id, authorKind, author);
|
|
670
670
|
if (row !== undefined) return { message: row, first: true };
|
|
671
671
|
const old = this.message(id);
|
|
672
672
|
if (old === undefined || old.card != null) return undefined;
|
package/collab/team-exit.mjs
CHANGED
|
@@ -22,6 +22,7 @@ const JARGON_TABLE = new RegExp(RULES.jargon.table_re);
|
|
|
22
22
|
const BACKGROUND = new RegExp(RULES.summary.background_re);
|
|
23
23
|
const TODO = new RegExp(RULES.summary.todo_re);
|
|
24
24
|
const DETAIL = new RegExp(RULES.summary.detail_re);
|
|
25
|
+
const ADDED = new RegExp(RULES.summary.added_re);
|
|
25
26
|
// Consecutive bounces that still wake the asking member; later ones are only posted.
|
|
26
27
|
const BOUNCE_WAKE_LIMIT = RULES.bounce.wake_limit;
|
|
27
28
|
|
|
@@ -56,7 +57,7 @@ export function exitHeadline(body) {
|
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* { summary, detail } of a marked body (Go: exitSplit): after the first line's #id, up to and
|
|
59
|
-
* including the 「要你做的:」 line is posted for the user; the lines after it go under that post.
|
|
60
|
+
* including the 「我加的:」 line (else the 「要你做的:」 line) is posted for the user; the lines after it go under that post.
|
|
60
61
|
*/
|
|
61
62
|
export function exitSplit(body) {
|
|
62
63
|
let lines = String(body ?? '').replaceAll('\r\n', '\n').split('\n');
|
|
@@ -64,6 +65,10 @@ export function exitSplit(body) {
|
|
|
64
65
|
if (m) lines[0] = m[2];
|
|
65
66
|
while (lines.length && !lines[0].trim()) lines.shift();
|
|
66
67
|
let end = lines.findIndex(l => TODO.test(l));
|
|
68
|
+
if (end >= 0) {
|
|
69
|
+
const added = lines.findIndex((l, i) => i > end && ADDED.test(l));
|
|
70
|
+
if (added >= 0) end = added;
|
|
71
|
+
}
|
|
67
72
|
if (end < 0) {
|
|
68
73
|
const blank = lines.findIndex(l => !l.trim());
|
|
69
74
|
end = blank < 0 ? lines.length - 1 : blank - 1;
|
|
@@ -75,7 +80,7 @@ export function exitSplit(body) {
|
|
|
75
80
|
return { summary, detail: rest.join('\n').trim() };
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
/** What the part for the user lacks: background, what was done, what the user is to do (Go: summaryProblems). */
|
|
83
|
+
/** What the part for the user lacks: background, what was done, what the user is to do, what the agent added (Go: summaryProblems). */
|
|
79
84
|
export function summaryProblems(summary) {
|
|
80
85
|
const lines = String(summary).split('\n');
|
|
81
86
|
const problems = [];
|
|
@@ -90,6 +95,7 @@ export function summaryProblems(summary) {
|
|
|
90
95
|
}
|
|
91
96
|
if (!done) problems.push(RULES.summary.missing_done);
|
|
92
97
|
if (todo < 0) problems.push(RULES.summary.missing_todo);
|
|
98
|
+
if (!lines.some(l => ADDED.test(l))) problems.push(RULES.summary.missing_added);
|
|
93
99
|
return problems;
|
|
94
100
|
}
|
|
95
101
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bothouse-dsh",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.43",
|
|
4
4
|
"description": "BotHouse channels for DeepSeek Harness: Discord-style rooms where DSH models and local CLIs (Claude Code, Codex, Gemini) are members",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./collab/plugin.mjs",
|